use core::str; use std::{borrow::BorrowMut, collections::{HashMap, LinkedList}, thread::JoinHandle}; use esp_idf_svc::mqtt::client::{EspMqttClient, EventPayload, MqttClientConfiguration}; use log::Log; use serde_json::{json, Value}; use crate::{events::{Event, EventBus, System as SystemNS}, properties::{NamespaceKey, PropertyID, PropertyKey, Variant}, render::props::Output as OutputNS, scenes::props::Scenes as SceneNS, task::{Environment, Task}}; use crate::platform::props::Board as BoardNS; struct HADevice { device_id: String, scenes: Vec } impl HADevice { fn new(device_id: String, scenes: Vec) -> Self { HADevice { device_id, scenes } } fn topic(&self, key: &str) -> String { format!("renderbug/{}/{}", self.device_id, key) } fn command_topic(&self) -> String { self.topic("command") } fn state_topic(&self) -> String { self.topic("state") } fn device_config_topic(&self) -> String { format!("homeassistant/device/renderbug-rs-{}/config", self.device_id) } fn registration(&self) -> Value { let components: HashMap = self.scenes.iter().map(|f| { let scene_id = format!("renderbug_{}_scene_{}", self.device_id, f); (scene_id.clone(), json!({ "p": "scene", "unique_id": scene_id, "payload_on": json!({"namespace": SceneNS::Namespace, "key": SceneNS::Current.key, "value": f}).to_string(), "name": f })) }).collect(); json!({ "stat_t": self.state_topic(), "cmd_t": self.command_topic(), "dev": { "name": format!("Renderbug-rs ESP32 {}", self.device_id), "mdl": "Renderbug-rs ESP32", "mf": "Phong Robotics", "identifiers": [self.device_id] }, "origin": { "name": "renderbug-rs" }, "cmps": components }) } } pub struct MqttTask { client: Option>, ha_device: Option } impl MqttTask { pub fn new() -> Self { MqttTask { client: None, ha_device: None } } fn start_mqtt(&mut self, env: &mut Environment) { log::info!("Starting MQTT"); let chip_id: u64 = env.get_property(BoardNS::ChipID).unwrap(); let mut client_bus = env.bus.clone(); self.client = Some(EspMqttClient::new_cb( "mqtt://10.0.0.2:1883", &MqttClientConfiguration { client_id: Some(&format!("{:X}", chip_id)), ..Default::default() }, move |evt| { match evt.payload() { EventPayload::Received { id, topic, data, details } => { log::info!("got event id={} topic={:?} data={:?} details={:?}", id, topic, str::from_utf8(data), details); let props: HashMap = serde_json::from_slice(data).unwrap(); let ns: String = props.get("namespace").unwrap().to_owned().into(); let key: String = props.get("key").unwrap().to_owned().into(); let value: Variant = props.get("value").unwrap().clone(); let all = client_bus.properties().all_props(); let prop_id = all.iter().find(|f| { f.key.0 == key && f.namespace.0 == ns }); client_bus.set_property(SystemNS::Idle, false); match prop_id { None => log::warn!("Could not find property {} in {}!", key, ns), Some(id) => client_bus.set_property(id.clone(), value), } }, EventPayload::Connected(_) => { log::info!("MQTT is online!"); client_bus.set_property(props::MQTT::Online, true); }, EventPayload::Disconnected => { log::info!("MQTT is offline!"); client_bus.set_property(props::MQTT::Online, false); }, _ => () } } ).unwrap()); log::info!("Connected!"); } } impl Task for MqttTask { fn start(&mut self, bus: &mut EventBus) { bus.set_property(props::MQTT::Online, false); } fn on_ready(&mut self, bus: &mut EventBus) { let chip_id: u64 = bus.get_property(BoardNS::ChipID).unwrap(); let scenes: Vec = bus.get_property(SceneNS::All).unwrap(); log::info!("Setting up scenes: {:?}", scenes); self.ha_device = Some(HADevice::new(format!("{:X}", chip_id), scenes)); } fn on_property_change(&mut self, key: PropertyID, value: &Variant, bus: &mut EventBus) { match (key, value) { (SystemNS::NetworkOnline, Variant::Boolean(true)) => { log::info!("Registering with MQTT"); self.start_mqtt(bus); }, (props::MQTT::Online, Variant::Boolean(true)) => { if let Some(ref mut client) = self.client { if let Some(ref device) = self.ha_device { client.enqueue( &device.device_config_topic(), esp_idf_svc::mqtt::client::QoS::AtLeastOnce, false, device.registration().to_string().as_bytes() ).unwrap(); log::info!("Subscribing to {}", device.command_topic()); client.subscribe(&device.command_topic(), esp_idf_svc::mqtt::client::QoS::AtLeastOnce).unwrap(); } } } /*(prop_id!(OutputNS::FPS), Variant::UInt(fps)) => { if let Some(ref mut client) = self.client { if let Some(ref sensor) = self.ha_device { client.enqueue( &sensor.topic("state"), esp_idf_svc::mqtt::client::QoS::AtLeastOnce, false, json!(fps).to_string().as_bytes() ).unwrap(); } } },*/ _ => () } } } pub mod props { use crate::property_namespace; property_namespace!( MQTT, Online => true ); }