This commit is contained in:
Torrie Fischer
2024-12-19 14:48:14 +01:00
parent 514c9defd6
commit 6de1704da2
15 changed files with 372 additions and 251 deletions

View File

@@ -1,167 +1,164 @@
use std::{collections::LinkedList, thread::JoinHandle};
use core::str;
use std::{borrow::BorrowMut, collections::{HashMap, LinkedList}, thread::JoinHandle};
use esp_idf_svc::mqtt::client::{EspMqttClient, MqttClientConfiguration};
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}, prop_id, properties::{PropertyID, Variant}, render::props::Output as OutputNS, scenes::props::Scenes as SceneNS, task::Task};
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;
use paste::paste;
struct HADevice {
prefix: String,
unique_id: String
device_id: String,
scenes: Vec<String>
}
impl HADevice {
fn new(component: &str, chip_id: u64, name: &str) -> Self {
fn new(device_id: String, scenes: Vec<String>) -> Self {
HADevice {
// eg: homeassistant/sensor/0BADCOFFEE/fps
unique_id: format!("{:X}-{}", chip_id, name),
prefix: format!("homeassistant/{}/renderbug-rs/{:X}-{}", component, chip_id, name)
device_id,
scenes
}
}
fn topic(&self, name: &str) -> String {
format!("{}/{}", self.prefix, name)
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<String, Value> = 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!({
"~": self.prefix,
"stat_t": "~/state",
"unique_id": self.unique_id,
"stat_t": self.state_topic(),
"cmd_t": self.command_topic(),
"dev": {
"name": "Renderbug-rs ESP32",
"name": format!("Renderbug-rs ESP32 {}", self.device_id),
"mdl": "Renderbug-rs ESP32",
"sw": "",
"mf": "Phong Robotics",
"ids": [self.unique_id]
}
})
}
}
struct HAScene {
prefix: String,
unique_id: String
}
impl HAScene {
fn new(chip_id: u64, name: &str) -> Self {
HAScene {
// eg: homeassistant/sensor/0BADCOFFEE/fps
unique_id: format!("{:X}-{}", chip_id, name),
prefix: format!("homeassistant/scene/renderbug-rs/{:X}-{}", chip_id, name)
}
}
fn topic(&self, name: &str) -> String {
format!("{}/{}", self.prefix, name)
}
fn registration(&self) -> Value {
json!({
"~": self.prefix,
"stat_t": "~/state",
"cmd_t": "~/command",
"unique_id": self.unique_id,
"payload_on": "on",
"dev": {
"name": "Renderbug-rs ESP32",
"mdl": "Renderbug-rs ESP32",
"sw": "",
"mf": "Phong Robotics",
"ids": [self.unique_id]
}
"identifiers": [self.device_id]
},
"origin": {
"name": "renderbug-rs"
},
"cmps": components
})
}
}
pub struct MqttTask {
client: Option<EspMqttClient<'static>>,
conn_thread: Option<JoinHandle<()>>,
fps_sensor: Option<HADevice>
ha_device: Option<HADevice>
}
impl MqttTask {
pub fn new() -> Self {
MqttTask {
conn_thread: None,
client: None,
fps_sensor: None
ha_device: None
}
}
fn start_mqtt(&mut self, bus: &EventBus) {
fn start_mqtt(&mut self, env: &mut Environment) {
log::info!("Starting MQTT");
let chip_id: u64 = bus.properties().get(BoardNS::ChipID).unwrap().into();
let chip_id: u64 = env.get_property(BoardNS::ChipID).unwrap();
self.fps_sensor = Some(HADevice::new("sensor", chip_id, "output-fps"));
let (client, mut conn) = EspMqttClient::new(
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()
}
).unwrap();
log::info!("Connected!");
},
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<String, Variant> = 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
});
self.conn_thread = Some(std::thread::Builder::new()
.stack_size(6000)
.spawn(move || {
conn.next().unwrap();
}).unwrap());
self.client = Some(client);
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);
}
let chip_id = bus.properties().get(BoardNS::ChipID).unwrap().into();
self.fps_sensor = Some(HADevice::new("sensor", chip_id, "fps"));
fn on_ready(&mut self, bus: &mut EventBus) {
let chip_id: u64 = bus.get_property(BoardNS::ChipID).unwrap();
let scenes: Vec<String> = 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) {
(prop_id!(SystemNS::NetworkOnline), Variant::Boolean(true)) => {
(SystemNS::NetworkOnline, Variant::Boolean(true)) => {
log::info!("Registering with MQTT");
let chip_id = bus.properties().get(BoardNS::ChipID).unwrap().into();
self.start_mqtt(bus);
if let Some(ref mut client) = self.client {
if let Some(ref sensor) = self.fps_sensor {
client.enqueue(
&sensor.topic("config"),
esp_idf_svc::mqtt::client::QoS::AtLeastOnce,
false,
sensor.registration().to_string().as_bytes()
).unwrap();
}
let scenes: Vec<Variant> = bus.properties().get(SceneNS::All).unwrap().into();
for scene in scenes.iter() {
let scene_name: String = scene.clone().into();
let scene_device = HAScene::new(chip_id, scene_name.as_str());
client.enqueue(
&scene_device.topic("config"),
esp_idf_svc::mqtt::client::QoS::AtLeastOnce,
false,
scene_device.registration().to_string().as_bytes()
).unwrap();
}
log::info!("MQTT should be online!");
bus.set_property(props::MQTT::Online, true);
}
},
(prop_id!(OutputNS::FPS), Variant::UInt(fps)) => {
(props::MQTT::Online, Variant::Boolean(true)) => {
if let Some(ref mut client) = self.client {
if let Some(ref sensor) = self.fps_sensor {
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,
@@ -170,7 +167,7 @@ impl Task for MqttTask {
).unwrap();
}
}
},
},*/
_ => ()
}
}

View File

@@ -1,6 +1,6 @@
use esp_idf_svc::{eventloop::{EspSubscription, EspSystemEventLoop, System}, hal::modem::Modem, netif::IpEvent, nvs::{EspNvsPartition, NvsDefault}, sntp::{EspSntp, SyncStatus}, wifi::{AuthMethod, ClientConfiguration, Configuration, EspWifi, WifiEvent}};
use crate::{events::{EventBus, System as SystemNS}, properties::Variant, task::Task, time::Periodically};
use crate::{events::{EventBus, System as SystemNS}, properties::Variant, task::{Environment, Task}, time::Periodically};
use std::fmt::Debug;
@@ -83,22 +83,22 @@ impl Task for WifiTask {
self.connect();
}
fn on_tick(&mut self, bus: &mut EventBus) {
fn on_tick(&mut self, env: &mut Environment) {
if self.connection_check.tick() {
if bus.properties().get(SystemNS::NetworkOnline).unwrap() == Variant::Boolean(true) {
if env.get_property(SystemNS::NetworkOnline).unwrap() {
match self.ntp.get_sync_status() {
SyncStatus::Completed => bus.set_property(SystemNS::TimeSync, true),
_ => bus.set_property(SystemNS::TimeSync, false)
SyncStatus::Completed => env.set_property(SystemNS::TimeSync, true),
_ => env.set_property(SystemNS::TimeSync, false)
}
}
}
}
fn stop(&mut self, bus: &mut EventBus) {
fn stop(&mut self, env: &mut Environment) {
log::info!("Stopping wifi");
self.wifi_sub.take().unwrap();
self.ip_sub.take().unwrap();
self.disconnect();
bus.set_property(SystemNS::NetworkOnline, false);
env.set_property(SystemNS::NetworkOnline, false);
}
}