From 44a40d4dfab4254ef2a4b34e26bd6cecb5ef4e51 Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Mon, 29 Apr 2024 02:53:21 +0200 Subject: [PATCH] LuaDevice macro now uses LuaDeviceCreate trait to create devices from configs --- automation_macro/src/lua_device.rs | 29 +++++------------------------ src/devices/air_filter.rs | 11 +++++++---- src/devices/audio_setup.rs | 11 +++++++---- src/devices/contact_sensor.rs | 11 +++++++---- src/devices/debug_bridge.rs | 13 +++++++++---- src/devices/hue_bridge.rs | 14 ++++++++++---- src/devices/hue_light.rs | 14 +++++++++----- src/devices/ikea_outlet.rs | 11 +++++++---- src/devices/kasa_outlet.rs | 13 ++++++++----- src/devices/light_sensor.rs | 11 +++++++---- src/devices/mod.rs | 11 +++++++++++ src/devices/ntfy.rs | 12 ++++++++---- src/devices/presence.rs | 11 +++++++---- src/devices/wake_on_lan.rs | 12 +++++++----- src/devices/washer.rs | 12 +++++++----- src/error.rs | 4 ---- 16 files changed, 116 insertions(+), 84 deletions(-) diff --git a/automation_macro/src/lua_device.rs b/automation_macro/src/lua_device.rs index 886d5a7..fcccc1b 100644 --- a/automation_macro/src/lua_device.rs +++ b/automation_macro/src/lua_device.rs @@ -1,30 +1,9 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{Data, DataStruct, DeriveInput, Fields, FieldsNamed}; +use syn::DeriveInput; pub fn impl_lua_device_macro(ast: &DeriveInput) -> TokenStream { let name = &ast.ident; - // TODO: Handle errors properly - // This includes making sure one, and only one config is specified - let config = if let Data::Struct(DataStruct { - fields: Fields::Named(FieldsNamed { ref named, .. }), - .. - }) = ast.data - { - named - .iter() - .find(|&field| { - field - .attrs - .iter() - .any(|attr| attr.path().is_ident("config")) - }) - .map(|field| field.ty.clone()) - .unwrap() - } else { - unimplemented!() - }; - let gen = quote! { impl #name { pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> { @@ -34,8 +13,10 @@ 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_async_function("new", |lua, config: mlua::Value| async { - let config: #config = mlua::FromLua::from_lua(config, lua)?; - let device = #name::create(config).await.map_err(mlua::ExternalError::into_lua_err)?; + let config = mlua::FromLua::from_lua(config, lua)?; + + // TODO: Using crate:: could cause issues + let device: #name = crate::devices::LuaDeviceCreate::create(config).await.map_err(mlua::ExternalError::into_lua_err)?; Ok(crate::device_manager::WrappedDevice::new(Box::new(device))) }); diff --git a/src/devices/air_filter.rs b/src/devices/air_filter.rs index 63461b3..a455c2d 100644 --- a/src/devices/air_filter.rs +++ b/src/devices/air_filter.rs @@ -8,9 +8,9 @@ use google_home::GoogleHomeDevice; use rumqttc::Publish; use tracing::{debug, error, trace, warn}; +use super::LuaDeviceCreate; use crate::config::{InfoConfig, MqttDeviceConfig}; use crate::devices::Device; -use crate::error::DeviceConfigError; use crate::event::OnMqtt; use crate::messages::{AirFilterFanState, AirFilterState, SetAirFilterFanState}; use crate::mqtt::WrappedAsyncClient; @@ -27,7 +27,6 @@ pub struct AirFilterConfig { #[derive(Debug, LuaDevice)] pub struct AirFilter { - #[config] config: AirFilterConfig, last_known_state: AirFilterState, @@ -53,8 +52,12 @@ impl AirFilter { } } -impl AirFilter { - async fn create(config: AirFilterConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for AirFilter { + type Config = AirFilterConfig; + type Error = rumqttc::ClientError; + + async fn create(config: Self::Config) -> Result { trace!(id = config.info.identifier(), "Setting up AirFilter"); config diff --git a/src/devices/audio_setup.rs b/src/devices/audio_setup.rs index d39da97..bbc9101 100644 --- a/src/devices/audio_setup.rs +++ b/src/devices/audio_setup.rs @@ -3,7 +3,7 @@ use automation_macro::{LuaDevice, LuaDeviceConfig}; use google_home::traits::OnOff; use tracing::{debug, error, trace, warn}; -use super::Device; +use super::{Device, LuaDeviceCreate}; use crate::config::MqttDeviceConfig; use crate::device_manager::WrappedDevice; use crate::error::DeviceConfigError; @@ -26,12 +26,15 @@ pub struct AudioSetupConfig { #[derive(Debug, LuaDevice)] pub struct AudioSetup { - #[config] config: AudioSetupConfig, } -impl AudioSetup { - async fn create(config: AudioSetupConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for AudioSetup { + type Config = AudioSetupConfig; + type Error = DeviceConfigError; + + async fn create(config: Self::Config) -> Result { trace!(id = config.identifier, "Setting up AudioSetup"); { diff --git a/src/devices/contact_sensor.rs b/src/devices/contact_sensor.rs index fc4c414..fe3b1d0 100644 --- a/src/devices/contact_sensor.rs +++ b/src/devices/contact_sensor.rs @@ -7,7 +7,7 @@ use mlua::FromLua; use tokio::task::JoinHandle; use tracing::{debug, error, trace, warn}; -use super::Device; +use super::{Device, LuaDeviceCreate}; use crate::config::MqttDeviceConfig; use crate::device_manager::WrappedDevice; use crate::devices::DEFAULT_PRESENCE; @@ -64,7 +64,6 @@ pub struct ContactSensorConfig { #[derive(Debug, LuaDevice)] pub struct ContactSensor { - #[config] config: ContactSensorConfig, overall_presence: bool, @@ -72,8 +71,12 @@ pub struct ContactSensor { handle: Option>, } -impl ContactSensor { - async fn create(config: ContactSensorConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for ContactSensor { + type Config = ContactSensorConfig; + type Error = DeviceConfigError; + + async fn create(config: Self::Config) -> Result { trace!(id = config.identifier, "Setting up ContactSensor"); // Make sure the devices implement the required traits diff --git a/src/devices/debug_bridge.rs b/src/devices/debug_bridge.rs index ab9c355..834847d 100644 --- a/src/devices/debug_bridge.rs +++ b/src/devices/debug_bridge.rs @@ -1,10 +1,12 @@ +use std::convert::Infallible; + use async_trait::async_trait; use automation_macro::{LuaDevice, LuaDeviceConfig}; use tracing::{trace, warn}; +use super::LuaDeviceCreate; use crate::config::MqttDeviceConfig; use crate::devices::Device; -use crate::error::DeviceConfigError; use crate::event::{OnDarkness, OnPresence}; use crate::messages::{DarknessMessage, PresenceMessage}; use crate::mqtt::WrappedAsyncClient; @@ -20,12 +22,15 @@ pub struct DebugBridgeConfig { #[derive(Debug, LuaDevice)] pub struct DebugBridge { - #[config] config: DebugBridgeConfig, } -impl DebugBridge { - async fn create(config: DebugBridgeConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for DebugBridge { + type Config = DebugBridgeConfig; + type Error = Infallible; + + async fn create(config: Self::Config) -> Result { trace!(id = config.identifier, "Setting up DebugBridge"); Ok(Self { config }) } diff --git a/src/devices/hue_bridge.rs b/src/devices/hue_bridge.rs index 0d03a5e..7cf7df0 100644 --- a/src/devices/hue_bridge.rs +++ b/src/devices/hue_bridge.rs @@ -1,3 +1,4 @@ +use std::convert::Infallible; use std::net::SocketAddr; use async_trait::async_trait; @@ -5,8 +6,8 @@ use automation_macro::{LuaDevice, LuaDeviceConfig}; use serde::{Deserialize, Serialize}; use tracing::{error, trace, warn}; +use super::LuaDeviceCreate; use crate::devices::Device; -use crate::error::DeviceConfigError; use crate::event::{OnDarkness, OnPresence}; #[derive(Debug)] @@ -32,7 +33,6 @@ pub struct HueBridgeConfig { #[derive(Debug, LuaDevice)] pub struct HueBridge { - #[config] config: HueBridgeConfig, } @@ -41,12 +41,18 @@ struct FlagMessage { flag: bool, } -impl HueBridge { - async fn create(config: HueBridgeConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for HueBridge { + type Config = HueBridgeConfig; + type Error = Infallible; + + async fn create(config: Self::Config) -> Result { trace!(id = config.identifier, "Setting up HueBridge"); Ok(Self { config }) } +} +impl HueBridge { pub async fn set_flag(&self, flag: Flag, value: bool) { let flag_id = match flag { Flag::Presence => self.config.flags.presence, diff --git a/src/devices/hue_light.rs b/src/devices/hue_light.rs index fc4606c..084b54b 100644 --- a/src/devices/hue_light.rs +++ b/src/devices/hue_light.rs @@ -9,9 +9,8 @@ use google_home::traits::OnOff; use rumqttc::{Publish, SubscribeFilter}; use tracing::{debug, error, trace, warn}; -use super::Device; +use super::{Device, LuaDeviceCreate}; use crate::config::MqttDeviceConfig; -use crate::error::DeviceConfigError; use crate::event::OnMqtt; use crate::messages::{RemoteAction, RemoteMessage}; use crate::mqtt::WrappedAsyncClient; @@ -34,13 +33,16 @@ pub struct HueGroupConfig { #[derive(Debug, LuaDevice)] pub struct HueGroup { - #[config] config: HueGroupConfig, } // Couple of helper function to get the correct urls -impl HueGroup { - async fn create(config: HueGroupConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for HueGroup { + type Config = HueGroupConfig; + type Error = rumqttc::ClientError; + + async fn create(config: Self::Config) -> Result { trace!(id = config.identifier, "Setting up AudioSetup"); if !config.remotes.is_empty() { @@ -55,7 +57,9 @@ impl HueGroup { Ok(Self { config }) } +} +impl HueGroup { fn url_base(&self) -> String { format!("http://{}/api/{}", self.config.addr, self.config.login) } diff --git a/src/devices/ikea_outlet.rs b/src/devices/ikea_outlet.rs index 85862c7..219054f 100644 --- a/src/devices/ikea_outlet.rs +++ b/src/devices/ikea_outlet.rs @@ -12,9 +12,9 @@ use serde::Deserialize; use tokio::task::JoinHandle; use tracing::{debug, error, trace, warn}; +use super::LuaDeviceCreate; use crate::config::{InfoConfig, MqttDeviceConfig}; use crate::devices::Device; -use crate::error::DeviceConfigError; use crate::event::{OnMqtt, OnPresence}; use crate::messages::{OnOffMessage, RemoteAction, RemoteMessage}; use crate::mqtt::WrappedAsyncClient; @@ -47,7 +47,6 @@ pub struct IkeaOutletConfig { #[derive(Debug, LuaDevice)] pub struct IkeaOutlet { - #[config] config: IkeaOutletConfig, last_known_state: bool, @@ -71,8 +70,12 @@ async fn set_on(client: WrappedAsyncClient, topic: &str, on: bool) { .ok(); } -impl IkeaOutlet { - async fn create(config: IkeaOutletConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for IkeaOutlet { + type Config = IkeaOutletConfig; + type Error = rumqttc::ClientError; + + async fn create(config: Self::Config) -> Result { trace!(id = config.info.identifier(), "Setting up IkeaOutlet"); if !config.remotes.is_empty() { diff --git a/src/devices/kasa_outlet.rs b/src/devices/kasa_outlet.rs index a16f740..440b9ec 100644 --- a/src/devices/kasa_outlet.rs +++ b/src/devices/kasa_outlet.rs @@ -1,3 +1,4 @@ +use std::convert::Infallible; use std::net::SocketAddr; use std::str::Utf8Error; @@ -12,8 +13,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; use tracing::trace; -use super::Device; -use crate::error::DeviceConfigError; +use super::{Device, LuaDeviceCreate}; #[derive(Debug, Clone, LuaDeviceConfig)] pub struct KasaOutletConfig { @@ -24,12 +24,15 @@ pub struct KasaOutletConfig { #[derive(Debug, LuaDevice)] pub struct KasaOutlet { - #[config] config: KasaOutletConfig, } -impl KasaOutlet { - async fn create(config: KasaOutletConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for KasaOutlet { + type Config = KasaOutletConfig; + type Error = Infallible; + + async fn create(config: Self::Config) -> Result { trace!(id = config.identifier, "Setting up KasaOutlet"); Ok(Self { config }) } diff --git a/src/devices/light_sensor.rs b/src/devices/light_sensor.rs index cf0d10a..99ba696 100644 --- a/src/devices/light_sensor.rs +++ b/src/devices/light_sensor.rs @@ -3,9 +3,9 @@ use automation_macro::{LuaDevice, LuaDeviceConfig}; use rumqttc::Publish; use tracing::{debug, trace, warn}; +use super::LuaDeviceCreate; use crate::config::MqttDeviceConfig; use crate::devices::Device; -use crate::error::DeviceConfigError; use crate::event::{self, Event, EventChannel, OnMqtt}; use crate::messages::BrightnessMessage; use crate::mqtt::WrappedAsyncClient; @@ -27,14 +27,17 @@ pub const DEFAULT: bool = false; #[derive(Debug, LuaDevice)] pub struct LightSensor { - #[config] config: LightSensorConfig, is_dark: bool, } -impl LightSensor { - async fn create(config: LightSensorConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for LightSensor { + type Config = LightSensorConfig; + type Error = rumqttc::ClientError; + + async fn create(config: Self::Config) -> Result { trace!(id = config.identifier, "Setting up LightSensor"); config diff --git a/src/devices/mod.rs b/src/devices/mod.rs index 9d7e67b..e1cc3c4 100644 --- a/src/devices/mod.rs +++ b/src/devices/mod.rs @@ -14,6 +14,7 @@ mod washer; use std::fmt::Debug; +use async_trait::async_trait; use automation_cast::Cast; use google_home::traits::OnOff; use google_home::GoogleHomeDevice; @@ -34,6 +35,16 @@ pub use self::washer::*; use crate::event::{OnDarkness, OnMqtt, OnNotification, OnPresence}; use crate::traits::Timeout; +#[async_trait] +pub trait LuaDeviceCreate { + type Config; + type Error; + + async fn create(config: Self::Config) -> Result + where + Self: Sized; +} + pub trait Device: Debug + Sync diff --git a/src/devices/ntfy.rs b/src/devices/ntfy.rs index 6a2f5f8..a4ab142 100644 --- a/src/devices/ntfy.rs +++ b/src/devices/ntfy.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::convert::Infallible; use async_trait::async_trait; use automation_macro::{LuaDevice, LuaDeviceConfig}; @@ -6,8 +7,8 @@ use serde::Serialize; use serde_repr::*; use tracing::{error, trace, warn}; +use super::LuaDeviceCreate; use crate::devices::Device; -use crate::error::DeviceConfigError; use crate::event::{self, Event, EventChannel, OnNotification, OnPresence}; #[derive(Debug, Serialize_repr, Clone, Copy)] @@ -121,12 +122,15 @@ pub struct NtfyConfig { #[derive(Debug, LuaDevice)] pub struct Ntfy { - #[config] config: NtfyConfig, } -impl Ntfy { - async fn create(config: NtfyConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for Ntfy { + type Config = NtfyConfig; + type Error = Infallible; + + async fn create(config: Self::Config) -> Result { trace!(id = "ntfy", "Setting up Ntfy"); Ok(Self { config }) } diff --git a/src/devices/presence.rs b/src/devices/presence.rs index d6f8ac0..1a7d39b 100644 --- a/src/devices/presence.rs +++ b/src/devices/presence.rs @@ -5,9 +5,9 @@ use automation_macro::{LuaDevice, LuaDeviceConfig}; use rumqttc::Publish; use tracing::{debug, trace, warn}; +use super::LuaDeviceCreate; use crate::config::MqttDeviceConfig; use crate::devices::Device; -use crate::error::DeviceConfigError; use crate::event::{self, Event, EventChannel, OnMqtt}; use crate::messages::PresenceMessage; use crate::mqtt::WrappedAsyncClient; @@ -26,14 +26,17 @@ pub const DEFAULT_PRESENCE: bool = false; #[derive(Debug, LuaDevice)] pub struct Presence { - #[config] config: PresenceConfig, devices: HashMap, current_overall_presence: bool, } -impl Presence { - async fn create(config: PresenceConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for Presence { + type Config = PresenceConfig; + type Error = rumqttc::ClientError; + + async fn create(config: Self::Config) -> Result { trace!(id = "ntfy", "Setting up Presence"); config diff --git a/src/devices/wake_on_lan.rs b/src/devices/wake_on_lan.rs index 23c92de..2e7c2af 100644 --- a/src/devices/wake_on_lan.rs +++ b/src/devices/wake_on_lan.rs @@ -10,9 +10,8 @@ use google_home::{device, GoogleHomeDevice}; use rumqttc::Publish; use tracing::{debug, error, trace}; -use super::Device; +use super::{Device, LuaDeviceCreate}; use crate::config::{InfoConfig, MqttDeviceConfig}; -use crate::error::DeviceConfigError; use crate::event::OnMqtt; use crate::messages::ActivateMessage; use crate::mqtt::WrappedAsyncClient; @@ -32,12 +31,15 @@ pub struct WakeOnLANConfig { #[derive(Debug, LuaDevice)] pub struct WakeOnLAN { - #[config] config: WakeOnLANConfig, } -impl WakeOnLAN { - async fn create(config: WakeOnLANConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for WakeOnLAN { + type Config = WakeOnLANConfig; + type Error = rumqttc::ClientError; + + async fn create(config: Self::Config) -> Result { trace!(id = config.info.identifier(), "Setting up WakeOnLAN"); config diff --git a/src/devices/washer.rs b/src/devices/washer.rs index 342af84..9d0f1f7 100644 --- a/src/devices/washer.rs +++ b/src/devices/washer.rs @@ -4,9 +4,8 @@ use rumqttc::Publish; use tracing::{debug, error, trace, warn}; use super::ntfy::Priority; -use super::{Device, Notification}; +use super::{Device, LuaDeviceCreate, Notification}; use crate::config::MqttDeviceConfig; -use crate::error::DeviceConfigError; use crate::event::{self, Event, EventChannel, OnMqtt}; use crate::messages::PowerMessage; use crate::mqtt::WrappedAsyncClient; @@ -27,14 +26,17 @@ pub struct WasherConfig { // TODO: Add google home integration #[derive(Debug, LuaDevice)] pub struct Washer { - #[config] config: WasherConfig, running: isize, } -impl Washer { - async fn create(config: WasherConfig) -> Result { +#[async_trait] +impl LuaDeviceCreate for Washer { + type Config = WasherConfig; + type Error = rumqttc::ClientError; + + async fn create(config: Self::Config) -> Result { trace!(id = config.identifier, "Setting up Washer"); config diff --git a/src/error.rs b/src/error.rs index c2f0723..0052984 100644 --- a/src/error.rs +++ b/src/error.rs @@ -92,13 +92,9 @@ impl MissingWildcard { #[derive(Debug, Error)] pub enum DeviceConfigError { - #[error("Child '{1}' of device '{0}' does not exist")] - MissingChild(String, String), #[error("Device '{0}' does not implement expected trait '{1}'")] MissingTrait(String, String), #[error(transparent)] - MissingWildcard(#[from] MissingWildcard), - #[error(transparent)] MqttClientError(#[from] rumqttc::ClientError), }