Compare commits

..

6 Commits

Author SHA1 Message Date
2f181e35f3 refactor!: ActionCallback can now receive any amount of arguments
All checks were successful
Build and deploy / build (push) Successful in 12m37s
Build and deploy / Deploy container (push) Successful in 33s
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.
2025-09-08 03:30:01 +02:00
613fd63895 feat: Added derive macro to implement IntoLua on structs that implement Serialize
This can be very useful if you want to convert a data struct to a lua
table without having to write the boilerplane (however small it may
be).

It also adds the macro on several state structs so they can be
converted to lua in the upcoming ActionCallback refactor.
2025-09-08 03:18:12 +02:00
cfa482aa03 fix: Added missing default for IkeaRemote callback 2025-09-08 02:14:09 +02:00
379d840158 feat: Allow for multiple callbacks inside of an ActionCallback
This also results in the conversion being performed when the
ActionCallback is instantiated instead of when it is called, this should
make it easier to catch errors.
2025-09-08 02:11:05 +02:00
0c428d1d9b refactor: Store callback function directly instead of in the registry 2025-09-08 02:09:11 +02:00
56275811f7 feat!: Log version string during startup
All checks were successful
Build and deploy / build (push) Successful in 10m11s
Build and deploy / Deploy container (push) Successful in 36s
2025-09-05 04:55:29 +02:00
2 changed files with 60 additions and 67 deletions

View File

@@ -28,13 +28,7 @@ impl mlua::UserData for WrappedAsyncClient {
methods.add_async_method( methods.add_async_method(
"send_message", "send_message",
async |_lua, this, (topic, message): (String, mlua::Value)| { async |_lua, this, (topic, message): (String, mlua::Value)| {
// serde_json converts nil => "null", but we actually want nil to send an empty let message = serde_json::to_string(&message).unwrap();
// message
let message = if message.is_nil() {
"".into()
} else {
serde_json::to_string(&message).unwrap()
};
debug!("message = {message}"); debug!("message = {message}");

View File

@@ -425,57 +425,49 @@ device_manager:add(HueSwitch.new({
local hallway_light_automation = { local hallway_light_automation = {
timeout = Timeout.new(), timeout = Timeout.new(),
forced = false, forced = false,
switch_callback = function(self) switch_callback = function(self, on)
return function(_, on) self.timeout:cancel()
self.group.set_on(on)
self.forced = on
end,
door_callback = function(self, open)
if open then
self.timeout:cancel() self.timeout:cancel()
self.group.set_on(on)
self.forced = on
end
end,
door_callback = function(self)
return function(_, open)
if open then
self.timeout:cancel()
self.group.set_on(true) self.group.set_on(true)
elseif not self.forced then elseif not self.forced then
self.timeout:start(debug and 10 or 2 * 60, function() self.timeout:start(debug and 10 or 2 * 60, function()
if self.trash == nil or self.trash:open_percent() == 0 then if self.trash == nil or self.trash:open_percent() == 0 then
self.group.set_on(false)
end
end)
end
end
end,
trash_callback = function(self)
return function(_, open)
if open then
self.group.set_on(true)
else
if
not self.timeout:is_waiting()
and (self.door == nil or self.door:open_percent() == 0)
and not self.forced
then
self.group.set_on(false) self.group.set_on(false)
end end
end)
end
end,
trash_callback = function(self, open)
if open then
self.group.set_on(true)
else
if
not self.timeout:is_waiting()
and (self.door == nil or self.door:open_percent() == 0)
and not self.forced
then
self.group.set_on(false)
end end
end end
end, end,
light_callback = function(self) light_callback = function(self, on)
return function(_, state) if
if on
state.on and (self.trash == nil or self.trash:open_percent()) == 0
and (self.trash == nil or self.trash:open_percent()) == 0 and (self.door == nil or self.door:open_percent() == 0)
and (self.door == nil or self.door:open_percent() == 0) then
then -- If the door and trash are not open, that means the light got turned on manually
-- If the door and trash are not open, that means the light got turned on manually self.timeout:cancel()
self.timeout:cancel() self.forced = true
self.forced = true elseif not on then
elseif not state.on then -- The light is never forced when it is off
-- The light is never forced when it is off self.forced = false
self.forced = false
end
end end
end, end,
} }
@@ -485,7 +477,9 @@ local hallway_storage = LightBrightness.new({
room = "Hallway", room = "Hallway",
topic = mqtt_z2m("hallway/storage"), topic = mqtt_z2m("hallway/storage"),
client = mqtt_client, client = mqtt_client,
callback = hallway_light_automation:light_callback(), callback = function(_, state)
hallway_light_automation:light_callback(state.state)
end,
}) })
turn_off_when_away(hallway_storage) turn_off_when_away(hallway_storage)
device_manager:add(hallway_storage) device_manager:add(hallway_storage)
@@ -510,12 +504,13 @@ hallway_light_automation.group = {
end, end,
} }
local function presence(duration) local frontdoor_presence = {
local timeout = Timeout.new() timeout = Timeout.new(),
}
return function(_, open) setmetatable(frontdoor_presence, {
__call = function(self, open)
if open then if open then
timeout:cancel() self.timeout:cancel()
if not presence_system:overall_presence() then if not presence_system:overall_presence() then
mqtt_client:send_message(mqtt_automation("presence/contact/frontdoor"), { mqtt_client:send_message(mqtt_automation("presence/contact/frontdoor"), {
@@ -524,19 +519,21 @@ local function presence(duration)
}) })
end end
else else
timeout:start(duration, function() self.timeout:start(debug and 10 or 15 * 60, function()
mqtt_client:send_message(mqtt_automation("presence/contact/frontdoor"), nil) mqtt_client:send_message(mqtt_automation("presence/contact/frontdoor"), {})
end) end)
end end
end end,
end })
device_manager:add(IkeaRemote.new({ device_manager:add(IkeaRemote.new({
name = "Remote", name = "Remote",
room = "Hallway", room = "Hallway",
client = mqtt_client, client = mqtt_client,
topic = mqtt_z2m("hallway/remote"), topic = mqtt_z2m("hallway/remote"),
callback = hallway_light_automation:switch_callback(), callback = function(_, on)
hallway_light_automation:switch_callback(on)
end,
battery_callback = check_battery, battery_callback = check_battery,
})) }))
local hallway_frontdoor = ContactSensor.new({ local hallway_frontdoor = ContactSensor.new({
@@ -549,10 +546,10 @@ local hallway_frontdoor = ContactSensor.new({
topic = mqtt_automation("presence/contact/frontdoor"), topic = mqtt_automation("presence/contact/frontdoor"),
timeout = debug and 10 or 15 * 60, timeout = debug and 10 or 15 * 60,
}, },
callback = { callback = function(_, open)
presence(debug and 10 or 15 * 60), hallway_light_automation:door_callback(open)
hallway_light_automation:door_callback(), frontdoor_presence(open)
}, end,
battery_callback = check_battery, battery_callback = check_battery,
}) })
device_manager:add(hallway_frontdoor) device_manager:add(hallway_frontdoor)
@@ -564,7 +561,9 @@ local hallway_trash = ContactSensor.new({
sensor_type = "Drawer", sensor_type = "Drawer",
topic = mqtt_z2m("hallway/trash"), topic = mqtt_z2m("hallway/trash"),
client = mqtt_client, client = mqtt_client,
callback = hallway_light_automation:trash_callback(), callback = function(_, open)
hallway_light_automation:trash_callback(open)
end,
battery_callback = check_battery, battery_callback = check_battery,
}) })
device_manager:add(hallway_trash) device_manager:add(hallway_trash)