Compare commits

...

4 Commits

Author SHA1 Message Date
33a2e4ba11
Run main binary by default
All checks were successful
Build and deploy automation_rs / Build automation_rs (push) Successful in 5m39s
Build and deploy automation_rs / Run pre-commit checks (push) Successful in 7m34s
Build and deploy automation_rs / Build Docker image (push) Successful in 50s
Build and deploy automation_rs / Deploy Docker container (push) Has been skipped
2024-05-10 01:48:50 +02:00
c89d5f3301
Make sure the generated lua definitions are up to date 2024-05-10 01:48:47 +02:00
73416fbbbd
Further work on automatically generating lua type definitions 2024-05-10 01:30:41 +02:00
e30b8e6608
Started work on generating definitions 2024-05-10 01:30:41 +02:00
27 changed files with 572 additions and 76 deletions

View File

@ -7,9 +7,33 @@ on:
- feature/**
jobs:
build:
name: Build automation_rs
runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
rustflags: ""
- name: Build
run: cargo build --release --all-targets
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: build
path: target/x86_64-unknown-linux-gnu/release/automation
check:
name: Run pre-commit checks
runs-on: ubuntu-latest
# The lua definition check require that generate_definitions is build first
needs: [build]
container: catthehacker/ubuntu:act-latest
steps:
- name: Checkout
@ -48,28 +72,6 @@ jobs:
run: pre-commit run --show-diff-on-failure --color=always --all-files
shell: bash
build:
name: Build automation_rs
runs-on: ubuntu-latest
container: catthehacker/ubuntu:act-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
rustflags: ""
- name: Build
run: cargo build --release
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: build
path: target/x86_64-unknown-linux-gnu/release/automation
container:
name: Build Docker image
runs-on: ubuntu-latest

View File

@ -30,3 +30,13 @@ repos:
rev: v0.1.0
hooks:
- id: dockerfilelint
- repo: local
hooks:
- id: lua-definitions
name: Generate lua definitions
description: This hook generates up to date lua definitions.
entry: cargo run --release --bin generate_definitions
language: system
types: [rust]
pass_filenames: false

View File

@ -2,11 +2,11 @@
name = "automation"
version = "0.1.0"
edition = "2021"
default-run = "automation"
[workspace]
members = ["google-home", "automation_macro", "automation_cast"]
[dependencies]
automation_macro = { path = "./automation_macro" }
automation_cast = { path = "./automation_cast/" }

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

@ -9,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) {

View File

@ -23,7 +23,7 @@ mod kw {
}
#[derive(Debug)]
enum Argument {
pub enum Argument {
Flatten {
_keyword: kw::flatten,
},
@ -107,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 {
@ -218,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;
@ -232,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'")}
}
};

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

@ -0,0 +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 = {}
automation.device_manager = {}
--- @param device WrappedDevice
function automation.device_manager:add(device) end
--- @param when string
--- @param func function
function automation.device_manager:schedule(when, func) end
automation.util = {}
--- @param env string
--- @return string
function automation.util.get_env(env) end
--- @class Fulfillment
--- @field openid_url string|nil
automation.fulfillment = {}
--- @class MqttConfig
--- @param config MqttConfig
--- @return WrappedAsyncClient
function automation.new_mqtt_client(config) end
--- TODO: Generate this automatically
--- @alias OutletType "Outlet"|"Kettle"|"Charger"|"Light"
--- @alias TriggerDevicesHelper WrappedDevice[]

184
definitions/generated.lua Normal file
View File

@ -0,0 +1,184 @@
-- 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
-- End of generated file

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,73 @@
use std::fs::File;
use std::io::{Result, Write};
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() -> Result<()> {
let mut d = File::create("./definitions/generated.lua")?;
writeln!(
&mut d,
"-- WARN: This file is automatically generated, do not manually edit\n"
)?;
writeln!(&mut d, "---@meta")?;
writeln!(&mut d, "{}", MqttDeviceConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", AirFilter::generate_lua_definition())?;
writeln!(&mut d, "{}", AirFilterConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", AudioSetup::generate_lua_definition())?;
writeln!(&mut d, "{}", AudioSetupConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", ContactSensor::generate_lua_definition())?;
writeln!(&mut d, "{}", ContactSensorConfig::generate_lua_definition())?;
writeln!(
&mut d,
"{}",
PresenceDeviceConfig::generate_lua_definition()
)?;
writeln!(&mut d, "{}", TriggerConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", DebugBridge::generate_lua_definition())?;
writeln!(&mut d, "{}", DebugBridgeConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", HueBridge::generate_lua_definition())?;
writeln!(&mut d, "{}", HueBridgeConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", FlagIDs::generate_lua_definition())?;
writeln!(&mut d, "{}", HueGroup::generate_lua_definition())?;
writeln!(&mut d, "{}", HueGroupConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", IkeaOutlet::generate_lua_definition())?;
writeln!(&mut d, "{}", IkeaOutletConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", KasaOutlet::generate_lua_definition())?;
writeln!(&mut d, "{}", KasaOutletConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", LightSensor::generate_lua_definition())?;
writeln!(&mut d, "{}", LightSensorConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", Ntfy::generate_lua_definition())?;
writeln!(&mut d, "{}", NtfyConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", Presence::generate_lua_definition())?;
writeln!(&mut d, "{}", PresenceConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", WakeOnLAN::generate_lua_definition())?;
writeln!(&mut d, "{}", WakeOnLANConfig::generate_lua_definition())?;
writeln!(&mut d, "{}", Washer::generate_lua_definition())?;
writeln!(&mut d, "{}", WasherConfig::generate_lua_definition())?;
writeln!(&mut d, "-- End of generated file")?;
Ok(())
}

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

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

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

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