#![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> { 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 { 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> { 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 = 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), } } } } }