From cebdac2fe0b0df9b933313853fecb1c58979caea Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Tue, 30 Apr 2024 02:08:29 +0200 Subject: [PATCH] Further work on automatically generating lua type definitions --- .gitignore | 1 - automation_macro/src/lib.rs | 11 +- automation_macro/src/lua_device.rs | 31 ++-- automation_macro/src/lua_device_config.rs | 36 ++-- automation_macro/src/lua_type_definition.rs | 137 +++++++++++++++ definitions/definitions.lua | 47 +++-- definitions/generated.lua | 183 ++++++++++++++++++++ definitions/rust.lua | 10 ++ src/bin/generate_definitions.rs | 57 ++++++ src/config.rs | 5 +- src/devices/air_filter.rs | 4 +- src/devices/audio_setup.rs | 4 +- src/devices/contact_sensor.rs | 12 +- src/devices/debug_bridge.rs | 4 +- src/devices/hue_bridge.rs | 10 +- src/devices/hue_group.rs | 8 +- src/devices/ikea_outlet.rs | 6 +- src/devices/kasa_outlet.rs | 8 +- src/devices/light_sensor.rs | 6 +- src/devices/mod.rs | 4 +- src/devices/ntfy.rs | 6 +- src/devices/presence.rs | 6 +- src/devices/wake_on_lan.rs | 4 +- src/devices/washer.rs | 9 +- src/event.rs | 2 +- 25 files changed, 510 insertions(+), 101 deletions(-) create mode 100644 automation_macro/src/lua_type_definition.rs create mode 100644 definitions/generated.lua create mode 100644 definitions/rust.lua create mode 100644 src/bin/generate_definitions.rs diff --git a/.gitignore b/.gitignore index 881bd77..fedaa2b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /target .env -/definitions/generated diff --git a/automation_macro/src/lib.rs b/automation_macro/src/lib.rs index 6e1654a..1073c06 100644 --- a/automation_macro/src/lib.rs +++ b/automation_macro/src/lib.rs @@ -1,11 +1,13 @@ mod lua_device; mod lua_device_config; +mod lua_type_definition; use lua_device::impl_lua_device_macro; use lua_device_config::impl_lua_device_config_macro; +use lua_type_definition::impl_lua_type_definition; use syn::{parse_macro_input, DeriveInput}; -#[proc_macro_derive(LuaDevice, attributes(config))] +#[proc_macro_derive(LuaDevice)] pub fn lua_device_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = parse_macro_input!(input as DeriveInput); @@ -18,3 +20,10 @@ pub fn lua_device_config_derive(input: proc_macro::TokenStream) -> proc_macro::T impl_lua_device_config_macro(&ast).into() } + +#[proc_macro_derive(LuaTypeDefinition, attributes(device_config))] +pub fn lua_type_definition_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + impl_lua_type_definition(&ast).into() +} diff --git a/automation_macro/src/lua_device.rs b/automation_macro/src/lua_device.rs index 0bbe216..35ee403 100644 --- a/automation_macro/src/lua_device.rs +++ b/automation_macro/src/lua_device.rs @@ -1,6 +1,3 @@ -use std::fs::File; -use std::io::Write; - use proc_macro2::TokenStream; use quote::quote; use syn::DeriveInput; @@ -12,6 +9,20 @@ pub fn impl_lua_device_macro(ast: &DeriveInput) -> TokenStream { pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> { lua.globals().set(stringify!(#name), lua.create_proxy::<#name>()?) } + + pub fn generate_lua_definition() -> String { + // TODO: Do not hardcode the name of the config type + let def = format!( + r#"--- @class {0} +{0} = {{}} +--- @param config {0}Config +--- @return WrappedDevice +function {0}.new(config) end +"#, stringify!(#name) + ); + + def + } } impl mlua::UserData for #name { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { @@ -27,19 +38,5 @@ pub fn impl_lua_device_macro(ast: &DeriveInput) -> TokenStream { } }; - let def = format!( - r#"--- @meta ---- @class {name} -{name} = {{}} ---- @param config {name}Config ---- @return Config -function {name}.new(config) end"# - ); - - File::create(format!("./definitions/generated/{name}.lua")) - .unwrap() - .write_all(def.as_bytes()) - .unwrap(); - gen } diff --git a/automation_macro/src/lua_device_config.rs b/automation_macro/src/lua_device_config.rs index 7b2b662..4cb009d 100644 --- a/automation_macro/src/lua_device_config.rs +++ b/automation_macro/src/lua_device_config.rs @@ -1,6 +1,3 @@ -use std::fs::File; -use std::io::Write; - use itertools::Itertools; use proc_macro2::TokenStream; use quote::{quote, quote_spanned}; @@ -26,7 +23,7 @@ mod kw { } #[derive(Debug)] -enum Argument { +pub enum Argument { Flatten { _keyword: kw::flatten, }, @@ -110,8 +107,8 @@ impl Parse for Argument { } #[derive(Debug)] -struct Args { - args: Punctuated, +pub(crate) struct Args { + pub(crate) args: Punctuated, } impl Parse for Args { @@ -221,6 +218,21 @@ fn field_from_lua(field: &Field) -> TokenStream { temp.into() } }), + _ => None, + }) + .collect::>() + .as_slice() + { + [] => value, + [value] => value.to_owned(), + _ => { + return quote_spanned! {field.span() => compile_error!("Field contains duplicate 'from'")} + } + }; + + let value = match args + .iter() + .filter_map(|arg| match arg { Argument::With { expr, .. } => Some(quote! { { let temp = #value; @@ -235,7 +247,7 @@ fn field_from_lua(field: &Field) -> TokenStream { [] => value, [value] => value.to_owned(), _ => { - return quote_spanned! {field.span() => compile_error!("Only one of either 'from' or 'with' is allowed")} + return quote_spanned! {field.span() => compile_error!("Field contains duplicate 'with'")} } }; @@ -279,15 +291,5 @@ pub fn impl_lua_device_config_macro(ast: &DeriveInput) -> TokenStream { } }; - let mut def = format!("--- @meta\n--- @class {name}\n"); - for field in fields { - def += &format!("--- @field {} any\n", field.ident.clone().unwrap()) - } - - File::create(format!("./definitions/generated/{name}.lua")) - .unwrap() - .write_all(def.as_bytes()) - .unwrap(); - impl_from_lua } diff --git a/automation_macro/src/lua_type_definition.rs b/automation_macro/src/lua_type_definition.rs new file mode 100644 index 0000000..e3e96ec --- /dev/null +++ b/automation_macro/src/lua_type_definition.rs @@ -0,0 +1,137 @@ +use itertools::Itertools; +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; +use syn::{ + AngleBracketedGenericArguments, Data, DataStruct, DeriveInput, Field, Fields, FieldsNamed, + PathArguments, Type, TypePath, +}; + +use crate::lua_device_config::{Args, Argument}; + +fn field_definition(field: &Field) -> TokenStream { + let (args, _): (Vec<_>, Vec<_>) = field + .attrs + .iter() + .filter_map(|attr| { + if attr.path().is_ident("device_config") { + Some(attr.parse_args::().map(|args| args.args)) + } else { + None + } + }) + .partition_result(); + let args: Vec<_> = args.into_iter().flatten().collect(); + + let field_name = if let Some(field_name) = args.iter().find_map(|arg| match arg { + Argument::Rename { ident, .. } => Some(ident), + _ => None, + }) { + field_name.value() + } else { + format!("{}", field.ident.clone().unwrap()) + }; + + let mut optional = args + .iter() + .filter(|arg| matches!(arg, Argument::Default { .. } | Argument::DefaultExpr { .. })) + .count() + >= 1; + + if args + .iter() + .filter(|arg| matches!(arg, Argument::Flatten { .. })) + .count() + >= 1 + { + let field_type = &field.ty; + quote! { + #field_type::generate_lua_fields().as_str() + } + } else { + let path = if let Some(ty) = args.iter().find_map(|arg| match arg { + Argument::From { ty, .. } => Some(ty), + _ => None, + }) { + if let Type::Path(TypePath { path, .. }) = ty { + path.clone() + } else { + todo!(); + } + } else if let Type::Path(TypePath { path, .. }) = field.ty.clone() { + path + } else { + todo!() + }; + + let seg = path.segments.first().unwrap(); + let field_type = if seg.ident == "Option" { + if let PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) = + seg.arguments.clone() + { + optional = true; + quote! { stringify!(#args) } + } else { + unreachable!("Option should always have angle brackets"); + } + } else if seg.ident == "Vec" { + if let PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) = + seg.arguments.clone() + { + optional = true; + quote! { stringify!(#args[]) } + } else { + unreachable!("Option should always have angle brackets"); + } + } else { + quote! { stringify!(#path).replace(" :: ", "_") } + }; + + let mut format = "--- @field {} {}".to_string(); + if optional { + format += "|nil"; + } + format += "\n"; + + quote! { + format!(#format, #field_name, #field_type).as_str() + } + } +} + +pub fn impl_lua_type_definition(ast: &DeriveInput) -> TokenStream { + let name = &ast.ident; + let fields = if let Data::Struct(DataStruct { + fields: Fields::Named(FieldsNamed { ref named, .. }), + .. + }) = ast.data + { + named + } else { + return quote_spanned! {ast.span() => compile_error!("This macro only works on named structs")}; + }; + + let fields: Vec<_> = fields.iter().map(field_definition).collect(); + + let gen = quote! { + impl #name { + pub fn generate_lua_definition() -> String { + let mut def = format!("--- @class {}\n", stringify!(#name)); + + def += #name::generate_lua_fields().as_str(); + + def + } + + pub fn generate_lua_fields() -> String { + let mut def = String::new(); + + #(def += #fields;)* + + def + } + } + }; + + gen +} diff --git a/definitions/definitions.lua b/definitions/definitions.lua index d3f9b00..95f199f 100644 --- a/definitions/definitions.lua +++ b/definitions/definitions.lua @@ -1,27 +1,40 @@ --- @meta +--- @class WrappedDevice +WrappedDevice = {} +--- @return string +function WrappedDevice:get_id() end + +--- @class WrappedAsyncClient + +--- @class EventChannel +--- @return EventChannel +function automation.device_manager:event_channel() end + automation = {} ---- @class Device ---- @class Config - ---- @class DeviceManager automation.device_manager = {} +--- @param device WrappedDevice +function automation.device_manager:add(device) end ---- @class MqttClient -automation.mqtt_client = {} +--- @param when string +--- @param func function +function automation.device_manager:schedule(when, func) end ---- @param identifier string ---- @param config Config ---- @return Device -function automation.device_manager:create(identifier, config) end +automation.util = {} +--- @param env string +--- @return string +function automation.util.get_env(env) end ---- @class DebugBridge -DebugBridge = {} +--- @class Fulfillment +--- @field openid_url string|nil +automation.fulfillment = {} ---- @class DebugBridgeConfig ---- @field topic string +--- @class MqttConfig +--- @param config MqttConfig +--- @return WrappedAsyncClient +function automation.new_mqtt_client(config) end ---- @param config DebugBridgeConfig ---- @return Config -function DebugBridge.new(config) end +--- TODO: Generate this automatically +--- @alias OutletType "Outlet"|"Kettle"|"Charger"|"Light" +--- @alias TriggerDevicesHelper WrappedDevice[] diff --git a/definitions/generated.lua b/definitions/generated.lua new file mode 100644 index 0000000..aea344e --- /dev/null +++ b/definitions/generated.lua @@ -0,0 +1,183 @@ +-- WARN: This file is automatically generated, do not manually edit + +---@meta +--- @class MqttDeviceConfig +--- @field topic String + +--- @class AirFilter +AirFilter = {} +--- @param config AirFilterConfig +--- @return WrappedDevice +function AirFilter.new(config) end + +--- @class AirFilterConfig +--- @field name String +--- @field room String|nil +--- @field topic String +--- @field client WrappedAsyncClient + +--- @class AudioSetup +AudioSetup = {} +--- @param config AudioSetupConfig +--- @return WrappedDevice +function AudioSetup.new(config) end + +--- @class AudioSetupConfig +--- @field identifier String +--- @field topic String +--- @field mixer WrappedDevice +--- @field speakers WrappedDevice +--- @field client WrappedAsyncClient + +--- @class ContactSensor +ContactSensor = {} +--- @param config ContactSensorConfig +--- @return WrappedDevice +function ContactSensor.new(config) end + +--- @class ContactSensorConfig +--- @field identifier String +--- @field topic String +--- @field presence PresenceDeviceConfig|nil +--- @field trigger TriggerConfig|nil +--- @field client WrappedAsyncClient + +--- @class PresenceDeviceConfig +--- @field topic String +--- @field timeout u64 + +--- @class TriggerConfig +--- @field devices TriggerDevicesHelper +--- @field timeout u64|nil + +--- @class DebugBridge +DebugBridge = {} +--- @param config DebugBridgeConfig +--- @return WrappedDevice +function DebugBridge.new(config) end + +--- @class DebugBridgeConfig +--- @field identifier String +--- @field topic String +--- @field client WrappedAsyncClient + +--- @class HueBridge +HueBridge = {} +--- @param config HueBridgeConfig +--- @return WrappedDevice +function HueBridge.new(config) end + +--- @class HueBridgeConfig +--- @field identifier String +--- @field ip Ipv4Addr +--- @field login String +--- @field flags FlagIDs + +--- @class FlagIDs +--- @field presence isize +--- @field darkness isize + +--- @class HueGroup +HueGroup = {} +--- @param config HueGroupConfig +--- @return WrappedDevice +function HueGroup.new(config) end + +--- @class HueGroupConfig +--- @field identifier String +--- @field ip Ipv4Addr +--- @field login String +--- @field group_id isize +--- @field timer_id isize +--- @field scene_id String +--- @field remotes MqttDeviceConfig []|nil +--- @field client WrappedAsyncClient + +--- @class IkeaOutlet +IkeaOutlet = {} +--- @param config IkeaOutletConfig +--- @return WrappedDevice +function IkeaOutlet.new(config) end + +--- @class IkeaOutletConfig +--- @field name String +--- @field room String|nil +--- @field topic String +--- @field outlet_type OutletType|nil +--- @field timeout u64|nil +--- @field remotes MqttDeviceConfig []|nil +--- @field client WrappedAsyncClient + +--- @class KasaOutlet +KasaOutlet = {} +--- @param config KasaOutletConfig +--- @return WrappedDevice +function KasaOutlet.new(config) end + +--- @class KasaOutletConfig +--- @field identifier String +--- @field ip Ipv4Addr + +--- @class LightSensor +LightSensor = {} +--- @param config LightSensorConfig +--- @return WrappedDevice +function LightSensor.new(config) end + +--- @class LightSensorConfig +--- @field identifier String +--- @field topic String +--- @field min isize +--- @field max isize +--- @field event_channel EventChannel +--- @field client WrappedAsyncClient + +--- @class Ntfy +Ntfy = {} +--- @param config NtfyConfig +--- @return WrappedDevice +function Ntfy.new(config) end + +--- @class NtfyConfig +--- @field url String|nil +--- @field topic String +--- @field event_channel EventChannel + +--- @class Presence +Presence = {} +--- @param config PresenceConfig +--- @return WrappedDevice +function Presence.new(config) end + +--- @class PresenceConfig +--- @field topic String +--- @field event_channel EventChannel +--- @field client WrappedAsyncClient + +--- @class WakeOnLAN +WakeOnLAN = {} +--- @param config WakeOnLANConfig +--- @return WrappedDevice +function WakeOnLAN.new(config) end + +--- @class WakeOnLANConfig +--- @field name String +--- @field room String|nil +--- @field topic String +--- @field mac_address MacAddress +--- @field broadcast_ip Ipv4Addr|nil +--- @field client WrappedAsyncClient + +--- @class Washer +Washer = {} +--- @param config WasherConfig +--- @return WrappedDevice +function Washer.new(config) end + +--- @class WasherConfig +--- @field identifier String +--- @field topic String +--- @field threshold f32 +--- @field event_channel EventChannel +--- @field client WrappedAsyncClient + diff --git a/definitions/rust.lua b/definitions/rust.lua new file mode 100644 index 0000000..5ffbee4 --- /dev/null +++ b/definitions/rust.lua @@ -0,0 +1,10 @@ +--- @meta + +--- @alias String string + +--- @alias u64 number +--- @alias isize number +--- @alias f32 number + +--- @alias Ipv4Addr string +--- @alias MacAddress string diff --git a/src/bin/generate_definitions.rs b/src/bin/generate_definitions.rs new file mode 100644 index 0000000..8ea743b --- /dev/null +++ b/src/bin/generate_definitions.rs @@ -0,0 +1,57 @@ +use automation::config::MqttDeviceConfig; +use automation::devices::{ + AirFilter, AirFilterConfig, AudioSetup, AudioSetupConfig, ContactSensor, ContactSensorConfig, + DebugBridge, DebugBridgeConfig, FlagIDs, HueBridge, HueBridgeConfig, HueGroup, HueGroupConfig, + IkeaOutlet, IkeaOutletConfig, KasaOutlet, KasaOutletConfig, LightSensor, LightSensorConfig, + Ntfy, NtfyConfig, Presence, PresenceConfig, PresenceDeviceConfig, TriggerConfig, WakeOnLAN, + WakeOnLANConfig, Washer, WasherConfig, +}; + +fn main() { + println!("-- WARN: This file is automatically generated, do not manually edit\n"); + println!("---@meta"); + + println!("{}", MqttDeviceConfig::generate_lua_definition()); + + println!("{}", AirFilter::generate_lua_definition()); + println!("{}", AirFilterConfig::generate_lua_definition()); + + println!("{}", AudioSetup::generate_lua_definition()); + println!("{}", AudioSetupConfig::generate_lua_definition()); + + println!("{}", ContactSensor::generate_lua_definition()); + println!("{}", ContactSensorConfig::generate_lua_definition()); + println!("{}", PresenceDeviceConfig::generate_lua_definition()); + println!("{}", TriggerConfig::generate_lua_definition()); + + println!("{}", DebugBridge::generate_lua_definition()); + println!("{}", DebugBridgeConfig::generate_lua_definition()); + + println!("{}", HueBridge::generate_lua_definition()); + println!("{}", HueBridgeConfig::generate_lua_definition()); + println!("{}", FlagIDs::generate_lua_definition()); + + println!("{}", HueGroup::generate_lua_definition()); + println!("{}", HueGroupConfig::generate_lua_definition()); + + println!("{}", IkeaOutlet::generate_lua_definition()); + println!("{}", IkeaOutletConfig::generate_lua_definition()); + + println!("{}", KasaOutlet::generate_lua_definition()); + println!("{}", KasaOutletConfig::generate_lua_definition()); + + println!("{}", LightSensor::generate_lua_definition()); + println!("{}", LightSensorConfig::generate_lua_definition()); + + println!("{}", Ntfy::generate_lua_definition()); + println!("{}", NtfyConfig::generate_lua_definition()); + + println!("{}", Presence::generate_lua_definition()); + println!("{}", PresenceConfig::generate_lua_definition()); + + println!("{}", WakeOnLAN::generate_lua_definition()); + println!("{}", WakeOnLANConfig::generate_lua_definition()); + + println!("{}", Washer::generate_lua_definition()); + println!("{}", WasherConfig::generate_lua_definition()); +} diff --git a/src/config.rs b/src/config.rs index 42bee56..e4c9451 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,7 @@ use std::net::{Ipv4Addr, SocketAddr}; use std::time::Duration; +use automation_macro::LuaTypeDefinition; use rumqttc::{MqttOptions, Transport}; use serde::Deserialize; @@ -52,7 +53,7 @@ fn default_fulfillment_port() -> u16 { 7878 } -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Deserialize, LuaTypeDefinition)] pub struct InfoConfig { pub name: String, pub room: Option, @@ -68,7 +69,7 @@ impl InfoConfig { } } -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Deserialize, LuaTypeDefinition)] pub struct MqttDeviceConfig { pub topic: String, } diff --git a/src/devices/air_filter.rs b/src/devices/air_filter.rs index 0fad2d9..8b820f7 100644 --- a/src/devices/air_filter.rs +++ b/src/devices/air_filter.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use google_home::device::Name; use google_home::errors::ErrorCode; use google_home::traits::{AvailableSpeeds, FanSpeed, HumiditySetting, OnOff, Speed, SpeedValues}; @@ -15,7 +15,7 @@ use crate::event::OnMqtt; use crate::messages::{AirFilterFanState, AirFilterState, SetAirFilterFanState}; use crate::mqtt::WrappedAsyncClient; -#[derive(Debug, Clone, LuaDeviceConfig)] +#[derive(Debug, Clone, LuaDeviceConfig, LuaTypeDefinition)] pub struct AirFilterConfig { #[device_config(flatten)] pub info: InfoConfig, diff --git a/src/devices/audio_setup.rs b/src/devices/audio_setup.rs index f6c6925..38a7753 100644 --- a/src/devices/audio_setup.rs +++ b/src/devices/audio_setup.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use google_home::traits::OnOff; use tracing::{debug, error, trace, warn}; @@ -11,7 +11,7 @@ use crate::event::{OnMqtt, OnPresence}; use crate::messages::{RemoteAction, RemoteMessage}; use crate::mqtt::WrappedAsyncClient; -#[derive(Debug, Clone, LuaDeviceConfig)] +#[derive(Debug, Clone, LuaDeviceConfig, LuaTypeDefinition)] pub struct AudioSetupConfig { pub identifier: String, #[device_config(flatten)] diff --git a/src/devices/contact_sensor.rs b/src/devices/contact_sensor.rs index a8e58c9..b073b2d 100644 --- a/src/devices/contact_sensor.rs +++ b/src/devices/contact_sensor.rs @@ -1,7 +1,7 @@ use std::time::Duration; use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use google_home::traits::OnOff; use mlua::FromLua; use tokio::task::JoinHandle; @@ -18,11 +18,11 @@ use crate::mqtt::WrappedAsyncClient; use crate::traits::Timeout; // NOTE: If we add more presence devices we might need to move this out of here -#[derive(Debug, Clone, LuaDeviceConfig)] +#[derive(Debug, Clone, LuaDeviceConfig, LuaTypeDefinition)] pub struct PresenceDeviceConfig { #[device_config(flatten)] pub mqtt: MqttDeviceConfig, - #[device_config(with(Duration::from_secs))] + #[device_config(from(u64), with(Duration::from_secs))] pub timeout: Duration, } @@ -41,15 +41,15 @@ impl From for Vec<(WrappedDevice, bool)> { } } -#[derive(Debug, Clone, LuaDeviceConfig)] +#[derive(Debug, Clone, LuaDeviceConfig, LuaTypeDefinition)] pub struct TriggerConfig { #[device_config(from_lua, from(TriggerDevicesHelper))] pub devices: Vec<(WrappedDevice, bool)>, - #[device_config(default, with(|t: Option<_>| t.map(Duration::from_secs)))] + #[device_config(default, from(Option), with(|t: Option<_>| t.map(Duration::from_secs)))] pub timeout: Option, } -#[derive(Debug, Clone, LuaDeviceConfig)] +#[derive(Debug, Clone, LuaDeviceConfig, LuaTypeDefinition)] pub struct ContactSensorConfig { pub identifier: String, #[device_config(flatten)] diff --git a/src/devices/debug_bridge.rs b/src/devices/debug_bridge.rs index a4de62c..c6f81b4 100644 --- a/src/devices/debug_bridge.rs +++ b/src/devices/debug_bridge.rs @@ -1,7 +1,7 @@ use std::convert::Infallible; use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use tracing::{trace, warn}; use super::LuaDeviceCreate; @@ -11,7 +11,7 @@ use crate::event::{OnDarkness, OnPresence}; use crate::messages::{DarknessMessage, PresenceMessage}; use crate::mqtt::WrappedAsyncClient; -#[derive(Debug, LuaDeviceConfig, Clone)] +#[derive(Debug, LuaDeviceConfig, Clone, LuaTypeDefinition)] pub struct DebugBridgeConfig { pub identifier: String, #[device_config(flatten)] diff --git a/src/devices/hue_bridge.rs b/src/devices/hue_bridge.rs index 2fc0fe1..860b7e3 100644 --- a/src/devices/hue_bridge.rs +++ b/src/devices/hue_bridge.rs @@ -1,8 +1,8 @@ use std::convert::Infallible; -use std::net::SocketAddr; +use std::net::{Ipv4Addr, SocketAddr}; use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use serde::{Deserialize, Serialize}; use tracing::{error, trace, warn}; @@ -16,16 +16,16 @@ pub enum Flag { Darkness, } -#[derive(Debug, Clone, Deserialize)] +#[derive(Debug, Clone, Deserialize, LuaTypeDefinition)] pub struct FlagIDs { presence: isize, darkness: isize, } -#[derive(Debug, LuaDeviceConfig, Clone)] +#[derive(Debug, LuaDeviceConfig, Clone, LuaTypeDefinition)] pub struct HueBridgeConfig { pub identifier: String, - #[device_config(rename("ip"), with(|ip| SocketAddr::new(ip, 80)))] + #[device_config(rename("ip"), from(Ipv4Addr), with(|ip| SocketAddr::new(ip, 80)))] pub addr: SocketAddr, pub login: String, pub flags: FlagIDs, diff --git a/src/devices/hue_group.rs b/src/devices/hue_group.rs index fdb8517..aff3342 100644 --- a/src/devices/hue_group.rs +++ b/src/devices/hue_group.rs @@ -1,9 +1,9 @@ -use std::net::SocketAddr; +use std::net::{Ipv4Addr, SocketAddr}; use std::time::Duration; use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use google_home::errors::ErrorCode; use google_home::traits::OnOff; use rumqttc::{Publish, SubscribeFilter}; @@ -16,10 +16,10 @@ use crate::messages::{RemoteAction, RemoteMessage}; use crate::mqtt::WrappedAsyncClient; use crate::traits::Timeout; -#[derive(Debug, Clone, LuaDeviceConfig)] +#[derive(Debug, Clone, LuaDeviceConfig, LuaTypeDefinition)] pub struct HueGroupConfig { pub identifier: String, - #[device_config(rename("ip"), with(|ip| SocketAddr::new(ip, 80)))] + #[device_config(rename("ip"), from(Ipv4Addr), with(|ip| SocketAddr::new(ip, 80)))] pub addr: SocketAddr, pub login: String, pub group_id: isize, diff --git a/src/devices/ikea_outlet.rs b/src/devices/ikea_outlet.rs index 3330d9c..ce23408 100644 --- a/src/devices/ikea_outlet.rs +++ b/src/devices/ikea_outlet.rs @@ -2,7 +2,7 @@ use std::time::Duration; use anyhow::Result; use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use google_home::errors::ErrorCode; use google_home::traits::{self, OnOff}; use google_home::types::Type; @@ -28,7 +28,7 @@ pub enum OutletType { Light, } -#[derive(Debug, Clone, LuaDeviceConfig)] +#[derive(Debug, Clone, LuaDeviceConfig, LuaTypeDefinition)] pub struct IkeaOutletConfig { #[device_config(flatten)] pub info: InfoConfig, @@ -36,7 +36,7 @@ pub struct IkeaOutletConfig { pub mqtt: MqttDeviceConfig, #[device_config(default(OutletType::Outlet))] pub outlet_type: OutletType, - #[device_config(default, with(|t: Option<_>| t.map(Duration::from_secs)))] + #[device_config(default, from(Option), with(|t: Option<_>| t.map(Duration::from_secs)))] pub timeout: Option, #[device_config(default)] pub remotes: Vec, diff --git a/src/devices/kasa_outlet.rs b/src/devices/kasa_outlet.rs index 4bb35da..36b373e 100644 --- a/src/devices/kasa_outlet.rs +++ b/src/devices/kasa_outlet.rs @@ -1,9 +1,9 @@ use std::convert::Infallible; -use std::net::SocketAddr; +use std::net::{Ipv4Addr, SocketAddr}; use std::str::Utf8Error; use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use bytes::{Buf, BufMut}; use google_home::errors::{self, DeviceError}; use google_home::traits; @@ -15,10 +15,10 @@ use tracing::trace; use super::{Device, LuaDeviceCreate}; -#[derive(Debug, Clone, LuaDeviceConfig)] +#[derive(Debug, Clone, LuaDeviceConfig, LuaTypeDefinition)] pub struct KasaOutletConfig { pub identifier: String, - #[device_config(rename("ip"), with(|ip| SocketAddr::new(ip, 9999)))] + #[device_config(rename("ip"), from(Ipv4Addr), with(|ip| SocketAddr::new(ip, 9999)))] pub addr: SocketAddr, } diff --git a/src/devices/light_sensor.rs b/src/devices/light_sensor.rs index 041aa57..cc167f2 100644 --- a/src/devices/light_sensor.rs +++ b/src/devices/light_sensor.rs @@ -1,5 +1,5 @@ use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use rumqttc::Publish; use tracing::{debug, trace, warn}; @@ -10,14 +10,14 @@ use crate::event::{self, Event, EventChannel, OnMqtt}; use crate::messages::BrightnessMessage; use crate::mqtt::WrappedAsyncClient; -#[derive(Debug, Clone, LuaDeviceConfig)] +#[derive(Debug, Clone, LuaDeviceConfig, LuaTypeDefinition)] pub struct LightSensorConfig { pub identifier: String, #[device_config(flatten)] pub mqtt: MqttDeviceConfig, pub min: isize, pub max: isize, - #[device_config(rename("event_channel"), from_lua, with(|ec: EventChannel| ec.get_tx()))] + #[device_config(rename("event_channel"), from(EventChannel), from_lua, with(|ec: EventChannel| ec.get_tx()))] pub tx: event::Sender, #[device_config(from_lua)] pub client: WrappedAsyncClient, diff --git a/src/devices/mod.rs b/src/devices/mod.rs index b69a1b1..8c2e7f5 100644 --- a/src/devices/mod.rs +++ b/src/devices/mod.rs @@ -7,7 +7,7 @@ mod hue_group; mod ikea_outlet; mod kasa_outlet; mod light_sensor; -mod ntfy; +pub mod ntfy; mod presence; mod wake_on_lan; mod washer; @@ -28,7 +28,7 @@ pub use self::hue_group::*; pub use self::ikea_outlet::*; pub use self::kasa_outlet::*; pub use self::light_sensor::*; -pub use self::ntfy::{Notification, Ntfy}; +pub use self::ntfy::{Ntfy, NtfyConfig}; pub use self::presence::{Presence, PresenceConfig, DEFAULT_PRESENCE}; pub use self::wake_on_lan::*; pub use self::washer::*; diff --git a/src/devices/ntfy.rs b/src/devices/ntfy.rs index 6d5d5af..8f33e26 100644 --- a/src/devices/ntfy.rs +++ b/src/devices/ntfy.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::convert::Infallible; use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use serde::Serialize; use serde_repr::*; use tracing::{error, trace, warn}; @@ -111,12 +111,12 @@ impl Default for Notification { } } -#[derive(Debug, LuaDeviceConfig)] +#[derive(Debug, LuaDeviceConfig, LuaTypeDefinition)] pub struct NtfyConfig { #[device_config(default("https://ntfy.sh".into()))] pub url: String, pub topic: String, - #[device_config(rename("event_channel"), from_lua, with(|ec: EventChannel| ec.get_tx()))] + #[device_config(rename("event_channel"), from_lua, from(EventChannel), with(|ec: EventChannel| ec.get_tx()))] pub tx: event::Sender, } diff --git a/src/devices/presence.rs b/src/devices/presence.rs index 18880b3..f4f2664 100644 --- a/src/devices/presence.rs +++ b/src/devices/presence.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use rumqttc::Publish; use tracing::{debug, trace, warn}; @@ -12,11 +12,11 @@ use crate::event::{self, Event, EventChannel, OnMqtt}; use crate::messages::PresenceMessage; use crate::mqtt::WrappedAsyncClient; -#[derive(Debug, LuaDeviceConfig)] +#[derive(Debug, LuaDeviceConfig, LuaTypeDefinition)] pub struct PresenceConfig { #[device_config(flatten)] pub mqtt: MqttDeviceConfig, - #[device_config(from_lua, rename("event_channel"), with(|ec: EventChannel| ec.get_tx()))] + #[device_config(from_lua, rename("event_channel"), from(EventChannel), with(|ec: EventChannel| ec.get_tx()))] pub tx: event::Sender, #[device_config(from_lua)] pub client: WrappedAsyncClient, diff --git a/src/devices/wake_on_lan.rs b/src/devices/wake_on_lan.rs index 51722e7..4336085 100644 --- a/src/devices/wake_on_lan.rs +++ b/src/devices/wake_on_lan.rs @@ -1,7 +1,7 @@ use std::net::Ipv4Addr; use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use eui48::MacAddress; use google_home::errors::ErrorCode; use google_home::traits::{self, Scene}; @@ -16,7 +16,7 @@ use crate::event::OnMqtt; use crate::messages::ActivateMessage; use crate::mqtt::WrappedAsyncClient; -#[derive(Debug, Clone, LuaDeviceConfig)] +#[derive(Debug, Clone, LuaDeviceConfig, LuaTypeDefinition)] pub struct WakeOnLANConfig { #[device_config(flatten)] pub info: InfoConfig, diff --git a/src/devices/washer.rs b/src/devices/washer.rs index c27d8a0..1f40ea9 100644 --- a/src/devices/washer.rs +++ b/src/devices/washer.rs @@ -1,23 +1,24 @@ use async_trait::async_trait; -use automation_macro::{LuaDevice, LuaDeviceConfig}; +use automation_macro::{LuaDevice, LuaDeviceConfig, LuaTypeDefinition}; use rumqttc::Publish; use tracing::{debug, error, trace, warn}; use super::ntfy::Priority; -use super::{Device, LuaDeviceCreate, Notification}; +use super::{Device, LuaDeviceCreate}; use crate::config::MqttDeviceConfig; +use crate::devices::ntfy::Notification; use crate::event::{self, Event, EventChannel, OnMqtt}; use crate::messages::PowerMessage; use crate::mqtt::WrappedAsyncClient; -#[derive(Debug, Clone, LuaDeviceConfig)] +#[derive(Debug, Clone, LuaDeviceConfig, LuaTypeDefinition)] pub struct WasherConfig { pub identifier: String, #[device_config(flatten)] pub mqtt: MqttDeviceConfig, // Power in Watt pub threshold: f32, - #[device_config(rename("event_channel"), from_lua, with(|ec: EventChannel| ec.get_tx()))] + #[device_config(rename("event_channel"), from_lua, from(EventChannel), with(|ec: EventChannel| ec.get_tx()))] pub tx: event::Sender, #[device_config(from_lua)] pub client: WrappedAsyncClient, diff --git a/src/event.rs b/src/event.rs index 62a6f15..b03ff18 100644 --- a/src/event.rs +++ b/src/event.rs @@ -3,7 +3,7 @@ use mlua::FromLua; use rumqttc::Publish; use tokio::sync::mpsc; -use crate::devices::Notification; +use crate::devices::ntfy::Notification; #[derive(Debug, Clone)] pub enum Event {