rhasspy-skill-server/src/lib.rs

112 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),
}
}
}
}
}