Compare commits
6 Commits
f7ba602762
...
295491c5fc
| Author | SHA1 | Date | |
|---|---|---|---|
|
295491c5fc
|
|||
|
9f244b3475
|
|||
|
15a6e83ad8
|
|||
|
c727579290
|
|||
|
19e8663f26
|
|||
|
f5c4495cad
|
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -96,7 +96,6 @@ dependencies = [
|
|||||||
"automation_lib",
|
"automation_lib",
|
||||||
"axum",
|
"axum",
|
||||||
"config",
|
"config",
|
||||||
"dotenvy",
|
|
||||||
"git-version",
|
"git-version",
|
||||||
"google_home",
|
"google_home",
|
||||||
"mlua",
|
"mlua",
|
||||||
@@ -433,12 +432,6 @@ dependencies = [
|
|||||||
"syn 2.0.106",
|
"syn 2.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dotenvy"
|
|
||||||
version = "0.15.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dyn-clone"
|
name = "dyn-clone"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
@@ -1108,7 +1101,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "lua_typed"
|
name = "lua_typed"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.huizinga.dev/Dreaded_X/lua_typed#d5d6fc1638bd108514899a792ee64335af50fc8b"
|
source = "git+https://git.huizinga.dev/Dreaded_X/lua_typed#08f5c4533a93131e8eda6702c062fb841d14d4e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"eui48",
|
"eui48",
|
||||||
"lua_typed_macro",
|
"lua_typed_macro",
|
||||||
@@ -1117,7 +1110,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "lua_typed_macro"
|
name = "lua_typed_macro"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.huizinga.dev/Dreaded_X/lua_typed#d5d6fc1638bd108514899a792ee64335af50fc8b"
|
source = "git+https://git.huizinga.dev/Dreaded_X/lua_typed#08f5c4533a93131e8eda6702c062fb841d14d4e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ automation_lib = { path = "./automation_lib" }
|
|||||||
automation_macro = { path = "./automation_macro" }
|
automation_macro = { path = "./automation_macro" }
|
||||||
axum = "0.8.4"
|
axum = "0.8.4"
|
||||||
bytes = "1.10.1"
|
bytes = "1.10.1"
|
||||||
dotenvy = "0.15.7"
|
|
||||||
dyn-clone = "1.0.20"
|
dyn-clone = "1.0.20"
|
||||||
eui48 = { version = "1.1.0", features = [
|
eui48 = { version = "1.1.0", features = [
|
||||||
"disp_hexstring",
|
"disp_hexstring",
|
||||||
@@ -75,7 +74,6 @@ config = { version = "0.15.15", default-features = false, features = [
|
|||||||
"async",
|
"async",
|
||||||
"toml",
|
"toml",
|
||||||
] }
|
] }
|
||||||
dotenvy = { workspace = true }
|
|
||||||
git-version = "0.3.9"
|
git-version = "0.3.9"
|
||||||
google_home = { workspace = true }
|
google_home = { workspace = true }
|
||||||
mlua = { workspace = true }
|
mlua = { workspace = true }
|
||||||
|
|||||||
@@ -3,18 +3,21 @@ use std::net::SocketAddr;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use automation_lib::device::{Device, LuaDeviceCreate};
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::lua::traits::PartialUserData;
|
||||||
use automation_macro::{Device, LuaDeviceConfig};
|
use automation_macro::{Device, LuaDeviceConfig};
|
||||||
use lua_typed::Typed;
|
use lua_typed::Typed;
|
||||||
use mlua::LuaSerdeExt;
|
use mlua::LuaSerdeExt;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize, Typed)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[typed(rename_all = "snake_case")]
|
||||||
pub enum Flag {
|
pub enum Flag {
|
||||||
Presence,
|
Presence,
|
||||||
Darkness,
|
Darkness,
|
||||||
}
|
}
|
||||||
|
crate::register_type!(Flag);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Typed)]
|
#[derive(Debug, Clone, Deserialize, Typed)]
|
||||||
pub struct FlagIDs {
|
pub struct FlagIDs {
|
||||||
@@ -36,12 +39,36 @@ pub struct Config {
|
|||||||
crate::register_type!(Config);
|
crate::register_type!(Config);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Device)]
|
#[derive(Debug, Clone, Device)]
|
||||||
#[device(add_methods = Self::add_methods)]
|
#[device(extra_user_data = SetFlag)]
|
||||||
pub struct HueBridge {
|
pub struct HueBridge {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
crate::register_device!(HueBridge);
|
crate::register_device!(HueBridge);
|
||||||
|
|
||||||
|
struct SetFlag;
|
||||||
|
impl PartialUserData<HueBridge> for SetFlag {
|
||||||
|
fn add_methods<M: mlua::UserDataMethods<HueBridge>>(methods: &mut M) {
|
||||||
|
methods.add_async_method(
|
||||||
|
"set_flag",
|
||||||
|
async |lua, this, (flag, value): (mlua::Value, bool)| {
|
||||||
|
let flag: Flag = lua.from_value(flag)?;
|
||||||
|
|
||||||
|
this.set_flag(flag, value).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn definitions() -> Option<String> {
|
||||||
|
Some(format!(
|
||||||
|
"---@async\n---@param flag {}\n---@param value boolean\nfunction {}:set_flag(flag, value) end\n",
|
||||||
|
<Flag as Typed>::type_name(),
|
||||||
|
<HueBridge as Typed>::type_name(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
struct FlagMessage {
|
struct FlagMessage {
|
||||||
flag: bool,
|
flag: bool,
|
||||||
@@ -89,19 +116,6 @@ impl HueBridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
|
||||||
methods.add_async_method(
|
|
||||||
"set_flag",
|
|
||||||
async |lua, this, (flag, value): (mlua::Value, bool)| {
|
|
||||||
let flag: Flag = lua.from_value(flag)?;
|
|
||||||
|
|
||||||
this.set_flag(flag, value).await;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Device for HueBridge {
|
impl Device for HueBridge {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use tracing::debug;
|
|||||||
macro_rules! register_device {
|
macro_rules! register_device {
|
||||||
($device:ty) => {
|
($device:ty) => {
|
||||||
::inventory::submit!(crate::RegisteredDevice::new(
|
::inventory::submit!(crate::RegisteredDevice::new(
|
||||||
stringify!($device),
|
<$device as ::lua_typed::Typed>::type_name,
|
||||||
::mlua::Lua::create_proxy::<$device>
|
::mlua::Lua::create_proxy::<$device>
|
||||||
));
|
));
|
||||||
|
|
||||||
@@ -30,20 +30,24 @@ macro_rules! register_device {
|
|||||||
|
|
||||||
pub(crate) use register_device;
|
pub(crate) use register_device;
|
||||||
|
|
||||||
|
type DeviceNameFn = fn() -> String;
|
||||||
type RegisterDeviceFn = fn(lua: &mlua::Lua) -> mlua::Result<mlua::AnyUserData>;
|
type RegisterDeviceFn = fn(lua: &mlua::Lua) -> mlua::Result<mlua::AnyUserData>;
|
||||||
|
|
||||||
pub struct RegisteredDevice {
|
pub struct RegisteredDevice {
|
||||||
name: &'static str,
|
name_fn: DeviceNameFn,
|
||||||
register_fn: RegisterDeviceFn,
|
register_fn: RegisterDeviceFn,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegisteredDevice {
|
impl RegisteredDevice {
|
||||||
pub const fn new(name: &'static str, register_fn: RegisterDeviceFn) -> Self {
|
pub const fn new(name_fn: DeviceNameFn, register_fn: RegisterDeviceFn) -> Self {
|
||||||
Self { name, register_fn }
|
Self {
|
||||||
|
name_fn,
|
||||||
|
register_fn,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn get_name(&self) -> &'static str {
|
pub fn get_name(&self) -> String {
|
||||||
self.name
|
(self.name_fn)()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(&self, lua: &mlua::Lua) -> mlua::Result<mlua::AnyUserData> {
|
pub fn register(&self, lua: &mlua::Lua) -> mlua::Result<mlua::AnyUserData> {
|
||||||
@@ -58,15 +62,16 @@ pub fn create_module(lua: &mlua::Lua) -> mlua::Result<mlua::Table> {
|
|||||||
|
|
||||||
debug!("Loading devices...");
|
debug!("Loading devices...");
|
||||||
for device in inventory::iter::<RegisteredDevice> {
|
for device in inventory::iter::<RegisteredDevice> {
|
||||||
debug!(name = device.get_name(), "Registering device");
|
let name = device.get_name();
|
||||||
|
debug!(name, "Registering device");
|
||||||
let proxy = device.register(lua)?;
|
let proxy = device.register(lua)?;
|
||||||
devices.set(device.get_name(), proxy)?;
|
devices.set(name, proxy)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(devices)
|
Ok(devices)
|
||||||
}
|
}
|
||||||
|
|
||||||
inventory::submit! {Module::new("devices", create_module)}
|
inventory::submit! {Module::new("automation:devices", create_module)}
|
||||||
|
|
||||||
macro_rules! register_type {
|
macro_rules! register_type {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use std::convert::Infallible;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use automation_lib::device::{Device, LuaDeviceCreate};
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::lua::traits::PartialUserData;
|
||||||
use automation_macro::{Device, LuaDeviceConfig};
|
use automation_macro::{Device, LuaDeviceConfig};
|
||||||
use lua_typed::Typed;
|
use lua_typed::Typed;
|
||||||
use mlua::LuaSerdeExt;
|
use mlua::LuaSerdeExt;
|
||||||
@@ -90,14 +91,15 @@ pub struct Config {
|
|||||||
crate::register_type!(Config);
|
crate::register_type!(Config);
|
||||||
|
|
||||||
#[derive(Debug, Clone, Device)]
|
#[derive(Debug, Clone, Device)]
|
||||||
#[device(add_methods = Self::add_methods)]
|
#[device(extra_user_data = SendNotification)]
|
||||||
pub struct Ntfy {
|
pub struct Ntfy {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
crate::register_device!(Ntfy);
|
crate::register_device!(Ntfy);
|
||||||
|
|
||||||
impl Ntfy {
|
struct SendNotification;
|
||||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
impl PartialUserData<Ntfy> for SendNotification {
|
||||||
|
fn add_methods<M: mlua::UserDataMethods<Ntfy>>(methods: &mut M) {
|
||||||
methods.add_async_method(
|
methods.add_async_method(
|
||||||
"send_notification",
|
"send_notification",
|
||||||
async |lua, this, notification: mlua::Value| {
|
async |lua, this, notification: mlua::Value| {
|
||||||
@@ -109,6 +111,14 @@ impl Ntfy {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn definitions() -> Option<String> {
|
||||||
|
Some(format!(
|
||||||
|
"---@async\n---@param notification {}\nfunction {}:send_notification(notification) end\n",
|
||||||
|
<Notification as Typed>::type_name(),
|
||||||
|
<Ntfy as Typed>::type_name(),
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use automation_lib::action_callback::ActionCallback;
|
|||||||
use automation_lib::config::MqttDeviceConfig;
|
use automation_lib::config::MqttDeviceConfig;
|
||||||
use automation_lib::device::{Device, LuaDeviceCreate};
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
use automation_lib::event::OnMqtt;
|
use automation_lib::event::OnMqtt;
|
||||||
|
use automation_lib::lua::traits::PartialUserData;
|
||||||
use automation_lib::messages::PresenceMessage;
|
use automation_lib::messages::PresenceMessage;
|
||||||
use automation_lib::mqtt::WrappedAsyncClient;
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::{Device, LuaDeviceConfig};
|
use automation_macro::{Device, LuaDeviceConfig};
|
||||||
@@ -39,13 +40,29 @@ pub struct State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Device)]
|
#[derive(Debug, Clone, Device)]
|
||||||
#[device(add_methods = Self::add_methods)]
|
#[device(extra_user_data = OverallPresence)]
|
||||||
pub struct Presence {
|
pub struct Presence {
|
||||||
config: Config,
|
config: Config,
|
||||||
state: Arc<RwLock<State>>,
|
state: Arc<RwLock<State>>,
|
||||||
}
|
}
|
||||||
crate::register_device!(Presence);
|
crate::register_device!(Presence);
|
||||||
|
|
||||||
|
struct OverallPresence;
|
||||||
|
impl PartialUserData<Presence> for OverallPresence {
|
||||||
|
fn add_methods<M: mlua::UserDataMethods<Presence>>(methods: &mut M) {
|
||||||
|
methods.add_async_method("overall_presence", async |_lua, this, ()| {
|
||||||
|
Ok(this.state().await.current_overall_presence)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn definitions() -> Option<String> {
|
||||||
|
Some(format!(
|
||||||
|
"---@async\n---@return boolean\nfunction {}:overall_presence() end\n",
|
||||||
|
<Presence as Typed>::type_name(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Presence {
|
impl Presence {
|
||||||
async fn state(&self) -> RwLockReadGuard<'_, State> {
|
async fn state(&self) -> RwLockReadGuard<'_, State> {
|
||||||
self.state.read().await
|
self.state.read().await
|
||||||
@@ -54,12 +71,6 @@ impl Presence {
|
|||||||
async fn state_mut(&self) -> RwLockWriteGuard<'_, State> {
|
async fn state_mut(&self) -> RwLockWriteGuard<'_, State> {
|
||||||
self.state.write().await
|
self.state.write().await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
|
||||||
methods.add_async_method("overall_presence", async |_lua, this, ()| {
|
|
||||||
Ok(this.state().await.current_overall_presence)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ pub struct ActionCallback<P> {
|
|||||||
_parameters: PhantomData<P>,
|
_parameters: PhantomData<P>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Typed for ActionCallback<()> {
|
||||||
|
fn type_name() -> String {
|
||||||
|
"fun() | fun()[]".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<A: Typed> Typed for ActionCallback<A> {
|
impl<A: Typed> Typed for ActionCallback<A> {
|
||||||
fn type_name() -> String {
|
fn type_name() -> String {
|
||||||
let type_name = A::type_name();
|
let type_name = A::type_name();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use lua_typed::Typed;
|
|||||||
use rumqttc::{MqttOptions, Transport};
|
use rumqttc::{MqttOptions, Transport};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize, Typed)]
|
||||||
pub struct MqttConfig {
|
pub struct MqttConfig {
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
|
use lua_typed::Typed;
|
||||||
|
use mlua::FromLua;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||||
use tokio_cron_scheduler::{Job, JobScheduler};
|
use tokio_cron_scheduler::{Job, JobScheduler};
|
||||||
use tracing::{debug, instrument, trace};
|
use tracing::{debug, instrument, trace};
|
||||||
@@ -13,7 +15,7 @@ use crate::event::{Event, EventChannel, OnMqtt};
|
|||||||
|
|
||||||
pub type DeviceMap = HashMap<String, Box<dyn Device>>;
|
pub type DeviceMap = HashMap<String, Box<dyn Device>>;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, FromLua)]
|
||||||
pub struct DeviceManager {
|
pub struct DeviceManager {
|
||||||
devices: Arc<RwLock<DeviceMap>>,
|
devices: Arc<RwLock<DeviceMap>>,
|
||||||
event_channel: EventChannel,
|
event_channel: EventChannel,
|
||||||
@@ -142,3 +144,9 @@ impl mlua::UserData for DeviceManager {
|
|||||||
methods.add_method("event_channel", |_lua, this, ()| Ok(this.event_channel()))
|
methods.add_method("event_channel", |_lua, this, ()| Ok(this.event_channel()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Typed for DeviceManager {
|
||||||
|
fn type_name() -> String {
|
||||||
|
"DeviceManager".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub fn load_modules(lua: &mlua::Lua) -> mlua::Result<()> {
|
|||||||
for module in inventory::iter::<Module> {
|
for module in inventory::iter::<Module> {
|
||||||
debug!(name = module.get_name(), "Loading module");
|
debug!(name = module.get_name(), "Loading module");
|
||||||
let table = module.register(lua)?;
|
let table = module.register(lua)?;
|
||||||
lua.register_module(&format!("automation:{}", module.get_name()), table)?;
|
lua.register_module(module.get_name(), table)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ pub trait PartialUserData<T> {
|
|||||||
fn interface_name() -> Option<&'static str> {
|
fn interface_name() -> Option<&'static str> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn definitions() -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Device;
|
pub struct Device;
|
||||||
|
|||||||
@@ -28,4 +28,4 @@ fn create_module(lua: &mlua::Lua) -> mlua::Result<mlua::Table> {
|
|||||||
Ok(utils)
|
Ok(utils)
|
||||||
}
|
}
|
||||||
|
|
||||||
inventory::submit! {Module::new("utils", create_module)}
|
inventory::submit! {Module::new("automation:utils", create_module)}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use lua_typed::Typed;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
@@ -74,3 +75,44 @@ impl mlua::UserData for Timeout {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Typed for Timeout {
|
||||||
|
fn type_name() -> String {
|
||||||
|
"Timeout".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_header() -> Option<String> {
|
||||||
|
let type_name = Self::type_name();
|
||||||
|
Some(format!("---@class {type_name}\nlocal {type_name}\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_members() -> Option<String> {
|
||||||
|
let mut output = String::new();
|
||||||
|
|
||||||
|
let type_name = Self::type_name();
|
||||||
|
|
||||||
|
output += &format!(
|
||||||
|
"---@async\n---@param timeout number\n---@param callback {}\nfunction {type_name}:start(timeout, callback) end\n",
|
||||||
|
ActionCallback::<()>::type_name()
|
||||||
|
);
|
||||||
|
|
||||||
|
output += &format!("---@async\nfunction {type_name}:cancel() end\n",);
|
||||||
|
|
||||||
|
output +=
|
||||||
|
&format!("---@async\n---@return boolean\nfunction {type_name}:is_waiting() end\n",);
|
||||||
|
|
||||||
|
Some(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_footer() -> Option<String> {
|
||||||
|
let mut output = String::new();
|
||||||
|
|
||||||
|
let type_name = Self::type_name();
|
||||||
|
|
||||||
|
output += &format!("utils.{type_name} = {{}}\n");
|
||||||
|
output += &format!("---@return {type_name}\n");
|
||||||
|
output += &format!("function utils.{type_name}.new() end\n");
|
||||||
|
|
||||||
|
Some(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use lua_typed::Typed;
|
use lua_typed::Typed;
|
||||||
use mlua::FromLua;
|
use mlua::{FromLua, LuaSerdeExt};
|
||||||
use rumqttc::{AsyncClient, Event, EventLoop, Incoming};
|
use rumqttc::{AsyncClient, Event, EventLoop, Incoming};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
|
use crate::Module;
|
||||||
|
use crate::config::MqttConfig;
|
||||||
|
use crate::device_manager::DeviceManager;
|
||||||
use crate::event::{self, EventChannel};
|
use crate::event::{self, EventChannel};
|
||||||
|
|
||||||
#[derive(Debug, Clone, FromLua)]
|
#[derive(Debug, Clone, FromLua)]
|
||||||
@@ -14,6 +17,37 @@ impl Typed for WrappedAsyncClient {
|
|||||||
fn type_name() -> String {
|
fn type_name() -> String {
|
||||||
"AsyncClient".into()
|
"AsyncClient".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_header() -> Option<String> {
|
||||||
|
let type_name = Self::type_name();
|
||||||
|
Some(format!("---@class {type_name}\nlocal {type_name}\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_members() -> Option<String> {
|
||||||
|
let mut output = String::new();
|
||||||
|
|
||||||
|
let type_name = Self::type_name();
|
||||||
|
|
||||||
|
output += &format!(
|
||||||
|
"---@async\n---@param topic string\n---@param message table?\nfunction {type_name}:send_message(topic, message) end\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_footer() -> Option<String> {
|
||||||
|
let mut output = String::new();
|
||||||
|
|
||||||
|
let type_name = Self::type_name();
|
||||||
|
|
||||||
|
output += &format!("mqtt.{type_name} = {{}}\n");
|
||||||
|
output += &format!("---@param device_manager {}\n", DeviceManager::type_name());
|
||||||
|
output += &format!("---@param config {}\n", MqttConfig::type_name());
|
||||||
|
output += &format!("---@return {type_name}\n");
|
||||||
|
output += "function mqtt.new(device_manager, config) end\n";
|
||||||
|
|
||||||
|
Some(output)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for WrappedAsyncClient {
|
impl Deref for WrappedAsyncClient {
|
||||||
@@ -77,3 +111,25 @@ pub fn start(mut eventloop: EventLoop, event_channel: &EventChannel) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_module(lua: &mlua::Lua) -> mlua::Result<mlua::Table> {
|
||||||
|
let mqtt = lua.create_table()?;
|
||||||
|
let mqtt_new = lua.create_function(
|
||||||
|
move |lua, (device_manager, config): (DeviceManager, mlua::Value)| {
|
||||||
|
let event_channel = device_manager.event_channel();
|
||||||
|
let config: MqttConfig = lua.from_value(config)?;
|
||||||
|
|
||||||
|
// Create a mqtt client
|
||||||
|
// TODO: When starting up, the devices are not yet created, this could lead to a device being out of sync
|
||||||
|
let (client, eventloop) = AsyncClient::new(config.into(), 100);
|
||||||
|
start(eventloop, &event_channel);
|
||||||
|
|
||||||
|
Ok(WrappedAsyncClient(client))
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
mqtt.set("new", mqtt_new)?;
|
||||||
|
|
||||||
|
Ok(mqtt)
|
||||||
|
}
|
||||||
|
|
||||||
|
inventory::submit! {Module::new("automation:mqtt", create_module)}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::{ToTokens, quote};
|
use quote::quote;
|
||||||
use syn::parse::{Parse, ParseStream};
|
use syn::parse::{Parse, ParseStream};
|
||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
@@ -9,7 +9,7 @@ use syn::{Attribute, DeriveInput, Token, parenthesized};
|
|||||||
|
|
||||||
enum Attr {
|
enum Attr {
|
||||||
Trait(TraitAttr),
|
Trait(TraitAttr),
|
||||||
AddMethods(AddMethodsAttr),
|
ExtraUserData(ExtraUserDataAttr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Attr {
|
impl Attr {
|
||||||
@@ -20,9 +20,9 @@ impl Attr {
|
|||||||
let input;
|
let input;
|
||||||
_ = parenthesized!(input in meta.input);
|
_ = parenthesized!(input in meta.input);
|
||||||
parsed = Some(Attr::Trait(input.parse()?));
|
parsed = Some(Attr::Trait(input.parse()?));
|
||||||
} else if meta.path.is_ident("add_methods") {
|
} else if meta.path.is_ident("extra_user_data") {
|
||||||
let value = meta.value()?;
|
let value = meta.value()?;
|
||||||
parsed = Some(Attr::AddMethods(value.parse()?));
|
parsed = Some(Attr::ExtraUserData(value.parse()?));
|
||||||
} else {
|
} else {
|
||||||
return Err(syn::Error::new(meta.path.span(), "Unknown attribute"));
|
return Err(syn::Error::new(meta.path.span(), "Unknown attribute"));
|
||||||
}
|
}
|
||||||
@@ -95,28 +95,18 @@ impl Parse for Aliases {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AddMethodsAttr(syn::Path);
|
struct ExtraUserDataAttr(syn::Ident);
|
||||||
|
|
||||||
impl Parse for AddMethodsAttr {
|
impl Parse for ExtraUserDataAttr {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
Ok(Self(input.parse()?))
|
Ok(Self(input.parse()?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for AddMethodsAttr {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
|
||||||
let Self(path) = self;
|
|
||||||
|
|
||||||
tokens.extend(quote! {
|
|
||||||
#path
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Implementation {
|
struct Implementation {
|
||||||
name: syn::Ident,
|
name: syn::Ident,
|
||||||
traits: Traits,
|
traits: Traits,
|
||||||
add_methods: Vec<AddMethodsAttr>,
|
extra_user_data: Vec<ExtraUserDataAttr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl quote::ToTokens for Implementation {
|
impl quote::ToTokens for Implementation {
|
||||||
@@ -124,9 +114,10 @@ impl quote::ToTokens for Implementation {
|
|||||||
let Self {
|
let Self {
|
||||||
name,
|
name,
|
||||||
traits,
|
traits,
|
||||||
add_methods,
|
extra_user_data,
|
||||||
} = &self;
|
} = &self;
|
||||||
let Traits(traits) = traits;
|
let Traits(traits) = traits;
|
||||||
|
let extra_user_data: Vec<_> = extra_user_data.iter().map(|tr| tr.0.clone()).collect();
|
||||||
|
|
||||||
tokens.extend(quote! {
|
tokens.extend(quote! {
|
||||||
impl mlua::UserData for #name {
|
impl mlua::UserData for #name {
|
||||||
@@ -151,7 +142,7 @@ impl quote::ToTokens for Implementation {
|
|||||||
)*
|
)*
|
||||||
|
|
||||||
#(
|
#(
|
||||||
#add_methods(methods);
|
<#extra_user_data as ::automation_lib::lua::traits::PartialUserData<#name>>::add_methods(methods);
|
||||||
)*
|
)*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,7 +169,7 @@ impl quote::ToTokens for Implementation {
|
|||||||
format!(": {interfaces}")
|
format!(": {interfaces}")
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(format!("---@class {type_name}{interfaces}\nlocal {type_name}"))
|
Some(format!("---@class {type_name}{interfaces}\nlocal {type_name}\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_members() -> Option<String> {
|
fn generate_members() -> Option<String> {
|
||||||
@@ -191,6 +182,15 @@ impl quote::ToTokens for Implementation {
|
|||||||
output += &format!("---@return {type_name}\n");
|
output += &format!("---@return {type_name}\n");
|
||||||
output += &format!("function devices.{type_name}.new(config) end\n");
|
output += &format!("function devices.{type_name}.new(config) end\n");
|
||||||
|
|
||||||
|
output += &<::automation_lib::lua::traits::Device as ::automation_lib::lua::traits::PartialUserData<#name>>::definitions().unwrap_or("".into());
|
||||||
|
|
||||||
|
#(
|
||||||
|
output += &<::automation_lib::lua::traits::#traits as ::automation_lib::lua::traits::PartialUserData<#name>>::definitions().unwrap_or("".into());
|
||||||
|
)*
|
||||||
|
#(
|
||||||
|
output += &<#extra_user_data as ::automation_lib::lua::traits::PartialUserData<#name>>::definitions().unwrap_or("".into());
|
||||||
|
)*
|
||||||
|
|
||||||
|
|
||||||
Some(output)
|
Some(output)
|
||||||
}
|
}
|
||||||
@@ -220,7 +220,7 @@ impl Implementations {
|
|||||||
all.extend(&attribute.traits);
|
all.extend(&attribute.traits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Attr::AddMethods(attribute) => add_methods.push(attribute),
|
Attr::ExtraUserData(attribute) => add_methods.push(attribute),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +238,7 @@ impl Implementations {
|
|||||||
.map(|(alias, traits)| Implementation {
|
.map(|(alias, traits)| Implementation {
|
||||||
name: alias.unwrap_or(name.clone()),
|
name: alias.unwrap_or(name.clone()),
|
||||||
traits,
|
traits,
|
||||||
add_methods: add_methods.clone(),
|
extra_user_data: add_methods.clone(),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ local fulfillment = {
|
|||||||
openid_url = "https://login.huizinga.dev/api/oidc",
|
openid_url = "https://login.huizinga.dev/api/oidc",
|
||||||
}
|
}
|
||||||
|
|
||||||
local mqtt_client = require("automation:mqtt").new({
|
local mqtt_client = require("automation:mqtt").new(device_manager, {
|
||||||
host = ((host == "zeus" or host == "hephaestus") and "olympus.lan.huizinga.dev") or "mosquitto",
|
host = ((host == "zeus" or host == "hephaestus") and "olympus.lan.huizinga.dev") or "mosquitto",
|
||||||
port = 8883,
|
port = 8883,
|
||||||
client_name = "automation-" .. host,
|
client_name = "automation-" .. host,
|
||||||
|
|||||||
22
src/main.rs
22
src/main.rs
@@ -9,18 +9,15 @@ use std::path::Path;
|
|||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
use ::config::{Environment, File};
|
use ::config::{Environment, File};
|
||||||
use automation_lib::config::{FulfillmentConfig, MqttConfig};
|
use automation_lib::config::FulfillmentConfig;
|
||||||
use automation_lib::device_manager::DeviceManager;
|
use automation_lib::device_manager::DeviceManager;
|
||||||
use automation_lib::mqtt::{self, WrappedAsyncClient};
|
|
||||||
use axum::extract::{FromRef, State};
|
use axum::extract::{FromRef, State};
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use axum::routing::post;
|
use axum::routing::post;
|
||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use dotenvy::dotenv;
|
|
||||||
use google_home::{GoogleHome, Request, Response};
|
use google_home::{GoogleHome, Request, Response};
|
||||||
use mlua::LuaSerdeExt;
|
use mlua::LuaSerdeExt;
|
||||||
use rumqttc::AsyncClient;
|
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
use web::{ApiError, User};
|
use web::{ApiError, User};
|
||||||
@@ -75,8 +72,6 @@ async fn fulfillment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn app() -> anyhow::Result<()> {
|
async fn app() -> anyhow::Result<()> {
|
||||||
dotenv().ok();
|
|
||||||
|
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
info!(version = VERSION, "automation_rs");
|
info!(version = VERSION, "automation_rs");
|
||||||
@@ -141,21 +136,6 @@ async fn app() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
automation_lib::load_modules(&lua)?;
|
automation_lib::load_modules(&lua)?;
|
||||||
|
|
||||||
let mqtt = lua.create_table()?;
|
|
||||||
let event_channel = device_manager.event_channel();
|
|
||||||
let mqtt_new = lua.create_function(move |lua, config: mlua::Value| {
|
|
||||||
let config: MqttConfig = lua.from_value(config)?;
|
|
||||||
|
|
||||||
// Create a mqtt client
|
|
||||||
// TODO: When starting up, the devices are not yet created, this could lead to a device being out of sync
|
|
||||||
let (client, eventloop) = AsyncClient::new(config.into(), 100);
|
|
||||||
mqtt::start(eventloop, &event_channel);
|
|
||||||
|
|
||||||
Ok(WrappedAsyncClient(client))
|
|
||||||
})?;
|
|
||||||
mqtt.set("new", mqtt_new)?;
|
|
||||||
lua.register_module("automation:mqtt", mqtt)?;
|
|
||||||
|
|
||||||
lua.register_module("automation:device_manager", device_manager.clone())?;
|
lua.register_module("automation:device_manager", device_manager.clone())?;
|
||||||
|
|
||||||
lua.register_module("automation:variables", lua.to_value(&config.variables)?)?;
|
lua.register_module("automation:variables", lua.to_value(&config.variables)?)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user