From 2f181e35f307b4c352d4f0ab24225ceedb013ac0 Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Mon, 8 Sep 2025 03:30:01 +0200 Subject: [PATCH] refactor!: ActionCallback can now receive any amount of arguments ActionCallback now only has one generics argument that has to implement IntoLuaMulti, this makes ActionCallback much more flexible as it no longer always requires two arguments. --- automation_devices/src/contact_sensor.rs | 11 +++-- automation_devices/src/hue_switch.rs | 33 ++++++++------ automation_devices/src/ikea_remote.rs | 11 +++-- automation_devices/src/light_sensor.rs | 4 +- automation_devices/src/presence.rs | 7 ++- automation_devices/src/washer.rs | 4 +- automation_devices/src/zigbee/light.rs | 8 ++-- automation_devices/src/zigbee/outlet.rs | 6 +-- automation_lib/src/action_callback.rs | 58 ++++++++---------------- automation_lib/src/helpers/timeout.rs | 4 +- 10 files changed, 71 insertions(+), 75 deletions(-) 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; } }));