111 lines
3.5 KiB
Rust
111 lines
3.5 KiB
Rust
#![warn(clippy::all)]
|
|
|
|
use rumqttc::{AsyncClient, Event, MqttOptions, Packet, QoS};
|
|
use std::error::Error;
|
|
|
|
use crate::hermes::{Intent, Response};
|
|
|
|
pub mod hermes;
|
|
pub mod weather;
|
|
|
|
// Main context
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct Context {
|
|
pub(crate) openweather_api_key: String,
|
|
pub(crate) mqtt_host: String,
|
|
pub(crate) mqtt_port: u16,
|
|
}
|
|
|
|
impl Context {
|
|
pub fn new() -> Result<Self, Box<dyn Error + 'static>> {
|
|
use std::env;
|
|
|
|
Ok(Context {
|
|
openweather_api_key: env::var("OPENWEATHER_API_KEY")?,
|
|
mqtt_host: env::var("MQTT_HOST")?,
|
|
mqtt_port: env::var("MQTT_PORT")?.parse()?,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Handling
|
|
|
|
pub async fn handle(context: &mut Context, intent: Intent) -> Option<Response> {
|
|
println!(
|
|
">>> Detected intent: {} (confidence: {:.2})",
|
|
intent.name, intent.confidence
|
|
);
|
|
|
|
match intent.name.as_ref() {
|
|
"test" => {
|
|
let choice = intent.slots.get("choice").unwrap();
|
|
Some(
|
|
Response::new(intent.session_id)
|
|
.with_text(format!("De test is geslaagd: {}", choice.value)),
|
|
)
|
|
}
|
|
"WeatherForecastLocal" => Some(match weather::get_forecast(&context).await {
|
|
Ok(description) => Response::new(intent.session_id).with_text(description),
|
|
Err(e) => {
|
|
println!("!!! Encountered weather API error: {}", e);
|
|
Response::new(intent.session_id).with_text("Ik kon het weerbericht niet ophalen!")
|
|
}
|
|
}),
|
|
_ => {
|
|
println!("??? Ignoring unsupported intent: {:?}", intent.name);
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn event_loop(mut context: Context) -> Result<(), Box<dyn Error + 'static>> {
|
|
let mut mqttoptions = MqttOptions::new(
|
|
"rhasspy-skill-server",
|
|
&context.mqtt_host,
|
|
context.mqtt_port,
|
|
);
|
|
mqttoptions.set_keep_alive(10);
|
|
|
|
let (client, mut eventloop) = AsyncClient::new(mqttoptions, 10);
|
|
client.subscribe("hermes/intent/#", QoS::AtMostOnce).await?;
|
|
|
|
loop {
|
|
let event = eventloop.poll().await?;
|
|
if let Event::Incoming(Packet::Publish(published)) = event {
|
|
let result: Result<Intent, _> = serde_json::from_slice(published.payload.as_ref());
|
|
let response = match result {
|
|
Ok(intent) => handle(&mut context, intent).await,
|
|
Err(e) => {
|
|
println!(
|
|
"!!! Could not parse intent payload: {} at\n{:?}",
|
|
e, published.payload
|
|
);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
if let Some(response) = response {
|
|
let text = response.text.clone();
|
|
let result = client
|
|
.publish(
|
|
"hermes/dialogueManager/endSession",
|
|
QoS::AtMostOnce,
|
|
false,
|
|
response,
|
|
)
|
|
.await;
|
|
match result {
|
|
Ok(_) => {
|
|
if let Some(text) = text {
|
|
println!("<<< Reacted with a response: {}!", text);
|
|
} else {
|
|
println!("<<< Reacted with a silent response");
|
|
}
|
|
}
|
|
Err(e) => println!("!!! Could not publish intent payload: {}", e),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|