diff --git a/automation_devices/src/contact_sensor.rs b/automation_devices/src/contact_sensor.rs index 6121056..b84df81 100644 --- a/automation_devices/src/contact_sensor.rs +++ b/automation_devices/src/contact_sensor.rs @@ -35,9 +35,9 @@ pub struct Config { pub sensor_type: SensorType, #[device_config(from_lua, default)] - pub callback: ActionCallback, + pub callback: ActionCallback<(ContactSensor, bool)>, #[device_config(from_lua, default)] - pub battery_callback: ActionCallback, + pub battery_callback: ActionCallback<(ContactSensor, f32)>, #[device_config(from_lua)] pub client: WrappedAsyncClient, @@ -165,14 +165,17 @@ impl OnMqtt for ContactSensor { return; } - self.config.callback.call(self, &!is_closed).await; + self.config.callback.call((self.clone(), !is_closed)).await; debug!(id = self.get_id(), "Updating state to {is_closed}"); self.state_mut().await.is_closed = is_closed; } if let Some(battery) = message.battery { - self.config.battery_callback.call(self, &battery).await; + self.config + .battery_callback + .call((self.clone(), battery)) + .await; } } } diff --git a/automation_devices/src/hue_switch.rs b/automation_devices/src/hue_switch.rs index a517d06..b7a153c 100644 --- a/automation_devices/src/hue_switch.rs +++ b/automation_devices/src/hue_switch.rs @@ -21,19 +21,19 @@ pub struct Config { pub client: WrappedAsyncClient, #[device_config(from_lua, default)] - pub left_callback: ActionCallback, + pub left_callback: ActionCallback, #[device_config(from_lua, default)] - pub right_callback: ActionCallback, + pub right_callback: ActionCallback, #[device_config(from_lua, default)] - pub left_hold_callback: ActionCallback, + pub left_hold_callback: ActionCallback, #[device_config(from_lua, default)] - pub right_hold_callback: ActionCallback, + pub right_hold_callback: ActionCallback, #[device_config(from_lua, default)] - pub battery_callback: ActionCallback, + pub battery_callback: ActionCallback<(HueSwitch, f32)>, } #[derive(Debug, Copy, Clone, Deserialize)] @@ -104,19 +104,21 @@ impl OnMqtt for HueSwitch { ); match action { - Action::LeftPressRelease => self.config.left_callback.call(self, &()).await, - Action::RightPressRelease => self.config.right_callback.call(self, &()).await, - Action::LeftHold => self.config.left_hold_callback.call(self, &()).await, - Action::RightHold => self.config.right_hold_callback.call(self, &()).await, + Action::LeftPressRelease => self.config.left_callback.call(self.clone()).await, + Action::RightPressRelease => { + self.config.right_callback.call(self.clone()).await + } + Action::LeftHold => self.config.left_hold_callback.call(self.clone()).await, + Action::RightHold => self.config.right_hold_callback.call(self.clone()).await, // If there is no hold action, the switch will act like a normal release Action::RightHoldRelease => { - if !self.config.right_hold_callback.is_set() { - self.config.right_callback.call(self, &()).await + if self.config.right_hold_callback.is_empty() { + self.config.right_callback.call(self.clone()).await } } Action::LeftHoldRelease => { - if !self.config.left_hold_callback.is_set() { - self.config.left_callback.call(self, &()).await + if self.config.left_hold_callback.is_empty() { + self.config.left_callback.call(self.clone()).await } } _ => {} @@ -124,7 +126,10 @@ impl OnMqtt for HueSwitch { } if let Some(battery) = message.battery { - self.config.battery_callback.call(self, &battery).await; + self.config + .battery_callback + .call((self.clone(), battery)) + .await; } } } diff --git a/automation_devices/src/ikea_remote.rs b/automation_devices/src/ikea_remote.rs index c6071a5..5f8782d 100644 --- a/automation_devices/src/ikea_remote.rs +++ b/automation_devices/src/ikea_remote.rs @@ -24,9 +24,9 @@ pub struct Config { pub client: WrappedAsyncClient, #[device_config(from_lua, default)] - pub callback: ActionCallback, + pub callback: ActionCallback<(IkeaRemote, bool)>, #[device_config(from_lua, default)] - pub battery_callback: ActionCallback, + pub battery_callback: ActionCallback<(IkeaRemote, f32)>, } #[derive(Debug, Clone, LuaDevice)] @@ -88,12 +88,15 @@ impl OnMqtt for IkeaRemote { }; if let Some(on) = on { - self.config.callback.call(self, &on).await; + self.config.callback.call((self.clone(), on)).await; } } if let Some(battery) = message.battery { - self.config.battery_callback.call(self, &battery).await; + self.config + .battery_callback + .call((self.clone(), battery)) + .await; } } } diff --git a/automation_devices/src/light_sensor.rs b/automation_devices/src/light_sensor.rs index e870860..e3cc259 100644 --- a/automation_devices/src/light_sensor.rs +++ b/automation_devices/src/light_sensor.rs @@ -21,7 +21,7 @@ pub struct Config { pub max: isize, #[device_config(from_lua, default)] - pub callback: ActionCallback, + pub callback: ActionCallback<(LightSensor, bool)>, #[device_config(from_lua)] pub client: WrappedAsyncClient, @@ -114,7 +114,7 @@ impl OnMqtt for LightSensor { self.config .callback - .call(self, &!self.state().await.is_dark) + .call((self.clone(), !self.state().await.is_dark)) .await; } } diff --git a/automation_devices/src/presence.rs b/automation_devices/src/presence.rs index 8d6bd48..77b8718 100644 --- a/automation_devices/src/presence.rs +++ b/automation_devices/src/presence.rs @@ -20,7 +20,7 @@ pub struct Config { pub mqtt: MqttDeviceConfig, #[device_config(from_lua, default)] - pub callback: ActionCallback, + pub callback: ActionCallback<(Presence, bool)>, #[device_config(from_lua)] pub client: WrappedAsyncClient, @@ -118,7 +118,10 @@ impl OnMqtt for Presence { debug!("Overall presence updated: {overall_presence}"); self.state_mut().await.current_overall_presence = overall_presence; - self.config.callback.call(self, &overall_presence).await; + self.config + .callback + .call((self.clone(), overall_presence)) + .await; } } } diff --git a/automation_devices/src/washer.rs b/automation_devices/src/washer.rs index 46d7dc3..8163844 100644 --- a/automation_devices/src/washer.rs +++ b/automation_devices/src/washer.rs @@ -21,7 +21,7 @@ pub struct Config { pub threshold: f32, #[device_config(from_lua, default)] - pub done_callback: ActionCallback, + pub done_callback: ActionCallback, #[device_config(from_lua)] pub client: WrappedAsyncClient, @@ -109,7 +109,7 @@ impl OnMqtt for Washer { self.state_mut().await.running = 0; - self.config.done_callback.call(self, &()).await; + self.config.done_callback.call(self.clone()).await; } else if power < self.config.threshold { // Prevent false positives self.state_mut().await.running = 0; diff --git a/automation_devices/src/zigbee/light.rs b/automation_devices/src/zigbee/light.rs index dc74dda..643c572 100644 --- a/automation_devices/src/zigbee/light.rs +++ b/automation_devices/src/zigbee/light.rs @@ -34,7 +34,7 @@ pub struct Config { pub mqtt: MqttDeviceConfig, #[device_config(from_lua, default)] - pub callback: ActionCallback, T>, + pub callback: ActionCallback<(Light, T)>, #[device_config(from_lua)] pub client: WrappedAsyncClient, @@ -165,7 +165,7 @@ impl OnMqtt for Light { self.config .callback - .call(self, self.state().await.deref()) + .call((self.clone(), self.state().await.clone())) .await; } } @@ -204,7 +204,7 @@ impl OnMqtt for Light { self.config .callback - .call(self, self.state().await.deref()) + .call((self.clone(), self.state().await.clone())) .await; } } @@ -245,7 +245,7 @@ impl OnMqtt for Light { self.config .callback - .call(self, self.state().await.deref()) + .call((self.clone(), self.state().await.clone())) .await; } } diff --git a/automation_devices/src/zigbee/outlet.rs b/automation_devices/src/zigbee/outlet.rs index a757595..3006a47 100644 --- a/automation_devices/src/zigbee/outlet.rs +++ b/automation_devices/src/zigbee/outlet.rs @@ -51,7 +51,7 @@ pub struct Config { pub outlet_type: OutletType, #[device_config(from_lua, default)] - pub callback: ActionCallback, T>, + pub callback: ActionCallback<(Outlet, T)>, #[device_config(from_lua)] pub client: WrappedAsyncClient, @@ -155,7 +155,7 @@ impl OnMqtt for Outlet { self.config .callback - .call(self, self.state().await.deref()) + .call((self.clone(), self.state().await.clone())) .await; } } @@ -192,7 +192,7 @@ impl OnMqtt for Outlet { self.config .callback - .call(self, self.state().await.deref()) + .call((self.clone(), self.state().await.clone())) .await; } } diff --git a/automation_lib/src/action_callback.rs b/automation_lib/src/action_callback.rs index bed3c99..2b9479e 100644 --- a/automation_lib/src/action_callback.rs +++ b/automation_lib/src/action_callback.rs @@ -1,34 +1,28 @@ use std::marker::PhantomData; use futures::future::try_join_all; -use mlua::{FromLua, IntoLua, LuaSerdeExt}; -use serde::Serialize; +use mlua::{FromLua, IntoLuaMulti}; #[derive(Debug, Clone)] -struct Internal { +pub struct ActionCallback

{ callbacks: Vec, - lua: mlua::Lua, + _parameters: PhantomData

, } -#[derive(Debug, Clone)] -pub struct ActionCallback { - internal: Option, - _this: PhantomData, - _state: PhantomData, -} - -impl Default for ActionCallback { +// NOTE: For some reason the derive macro combined with PhantomData leads to issues where it +// requires all types part of P to implement default, even if they never actually get constructed. +// By manually implemented Default it works fine. +impl

Default for ActionCallback

{ fn default() -> Self { Self { - internal: None, - _this: PhantomData::, - _state: PhantomData::, + callbacks: Default::default(), + _parameters: Default::default(), } } } -impl FromLua for ActionCallback { - fn from_lua(value: mlua::Value, lua: &mlua::Lua) -> mlua::Result { +impl

FromLua for ActionCallback

{ + fn from_lua(value: mlua::Value, _lua: &mlua::Lua) -> mlua::Result { let callbacks = match value { mlua::Value::Function(f) => vec![f], mlua::Value::Table(table) => table @@ -49,40 +43,28 @@ impl FromLua for ActionCallback { }; Ok(ActionCallback { - internal: Some(Internal { - callbacks, - lua: lua.clone(), - }), - _this: PhantomData::, - _state: PhantomData::, + callbacks, + _parameters: PhantomData::

, }) } } // TODO: Return proper error here -impl ActionCallback +impl

ActionCallback

where - T: IntoLua + Sync + Send + Clone + 'static, - S: Serialize, + P: IntoLuaMulti + Sync + Clone, { - pub async fn call(&self, this: &T, state: &S) { - let Some(internal) = self.internal.as_ref() else { - return; - }; - - let state = internal.lua.to_value(state).unwrap(); - + pub async fn call(&self, parameters: P) { try_join_all( - internal - .callbacks + self.callbacks .iter() - .map(async |f| f.call_async::<()>((this.clone(), state.clone())).await), + .map(async |f| f.call_async::<()>(parameters.clone()).await), ) .await .unwrap(); } - pub fn is_set(&self) -> bool { - self.internal.is_some() + pub fn is_empty(&self) -> bool { + self.callbacks.is_empty() } } diff --git a/automation_lib/src/helpers/timeout.rs b/automation_lib/src/helpers/timeout.rs index 4b5ae7e..c770d36 100644 --- a/automation_lib/src/helpers/timeout.rs +++ b/automation_lib/src/helpers/timeout.rs @@ -29,7 +29,7 @@ impl mlua::UserData for Timeout { methods.add_async_method( "start", - async |_lua, this, (timeout, callback): (f32, ActionCallback)| { + async |_lua, this, (timeout, callback): (f32, ActionCallback<()>)| { if let Some(handle) = this.state.write().await.handle.take() { handle.abort(); } @@ -42,7 +42,7 @@ impl mlua::UserData for Timeout { async move { tokio::time::sleep(timeout).await; - callback.call(&mlua::Nil, &false).await; + callback.call(()).await; } }));