diff --git a/automation_macro/src/lua_device.rs b/automation_macro/src/lua_device.rs index 6d9ce59..886d5a7 100644 --- a/automation_macro/src/lua_device.rs +++ b/automation_macro/src/lua_device.rs @@ -33,10 +33,11 @@ pub fn impl_lua_device_macro(ast: &DeriveInput) -> TokenStream { } impl mlua::UserData for #name { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_function("new", |lua, config: mlua::Value| { + methods.add_async_function("new", |lua, config: mlua::Value| async { let config: #config = mlua::FromLua::from_lua(config, lua)?; - let config: Box = Box::new(config); - Ok(config) + let device = #name::create(config).await.map_err(mlua::ExternalError::into_lua_err)?; + + Ok(crate::device_manager::WrappedDevice::new(Box::new(device))) }); } } diff --git a/config.lua b/config.lua index 8b991e8..39e0c13 100644 --- a/config.lua +++ b/config.lua @@ -13,160 +13,140 @@ local function mqtt_automation(topic) return "automation/" .. topic end -automation.device_manager:create( - "debug_bridge", - DebugBridge.new({ - topic = mqtt_automation("debug"), - client = automation.mqtt_client, - }) -) +automation.device_manager:add(Ntfy.new({ + topic = automation.util.get_env("NTFY_TOPIC"), + event_channel = automation.event_channel, +})) + +automation.device_manager:add(Presence.new({ + topic = "automation_dev/presence/+/#", + event_channel = automation.event_channel, +})) + +automation.device_manager:add(DebugBridge.new({ + identifier = "debug_bridge", + topic = mqtt_automation("debug"), + client = automation.mqtt_client, +})) local hue_ip = "10.0.0.146" local hue_token = automation.util.get_env("HUE_TOKEN") -automation.device_manager:create( - "hue_bridge", - HueBridge.new({ - ip = hue_ip, - login = hue_token, - flags = { - presence = 41, - darkness = 43, - }, - }) -) +automation.device_manager:add(HueBridge.new({ + identifier = "hue_bridge", + ip = hue_ip, + login = hue_token, + flags = { + presence = 41, + darkness = 43, + }, +})) -automation.device_manager:create( - "living_light_sensor", - LightSensor.new({ - topic = mqtt_z2m("living/light"), - min = 22000, - max = 23500, - event_channel = automation.event_channel, - }) -) +automation.device_manager:add(LightSensor.new({ + identifier = "living_light_sensor", + topic = mqtt_z2m("living/light"), + min = 22000, + max = 23500, + event_channel = automation.event_channel, +})) -automation.device_manager:create( - "living_zeus", - WakeOnLAN.new({ - name = "Zeus", - room = "Living Room", - topic = mqtt_automation("appliance/living_room/zeus"), - mac_address = "30:9c:23:60:9c:13", - broadcast_ip = "10.0.0.255", - }) -) +automation.device_manager:add(WakeOnLAN.new({ + name = "Zeus", + room = "Living Room", + topic = mqtt_automation("appliance/living_room/zeus"), + mac_address = "30:9c:23:60:9c:13", + broadcast_ip = "10.0.0.255", +})) -local living_mixer = automation.device_manager:create("living_mixer", KasaOutlet.new({ ip = "10.0.0.49" })) -local living_speakers = automation.device_manager:create("living_speakers", KasaOutlet.new({ ip = "10.0.0.182" })) +local living_mixer = KasaOutlet.new({ identifier = "living_mixer", ip = "10.0.0.49" }) +automation.device_manager:add(living_mixer) +local living_speakers = KasaOutlet.new({ identifier = "living_speakers", ip = "10.0.0.182" }) +automation.device_manager:add(living_speakers) -automation.device_manager:create( - "living_audio", - AudioSetup.new({ - topic = mqtt_z2m("living/remote"), - mixer = living_mixer, - speakers = living_speakers, - }) -) +automation.device_manager:add(AudioSetup.new({ + identifier = "living_audio", + topic = mqtt_z2m("living/remote"), + mixer = living_mixer, + speakers = living_speakers, +})) -automation.device_manager:create( - "kitchen_kettle", - IkeaOutlet.new({ - outlet_type = "Kettle", - name = "Kettle", - room = "Kitchen", - topic = mqtt_z2m("kitchen/kettle"), - client = automation.mqtt_client, - timeout = debug and 5 or 300, - remotes = { - { topic = mqtt_z2m("bedroom/remote") }, - { topic = mqtt_z2m("kitchen/remote") }, - }, - }) -) +automation.device_manager:add(IkeaOutlet.new({ + outlet_type = "Kettle", + name = "Kettle", + room = "Kitchen", + topic = mqtt_z2m("kitchen/kettle"), + client = automation.mqtt_client, + timeout = debug and 5 or 300, + remotes = { + { topic = mqtt_z2m("bedroom/remote") }, + { topic = mqtt_z2m("kitchen/remote") }, + }, +})) -automation.device_manager:create( - "batchroom_light", - IkeaOutlet.new({ - outlet_type = "Light", - name = "Light", - room = "Bathroom", - topic = mqtt_z2m("batchroom/light"), - client = automation.mqtt_client, - timeout = debug and 60 or 45 * 60, - }) -) +automation.device_manager:add(IkeaOutlet.new({ + outlet_type = "Light", + name = "Light", + room = "Bathroom", + topic = mqtt_z2m("batchroom/light"), + client = automation.mqtt_client, + timeout = debug and 60 or 45 * 60, +})) -automation.device_manager:create( - "bathroom_washer", - Washer.new({ - topic = mqtt_z2m("batchroom/washer"), - threshold = 1, - event_channel = automation.event_channel, - }) -) +automation.device_manager:add(Washer.new({ + identifier = "bathroom_washer", + topic = mqtt_z2m("batchroom/washer"), + threshold = 1, + event_channel = automation.event_channel, +})) -automation.device_manager:create( - "workbench_charger", - IkeaOutlet.new({ - outlet_type = "Charger", - name = "Charger", - room = "Workbench", - topic = mqtt_z2m("workbench/charger"), - client = automation.mqtt_client, - timeout = debug and 5 or 20 * 3600, - }) -) +automation.device_manager:add(IkeaOutlet.new({ + outlet_type = "Charger", + name = "Charger", + room = "Workbench", + topic = mqtt_z2m("workbench/charger"), + client = automation.mqtt_client, + timeout = debug and 5 or 20 * 3600, +})) -automation.device_manager:create( - "workbench_outlet", - IkeaOutlet.new({ - name = "Outlet", - room = "Workbench", - topic = mqtt_z2m("workbench/outlet"), - client = automation.mqtt_client, - }) -) +automation.device_manager:add(IkeaOutlet.new({ + name = "Outlet", + room = "Workbench", + topic = mqtt_z2m("workbench/outlet"), + client = automation.mqtt_client, +})) -local hallway_lights = automation.device_manager:create( - "hallway_lights", - HueGroup.new({ - ip = hue_ip, - login = hue_token, - group_id = 81, - scene_id = "3qWKxGVadXFFG4o", - timer_id = 1, - remotes = { - { topic = mqtt_z2m("hallway/remote") }, - }, - }) -) +local hallway_lights = automation.device_manager:add(HueGroup.new({ + identifier = "hallway_lights", + ip = hue_ip, + login = hue_token, + group_id = 81, + scene_id = "3qWKxGVadXFFG4o", + timer_id = 1, + remotes = { + { topic = mqtt_z2m("hallway/remote") }, + }, +})) -automation.device_manager:create( - "hallway_frontdoor", - ContactSensor.new({ - topic = mqtt_z2m("hallway/frontdoor"), - client = automation.mqtt_client, - presence = { - topic = mqtt_automation("presence/contact/frontdoor"), - timeout = debug and 10 or 15 * 60, - }, - trigger = { - devices = { hallway_lights }, - timeout = debug and 10 or 2 * 60, - }, - }) -) +automation.device_manager:add(ContactSensor.new({ + identifier = "hallway_frontdoor", + topic = mqtt_z2m("hallway/frontdoor"), + client = automation.mqtt_client, + presence = { + topic = mqtt_automation("presence/contact/frontdoor"), + timeout = debug and 10 or 15 * 60, + }, + trigger = { + devices = { hallway_lights }, + timeout = debug and 10 or 2 * 60, + }, +})) -local bedroom_air_filter = automation.device_manager:create( - "bedroom_air_filter", - AirFilter.new({ - name = "Air Filter", - room = "Bedroom", - topic = "pico/filter/bedroom", - client = automation.mqtt_client, - }) -) +local bedroom_air_filter = automation.device_manager:add(AirFilter.new({ + name = "Air Filter", + room = "Bedroom", + topic = "pico/filter/bedroom", + client = automation.mqtt_client, +})) -- TODO: Use the wrapped device bedroom_air_filter instead of the string automation.device_manager:add_schedule({ diff --git a/config/config.yml b/config/config.yml index 43e6466..28ec0f0 100644 --- a/config/config.yml +++ b/config/config.yml @@ -7,9 +7,3 @@ mqtt: client_name: "automation_rs" username: "mqtt" password: "${MQTT_PASSWORD}" - -ntfy: - topic: "${NTFY_TOPIC}" - -presence: - topic: "automation/presence/+/#" diff --git a/config/zeus.dev.yml b/config/zeus.dev.yml index 9ba2789..10860f2 100644 --- a/config/zeus.dev.yml +++ b/config/zeus.dev.yml @@ -8,9 +8,3 @@ mqtt: username: "mqtt" password: "${MQTT_PASSWORD}" tls: true - -ntfy: - topic: "${NTFY_TOPIC}" - -presence: - topic: "automation_dev/presence/+/#" diff --git a/google-home/src/device.rs b/google-home/src/device.rs index 597e0bb..f40ee39 100644 --- a/google-home/src/device.rs +++ b/google-home/src/device.rs @@ -14,7 +14,7 @@ pub trait GoogleHomeDevice: { fn get_device_type(&self) -> Type; fn get_device_name(&self) -> Name; - fn get_id(&self) -> &str; + fn get_id(&self) -> String; fn is_online(&self) -> bool; // Default values that can optionally be overriden @@ -31,7 +31,7 @@ pub trait GoogleHomeDevice: async fn sync(&self) -> response::sync::Device { let name = self.get_device_name(); let mut device = - response::sync::Device::new(self.get_id(), &name.name, self.get_device_type()); + response::sync::Device::new(&self.get_id(), &name.name, self.get_device_type()); device.name = name; device.will_report_state = self.will_report_state(); diff --git a/src/config.rs b/src/config.rs index 3d9d580..06b1187 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,7 +8,6 @@ use serde::{Deserialize, Deserializer}; use tracing::debug; use crate::auth::OpenIDConfig; -use crate::devices::PresenceConfig; use crate::error::{ConfigParseError, MissingEnv}; #[derive(Debug, Deserialize)] @@ -18,8 +17,6 @@ pub struct Config { pub mqtt: MqttOptions, #[serde(default)] pub fullfillment: FullfillmentConfig, - pub ntfy: Option, - pub presence: PresenceConfig, } #[derive(Debug, Clone, Deserialize)] @@ -85,23 +82,22 @@ fn default_fullfillment_port() -> u16 { 7878 } -#[derive(Debug, Deserialize)] -pub struct NtfyConfig { - #[serde(default = "default_ntfy_url")] - pub url: String, - pub topic: String, -} - -fn default_ntfy_url() -> String { - "https://ntfy.sh".into() -} - #[derive(Debug, Clone, Deserialize)] pub struct InfoConfig { pub name: String, pub room: Option, } +impl InfoConfig { + pub fn identifier(&self) -> String { + (if let Some(room) = &self.room { + room.to_ascii_lowercase().replace(' ', "_") + "_" + } else { + String::new() + }) + &self.name.to_ascii_lowercase().replace(' ', "_") + } +} + #[derive(Debug, Clone, Deserialize)] pub struct MqttDeviceConfig { pub topic: String, diff --git a/src/device_manager.rs b/src/device_manager.rs index 04139d4..f3df545 100644 --- a/src/device_manager.rs +++ b/src/device_manager.rs @@ -2,8 +2,6 @@ use std::collections::HashMap; use std::ops::{Deref, DerefMut}; use std::sync::Arc; -use async_trait::async_trait; -use enum_dispatch::enum_dispatch; use futures::future::join_all; use google_home::traits::OnOff; use mlua::{FromLua, LuaSerdeExt}; @@ -13,22 +11,14 @@ use tokio_cron_scheduler::{Job, JobScheduler}; use tracing::{debug, error, instrument, trace}; use crate::devices::Device; -use crate::error::DeviceConfigError; use crate::event::{Event, EventChannel, OnDarkness, OnMqtt, OnNotification, OnPresence}; use crate::schedule::{Action, Schedule}; -#[async_trait] -#[enum_dispatch] -pub trait DeviceConfig { - async fn create(&self, identifier: &str) -> Result, DeviceConfigError>; -} -impl mlua::UserData for Box {} - #[derive(Debug, FromLua, Clone)] pub struct WrappedDevice(Arc>>); impl WrappedDevice { - fn new(device: Box) -> Self { + pub fn new(device: Box) -> Self { Self(Arc::new(RwLock::new(device))) } } @@ -132,13 +122,14 @@ impl DeviceManager { sched.start().await.unwrap(); } - pub async fn add(&self, device: Box) -> WrappedDevice { - let id = device.get_id().into(); + pub async fn add(&self, device: &WrappedDevice) { + let id = device.read().await.get_id().to_owned(); debug!(id, "Adding device"); { // If the device listens to mqtt, subscribe to the topics + let device = device.read().await; let device: Option<&dyn OnMqtt> = device.as_ref().cast(); if let Some(device) = device { for topic in device.topics() { @@ -152,12 +143,7 @@ impl DeviceManager { } } - // Wrap the device - let device = WrappedDevice::new(device); - self.devices.write().await.insert(id, device.0.clone()); - - device } pub fn event_channel(&self) -> EventChannel { @@ -196,6 +182,7 @@ impl DeviceManager { if subscribed { trace!(id, "Handling"); device.on_mqtt(message).await; + trace!(id, "Done"); } } } @@ -211,6 +198,7 @@ impl DeviceManager { if let Some(device) = device { trace!(id, "Handling"); device.on_darkness(dark).await; + trace!(id, "Done"); } }); @@ -224,6 +212,7 @@ impl DeviceManager { if let Some(device) = device { trace!(id, "Handling"); device.on_presence(presence).await; + trace!(id, "Done"); } }); @@ -239,6 +228,7 @@ impl DeviceManager { if let Some(device) = device { trace!(id, "Handling"); device.on_notification(notification).await; + trace!(id, "Done"); } } }); @@ -251,20 +241,11 @@ impl DeviceManager { impl mlua::UserData for DeviceManager { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_async_method( - "create", - |_lua, this, (identifier, config): (String, mlua::Value)| async move { - // TODO: Handle the error here properly - let config: Box = config.as_userdata().unwrap().take()?; + methods.add_async_method("add", |_lua, this, device: WrappedDevice| async move { + this.add(&device).await; - let device = config - .create(&identifier) - .await - .map_err(mlua::ExternalError::into_lua_err)?; - - Ok(this.add(device).await) - }, - ); + Ok(()) + }); methods.add_async_method("add_schedule", |lua, this, schedule| async { let schedule = lua.from_value(schedule)?; diff --git a/src/devices/air_filter.rs b/src/devices/air_filter.rs index 168c287..ac2346c 100644 --- a/src/devices/air_filter.rs +++ b/src/devices/air_filter.rs @@ -6,10 +6,9 @@ use google_home::traits::{AvailableSpeeds, FanSpeed, HumiditySetting, OnOff, Spe use google_home::types::Type; use google_home::GoogleHomeDevice; use rumqttc::Publish; -use tracing::{debug, error, warn}; +use tracing::{debug, error, trace, warn}; use crate::config::{InfoConfig, MqttDeviceConfig}; -use crate::device_manager::DeviceConfig; use crate::devices::Device; use crate::error::DeviceConfigError; use crate::event::OnMqtt; @@ -26,25 +25,8 @@ pub struct AirFilterConfig { client: WrappedAsyncClient, } -#[async_trait] -impl DeviceConfig for AirFilterConfig { - async fn create(&self, identifier: &str) -> Result, DeviceConfigError> { - let device = AirFilter { - identifier: identifier.into(), - config: self.clone(), - last_known_state: AirFilterState { - state: AirFilterFanState::Off, - humidity: 0.0, - }, - }; - - Ok(Box::new(device)) - } -} - #[derive(Debug, LuaDevice)] pub struct AirFilter { - identifier: String, #[config] config: AirFilterConfig, @@ -71,9 +53,22 @@ impl AirFilter { } } +impl AirFilter { + async fn create(config: AirFilterConfig) -> Result { + trace!(id = config.info.identifier(), "Setting up AirFilter"); + Ok(Self { + config, + last_known_state: AirFilterState { + state: AirFilterFanState::Off, + humidity: 0.0, + }, + }) + } +} + impl Device for AirFilter { - fn get_id(&self) -> &str { - &self.identifier + fn get_id(&self) -> String { + self.config.info.identifier() } } @@ -87,7 +82,7 @@ impl OnMqtt for AirFilter { let state = match AirFilterState::try_from(message) { Ok(state) => state, Err(err) => { - error!(id = self.identifier, "Failed to parse message: {err}"); + error!(id = Device::get_id(self), "Failed to parse message: {err}"); return; } }; @@ -96,7 +91,7 @@ impl OnMqtt for AirFilter { return; } - debug!(id = self.identifier, "Updating state to {state:?}"); + debug!(id = Device::get_id(self), "Updating state to {state:?}"); self.last_known_state = state; } @@ -111,7 +106,7 @@ impl GoogleHomeDevice for AirFilter { Name::new(&self.config.info.name) } - fn get_id(&self) -> &str { + fn get_id(&self) -> String { Device::get_id(self) } diff --git a/src/devices/audio_setup.rs b/src/devices/audio_setup.rs index 5111f79..15625ce 100644 --- a/src/devices/audio_setup.rs +++ b/src/devices/audio_setup.rs @@ -5,13 +5,14 @@ use tracing::{debug, error, trace, warn}; use super::Device; use crate::config::MqttDeviceConfig; -use crate::device_manager::{DeviceConfig, WrappedDevice}; +use crate::device_manager::WrappedDevice; use crate::error::DeviceConfigError; use crate::event::{OnMqtt, OnPresence}; use crate::messages::{RemoteAction, RemoteMessage}; #[derive(Debug, Clone, LuaDeviceConfig)] pub struct AudioSetupConfig { + identifier: String, #[device_config(flatten)] mqtt: MqttDeviceConfig, #[device_config(from_lua)] @@ -20,43 +21,37 @@ pub struct AudioSetupConfig { speakers: WrappedDevice, } -#[async_trait] -impl DeviceConfig for AudioSetupConfig { - async fn create(&self, identifier: &str) -> Result, DeviceConfigError> { - trace!(id = identifier, "Setting up AudioSetup"); - - let mixer = self.mixer.read().await; - let mixer_id = mixer.get_id().to_owned(); - if (mixer.as_ref().cast() as Option<&dyn OnOff>).is_none() { - return Err(DeviceConfigError::MissingTrait(mixer_id, "OnOff".into())); - } - - let speakers = self.speakers.read().await; - let speakers_id = speakers.get_id().to_owned(); - if (speakers.as_ref().cast() as Option<&dyn OnOff>).is_none() { - return Err(DeviceConfigError::MissingTrait(speakers_id, "OnOff".into())); - } - - let device = AudioSetup { - identifier: identifier.into(), - config: self.clone(), - }; - - Ok(Box::new(device)) - } -} - -// TODO: We need a better way to store the children devices #[derive(Debug, LuaDevice)] pub struct AudioSetup { - identifier: String, #[config] config: AudioSetupConfig, } +impl AudioSetup { + async fn create(config: AudioSetupConfig) -> Result { + trace!(id = config.identifier, "Setting up AudioSetup"); + + { + let mixer = config.mixer.read().await; + let mixer_id = mixer.get_id().to_owned(); + if (mixer.as_ref().cast() as Option<&dyn OnOff>).is_none() { + return Err(DeviceConfigError::MissingTrait(mixer_id, "OnOff".into())); + } + + let speakers = config.speakers.read().await; + let speakers_id = speakers.get_id().to_owned(); + if (speakers.as_ref().cast() as Option<&dyn OnOff>).is_none() { + return Err(DeviceConfigError::MissingTrait(speakers_id, "OnOff".into())); + } + } + + Ok(AudioSetup { config }) + } +} + impl Device for AudioSetup { - fn get_id(&self) -> &str { - &self.identifier + fn get_id(&self) -> String { + self.config.identifier.clone() } } @@ -70,7 +65,10 @@ impl OnMqtt for AudioSetup { let action = match RemoteMessage::try_from(message) { Ok(message) => message.action(), Err(err) => { - error!(id = self.identifier, "Failed to parse message: {err}"); + error!( + id = self.config.identifier, + "Failed to parse message: {err}" + ); return; } }; @@ -119,7 +117,7 @@ impl OnPresence for AudioSetup { ) { // Turn off the audio setup when we leave the house if !presence { - debug!(id = self.identifier, "Turning devices off"); + debug!(id = self.config.identifier, "Turning devices off"); speakers.set_on(false).await.unwrap(); mixer.set_on(false).await.unwrap(); } diff --git a/src/devices/contact_sensor.rs b/src/devices/contact_sensor.rs index 1a0f698..18c7a67 100644 --- a/src/devices/contact_sensor.rs +++ b/src/devices/contact_sensor.rs @@ -9,7 +9,7 @@ use tracing::{debug, error, trace, warn}; use super::Device; use crate::config::MqttDeviceConfig; -use crate::device_manager::{DeviceConfig, WrappedDevice}; +use crate::device_manager::WrappedDevice; use crate::devices::DEFAULT_PRESENCE; use crate::error::DeviceConfigError; use crate::event::{OnMqtt, OnPresence}; @@ -51,6 +51,7 @@ pub struct TriggerConfig { #[derive(Debug, Clone, LuaDeviceConfig)] pub struct ContactSensorConfig { + identifier: String, #[device_config(flatten)] mqtt: MqttDeviceConfig, #[device_config(from_lua)] @@ -61,13 +62,22 @@ pub struct ContactSensorConfig { client: WrappedAsyncClient, } -#[async_trait] -impl DeviceConfig for ContactSensorConfig { - async fn create(&self, identifier: &str) -> Result, DeviceConfigError> { - trace!(id = identifier, "Setting up ContactSensor"); +#[derive(Debug, LuaDevice)] +pub struct ContactSensor { + #[config] + config: ContactSensorConfig, + + overall_presence: bool, + is_closed: bool, + handle: Option>, +} + +impl ContactSensor { + async fn create(config: ContactSensorConfig) -> Result { + trace!(id = config.identifier, "Setting up ContactSensor"); // Make sure the devices implement the required traits - if let Some(trigger) = &self.trigger { + if let Some(trigger) = &config.trigger { for (device, _) in &trigger.devices { { let device = device.read().await; @@ -85,32 +95,18 @@ impl DeviceConfig for ContactSensorConfig { } } - let device = ContactSensor { - identifier: identifier.into(), - config: self.clone(), + Ok(Self { + config: config.clone(), overall_presence: DEFAULT_PRESENCE, is_closed: true, handle: None, - }; - - Ok(Box::new(device)) + }) } } -#[derive(Debug, LuaDevice)] -pub struct ContactSensor { - identifier: String, - #[config] - config: ContactSensorConfig, - - overall_presence: bool, - is_closed: bool, - handle: Option>, -} - impl Device for ContactSensor { - fn get_id(&self) -> &str { - &self.identifier + fn get_id(&self) -> String { + self.config.identifier.clone() } } @@ -131,7 +127,10 @@ impl OnMqtt for ContactSensor { let is_closed = match ContactMessage::try_from(message) { Ok(state) => state.is_closed(), Err(err) => { - error!(id = self.identifier, "Failed to parse message: {err}"); + error!( + id = self.config.identifier, + "Failed to parse message: {err}" + ); return; } }; @@ -140,7 +139,7 @@ impl OnMqtt for ContactSensor { return; } - debug!(id = self.identifier, "Updating state to {is_closed}"); + debug!(id = self.config.identifier, "Updating state to {is_closed}"); self.is_closed = is_closed; if let Some(trigger) = &mut self.config.trigger { @@ -210,7 +209,7 @@ impl OnMqtt for ContactSensor { } else { // Once the door is closed again we start a timeout for removing the presence let client = self.config.client.clone(); - let id = self.identifier.clone(); + let id = self.config.identifier.clone(); let timeout = presence.timeout; let topic = presence.mqtt.topic.clone(); self.handle = Some(tokio::spawn(async move { diff --git a/src/devices/debug_bridge.rs b/src/devices/debug_bridge.rs index 2b22da6..ab9c355 100644 --- a/src/devices/debug_bridge.rs +++ b/src/devices/debug_bridge.rs @@ -1,9 +1,8 @@ use async_trait::async_trait; use automation_macro::{LuaDevice, LuaDeviceConfig}; -use tracing::warn; +use tracing::{trace, warn}; use crate::config::MqttDeviceConfig; -use crate::device_manager::DeviceConfig; use crate::devices::Device; use crate::error::DeviceConfigError; use crate::event::{OnDarkness, OnPresence}; @@ -12,34 +11,29 @@ use crate::mqtt::WrappedAsyncClient; #[derive(Debug, LuaDeviceConfig, Clone)] pub struct DebugBridgeConfig { + identifier: String, #[device_config(flatten)] pub mqtt: MqttDeviceConfig, #[device_config(from_lua)] client: WrappedAsyncClient, } -#[async_trait] -impl DeviceConfig for DebugBridgeConfig { - async fn create(&self, identifier: &str) -> Result, DeviceConfigError> { - let device = DebugBridge { - identifier: identifier.into(), - config: self.clone(), - }; - - Ok(Box::new(device)) - } -} - #[derive(Debug, LuaDevice)] pub struct DebugBridge { - identifier: String, #[config] config: DebugBridgeConfig, } +impl DebugBridge { + async fn create(config: DebugBridgeConfig) -> Result { + trace!(id = config.identifier, "Setting up DebugBridge"); + Ok(Self { config }) + } +} + impl Device for DebugBridge { - fn get_id(&self) -> &str { - &self.identifier + fn get_id(&self) -> String { + self.config.identifier.clone() } } diff --git a/src/devices/hue_bridge.rs b/src/devices/hue_bridge.rs index c2b2a0f..0d03a5e 100644 --- a/src/devices/hue_bridge.rs +++ b/src/devices/hue_bridge.rs @@ -5,7 +5,6 @@ use automation_macro::{LuaDevice, LuaDeviceConfig}; use serde::{Deserialize, Serialize}; use tracing::{error, trace, warn}; -use crate::device_manager::DeviceConfig; use crate::devices::Device; use crate::error::DeviceConfigError; use crate::event::{OnDarkness, OnPresence}; @@ -24,27 +23,15 @@ pub struct FlagIDs { #[derive(Debug, LuaDeviceConfig, Clone)] pub struct HueBridgeConfig { + pub identifier: String, #[device_config(rename("ip"), with(|ip| SocketAddr::new(ip, 80)))] pub addr: SocketAddr, pub login: String, pub flags: FlagIDs, } -#[async_trait] -impl DeviceConfig for HueBridgeConfig { - async fn create(&self, identifier: &str) -> Result, DeviceConfigError> { - let device = HueBridge { - identifier: identifier.into(), - config: self.clone(), - }; - - Ok(Box::new(device)) - } -} - #[derive(Debug, LuaDevice)] pub struct HueBridge { - identifier: String, #[config] config: HueBridgeConfig, } @@ -55,6 +42,11 @@ struct FlagMessage { } impl HueBridge { + async fn create(config: HueBridgeConfig) -> Result { + trace!(id = config.identifier, "Setting up HueBridge"); + Ok(Self { config }) + } + pub async fn set_flag(&self, flag: Flag, value: bool) { let flag_id = match flag { Flag::Presence => self.config.flags.presence, @@ -88,8 +80,8 @@ impl HueBridge { } impl Device for HueBridge { - fn get_id(&self) -> &str { - &self.identifier + fn get_id(&self) -> String { + self.config.identifier.clone() } } diff --git a/src/devices/hue_light.rs b/src/devices/hue_light.rs index 8bd60f5..47649ba 100644 --- a/src/devices/hue_light.rs +++ b/src/devices/hue_light.rs @@ -7,11 +7,10 @@ use automation_macro::{LuaDevice, LuaDeviceConfig}; use google_home::errors::ErrorCode; use google_home::traits::OnOff; use rumqttc::Publish; -use tracing::{debug, error, warn}; +use tracing::{debug, error, trace, warn}; use super::Device; use crate::config::MqttDeviceConfig; -use crate::device_manager::DeviceConfig; use crate::error::DeviceConfigError; use crate::event::OnMqtt; use crate::messages::{RemoteAction, RemoteMessage}; @@ -19,6 +18,7 @@ use crate::traits::Timeout; #[derive(Debug, Clone, LuaDeviceConfig)] pub struct HueGroupConfig { + identifier: String, #[device_config(rename("ip"), with(|ip| SocketAddr::new(ip, 80)))] pub addr: SocketAddr, pub login: String, @@ -29,27 +29,19 @@ pub struct HueGroupConfig { pub remotes: Vec, } -#[async_trait] -impl DeviceConfig for HueGroupConfig { - async fn create(&self, identifier: &str) -> Result, DeviceConfigError> { - let device = HueGroup { - identifier: identifier.into(), - config: self.clone(), - }; - - Ok(Box::new(device)) - } -} - #[derive(Debug, LuaDevice)] pub struct HueGroup { - identifier: String, #[config] config: HueGroupConfig, } // Couple of helper function to get the correct urls impl HueGroup { + async fn create(config: HueGroupConfig) -> Result { + trace!(id = config.identifier, "Setting up AudioSetup"); + Ok(Self { config }) + } + fn url_base(&self) -> String { format!("http://{}/api/{}", self.config.addr, self.config.login) } @@ -68,8 +60,8 @@ impl HueGroup { } impl Device for HueGroup { - fn get_id(&self) -> &str { - &self.identifier + fn get_id(&self) -> String { + self.config.identifier.clone() } } @@ -87,7 +79,10 @@ impl OnMqtt for HueGroup { let action = match RemoteMessage::try_from(message) { Ok(message) => message.action(), Err(err) => { - error!(id = self.identifier, "Failed to parse message: {err}"); + error!( + id = self.config.identifier, + "Failed to parse message: {err}" + ); return; } }; @@ -126,10 +121,13 @@ impl OnOff for HueGroup { Ok(res) => { let status = res.status(); if !status.is_success() { - warn!(id = self.identifier, "Status code is not success: {status}"); + warn!( + id = self.config.identifier, + "Status code is not success: {status}" + ); } } - Err(err) => error!(id = self.identifier, "Error: {err}"), + Err(err) => error!(id = self.config.identifier, "Error: {err}"), } Ok(()) @@ -145,13 +143,19 @@ impl OnOff for HueGroup { Ok(res) => { let status = res.status(); if !status.is_success() { - warn!(id = self.identifier, "Status code is not success: {status}"); + warn!( + id = self.config.identifier, + "Status code is not success: {status}" + ); } let on = match res.json::().await { Ok(info) => info.any_on(), Err(err) => { - error!(id = self.identifier, "Failed to parse message: {err}"); + error!( + id = self.config.identifier, + "Failed to parse message: {err}" + ); // TODO: Error code return Ok(false); } @@ -159,7 +163,7 @@ impl OnOff for HueGroup { return Ok(on); } - Err(err) => error!(id = self.identifier, "Error: {err}"), + Err(err) => error!(id = self.config.identifier, "Error: {err}"), } Ok(false) diff --git a/src/devices/ikea_outlet.rs b/src/devices/ikea_outlet.rs index 23f9511..3ad0150 100644 --- a/src/devices/ikea_outlet.rs +++ b/src/devices/ikea_outlet.rs @@ -13,7 +13,6 @@ use tokio::task::JoinHandle; use tracing::{debug, error, trace, warn}; use crate::config::{InfoConfig, MqttDeviceConfig}; -use crate::device_manager::DeviceConfig; use crate::devices::Device; use crate::error::DeviceConfigError; use crate::event::{OnMqtt, OnPresence}; @@ -46,30 +45,8 @@ pub struct IkeaOutletConfig { client: WrappedAsyncClient, } -#[async_trait] -impl DeviceConfig for IkeaOutletConfig { - async fn create(&self, identifier: &str) -> Result, DeviceConfigError> { - trace!( - id = identifier, - name = self.info.name, - room = self.info.room, - "Setting up IkeaOutlet" - ); - - let device = IkeaOutlet { - identifier: identifier.into(), - config: self.clone(), - last_known_state: false, - handle: None, - }; - - Ok(Box::new(device)) - } -} - #[derive(Debug, LuaDevice)] pub struct IkeaOutlet { - identifier: String, #[config] config: IkeaOutletConfig, @@ -94,9 +71,21 @@ async fn set_on(client: WrappedAsyncClient, topic: &str, on: bool) { .ok(); } +impl IkeaOutlet { + async fn create(config: IkeaOutletConfig) -> Result { + trace!(id = config.info.identifier(), "Setting up IkeaOutlet"); + + Ok(Self { + config, + last_known_state: false, + handle: None, + }) + } +} + impl Device for IkeaOutlet { - fn get_id(&self) -> &str { - &self.identifier + fn get_id(&self) -> String { + self.config.info.identifier() } } @@ -122,7 +111,7 @@ impl OnMqtt for IkeaOutlet { let state = match OnOffMessage::try_from(message) { Ok(state) => state.state(), Err(err) => { - error!(id = self.identifier, "Failed to parse message: {err}"); + error!(id = Device::get_id(self), "Failed to parse message: {err}"); return; } }; @@ -135,7 +124,7 @@ impl OnMqtt for IkeaOutlet { // Abort any timer that is currently running self.stop_timeout().await.unwrap(); - debug!(id = self.identifier, "Updating state to {state}"); + debug!(id = Device::get_id(self), "Updating state to {state}"); self.last_known_state = state; // If this is a kettle start a timeout for turning it of again @@ -146,7 +135,7 @@ impl OnMqtt for IkeaOutlet { let action = match RemoteMessage::try_from(message) { Ok(message) => message.action(), Err(err) => { - error!(id = self.identifier, "Failed to parse message: {err}"); + error!(id = Device::get_id(self), "Failed to parse message: {err}"); return; } }; @@ -166,7 +155,7 @@ impl OnPresence for IkeaOutlet { async fn on_presence(&mut self, presence: bool) { // Turn off the outlet when we leave the house (Not if it is a battery charger) if !presence && self.config.outlet_type != OutletType::Charger { - debug!(id = self.identifier, "Turning device off"); + debug!(id = Device::get_id(self), "Turning device off"); self.set_on(false).await.ok(); } } @@ -186,7 +175,7 @@ impl GoogleHomeDevice for IkeaOutlet { device::Name::new(&self.config.info.name) } - fn get_id(&self) -> &str { + fn get_id(&self) -> String { Device::get_id(self) } @@ -228,7 +217,7 @@ impl crate::traits::Timeout for IkeaOutlet { // get dropped let client = self.config.client.clone(); let topic = self.config.mqtt.topic.clone(); - let id = self.identifier.clone(); + let id = Device::get_id(self).clone(); self.handle = Some(tokio::spawn(async move { debug!(id, "Starting timeout ({timeout:?})..."); tokio::time::sleep(timeout).await; diff --git a/src/devices/kasa_outlet.rs b/src/devices/kasa_outlet.rs index 796ab97..a16f740 100644 --- a/src/devices/kasa_outlet.rs +++ b/src/devices/kasa_outlet.rs @@ -13,39 +13,31 @@ use tokio::net::TcpStream; use tracing::trace; use super::Device; -use crate::device_manager::DeviceConfig; use crate::error::DeviceConfigError; #[derive(Debug, Clone, LuaDeviceConfig)] pub struct KasaOutletConfig { + identifier: String, #[device_config(rename("ip"), with(|ip| SocketAddr::new(ip, 9999)))] addr: SocketAddr, } -#[async_trait] -impl DeviceConfig for KasaOutletConfig { - async fn create(&self, identifier: &str) -> Result, DeviceConfigError> { - trace!(id = identifier, "Setting up KasaOutlet"); - - let device = KasaOutlet { - identifier: identifier.into(), - config: self.clone(), - }; - - Ok(Box::new(device)) - } -} - #[derive(Debug, LuaDevice)] pub struct KasaOutlet { - identifier: String, #[config] config: KasaOutletConfig, } +impl KasaOutlet { + async fn create(config: KasaOutletConfig) -> Result { + trace!(id = config.identifier, "Setting up KasaOutlet"); + Ok(Self { config }) + } +} + impl Device for KasaOutlet { - fn get_id(&self) -> &str { - &self.identifier + fn get_id(&self) -> String { + self.config.identifier.clone() } } diff --git a/src/devices/light_sensor.rs b/src/devices/light_sensor.rs index a0e51ba..00e4b0e 100644 --- a/src/devices/light_sensor.rs +++ b/src/devices/light_sensor.rs @@ -4,7 +4,6 @@ use rumqttc::Publish; use tracing::{debug, trace, warn}; use crate::config::MqttDeviceConfig; -use crate::device_manager::DeviceConfig; use crate::devices::Device; use crate::error::DeviceConfigError; use crate::event::{self, Event, EventChannel, OnMqtt}; @@ -12,6 +11,7 @@ use crate::messages::BrightnessMessage; #[derive(Debug, Clone, LuaDeviceConfig)] pub struct LightSensorConfig { + identifier: String, #[device_config(flatten)] pub mqtt: MqttDeviceConfig, pub min: isize, @@ -22,34 +22,27 @@ pub struct LightSensorConfig { pub const DEFAULT: bool = false; -// TODO: The light sensor should get a list of devices that it should inform - -#[async_trait] -impl DeviceConfig for LightSensorConfig { - async fn create(&self, identifier: &str) -> Result, DeviceConfigError> { - let device = LightSensor { - identifier: identifier.into(), - // Add helper type that does this conversion for us - config: self.clone(), - is_dark: DEFAULT, - }; - - Ok(Box::new(device)) - } -} - #[derive(Debug, LuaDevice)] pub struct LightSensor { - identifier: String, #[config] config: LightSensorConfig, is_dark: bool, } +impl LightSensor { + async fn create(config: LightSensorConfig) -> Result { + trace!(id = config.identifier, "Setting up LightSensor"); + Ok(Self { + config, + is_dark: DEFAULT, + }) + } +} + impl Device for LightSensor { - fn get_id(&self) -> &str { - &self.identifier + fn get_id(&self) -> String { + self.config.identifier.clone() } } diff --git a/src/devices/mod.rs b/src/devices/mod.rs index 35eff8b..9d7e67b 100644 --- a/src/devices/mod.rs +++ b/src/devices/mod.rs @@ -47,5 +47,5 @@ pub trait Device: + Cast + Cast { - fn get_id(&self) -> &str; + fn get_id(&self) -> String; } diff --git a/src/devices/ntfy.rs b/src/devices/ntfy.rs index 5fb4f92..6a2f5f8 100644 --- a/src/devices/ntfy.rs +++ b/src/devices/ntfy.rs @@ -1,21 +1,15 @@ use std::collections::HashMap; use async_trait::async_trait; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use serde::Serialize; use serde_repr::*; -use tracing::{debug, error, warn}; +use tracing::{error, trace, warn}; -use crate::config::NtfyConfig; use crate::devices::Device; +use crate::error::DeviceConfigError; use crate::event::{self, Event, EventChannel, OnNotification, OnPresence}; -#[derive(Debug)] -pub struct Ntfy { - base_url: String, - topic: String, - tx: event::Sender, -} - #[derive(Debug, Serialize_repr, Clone, Copy)] #[repr(u8)] pub enum Priority { @@ -116,22 +110,41 @@ impl Default for Notification { } } -impl Ntfy { - pub fn new(config: NtfyConfig, event_channel: &EventChannel) -> Self { - Self { - base_url: config.url, - topic: config.topic, - tx: event_channel.get_tx(), - } - } +#[derive(Debug, LuaDeviceConfig)] +pub struct NtfyConfig { + #[device_config(default("https://ntfy.sh".into()))] + url: String, + topic: String, + #[device_config(rename("event_channel"), from_lua, with(|ec: EventChannel| ec.get_tx()))] + tx: event::Sender, +} +#[derive(Debug, LuaDevice)] +pub struct Ntfy { + #[config] + config: NtfyConfig, +} + +impl Ntfy { + async fn create(config: NtfyConfig) -> Result { + trace!(id = "ntfy", "Setting up Ntfy"); + Ok(Self { config }) + } +} + +impl Device for Ntfy { + fn get_id(&self) -> String { + "ntfy".to_string() + } +} + +impl Ntfy { async fn send(&self, notification: Notification) { - let notification = notification.finalize(&self.topic); - debug!("Sending notfication"); + let notification = notification.finalize(&self.config.topic); // Create the request let res = reqwest::Client::new() - .post(self.base_url.clone()) + .post(self.config.url.clone()) .json(¬ification) .send() .await; @@ -147,12 +160,6 @@ impl Ntfy { } } -impl Device for Ntfy { - fn get_id(&self) -> &str { - "ntfy" - } -} - #[async_trait] impl OnPresence for Ntfy { async fn on_presence(&mut self, presence: bool) { @@ -177,7 +184,13 @@ impl OnPresence for Ntfy { .add_action(action) .set_priority(Priority::Low); - if self.tx.send(Event::Ntfy(notification)).await.is_err() { + if self + .config + .tx + .send(Event::Ntfy(notification)) + .await + .is_err() + { warn!("There are no receivers on the event channel"); } } diff --git a/src/devices/presence.rs b/src/devices/presence.rs index 0b520fc..712b6e5 100644 --- a/src/devices/presence.rs +++ b/src/devices/presence.rs @@ -1,60 +1,64 @@ use std::collections::HashMap; use async_trait::async_trait; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use rumqttc::Publish; -use serde::Deserialize; -use tracing::{debug, warn}; +use tracing::{debug, trace, warn}; use crate::config::MqttDeviceConfig; use crate::devices::Device; +use crate::error::DeviceConfigError; use crate::event::{self, Event, EventChannel, OnMqtt}; use crate::messages::PresenceMessage; -#[derive(Debug, Deserialize)] +#[derive(Debug, LuaDeviceConfig)] pub struct PresenceConfig { - #[serde(flatten)] + #[device_config(flatten)] pub mqtt: MqttDeviceConfig, + #[device_config(from_lua, rename("event_channel"), with(|ec: EventChannel| ec.get_tx()))] + tx: event::Sender, } pub const DEFAULT_PRESENCE: bool = false; -#[derive(Debug)] +#[derive(Debug, LuaDevice)] pub struct Presence { - tx: event::Sender, - mqtt: MqttDeviceConfig, + #[config] + config: PresenceConfig, devices: HashMap, current_overall_presence: bool, } impl Presence { - pub fn new(config: PresenceConfig, event_channel: &EventChannel) -> Self { - Self { - tx: event_channel.get_tx(), - mqtt: config.mqtt, + async fn create(config: PresenceConfig) -> Result { + trace!(id = "ntfy", "Setting up Presence"); + Ok(Self { + config, devices: HashMap::new(), current_overall_presence: DEFAULT_PRESENCE, - } + }) } } impl Device for Presence { - fn get_id(&self) -> &str { - "presence" + fn get_id(&self) -> String { + "presence".to_string() } } #[async_trait] impl OnMqtt for Presence { fn topics(&self) -> Vec<&str> { - vec![&self.mqtt.topic] + vec![&self.config.mqtt.topic] } async fn on_mqtt(&mut self, message: Publish) { let offset = self + .config .mqtt .topic .find('+') - .or(self.mqtt.topic.find('#')) + .or(self.config.mqtt.topic.find('#')) .expect("Presence::create fails if it does not contain wildcards"); let device_name = message.topic[offset..].into(); @@ -81,6 +85,7 @@ impl OnMqtt for Presence { self.current_overall_presence = overall_presence; if self + .config .tx .send(Event::Presence(overall_presence)) .await diff --git a/src/devices/wake_on_lan.rs b/src/devices/wake_on_lan.rs index bddd7f9..9a97ee4 100644 --- a/src/devices/wake_on_lan.rs +++ b/src/devices/wake_on_lan.rs @@ -12,7 +12,6 @@ use tracing::{debug, error, trace}; use super::Device; use crate::config::{InfoConfig, MqttDeviceConfig}; -use crate::device_manager::DeviceConfig; use crate::error::DeviceConfigError; use crate::event::OnMqtt; use crate::messages::ActivateMessage; @@ -28,37 +27,23 @@ pub struct WakeOnLANConfig { broadcast_ip: Ipv4Addr, } -#[async_trait] -impl DeviceConfig for WakeOnLANConfig { - async fn create(&self, identifier: &str) -> Result, DeviceConfigError> { - trace!( - id = identifier, - name = self.info.name, - room = self.info.room, - "Setting up WakeOnLAN" - ); - - debug!("broadcast_ip = {}", self.broadcast_ip); - - let device = WakeOnLAN { - identifier: identifier.into(), - config: self.clone(), - }; - - Ok(Box::new(device)) - } -} - #[derive(Debug, LuaDevice)] pub struct WakeOnLAN { - identifier: String, #[config] config: WakeOnLANConfig, } +impl WakeOnLAN { + async fn create(config: WakeOnLANConfig) -> Result { + trace!(id = config.info.identifier(), "Setting up WakeOnLAN"); + + Ok(Self { config }) + } +} + impl Device for WakeOnLAN { - fn get_id(&self) -> &str { - &self.identifier + fn get_id(&self) -> String { + self.config.info.identifier() } } @@ -72,7 +57,7 @@ impl OnMqtt for WakeOnLAN { let activate = match ActivateMessage::try_from(message) { Ok(message) => message.activate(), Err(err) => { - error!(id = self.identifier, "Failed to parse message: {err}"); + error!(id = Device::get_id(self), "Failed to parse message: {err}"); return; } }; @@ -93,7 +78,7 @@ impl GoogleHomeDevice for WakeOnLAN { name } - fn get_id(&self) -> &str { + fn get_id(&self) -> String { Device::get_id(self) } @@ -111,14 +96,14 @@ impl traits::Scene for WakeOnLAN { async fn set_active(&self, activate: bool) -> Result<(), ErrorCode> { if activate { debug!( - id = self.identifier, + id = Device::get_id(self), "Activating Computer: {} (Sending to {})", self.config.mac_address, self.config.broadcast_ip ); let wol = wakey::WolPacket::from_bytes(&self.config.mac_address.to_array()).map_err( |err| { - error!(id = self.identifier, "invalid mac address: {err}"); + error!(id = Device::get_id(self), "invalid mac address: {err}"); google_home::errors::DeviceError::TransientError }, )?; @@ -129,13 +114,16 @@ impl traits::Scene for WakeOnLAN { ) .await .map_err(|err| { - error!(id = self.identifier, "Failed to activate computer: {err}"); + error!( + id = Device::get_id(self), + "Failed to activate computer: {err}" + ); google_home::errors::DeviceError::TransientError.into() }) - .map(|_| debug!(id = self.identifier, "Success!")) + .map(|_| debug!(id = Device::get_id(self), "Success!")) } else { debug!( - id = self.identifier, + id = Device::get_id(self), "Trying to deactivate computer, this is not currently supported" ); // We do not support deactivating this scene diff --git a/src/devices/washer.rs b/src/devices/washer.rs index 6abe4cb..75e2f91 100644 --- a/src/devices/washer.rs +++ b/src/devices/washer.rs @@ -1,18 +1,18 @@ use async_trait::async_trait; use automation_macro::{LuaDevice, LuaDeviceConfig}; use rumqttc::Publish; -use tracing::{debug, error, warn}; +use tracing::{debug, error, trace, warn}; use super::ntfy::Priority; use super::{Device, Notification}; use crate::config::MqttDeviceConfig; -use crate::device_manager::DeviceConfig; use crate::error::DeviceConfigError; use crate::event::{self, Event, EventChannel, OnMqtt}; use crate::messages::PowerMessage; #[derive(Debug, Clone, LuaDeviceConfig)] pub struct WasherConfig { + identifier: String, #[device_config(flatten)] mqtt: MqttDeviceConfig, // Power in Watt @@ -21,33 +21,25 @@ pub struct WasherConfig { pub tx: event::Sender, } -#[async_trait] -impl DeviceConfig for WasherConfig { - async fn create(&self, identifier: &str) -> Result, DeviceConfigError> { - let device = Washer { - identifier: identifier.into(), - config: self.clone(), - running: 0, - }; - - Ok(Box::new(device)) - } -} - // TODO: Add google home integration - #[derive(Debug, LuaDevice)] pub struct Washer { - identifier: String, #[config] config: WasherConfig, running: isize, } +impl Washer { + async fn create(config: WasherConfig) -> Result { + trace!(id = config.identifier, "Setting up Washer"); + Ok(Self { config, running: 0 }) + } +} + impl Device for Washer { - fn get_id(&self) -> &str { - &self.identifier + fn get_id(&self) -> String { + self.config.identifier.clone() } } @@ -66,7 +58,10 @@ impl OnMqtt for Washer { let power = match PowerMessage::try_from(message) { Ok(state) => state.power(), Err(err) => { - error!(id = self.identifier, "Failed to parse message: {err}"); + error!( + id = self.config.identifier, + "Failed to parse message: {err}" + ); return; } }; @@ -76,7 +71,7 @@ impl OnMqtt for Washer { if power < self.config.threshold && self.running >= HYSTERESIS { // The washer is done running debug!( - id = self.identifier, + id = self.config.identifier, power, threshold = self.config.threshold, "Washer is done" @@ -104,7 +99,7 @@ impl OnMqtt for Washer { } else if power >= self.config.threshold && self.running < HYSTERESIS { // Washer could be starting debug!( - id = self.identifier, + id = self.config.identifier, power, threshold = self.config.threshold, "Washer is starting" diff --git a/src/main.rs b/src/main.rs index 0d4824a..e87fb2c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,18 +65,6 @@ async fn app() -> anyhow::Result<()> { let event_channel = device_manager.event_channel(); - // Create and add the presence system - { - let presence = Presence::new(config.presence, &event_channel); - device_manager.add(Box::new(presence)).await; - } - - // Start the ntfy service if it is configured - if let Some(config) = config.ntfy { - let ntfy = Ntfy::new(config, &event_channel); - device_manager.add(Box::new(ntfy)).await; - } - // Lua testing { let lua = mlua::Lua::new(); @@ -102,6 +90,8 @@ async fn app() -> anyhow::Result<()> { lua.globals().set("automation", automation)?; // Register all the device types + Ntfy::register_with_lua(&lua)?; + Presence::register_with_lua(&lua)?; AirFilter::register_with_lua(&lua)?; AudioSetup::register_with_lua(&lua)?; ContactSensor::register_with_lua(&lua)?;