Devices now keep type in lua
All checks were successful
Build and deploy / Build application (push) Successful in 4m5s
Build and deploy / Build container (push) Successful in 1m9s
Build and deploy / Deploy container (push) Successful in 37s

This commit is contained in:
Dreaded_X 2024-08-08 01:31:29 +02:00
parent b0467b8012
commit d11e79cdfa
Signed by: Dreaded_X
GPG Key ID: FA5F485356B0D2D4
6 changed files with 76 additions and 60 deletions

7
Cargo.lock generated
View File

@ -106,6 +106,7 @@ dependencies = [
"futures", "futures",
"google_home", "google_home",
"hostname", "hostname",
"impls",
"indexmap 2.2.6", "indexmap 2.2.6",
"mlua", "mlua",
"once_cell", "once_cell",
@ -877,6 +878,12 @@ dependencies = [
"unicode-normalization", "unicode-normalization",
] ]
[[package]]
name = "impls"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a46645bbd70538861a90d0f26c31537cdf1e44aae99a794fb75a664b70951bc"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.9.3" version = "1.9.3"

View File

@ -56,6 +56,7 @@ 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 = "1.8.0"
dyn-clone = "1.0.17" dyn-clone = "1.0.17"
impls = "1.0.3"
[patch.crates-io] [patch.crates-io]
wakey = { git = "https://git.huizinga.dev/Dreaded_X/wakey" } wakey = { git = "https://git.huizinga.dev/Dreaded_X/wakey" }

View File

@ -1,12 +1,9 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Deref;
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use futures::future::join_all; use futures::future::join_all;
use futures::Future; use futures::Future;
use google_home::traits::OnOff;
use mlua::FromLua;
use tokio::sync::{RwLock, RwLockReadGuard}; use tokio::sync::{RwLock, RwLockReadGuard};
use tokio_cron_scheduler::{Job, JobScheduler}; use tokio_cron_scheduler::{Job, JobScheduler};
use tokio_util::task::LocalPoolHandle; use tokio_util::task::LocalPoolHandle;
@ -16,37 +13,6 @@ use crate::devices::Device;
use crate::event::{Event, EventChannel, OnDarkness, OnMqtt, OnNotification, OnPresence}; use crate::event::{Event, EventChannel, OnDarkness, OnMqtt, OnNotification, OnPresence};
use crate::LUA; use crate::LUA;
#[derive(Debug, FromLua, Clone)]
pub struct WrappedDevice(Box<dyn Device>);
impl WrappedDevice {
pub fn new(device: impl Device + 'static) -> Self {
Self(Box::new(device))
}
}
impl Deref for WrappedDevice {
type Target = Box<dyn Device>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl mlua::UserData for WrappedDevice {
fn add_methods<'lua, M: mlua::prelude::LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("get_id", |_lua, this, _: ()| async { Ok(this.get_id()) });
methods.add_async_method("set_on", |_lua, this, on: bool| async move {
if let Some(device) = this.cast() as Option<&dyn OnOff> {
device.set_on(on).await.unwrap()
};
Ok(())
});
}
}
pub type DeviceMap = HashMap<String, Box<dyn Device>>; pub type DeviceMap = HashMap<String, Box<dyn Device>>;
#[derive(Clone)] #[derive(Clone)]
@ -195,8 +161,8 @@ fn run_schedule(
impl mlua::UserData for DeviceManager { impl mlua::UserData for DeviceManager {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("add", |_lua, this, device: WrappedDevice| async move { methods.add_async_method("add", |_lua, this, device: Box<dyn Device>| async move {
this.add(device.0).await; this.add(device).await;
Ok(()) Ok(())
}); });

View File

@ -5,7 +5,6 @@ use tracing::{debug, error, trace, warn};
use super::{Device, LuaDeviceCreate}; use super::{Device, LuaDeviceCreate};
use crate::config::MqttDeviceConfig; use crate::config::MqttDeviceConfig;
use crate::device_manager::WrappedDevice;
use crate::error::DeviceConfigError; use crate::error::DeviceConfigError;
use crate::event::{OnMqtt, OnPresence}; use crate::event::{OnMqtt, OnPresence};
use crate::messages::{RemoteAction, RemoteMessage}; use crate::messages::{RemoteAction, RemoteMessage};
@ -17,9 +16,9 @@ pub struct Config {
#[device_config(flatten)] #[device_config(flatten)]
pub mqtt: MqttDeviceConfig, pub mqtt: MqttDeviceConfig,
#[device_config(from_lua)] #[device_config(from_lua)]
pub mixer: WrappedDevice, pub mixer: Box<dyn Device>,
#[device_config(from_lua)] #[device_config(from_lua)]
pub speakers: WrappedDevice, pub speakers: Box<dyn Device>,
#[device_config(from_lua)] #[device_config(from_lua)]
pub client: WrappedAsyncClient, pub client: WrappedAsyncClient,
} }

View File

@ -10,7 +10,6 @@ use tracing::{debug, error, trace, warn};
use super::{Device, LuaDeviceCreate}; use super::{Device, LuaDeviceCreate};
use crate::config::MqttDeviceConfig; use crate::config::MqttDeviceConfig;
use crate::device_manager::WrappedDevice;
use crate::devices::DEFAULT_PRESENCE; use crate::devices::DEFAULT_PRESENCE;
use crate::error::DeviceConfigError; use crate::error::DeviceConfigError;
use crate::event::{OnMqtt, OnPresence}; use crate::event::{OnMqtt, OnPresence};
@ -30,7 +29,7 @@ pub struct PresenceDeviceConfig {
#[derive(Debug, Clone, LuaDeviceConfig)] #[derive(Debug, Clone, LuaDeviceConfig)]
pub struct TriggerConfig { pub struct TriggerConfig {
#[device_config(from_lua)] #[device_config(from_lua)]
pub devices: Vec<WrappedDevice>, pub devices: Vec<Box<dyn Device>>,
#[device_config(default, with(|t: Option<_>| t.map(Duration::from_secs)))] #[device_config(default, with(|t: Option<_>| t.map(Duration::from_secs)))]
pub timeout: Option<Duration>, pub timeout: Option<Duration>,
} }

View File

@ -18,6 +18,7 @@ use async_trait::async_trait;
use automation_cast::Cast; use automation_cast::Cast;
use dyn_clone::DynClone; use dyn_clone::DynClone;
use google_home::traits::OnOff; use google_home::traits::OnOff;
use mlua::AnyUserDataExt;
pub use self::air_filter::AirFilter; pub use self::air_filter::AirFilter;
pub use self::audio_setup::AudioSetup; pub use self::audio_setup::AudioSetup;
@ -53,37 +54,61 @@ macro_rules! register_device {
} }
macro_rules! impl_device { macro_rules! impl_device {
($lua:expr, $device:ty) => { ($device:ty) => {
impl mlua::UserData for $device { impl mlua::UserData for $device {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_function("new", |lua, config: mlua::Value| async { methods.add_async_function("new", |_lua, config| async {
let config = mlua::FromLua::from_lua(config, lua)?;
// TODO: Using crate:: could cause issues
let device: $device = crate::devices::LuaDeviceCreate::create(config) let device: $device = crate::devices::LuaDeviceCreate::create(config)
.await .await
.map_err(mlua::ExternalError::into_lua_err)?; .map_err(mlua::ExternalError::into_lua_err)?;
Ok(crate::device_manager::WrappedDevice::new(device)) Ok(device)
}); });
methods.add_method("__box", |_lua, this, _: ()| {
let b: Box<dyn Device> = Box::new(this.clone());
Ok(b)
});
methods.add_async_method("get_id", |_lua, this, _: ()| async { Ok(this.get_id()) });
if impls::impls!($device: OnOff) {
methods.add_async_method("set_on", |_lua, this, on: bool| async move {
(this.cast() as Option<&dyn OnOff>)
.unwrap()
.set_on(on)
.await
.unwrap();
Ok(())
});
methods.add_async_method("is_on", |_lua, this, _: ()| async move {
Ok((this.cast() as Option<&dyn OnOff>)
.unwrap()
.on()
.await
.unwrap())
});
}
} }
} }
}; };
} }
impl_device!(lua, AirFilter); impl_device!(AirFilter);
impl_device!(lua, AudioSetup); impl_device!(AudioSetup);
impl_device!(lua, ContactSensor); impl_device!(ContactSensor);
impl_device!(lua, DebugBridge); impl_device!(DebugBridge);
impl_device!(lua, HueBridge); impl_device!(HueBridge);
impl_device!(lua, HueGroup); impl_device!(HueGroup);
impl_device!(lua, IkeaOutlet); impl_device!(IkeaOutlet);
impl_device!(lua, KasaOutlet); impl_device!(KasaOutlet);
impl_device!(lua, LightSensor); impl_device!(LightSensor);
impl_device!(lua, Ntfy); impl_device!(Ntfy);
impl_device!(lua, Presence); impl_device!(Presence);
impl_device!(lua, WakeOnLAN); impl_device!(WakeOnLAN);
impl_device!(lua, Washer); impl_device!(Washer);
pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> { pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> {
register_device!(lua, AirFilter); register_device!(lua, AirFilter);
@ -120,4 +145,23 @@ pub trait Device:
fn get_id(&self) -> String; fn get_id(&self) -> String;
} }
impl<'lua> mlua::FromLua<'lua> for Box<dyn Device> {
fn from_lua(value: mlua::Value<'lua>, _lua: &'lua mlua::Lua) -> mlua::Result<Self> {
match value {
mlua::Value::UserData(ud) => {
let ud = if ud.is::<Box<dyn Device>>() {
ud
} else {
ud.call_method::<_, mlua::AnyUserData>("__box", ())?
};
let b = ud.borrow::<Self>()?.clone();
Ok(b)
}
_ => Err(mlua::Error::RuntimeError("Expected user data".into())),
}
}
}
impl mlua::UserData for Box<dyn Device> {}
dyn_clone::clone_trait_object!(Device); dyn_clone::clone_trait_object!(Device);