Further work on automatically generating lua type definitions
All checks were successful
Build and deploy automation_rs / Build automation_rs (push) Successful in 6m25s
Build and deploy automation_rs / Build Docker image (push) Successful in 1m24s
Build and deploy automation_rs / Deploy Docker container (push) Has been skipped

This commit is contained in:
Dreaded_X 2024-04-30 02:08:29 +02:00
parent 2f494a7dd6
commit 11d5d5db4d
Signed by: Dreaded_X
GPG Key ID: FA5F485356B0D2D4
25 changed files with 510 additions and 101 deletions

1
.gitignore vendored
View File

@ -1,3 +1,2 @@
/target
.env
/definitions/generated

View File

@ -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()
}

View File

@ -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
}

View File

@ -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<Argument, Token![,]>,
pub(crate) struct Args {
pub(crate) args: Punctuated<Argument, Token![,]>,
}
impl Parse for Args {
@ -221,6 +218,21 @@ fn field_from_lua(field: &Field) -> TokenStream {
temp.into()
}
}),
_ => None,
})
.collect::<Vec<_>>()
.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
}

View File

@ -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::<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
}

View File

@ -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[]

183
definitions/generated.lua Normal file
View File

@ -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

10
definitions/rust.lua Normal file
View File

@ -0,0 +1,10 @@
--- @meta
--- @alias String string
--- @alias u64 number
--- @alias isize number
--- @alias f32 number
--- @alias Ipv4Addr string
--- @alias MacAddress string

View File

@ -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());
}

View File

@ -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<String>,
@ -68,7 +69,7 @@ impl InfoConfig {
}
}
#[derive(Debug, Clone, Deserialize)]
#[derive(Debug, Clone, Deserialize, LuaTypeDefinition)]
pub struct MqttDeviceConfig {
pub topic: String,
}

View File

@ -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,

View File

@ -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};
@ -12,7 +12,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)]

View File

@ -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<TriggerDevicesHelper> 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<u64>), with(|t: Option<_>| t.map(Duration::from_secs)))]
pub timeout: Option<Duration>,
}
#[derive(Debug, Clone, LuaDeviceConfig)]
#[derive(Debug, Clone, LuaDeviceConfig, LuaTypeDefinition)]
pub struct ContactSensorConfig {
pub identifier: String,
#[device_config(flatten)]

View File

@ -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)]

View File

@ -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,

View File

@ -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,

View File

@ -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<u64>), with(|t: Option<_>| t.map(Duration::from_secs)))]
pub timeout: Option<Duration>,
#[device_config(default)]
pub remotes: Vec<MqttDeviceConfig>,

View File

@ -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,
}

View File

@ -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,

View File

@ -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;
@ -25,7 +25,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::*;

View File

@ -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,
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -4,7 +4,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 {