161 lines
3.7 KiB
Rust
161 lines
3.7 KiB
Rust
#![feature(debug_closure_helpers)]
|
|
#![feature(iter_intersperse)]
|
|
mod air_filter;
|
|
mod bambu;
|
|
mod contact_sensor;
|
|
mod hue_bridge;
|
|
mod hue_group;
|
|
mod hue_switch;
|
|
mod ikea_remote;
|
|
mod kasa_outlet;
|
|
mod light_sensor;
|
|
mod ntfy;
|
|
mod presence;
|
|
mod wake_on_lan;
|
|
mod washer;
|
|
mod zigbee;
|
|
|
|
use std::fmt;
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
use automation_lib::Module;
|
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
|
use tracing::{debug, warn};
|
|
|
|
type DeviceNameFn = fn() -> String;
|
|
type RegisterDeviceFn = fn(lua: &mlua::Lua) -> mlua::Result<mlua::AnyUserData>;
|
|
|
|
#[derive(Clone)]
|
|
struct DebugWrap<T: Clone>(T);
|
|
|
|
impl<T: Clone> DerefMut for DebugWrap<T> {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
impl<T: Clone> Deref for DebugWrap<T> {
|
|
type Target = T;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl<T: Clone> fmt::Debug for DebugWrap<T> {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_tuple("DebugWrap")
|
|
.field_with(|f| f.write_str(stringify!(T)))
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
pub struct RegisteredDevice {
|
|
name_fn: DeviceNameFn,
|
|
register_fn: RegisterDeviceFn,
|
|
}
|
|
|
|
impl RegisteredDevice {
|
|
pub const fn new(name_fn: DeviceNameFn, register_fn: RegisterDeviceFn) -> Self {
|
|
Self {
|
|
name_fn,
|
|
register_fn,
|
|
}
|
|
}
|
|
|
|
pub fn get_name(&self) -> String {
|
|
(self.name_fn)()
|
|
}
|
|
|
|
pub fn register(&self, lua: &mlua::Lua) -> mlua::Result<mlua::AnyUserData> {
|
|
(self.register_fn)(lua)
|
|
}
|
|
}
|
|
|
|
macro_rules! register_device {
|
|
($device:ty) => {
|
|
::inventory::submit!(crate::RegisteredDevice::new(
|
|
<$device as ::lua_typed::Typed>::type_name,
|
|
::mlua::Lua::create_proxy::<$device>
|
|
));
|
|
|
|
crate::register_type!($device);
|
|
};
|
|
}
|
|
pub(crate) use register_device;
|
|
|
|
inventory::collect!(RegisteredDevice);
|
|
|
|
pub fn create_module(lua: &mlua::Lua) -> mlua::Result<mlua::Table> {
|
|
let devices = lua.create_table()?;
|
|
|
|
debug!("Loading devices...");
|
|
for device in inventory::iter::<RegisteredDevice> {
|
|
let name = device.get_name();
|
|
debug!(name, "Registering device");
|
|
let proxy = device.register(lua)?;
|
|
devices.set(name, proxy)?;
|
|
}
|
|
|
|
Ok(devices)
|
|
}
|
|
|
|
type TypeNameFn = fn() -> String;
|
|
type TypeDefinitionFn = fn() -> Option<String>;
|
|
|
|
pub struct RegisteredType {
|
|
name_fn: TypeNameFn,
|
|
definition_fn: TypeDefinitionFn,
|
|
}
|
|
|
|
impl RegisteredType {
|
|
pub const fn new(name_fn: TypeNameFn, definition_fn: TypeDefinitionFn) -> Self {
|
|
Self {
|
|
name_fn,
|
|
definition_fn,
|
|
}
|
|
}
|
|
|
|
pub fn get_name(&self) -> String {
|
|
(self.name_fn)()
|
|
}
|
|
|
|
pub fn register(&self) -> Option<String> {
|
|
(self.definition_fn)()
|
|
}
|
|
}
|
|
|
|
macro_rules! register_type {
|
|
($ty:ty) => {
|
|
::inventory::submit!(crate::RegisteredType::new(
|
|
<$ty as ::lua_typed::Typed>::type_name,
|
|
<$ty as ::lua_typed::Typed>::generate_full
|
|
));
|
|
};
|
|
}
|
|
pub(crate) use register_type;
|
|
|
|
inventory::collect!(RegisteredType);
|
|
|
|
fn generate_definitions() -> String {
|
|
let mut output = String::new();
|
|
|
|
let mut types: Vec<_> = inventory::iter::<RegisteredType>.into_iter().collect();
|
|
types.sort_by_key(|ty| ty.get_name());
|
|
|
|
output += "---@meta\n\nlocal devices\n\n";
|
|
for ty in types {
|
|
if let Some(def) = (ty.definition_fn)() {
|
|
output += &(def + "\n");
|
|
} else {
|
|
// NOTE: Due to how this works the typed is erased, so we don't know the cause
|
|
warn!("Registered type is missing generate_full function");
|
|
}
|
|
}
|
|
output += "return devices";
|
|
|
|
output
|
|
}
|
|
|
|
inventory::submit! {Module::new("automation:devices", create_module, Some(generate_definitions))}
|