WIP: Lua action callback
This commit is contained in:
parent
88e31699ad
commit
29e779b023
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -2228,9 +2228,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uuid"
|
name = "uuid"
|
||||||
version = "1.8.0"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,7 +4,12 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["automation_macro", "automation_cast", "google_home/google_home", "google_home/google_home_macro"]
|
members = [
|
||||||
|
"automation_macro",
|
||||||
|
"automation_cast",
|
||||||
|
"google_home/google_home",
|
||||||
|
"google_home/google_home_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -54,7 +59,7 @@ mlua = { version = "0.9.7", features = [
|
||||||
once_cell = "1.19.0"
|
once_cell = "1.19.0"
|
||||||
hostname = "0.4.0"
|
hostname = "0.4.0"
|
||||||
tokio-util = { version = "0.7.11", features = ["full"] }
|
tokio-util = { version = "0.7.11", features = ["full"] }
|
||||||
uuid = "1.8.0"
|
uuid = { version = "1.8.0", features = ["v4"] }
|
||||||
dyn-clone = "1.0.17"
|
dyn-clone = "1.0.17"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
|
|
48
config.lua
48
config.lua
|
@ -36,11 +36,12 @@ automation.device_manager:add(Ntfy.new({
|
||||||
event_channel = automation.device_manager:event_channel(),
|
event_channel = automation.device_manager:event_channel(),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
automation.device_manager:add(Presence.new({
|
local presence = Presence.new({
|
||||||
topic = mqtt_automation("presence/+/#"),
|
topic = mqtt_automation("presence/+/#"),
|
||||||
client = mqtt_client,
|
client = mqtt_client,
|
||||||
event_channel = automation.device_manager:event_channel(),
|
event_channel = automation.device_manager:event_channel(),
|
||||||
}))
|
})
|
||||||
|
automation.device_manager:add(presence)
|
||||||
|
|
||||||
automation.device_manager:add(DebugBridge.new({
|
automation.device_manager:add(DebugBridge.new({
|
||||||
identifier = "debug_bridge",
|
identifier = "debug_bridge",
|
||||||
|
@ -164,15 +165,48 @@ automation.device_manager:add(ContactSensor.new({
|
||||||
devices = { hallway_lights },
|
devices = { hallway_lights },
|
||||||
timeout = debug and 10 or 2 * 60,
|
timeout = debug and 10 or 2 * 60,
|
||||||
},
|
},
|
||||||
|
action = function(closed)
|
||||||
|
-- if state then
|
||||||
|
-- hallway_lights:set_on(true)
|
||||||
|
-- presence:set_presence("contact/frontdoor", true)
|
||||||
|
-- else
|
||||||
|
-- -- TODO: New timeout implementation -> Device stores timestamp of last state change.
|
||||||
|
-- -- Generic timeout implementation then works by storing the timestamp at the start and comparing once the timer expires.
|
||||||
|
-- -- If the timestamp has changed -> Cancel the state change
|
||||||
|
-- -- Maybe add some sort identifier, such that if the function gets called repeatedly it will overwrite the existing timeout?
|
||||||
|
-- -- Tag? Autogenerated from position in lua code? Not sure if that is possible
|
||||||
|
-- automation.timeout(2 * 60, function()
|
||||||
|
-- hallway_lights:set_on(false)
|
||||||
|
-- end)
|
||||||
|
-- automation.timeout(15 * 60, function()
|
||||||
|
-- presence:set_presence("contact/frontdoor", false)
|
||||||
|
-- end)
|
||||||
|
-- end
|
||||||
|
end,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
local function trash_action(device)
|
||||||
|
local previous = device:is_on()
|
||||||
|
local function f(closed)
|
||||||
|
if closed then
|
||||||
|
if not previous then
|
||||||
|
device:set_on(false)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
previous = device:is_on()
|
||||||
|
device:set_on(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
automation.device_manager:add(ContactSensor.new({
|
automation.device_manager:add(ContactSensor.new({
|
||||||
identifier = "hallway_trash",
|
identifier = "hallway_trash",
|
||||||
topic = mqtt_z2m("hallway/trash"),
|
topic = mqtt_z2m("hallway/trash"),
|
||||||
client = mqtt_client,
|
client = mqtt_client,
|
||||||
trigger = {
|
trigger = { devices = {} },
|
||||||
devices = { hallway_lights },
|
action = trash_action(hallway_lights),
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
local bedroom_air_filter = AirFilter.new({
|
local bedroom_air_filter = AirFilter.new({
|
||||||
|
@ -189,3 +223,7 @@ end)
|
||||||
automation.device_manager:schedule("0 0 20 * * *", function()
|
automation.device_manager:schedule("0 0 20 * * *", function()
|
||||||
bedroom_air_filter:set_on(false)
|
bedroom_air_filter:set_on(false)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
automation.timeout(10, function()
|
||||||
|
print("Cool stuff")
|
||||||
|
end)
|
||||||
|
|
|
@ -44,6 +44,17 @@ impl mlua::UserData for WrappedDevice {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
methods.add_async_method("is_on", |_lua, this, _: ()| async move {
|
||||||
|
let device = this.0.read().await;
|
||||||
|
let device = device.as_ref();
|
||||||
|
|
||||||
|
if let Some(device) = device.cast() as Option<&dyn OnOff> {
|
||||||
|
return Ok(device.on().await.unwrap());
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(false)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,8 +221,7 @@ impl mlua::UserData for DeviceManager {
|
||||||
let uuid = this.scheduler.add(job).await.unwrap();
|
let uuid = this.scheduler.add(job).await.unwrap();
|
||||||
|
|
||||||
// Store the function in the registry
|
// Store the function in the registry
|
||||||
lua.set_named_registry_value(uuid.to_string().as_str(), f)
|
lua.set_named_registry_value(&uuid.to_string(), f).unwrap();
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,6 +6,7 @@ use automation_macro::LuaDeviceConfig;
|
||||||
use google_home::traits::OnOff;
|
use google_home::traits::OnOff;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
use tokio_util::task::LocalPoolHandle;
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use super::{Device, LuaDeviceCreate};
|
use super::{Device, LuaDeviceCreate};
|
||||||
|
@ -17,6 +18,7 @@ use crate::event::{OnMqtt, OnPresence};
|
||||||
use crate::messages::{ContactMessage, PresenceMessage};
|
use crate::messages::{ContactMessage, PresenceMessage};
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
use crate::traits::Timeout;
|
use crate::traits::Timeout;
|
||||||
|
use crate::LUA;
|
||||||
|
|
||||||
// NOTE: If we add more presence devices we might need to move this out of here
|
// NOTE: If we add more presence devices we might need to move this out of here
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
|
@ -35,6 +37,37 @@ pub struct TriggerConfig {
|
||||||
pub timeout: Option<Duration>,
|
pub timeout: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ActionCallback(uuid::Uuid);
|
||||||
|
|
||||||
|
impl<'lua> FromLua<'lua> for ActionCallback {
|
||||||
|
fn from_lua(value: mlua::Value<'lua>, lua: &'lua mlua::Lua) -> mlua::Result<Self> {
|
||||||
|
let uuid = uuid::Uuid::new_v4();
|
||||||
|
lua.set_named_registry_value(&uuid.to_string(), value)?;
|
||||||
|
|
||||||
|
Ok(ActionCallback(uuid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActionCallback {
|
||||||
|
async fn call(&self, closed: bool) {
|
||||||
|
let pool = LocalPoolHandle::new(1);
|
||||||
|
let uuid = self.0;
|
||||||
|
|
||||||
|
pool.spawn_pinned(move || async move {
|
||||||
|
let lua = LUA.lock().await;
|
||||||
|
let action: mlua::Value = lua.named_registry_value(&uuid.to_string())?;
|
||||||
|
match action {
|
||||||
|
mlua::Value::Function(f) => f.call_async::<_, ()>(closed).await,
|
||||||
|
_ => todo!("Only functions are currently supported"),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub identifier: String,
|
pub identifier: String,
|
||||||
|
@ -46,6 +79,8 @@ pub struct Config {
|
||||||
pub trigger: Option<TriggerConfig>,
|
pub trigger: Option<TriggerConfig>,
|
||||||
#[device_config(from_lua)]
|
#[device_config(from_lua)]
|
||||||
pub client: WrappedAsyncClient,
|
pub client: WrappedAsyncClient,
|
||||||
|
#[device_config(from_lua)]
|
||||||
|
pub action: ActionCallback,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -152,6 +187,8 @@ impl OnMqtt for ContactSensor {
|
||||||
debug!(id = self.get_id(), "Updating state to {is_closed}");
|
debug!(id = self.get_id(), "Updating state to {is_closed}");
|
||||||
self.state_mut().await.is_closed = is_closed;
|
self.state_mut().await.is_closed = is_closed;
|
||||||
|
|
||||||
|
self.config.action.call(self.is_closed).await;
|
||||||
|
|
||||||
if let Some(trigger) = &self.config.trigger {
|
if let Some(trigger) = &self.config.trigger {
|
||||||
if !is_closed {
|
if !is_closed {
|
||||||
for (light, previous) in trigger
|
for (light, previous) in trigger
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
#![feature(specialization)]
|
#![feature(specialization)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
#![feature(unboxed_closures)]
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -16,6 +16,7 @@ use dotenvy::dotenv;
|
||||||
use google_home::{GoogleHome, Request, Response};
|
use google_home::{GoogleHome, Request, Response};
|
||||||
use mlua::LuaSerdeExt;
|
use mlua::LuaSerdeExt;
|
||||||
use rumqttc::AsyncClient;
|
use rumqttc::AsyncClient;
|
||||||
|
use tokio_util::task::LocalPoolHandle;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -96,6 +97,21 @@ async fn app() -> anyhow::Result<()> {
|
||||||
automation.set("new_mqtt_client", new_mqtt_client)?;
|
automation.set("new_mqtt_client", new_mqtt_client)?;
|
||||||
automation.set("device_manager", device_manager.clone())?;
|
automation.set("device_manager", device_manager.clone())?;
|
||||||
|
|
||||||
|
let timeout = lua.create_function(|lua, (t, f): (u64, mlua::Function)| {
|
||||||
|
let pool = LocalPoolHandle::new(1);
|
||||||
|
let key = lua.create_registry_value(f).unwrap();
|
||||||
|
pool.spawn_pinned(move || async move {
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(t)).await;
|
||||||
|
let lua = LUA.lock().await;
|
||||||
|
let f: mlua::Function = lua.registry_value(&key).unwrap();
|
||||||
|
f.call_async::<_, ()>(()).await.unwrap();
|
||||||
|
lua.remove_registry_value(key).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
automation.set("timeout", timeout)?;
|
||||||
|
|
||||||
let util = lua.create_table()?;
|
let util = lua.create_table()?;
|
||||||
let get_env = lua.create_function(|_lua, name: String| {
|
let get_env = lua.create_function(|_lua, name: String| {
|
||||||
std::env::var(name).map_err(mlua::ExternalError::into_lua_err)
|
std::env::var(name).map_err(mlua::ExternalError::into_lua_err)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user