Compare commits
4 Commits
e9f080ef19
...
14aabe202d
Author | SHA1 | Date | |
---|---|---|---|
14aabe202d | |||
e8d5698835 | |||
8877b24e84 | |||
42f391cde6 |
4314
Cargo.lock
generated
4314
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
103
Cargo.toml
103
Cargo.toml
|
@ -9,45 +9,11 @@ members = [
|
||||||
"automation_cast",
|
"automation_cast",
|
||||||
"google_home/google_home",
|
"google_home/google_home",
|
||||||
"google_home/google_home_macro",
|
"google_home/google_home_macro",
|
||||||
|
"automation_devices",
|
||||||
|
"automation_lib",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
[dependencies]
|
|
||||||
automation_macro = { path = "./automation_macro" }
|
|
||||||
automation_cast = { path = "./automation_cast/" }
|
|
||||||
rumqttc = "0.18"
|
|
||||||
serde = { version = "1.0.149", features = ["derive"] }
|
|
||||||
serde_json = "1.0.89"
|
|
||||||
google_home = { path = "./google_home/google_home/" }
|
|
||||||
paste = "1.0.10"
|
|
||||||
tokio = { version = "1", features = ["rt-multi-thread"] }
|
|
||||||
dotenvy = "0.15.0"
|
|
||||||
reqwest = { version = "0.11.13", features = [
|
|
||||||
"json",
|
|
||||||
"rustls-tls",
|
|
||||||
], default-features = false } # Use rustls, since the other packages also use rustls
|
|
||||||
axum = "0.6.1"
|
|
||||||
serde_repr = "0.1.10"
|
|
||||||
tracing = "0.1.37"
|
|
||||||
bytes = "1.3.0"
|
|
||||||
pollster = "0.2.5"
|
|
||||||
regex = "1.7.0"
|
|
||||||
async-trait = "0.1.61"
|
|
||||||
futures = "0.3.25"
|
|
||||||
eui48 = { version = "1.1.0", default-features = false, features = [
|
|
||||||
"disp_hexstring",
|
|
||||||
"serde",
|
|
||||||
] }
|
|
||||||
thiserror = "1.0.38"
|
|
||||||
anyhow = "1.0.68"
|
|
||||||
wakey = "0.3.0"
|
|
||||||
console-subscriber = "0.1.8"
|
|
||||||
tracing-subscriber = "0.3.16"
|
|
||||||
serde_with = "3.2.0"
|
|
||||||
enum_dispatch = "0.3.12"
|
|
||||||
indexmap = { version = "2.0.0", features = ["serde"] }
|
|
||||||
serde_yaml = "0.9.27"
|
|
||||||
tokio-cron-scheduler = "0.9.4"
|
|
||||||
mlua = { version = "0.10.1", features = [
|
mlua = { version = "0.10.1", features = [
|
||||||
"lua54",
|
"lua54",
|
||||||
"vendored",
|
"vendored",
|
||||||
|
@ -56,12 +22,67 @@ mlua = { version = "0.10.1", features = [
|
||||||
"async",
|
"async",
|
||||||
"send",
|
"send",
|
||||||
] }
|
] }
|
||||||
hostname = "0.4.0"
|
automation_macro = { path = "./automation_macro" }
|
||||||
tokio-util = { version = "0.7.11", features = ["full"] }
|
automation_cast = { path = "./automation_cast" }
|
||||||
uuid = "1.8.0"
|
automation_lib = { path = "./automation_lib" }
|
||||||
|
automation_devices = { path = "./automation_devices" }
|
||||||
|
google_home = { path = "./google_home/google_home" }
|
||||||
|
google_home_macro = { path = "./google_home/google_home_macro" }
|
||||||
|
tokio = { version = "1", features = ["rt-multi-thread"] }
|
||||||
|
rumqttc = "0.24.0"
|
||||||
|
tracing = "0.1.37"
|
||||||
|
anyhow = "1.0.68"
|
||||||
|
async-trait = "0.1.83"
|
||||||
|
axum = "0.7.9"
|
||||||
|
bytes = "1.3.0"
|
||||||
|
dotenvy = "0.15.0"
|
||||||
dyn-clone = "1.0.17"
|
dyn-clone = "1.0.17"
|
||||||
|
eui48 = { version = "1.1.0", features = [
|
||||||
|
"disp_hexstring",
|
||||||
|
"serde",
|
||||||
|
], default-features = false }
|
||||||
|
futures = "0.3.25"
|
||||||
|
hostname = "0.4.0"
|
||||||
impls = "1.0.3"
|
impls = "1.0.3"
|
||||||
zigbee2mqtt-types = { version = "0.2.0", features = ["debug", "philips"] }
|
indexmap = { version = "2.0.0", features = ["serde"] }
|
||||||
|
itertools = "0.13.0"
|
||||||
|
json_value_merge = "2.0.0"
|
||||||
|
pollster = "0.4.0"
|
||||||
|
proc-macro2 = "1.0.81"
|
||||||
|
quote = "1.0.36"
|
||||||
|
reqwest = { version = "0.12.9", features = [
|
||||||
|
"json",
|
||||||
|
"rustls-tls",
|
||||||
|
], default-features = false } # Use rustls, since the other packages also use rustls
|
||||||
|
serde = { version = "1.0.149", features = ["derive"] }
|
||||||
|
serde_json = "1.0.89"
|
||||||
|
serde_repr = "0.1.10"
|
||||||
|
syn = { version = "2.0.60", features = ["extra-traits", "full"] }
|
||||||
|
thiserror = "2.0.5"
|
||||||
|
tokio-cron-scheduler = "0.13.0"
|
||||||
|
tokio-util = { version = "0.7.11", features = ["full"] }
|
||||||
|
tracing-subscriber = "0.3.16"
|
||||||
|
uuid = "1.8.0"
|
||||||
|
wakey = "0.3.0"
|
||||||
|
zigbee2mqtt-types = { version = "0.4.0", features = ["debug", "philips"] }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
automation_lib = { workspace = true }
|
||||||
|
automation_devices = { workspace = true }
|
||||||
|
google_home = { workspace = true }
|
||||||
|
mlua = { workspace = true }
|
||||||
|
tokio = { workspace = true }
|
||||||
|
hostname = { workspace = true }
|
||||||
|
rumqttc = { workspace = true }
|
||||||
|
axum = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
anyhow = { workspace = true }
|
||||||
|
dotenvy = { workspace = true }
|
||||||
|
tracing-subscriber = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
reqwest = { workspace = true }
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
wakey = { git = "https://git.huizinga.dev/Dreaded_X/wakey" }
|
wakey = { git = "https://git.huizinga.dev/Dreaded_X/wakey" }
|
||||||
|
|
27
automation_devices/Cargo.toml
Normal file
27
automation_devices/Cargo.toml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
[package]
|
||||||
|
name = "automation_devices"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
automation_lib = { workspace = true }
|
||||||
|
automation_macro = { workspace = true }
|
||||||
|
automation_cast = { workspace = true }
|
||||||
|
google_home = { workspace = true }
|
||||||
|
mlua = { workspace = true }
|
||||||
|
async-trait = { workspace = true }
|
||||||
|
dyn-clone = { workspace = true }
|
||||||
|
rumqttc = { workspace = true }
|
||||||
|
tokio = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
impls = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
|
reqwest = { workspace = true } # Use rustls, since the other packages also use rustls
|
||||||
|
anyhow = { workspace = true }
|
||||||
|
zigbee2mqtt-types = { workspace = true }
|
||||||
|
axum = { workspace = true }
|
||||||
|
bytes = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
eui48 = { workspace = true }
|
||||||
|
wakey = { workspace = true }
|
|
@ -1,6 +1,11 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use automation_lib::config::{InfoConfig, MqttDeviceConfig};
|
||||||
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::event::OnMqtt;
|
||||||
|
use automation_lib::messages::{AirFilterFanState, AirFilterState, SetAirFilterFanState};
|
||||||
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use google_home::device::Name;
|
use google_home::device::Name;
|
||||||
use google_home::errors::ErrorCode;
|
use google_home::errors::ErrorCode;
|
||||||
|
@ -13,13 +18,6 @@ use rumqttc::Publish;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use super::LuaDeviceCreate;
|
|
||||||
use crate::config::{InfoConfig, MqttDeviceConfig};
|
|
||||||
use crate::devices::Device;
|
|
||||||
use crate::event::OnMqtt;
|
|
||||||
use crate::messages::{AirFilterFanState, AirFilterState, SetAirFilterFanState};
|
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[device_config(flatten)]
|
#[device_config(flatten)]
|
|
@ -2,20 +2,19 @@ use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use automation_lib::action_callback::ActionCallback;
|
||||||
|
use automation_lib::config::MqttDeviceConfig;
|
||||||
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::error::DeviceConfigError;
|
||||||
|
use automation_lib::event::{OnMqtt, OnPresence};
|
||||||
|
use automation_lib::messages::{ContactMessage, PresenceMessage};
|
||||||
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
|
use automation_lib::presence::DEFAULT_PRESENCE;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use super::{Device, LuaDeviceCreate};
|
|
||||||
use crate::action_callback::ActionCallback;
|
|
||||||
use crate::config::MqttDeviceConfig;
|
|
||||||
use crate::devices::DEFAULT_PRESENCE;
|
|
||||||
use crate::error::DeviceConfigError;
|
|
||||||
use crate::event::{OnMqtt, OnPresence};
|
|
||||||
use crate::messages::{ContactMessage, PresenceMessage};
|
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
|
||||||
|
|
||||||
// NOTE: If we add more presence devices we might need to move this out of here
|
// NOTE: If we add more presence devices we might need to move this out of here
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct PresenceDeviceConfig {
|
pub struct PresenceDeviceConfig {
|
|
@ -1,16 +1,14 @@
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use automation_lib::config::MqttDeviceConfig;
|
||||||
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::event::{OnDarkness, OnPresence};
|
||||||
|
use automation_lib::messages::{DarknessMessage, PresenceMessage};
|
||||||
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use tracing::{trace, warn};
|
use tracing::{trace, warn};
|
||||||
|
|
||||||
use super::LuaDeviceCreate;
|
|
||||||
use crate::config::MqttDeviceConfig;
|
|
||||||
use crate::devices::Device;
|
|
||||||
use crate::event::{OnDarkness, OnPresence};
|
|
||||||
use crate::messages::{DarknessMessage, PresenceMessage};
|
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
|
||||||
|
|
||||||
#[derive(Debug, LuaDeviceConfig, Clone)]
|
#[derive(Debug, LuaDeviceConfig, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub identifier: String,
|
pub identifier: String,
|
|
@ -2,14 +2,12 @@ use std::convert::Infallible;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::event::{OnDarkness, OnPresence};
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
|
|
||||||
use super::LuaDeviceCreate;
|
|
||||||
use crate::devices::Device;
|
|
||||||
use crate::event::{OnDarkness, OnPresence};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Flag {
|
pub enum Flag {
|
||||||
Presence,
|
Presence,
|
|
@ -2,13 +2,13 @@ use std::net::SocketAddr;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use google_home::errors::ErrorCode;
|
use google_home::errors::ErrorCode;
|
||||||
use google_home::traits::OnOff;
|
use google_home::traits::OnOff;
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
|
|
||||||
use super::{Device, LuaDeviceCreate};
|
use super::{Device, LuaDeviceCreate};
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
@ -120,9 +120,6 @@ impl OnOff for HueGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod message {
|
mod message {
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use serde::ser::SerializeStruct;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
@ -164,46 +161,5 @@ mod message {
|
||||||
pub fn any_on(&self) -> bool {
|
pub fn any_on(&self) -> bool {
|
||||||
self.state.any_on
|
self.state.any_on
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn all_on(&self) -> bool {
|
|
||||||
// self.state.all_on
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Timeout {
|
|
||||||
timeout: Option<Duration>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Timeout {
|
|
||||||
pub fn new(timeout: Option<Duration>) -> Self {
|
|
||||||
Self { timeout }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for Timeout {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
let len = if self.timeout.is_some() { 2 } else { 1 };
|
|
||||||
let mut state = serializer.serialize_struct("TimerMessage", len)?;
|
|
||||||
if self.timeout.is_some() {
|
|
||||||
state.serialize_field("status", "enabled")?;
|
|
||||||
} else {
|
|
||||||
state.serialize_field("status", "disabled")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(timeout) = self.timeout {
|
|
||||||
let seconds = timeout.as_secs() % 60;
|
|
||||||
let minutes = (timeout.as_secs() / 60) % 60;
|
|
||||||
let hours = timeout.as_secs() / 3600;
|
|
||||||
|
|
||||||
let time = format!("PT{hours:<02}:{minutes:<02}:{seconds:<02}");
|
|
||||||
state.serialize_field("localtime", &time)?;
|
|
||||||
};
|
|
||||||
|
|
||||||
state.end()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,13 @@
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use automation_lib::action_callback::ActionCallback;
|
||||||
|
use automation_lib::config::{InfoConfig, MqttDeviceConfig};
|
||||||
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::event::OnMqtt;
|
||||||
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use axum::async_trait;
|
|
||||||
use rumqttc::{matches, Publish};
|
use rumqttc::{matches, Publish};
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
use zigbee2mqtt_types::vendors::philips::Zigbee929003017102;
|
use zigbee2mqtt_types::philips::{Zigbee929003017102, Zigbee929003017102Action};
|
||||||
|
|
||||||
use super::LuaDeviceCreate;
|
|
||||||
use crate::action_callback::ActionCallback;
|
|
||||||
use crate::config::{InfoConfig, MqttDeviceConfig};
|
|
||||||
use crate::devices::Device;
|
|
||||||
use crate::event::OnMqtt;
|
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
@ -72,12 +70,8 @@ impl OnMqtt for HueSwitch {
|
||||||
debug!(id = Device::get_id(self), "Remote action = {:?}", action);
|
debug!(id = Device::get_id(self), "Remote action = {:?}", action);
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
zigbee2mqtt_types::vendors::philips::Zigbee929003017102Action::Leftpress => {
|
Zigbee929003017102Action::LeftPress => self.config.left_callback.call(()).await,
|
||||||
self.config.left_callback.call(()).await
|
Zigbee929003017102Action::RightPress => self.config.right_callback.call(()).await,
|
||||||
}
|
|
||||||
zigbee2mqtt_types::vendors::philips::Zigbee929003017102Action::Rightpress => {
|
|
||||||
self.config.right_callback.call(()).await
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,6 +2,12 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use automation_lib::action_callback::ActionCallback;
|
||||||
|
use automation_lib::config::{InfoConfig, MqttDeviceConfig};
|
||||||
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::event::{OnMqtt, OnPresence};
|
||||||
|
use automation_lib::messages::OnOffMessage;
|
||||||
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use google_home::device;
|
use google_home::device;
|
||||||
use google_home::errors::ErrorCode;
|
use google_home::errors::ErrorCode;
|
||||||
|
@ -12,14 +18,6 @@ use serde::Deserialize;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use super::LuaDeviceCreate;
|
|
||||||
use crate::action_callback::ActionCallback;
|
|
||||||
use crate::config::{InfoConfig, MqttDeviceConfig};
|
|
||||||
use crate::devices::Device;
|
|
||||||
use crate::event::{OnMqtt, OnPresence};
|
|
||||||
use crate::messages::OnOffMessage;
|
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Copy)]
|
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Copy)]
|
||||||
pub enum OutletType {
|
pub enum OutletType {
|
||||||
Outlet,
|
Outlet,
|
|
@ -1,16 +1,14 @@
|
||||||
|
use automation_lib::action_callback::ActionCallback;
|
||||||
|
use automation_lib::config::{InfoConfig, MqttDeviceConfig};
|
||||||
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::event::OnMqtt;
|
||||||
|
use automation_lib::messages::{RemoteAction, RemoteMessage};
|
||||||
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use axum::async_trait;
|
use axum::async_trait;
|
||||||
use rumqttc::{matches, Publish};
|
use rumqttc::{matches, Publish};
|
||||||
use tracing::{debug, error, trace};
|
use tracing::{debug, error, trace};
|
||||||
|
|
||||||
use super::LuaDeviceCreate;
|
|
||||||
use crate::action_callback::ActionCallback;
|
|
||||||
use crate::config::{InfoConfig, MqttDeviceConfig};
|
|
||||||
use crate::devices::Device;
|
|
||||||
use crate::event::OnMqtt;
|
|
||||||
use crate::messages::RemoteMessage;
|
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[device_config(flatten)]
|
#[device_config(flatten)]
|
||||||
|
@ -73,14 +71,14 @@ impl OnMqtt for IkeaRemote {
|
||||||
|
|
||||||
let on = if self.config.single_button {
|
let on = if self.config.single_button {
|
||||||
match action {
|
match action {
|
||||||
crate::messages::RemoteAction::On => Some(true),
|
RemoteAction::On => Some(true),
|
||||||
crate::messages::RemoteAction::BrightnessMoveUp => Some(false),
|
RemoteAction::BrightnessMoveUp => Some(false),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match action {
|
match action {
|
||||||
crate::messages::RemoteAction::On => Some(true),
|
RemoteAction::On => Some(true),
|
||||||
crate::messages::RemoteAction::Off => Some(false),
|
RemoteAction::Off => Some(false),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -3,6 +3,8 @@ use std::net::SocketAddr;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::event::OnPresence;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use bytes::{Buf, BufMut};
|
use bytes::{Buf, BufMut};
|
||||||
use google_home::errors::{self, DeviceError};
|
use google_home::errors::{self, DeviceError};
|
||||||
|
@ -13,9 +15,6 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
use super::{Device, LuaDeviceCreate};
|
|
||||||
use crate::event::OnPresence;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub identifier: String,
|
pub identifier: String,
|
|
@ -8,19 +8,13 @@ mod ikea_outlet;
|
||||||
mod ikea_remote;
|
mod ikea_remote;
|
||||||
mod kasa_outlet;
|
mod kasa_outlet;
|
||||||
mod light_sensor;
|
mod light_sensor;
|
||||||
mod ntfy;
|
|
||||||
mod presence;
|
|
||||||
mod wake_on_lan;
|
mod wake_on_lan;
|
||||||
mod washer;
|
mod washer;
|
||||||
|
|
||||||
use std::fmt::Debug;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use automation_cast::Cast;
|
use automation_cast::Cast;
|
||||||
use dyn_clone::DynClone;
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
use google_home::traits::OnOff;
|
|
||||||
use mlua::ObjectLike;
|
|
||||||
|
|
||||||
pub use self::air_filter::AirFilter;
|
pub use self::air_filter::AirFilter;
|
||||||
pub use self::contact_sensor::ContactSensor;
|
pub use self::contact_sensor::ContactSensor;
|
||||||
|
@ -32,21 +26,8 @@ pub use self::ikea_outlet::IkeaOutlet;
|
||||||
pub use self::ikea_remote::IkeaRemote;
|
pub use self::ikea_remote::IkeaRemote;
|
||||||
pub use self::kasa_outlet::KasaOutlet;
|
pub use self::kasa_outlet::KasaOutlet;
|
||||||
pub use self::light_sensor::LightSensor;
|
pub use self::light_sensor::LightSensor;
|
||||||
pub use self::ntfy::{Notification, Ntfy};
|
|
||||||
pub use self::presence::{Presence, DEFAULT_PRESENCE};
|
|
||||||
pub use self::wake_on_lan::WakeOnLAN;
|
pub use self::wake_on_lan::WakeOnLAN;
|
||||||
pub use self::washer::Washer;
|
pub use self::washer::Washer;
|
||||||
use crate::event::{OnDarkness, OnMqtt, OnNotification, OnPresence};
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait LuaDeviceCreate {
|
|
||||||
type Config;
|
|
||||||
type Error;
|
|
||||||
|
|
||||||
async fn create(config: Self::Config) -> Result<Self, Self::Error>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! register_device {
|
macro_rules! register_device {
|
||||||
($lua:expr, $device:ty) => {
|
($lua:expr, $device:ty) => {
|
||||||
|
@ -60,7 +41,7 @@ macro_rules! impl_device {
|
||||||
impl mlua::UserData for $device {
|
impl mlua::UserData for $device {
|
||||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||||
methods.add_async_function("new", |_lua, config| async {
|
methods.add_async_function("new", |_lua, config| async {
|
||||||
let device: $device = crate::devices::LuaDeviceCreate::create(config)
|
let device: $device = LuaDeviceCreate::create(config)
|
||||||
.await
|
.await
|
||||||
.map_err(mlua::ExternalError::into_lua_err)?;
|
.map_err(mlua::ExternalError::into_lua_err)?;
|
||||||
|
|
||||||
|
@ -74,9 +55,9 @@ macro_rules! impl_device {
|
||||||
|
|
||||||
methods.add_async_method("get_id", |_lua, this, _: ()| async move { Ok(this.get_id()) });
|
methods.add_async_method("get_id", |_lua, this, _: ()| async move { Ok(this.get_id()) });
|
||||||
|
|
||||||
if impls::impls!($device: OnOff) {
|
if impls::impls!($device: google_home::traits::OnOff) {
|
||||||
methods.add_async_method("set_on", |_lua, this, on: bool| async move {
|
methods.add_async_method("set_on", |_lua, this, on: bool| async move {
|
||||||
(this.deref().cast() as Option<&dyn OnOff>)
|
(this.deref().cast() as Option<&dyn google_home::traits::OnOff>)
|
||||||
.expect("Cast should be valid")
|
.expect("Cast should be valid")
|
||||||
.set_on(on)
|
.set_on(on)
|
||||||
.await
|
.await
|
||||||
|
@ -86,7 +67,7 @@ macro_rules! impl_device {
|
||||||
});
|
});
|
||||||
|
|
||||||
methods.add_async_method("is_on", |_lua, this, _: ()| async move {
|
methods.add_async_method("is_on", |_lua, this, _: ()| async move {
|
||||||
Ok((this.deref().cast() as Option<&dyn OnOff>)
|
Ok((this.deref().cast() as Option<&dyn google_home::traits::OnOff>)
|
||||||
.expect("Cast should be valid")
|
.expect("Cast should be valid")
|
||||||
.on()
|
.on()
|
||||||
.await
|
.await
|
||||||
|
@ -108,8 +89,6 @@ impl_device!(IkeaOutlet);
|
||||||
impl_device!(IkeaRemote);
|
impl_device!(IkeaRemote);
|
||||||
impl_device!(KasaOutlet);
|
impl_device!(KasaOutlet);
|
||||||
impl_device!(LightSensor);
|
impl_device!(LightSensor);
|
||||||
impl_device!(Ntfy);
|
|
||||||
impl_device!(Presence);
|
|
||||||
impl_device!(WakeOnLAN);
|
impl_device!(WakeOnLAN);
|
||||||
impl_device!(Washer);
|
impl_device!(Washer);
|
||||||
|
|
||||||
|
@ -124,47 +103,8 @@ pub fn register_with_lua(lua: &mlua::Lua) -> mlua::Result<()> {
|
||||||
register_device!(lua, IkeaRemote);
|
register_device!(lua, IkeaRemote);
|
||||||
register_device!(lua, KasaOutlet);
|
register_device!(lua, KasaOutlet);
|
||||||
register_device!(lua, LightSensor);
|
register_device!(lua, LightSensor);
|
||||||
register_device!(lua, Ntfy);
|
|
||||||
register_device!(lua, Presence);
|
|
||||||
register_device!(lua, WakeOnLAN);
|
register_device!(lua, WakeOnLAN);
|
||||||
register_device!(lua, Washer);
|
register_device!(lua, Washer);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Device:
|
|
||||||
Debug
|
|
||||||
+ DynClone
|
|
||||||
+ Sync
|
|
||||||
+ Send
|
|
||||||
+ Cast<dyn google_home::Device>
|
|
||||||
+ Cast<dyn OnMqtt>
|
|
||||||
+ Cast<dyn OnMqtt>
|
|
||||||
+ Cast<dyn OnPresence>
|
|
||||||
+ Cast<dyn OnDarkness>
|
|
||||||
+ Cast<dyn OnNotification>
|
|
||||||
+ Cast<dyn OnOff>
|
|
||||||
{
|
|
||||||
fn get_id(&self) -> String;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl mlua::FromLua for Box<dyn Device> {
|
|
||||||
fn from_lua(value: mlua::Value, _lua: &mlua::Lua) -> mlua::Result<Self> {
|
|
||||||
match value {
|
|
||||||
mlua::Value::UserData(ud) => {
|
|
||||||
let ud = if ud.is::<Box<dyn Device>>() {
|
|
||||||
ud
|
|
||||||
} else {
|
|
||||||
ud.call_method::<_>("__box", ())?
|
|
||||||
};
|
|
||||||
|
|
||||||
let b = ud.borrow::<Self>()?.clone();
|
|
||||||
Ok(b)
|
|
||||||
}
|
|
||||||
_ => Err(mlua::Error::RuntimeError("Expected user data".into())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl mlua::UserData for Box<dyn Device> {}
|
|
||||||
|
|
||||||
dyn_clone::clone_trait_object!(Device);
|
|
|
@ -1,18 +1,16 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use automation_lib::config::MqttDeviceConfig;
|
||||||
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::event::{self, Event, EventChannel, OnMqtt};
|
||||||
|
use automation_lib::messages::BrightnessMessage;
|
||||||
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
use super::LuaDeviceCreate;
|
|
||||||
use crate::config::MqttDeviceConfig;
|
|
||||||
use crate::devices::Device;
|
|
||||||
use crate::event::{self, Event, EventChannel, OnMqtt};
|
|
||||||
use crate::messages::BrightnessMessage;
|
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub identifier: String,
|
pub identifier: String,
|
|
@ -1,6 +1,11 @@
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use automation_lib::config::{InfoConfig, MqttDeviceConfig};
|
||||||
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::event::OnMqtt;
|
||||||
|
use automation_lib::messages::ActivateMessage;
|
||||||
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use eui48::MacAddress;
|
use eui48::MacAddress;
|
||||||
use google_home::device;
|
use google_home::device;
|
||||||
|
@ -10,12 +15,6 @@ use google_home::types::Type;
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tracing::{debug, error, trace};
|
use tracing::{debug, error, trace};
|
||||||
|
|
||||||
use super::{Device, LuaDeviceCreate};
|
|
||||||
use crate::config::{InfoConfig, MqttDeviceConfig};
|
|
||||||
use crate::event::OnMqtt;
|
|
||||||
use crate::messages::ActivateMessage;
|
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
#[device_config(flatten)]
|
#[device_config(flatten)]
|
|
@ -1,18 +1,17 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use automation_lib::config::MqttDeviceConfig;
|
||||||
|
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||||
|
use automation_lib::event::{self, Event, EventChannel, OnMqtt};
|
||||||
|
use automation_lib::messages::PowerMessage;
|
||||||
|
use automation_lib::mqtt::WrappedAsyncClient;
|
||||||
|
use automation_lib::ntfy::{Notification, Priority};
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use tracing::{debug, error, trace, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use super::ntfy::Priority;
|
|
||||||
use super::{Device, LuaDeviceCreate, Notification};
|
|
||||||
use crate::config::MqttDeviceConfig;
|
|
||||||
use crate::event::{self, Event, EventChannel, OnMqtt};
|
|
||||||
use crate::messages::PowerMessage;
|
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub identifier: String,
|
pub identifier: String,
|
28
automation_lib/Cargo.toml
Normal file
28
automation_lib/Cargo.toml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
[package]
|
||||||
|
name = "automation_lib"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
automation_macro = { workspace = true }
|
||||||
|
automation_cast = { workspace = true }
|
||||||
|
google_home = { workspace = true }
|
||||||
|
rumqttc = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
|
serde_json = { workspace = true }
|
||||||
|
tokio = { workspace = true }
|
||||||
|
reqwest = { workspace = true }
|
||||||
|
serde_repr = { workspace = true }
|
||||||
|
tracing = { workspace = true }
|
||||||
|
bytes = { workspace = true }
|
||||||
|
pollster = { workspace = true }
|
||||||
|
async-trait = { workspace = true }
|
||||||
|
futures = { workspace = true }
|
||||||
|
thiserror = { workspace = true }
|
||||||
|
indexmap = { workspace = true }
|
||||||
|
tokio-cron-scheduler = { workspace = true }
|
||||||
|
mlua = { workspace = true }
|
||||||
|
tokio-util = { workspace = true }
|
||||||
|
uuid = { workspace = true }
|
||||||
|
dyn-clone = { workspace = true }
|
||||||
|
impls = { workspace = true }
|
99
automation_lib/src/device.rs
Normal file
99
automation_lib/src/device.rs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use automation_cast::Cast;
|
||||||
|
use dyn_clone::DynClone;
|
||||||
|
use google_home::traits::OnOff;
|
||||||
|
use mlua::ObjectLike;
|
||||||
|
|
||||||
|
use crate::event::{OnDarkness, OnMqtt, OnNotification, OnPresence};
|
||||||
|
|
||||||
|
// TODO: Make this a proper macro
|
||||||
|
macro_rules! impl_device {
|
||||||
|
($device:ty) => {
|
||||||
|
impl mlua::UserData for $device {
|
||||||
|
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||||
|
methods.add_async_function("new", |_lua, config| async {
|
||||||
|
let device: $device = LuaDeviceCreate::create(config)
|
||||||
|
.await
|
||||||
|
.map_err(mlua::ExternalError::into_lua_err)?;
|
||||||
|
|
||||||
|
Ok(device)
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_method("__box", |_lua, this, _: ()| {
|
||||||
|
let b: Box<dyn Device> = Box::new(this.clone());
|
||||||
|
Ok(b)
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_async_method("get_id", |_lua, this, _: ()| async move { Ok(this.get_id()) });
|
||||||
|
|
||||||
|
if impls::impls!($device: google_home::traits::OnOff) {
|
||||||
|
methods.add_async_method("set_on", |_lua, this, on: bool| async move {
|
||||||
|
(this.deref().cast() as Option<&dyn google_home::traits::OnOff>)
|
||||||
|
.expect("Cast should be valid")
|
||||||
|
.set_on(on)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_async_method("is_on", |_lua, this, _: ()| async move {
|
||||||
|
Ok((this.deref().cast() as Option<&dyn google_home::traits::OnOff>)
|
||||||
|
.expect("Cast should be valid")
|
||||||
|
.on()
|
||||||
|
.await
|
||||||
|
.unwrap())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub(crate) use impl_device;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait LuaDeviceCreate {
|
||||||
|
type Config;
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
async fn create(config: Self::Config) -> Result<Self, Self::Error>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Device:
|
||||||
|
Debug
|
||||||
|
+ DynClone
|
||||||
|
+ Sync
|
||||||
|
+ Send
|
||||||
|
+ Cast<dyn google_home::Device>
|
||||||
|
+ Cast<dyn OnMqtt>
|
||||||
|
+ Cast<dyn OnPresence>
|
||||||
|
+ Cast<dyn OnDarkness>
|
||||||
|
+ Cast<dyn OnNotification>
|
||||||
|
+ Cast<dyn OnOff>
|
||||||
|
{
|
||||||
|
fn get_id(&self) -> String;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl mlua::FromLua for Box<dyn Device> {
|
||||||
|
fn from_lua(value: mlua::Value, _lua: &mlua::Lua) -> mlua::Result<Self> {
|
||||||
|
match value {
|
||||||
|
mlua::Value::UserData(ud) => {
|
||||||
|
let ud = if ud.is::<Box<dyn Device>>() {
|
||||||
|
ud
|
||||||
|
} else {
|
||||||
|
ud.call_method::<_>("__box", ())?
|
||||||
|
};
|
||||||
|
|
||||||
|
let b = ud.borrow::<Self>()?.clone();
|
||||||
|
Ok(b)
|
||||||
|
}
|
||||||
|
_ => Err(mlua::Error::RuntimeError("Expected user data".into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl mlua::UserData for Box<dyn Device> {}
|
||||||
|
|
||||||
|
dyn_clone::clone_trait_object!(Device);
|
|
@ -8,7 +8,7 @@ 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};
|
||||||
|
|
||||||
use crate::devices::Device;
|
use crate::device::Device;
|
||||||
use crate::event::{Event, EventChannel, OnDarkness, OnMqtt, OnNotification, OnPresence};
|
use crate::event::{Event, EventChannel, OnDarkness, OnMqtt, OnNotification, OnPresence};
|
||||||
|
|
||||||
pub type DeviceMap = HashMap<String, Box<dyn Device>>;
|
pub type DeviceMap = HashMap<String, Box<dyn Device>>;
|
|
@ -1,10 +1,7 @@
|
||||||
use std::{error, fmt, result};
|
use std::{error, fmt, result};
|
||||||
|
|
||||||
use axum::http::status::InvalidStatusCode;
|
|
||||||
use axum::response::IntoResponse;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use rumqttc::ClientError;
|
use rumqttc::ClientError;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -101,68 +98,3 @@ pub enum LightSensorError {
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
SubscribeError(#[from] ClientError),
|
SubscribeError(#[from] ClientError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
#[error("{source}")]
|
|
||||||
pub struct ApiError {
|
|
||||||
status_code: axum::http::StatusCode,
|
|
||||||
source: Box<dyn std::error::Error>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApiError {
|
|
||||||
pub fn new(status_code: axum::http::StatusCode, source: Box<dyn std::error::Error>) -> Self {
|
|
||||||
Self {
|
|
||||||
status_code,
|
|
||||||
source,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ApiError> for ApiErrorJson {
|
|
||||||
fn from(value: ApiError) -> Self {
|
|
||||||
let error = ApiErrorJsonError {
|
|
||||||
code: value.status_code.as_u16(),
|
|
||||||
status: value.status_code.to_string(),
|
|
||||||
reason: value.source.to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Self { error }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoResponse for ApiError {
|
|
||||||
fn into_response(self) -> axum::response::Response {
|
|
||||||
(
|
|
||||||
self.status_code,
|
|
||||||
serde_json::to_string::<ApiErrorJson>(&self.into())
|
|
||||||
.expect("Serialization should not fail"),
|
|
||||||
)
|
|
||||||
.into_response()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct ApiErrorJsonError {
|
|
||||||
code: u16,
|
|
||||||
status: String,
|
|
||||||
reason: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct ApiErrorJson {
|
|
||||||
error: ApiErrorJsonError,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<ApiErrorJson> for ApiError {
|
|
||||||
type Error = InvalidStatusCode;
|
|
||||||
|
|
||||||
fn try_from(value: ApiErrorJson) -> result::Result<Self, Self::Error> {
|
|
||||||
let status_code = axum::http::StatusCode::from_u16(value.error.code)?;
|
|
||||||
let source = value.error.reason.into();
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
status_code,
|
|
||||||
source,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ use mlua::FromLua;
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
use crate::devices::Notification;
|
use crate::ntfy::Notification;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Event {
|
pub enum Event {
|
|
@ -3,13 +3,14 @@
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
|
||||||
pub mod action_callback;
|
pub mod action_callback;
|
||||||
pub mod auth;
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod device;
|
||||||
pub mod device_manager;
|
pub mod device_manager;
|
||||||
pub mod devices;
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
pub mod messages;
|
pub mod messages;
|
||||||
pub mod mqtt;
|
pub mod mqtt;
|
||||||
|
pub mod ntfy;
|
||||||
|
pub mod presence;
|
||||||
pub mod schedule;
|
pub mod schedule;
|
|
@ -1,14 +1,15 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use automation_cast::Cast;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_repr::*;
|
use serde_repr::*;
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
|
|
||||||
use super::LuaDeviceCreate;
|
use crate::device::{impl_device, Device, LuaDeviceCreate};
|
||||||
use crate::devices::Device;
|
|
||||||
use crate::event::{self, Event, EventChannel, OnNotification, OnPresence};
|
use crate::event::{self, Event, EventChannel, OnNotification, OnPresence};
|
||||||
|
|
||||||
#[derive(Debug, Serialize_repr, Clone, Copy)]
|
#[derive(Debug, Serialize_repr, Clone, Copy)]
|
||||||
|
@ -125,6 +126,8 @@ pub struct Ntfy {
|
||||||
config: Config,
|
config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_device!(Ntfy);
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl LuaDeviceCreate for Ntfy {
|
impl LuaDeviceCreate for Ntfy {
|
||||||
type Config = Config;
|
type Config = Config;
|
|
@ -1,15 +1,16 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use automation_cast::Cast;
|
||||||
use automation_macro::LuaDeviceConfig;
|
use automation_macro::LuaDeviceConfig;
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
use super::LuaDeviceCreate;
|
|
||||||
use crate::config::MqttDeviceConfig;
|
use crate::config::MqttDeviceConfig;
|
||||||
use crate::devices::Device;
|
use crate::device::{impl_device, Device, LuaDeviceCreate};
|
||||||
use crate::event::{self, Event, EventChannel, OnMqtt};
|
use crate::event::{self, Event, EventChannel, OnMqtt};
|
||||||
use crate::messages::PresenceMessage;
|
use crate::messages::PresenceMessage;
|
||||||
use crate::mqtt::WrappedAsyncClient;
|
use crate::mqtt::WrappedAsyncClient;
|
||||||
|
@ -48,6 +49,8 @@ impl Presence {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_device!(Presence);
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl LuaDeviceCreate for Presence {
|
impl LuaDeviceCreate for Presence {
|
||||||
type Config = Config;
|
type Config = Config;
|
|
@ -7,14 +7,7 @@ edition = "2021"
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
automation_cast = { path = "../automation_cast" }
|
itertools = { workspace = true }
|
||||||
async-trait = "0.1.80"
|
proc-macro2 = { workspace = true }
|
||||||
itertools = "0.12.1"
|
quote = { workspace = true }
|
||||||
proc-macro2 = "1.0.81"
|
syn = { workspace = true }
|
||||||
quote = "1.0.36"
|
|
||||||
serde = { version = "1.0.202", features = ["derive"] }
|
|
||||||
syn = { version = "2.0.60", features = ["extra-traits", "full"] }
|
|
||||||
serde_json = "1.0.118"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
serde = { version = "1.0.202", features = ["derive"] }
|
|
||||||
|
|
|
@ -6,13 +6,12 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
automation_cast = { path = "../../automation_cast/" }
|
automation_cast = { workspace = true }
|
||||||
google_home_macro = { path = "../google_home_macro/" }
|
google_home_macro = { workspace = true }
|
||||||
serde = { version = "1.0.149", features = ["derive"] }
|
serde = { workspace = true }
|
||||||
serde_json = "1.0.89"
|
serde_json = { workspace = true }
|
||||||
thiserror = "1.0.37"
|
thiserror = { workspace = true }
|
||||||
tokio = { version = "1", features = ["sync", "full"] }
|
tokio = { workspace = true }
|
||||||
async-trait = "0.1.61"
|
async-trait = { workspace = true }
|
||||||
futures = "0.3.25"
|
futures = { workspace = true }
|
||||||
anyhow = "1.0.75"
|
json_value_merge = { workspace = true }
|
||||||
json_value_merge = "2.0.0"
|
|
||||||
|
|
|
@ -7,6 +7,6 @@ edition = "2021"
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
proc-macro2 = "1.0.81"
|
proc-macro2 = { workspace = true }
|
||||||
quote = "1.0.36"
|
quote = { workspace = true }
|
||||||
syn = { version = "2.0.60", features = ["extra-traits", "full"] }
|
syn = { workspace = true }
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly-2024-07-25"
|
channel = "nightly-2024-12-06"
|
||||||
components = ["rustfmt", "clippy", "rust-analyzer"]
|
components = ["rustfmt", "clippy", "rust-analyzer"]
|
||||||
profile = "minimal"
|
profile = "minimal"
|
||||||
|
|
29
src/main.rs
29
src/main.rs
|
@ -1,13 +1,16 @@
|
||||||
|
mod web;
|
||||||
|
|
||||||
|
use std::net::SocketAddr;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use automation::auth::User;
|
use automation_lib::config::{FulfillmentConfig, MqttConfig};
|
||||||
use automation::config::{FulfillmentConfig, MqttConfig};
|
use automation_lib::device_manager::DeviceManager;
|
||||||
use automation::device_manager::DeviceManager;
|
use automation_lib::helpers;
|
||||||
use automation::error::ApiError;
|
use automation_lib::mqtt::{self, WrappedAsyncClient};
|
||||||
use automation::mqtt::{self, WrappedAsyncClient};
|
use automation_lib::ntfy::Ntfy;
|
||||||
use automation::{devices, helpers};
|
use automation_lib::presence::Presence;
|
||||||
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;
|
||||||
|
@ -16,7 +19,9 @@ 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 rumqttc::AsyncClient;
|
||||||
|
use tokio::net::TcpListener;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
|
use web::{ApiError, User};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AppState {
|
struct AppState {
|
||||||
|
@ -111,8 +116,11 @@ async fn app() -> anyhow::Result<()> {
|
||||||
|
|
||||||
lua.globals().set("automation", automation)?;
|
lua.globals().set("automation", automation)?;
|
||||||
|
|
||||||
devices::register_with_lua(&lua)?;
|
automation_devices::register_with_lua(&lua)?;
|
||||||
helpers::register_with_lua(&lua)?;
|
helpers::register_with_lua(&lua)?;
|
||||||
|
lua.globals().set("Ntfy", lua.create_proxy::<Ntfy>()?)?;
|
||||||
|
lua.globals()
|
||||||
|
.set("Presence", lua.create_proxy::<Presence>()?)?;
|
||||||
|
|
||||||
// TODO: Make this not hardcoded
|
// TODO: Make this not hardcoded
|
||||||
let config_filename = std::env::var("AUTOMATION_CONFIG").unwrap_or("./config.lua".into());
|
let config_filename = std::env::var("AUTOMATION_CONFIG").unwrap_or("./config.lua".into());
|
||||||
|
@ -148,11 +156,10 @@ async fn app() -> anyhow::Result<()> {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start the web server
|
// Start the web server
|
||||||
let addr = fulfillment_config.into();
|
let addr: SocketAddr = fulfillment_config.into();
|
||||||
info!("Server started on http://{addr}");
|
info!("Server started on http://{addr}");
|
||||||
axum::Server::try_bind(&addr)?
|
let listener = TcpListener::bind(addr).await?;
|
||||||
.serve(app.into_make_service())
|
axum::serve(listener, app).await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,78 @@
|
||||||
|
use std::result;
|
||||||
|
|
||||||
use axum::async_trait;
|
use axum::async_trait;
|
||||||
use axum::extract::{FromRef, FromRequestParts};
|
use axum::extract::{FromRef, FromRequestParts};
|
||||||
use axum::http::request::Parts;
|
use axum::http::request::Parts;
|
||||||
|
use axum::http::status::InvalidStatusCode;
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use serde::Deserialize;
|
use axum::response::IntoResponse;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::error::{ApiError, ApiErrorJson};
|
#[derive(Debug, Error)]
|
||||||
|
#[error("{source}")]
|
||||||
|
pub struct ApiError {
|
||||||
|
status_code: axum::http::StatusCode,
|
||||||
|
source: Box<dyn std::error::Error>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiError {
|
||||||
|
pub fn new(status_code: axum::http::StatusCode, source: Box<dyn std::error::Error>) -> Self {
|
||||||
|
Self {
|
||||||
|
status_code,
|
||||||
|
source,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ApiError> for ApiErrorJson {
|
||||||
|
fn from(value: ApiError) -> Self {
|
||||||
|
let error = ApiErrorJsonError {
|
||||||
|
code: value.status_code.as_u16(),
|
||||||
|
status: value.status_code.to_string(),
|
||||||
|
reason: value.source.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self { error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoResponse for ApiError {
|
||||||
|
fn into_response(self) -> axum::response::Response {
|
||||||
|
(
|
||||||
|
self.status_code,
|
||||||
|
serde_json::to_string::<ApiErrorJson>(&self.into())
|
||||||
|
.expect("Serialization should not fail"),
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct ApiErrorJsonError {
|
||||||
|
code: u16,
|
||||||
|
status: String,
|
||||||
|
reason: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ApiErrorJson {
|
||||||
|
error: ApiErrorJsonError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<ApiErrorJson> for ApiError {
|
||||||
|
type Error = InvalidStatusCode;
|
||||||
|
|
||||||
|
fn try_from(value: ApiErrorJson) -> result::Result<Self, Self::Error> {
|
||||||
|
let status_code = axum::http::StatusCode::from_u16(value.error.code)?;
|
||||||
|
let source = value.error.reason.into();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
status_code,
|
||||||
|
source,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
|
@ -25,6 +93,8 @@ where
|
||||||
|
|
||||||
// Create a request to the auth server
|
// Create a request to the auth server
|
||||||
// TODO: Do some discovery to find the correct url for this instead of assuming
|
// TODO: Do some discovery to find the correct url for this instead of assuming
|
||||||
|
// TODO: I think we can also just run Authlia in front of the endpoint instead
|
||||||
|
// This would then give us a header containing the logged in user info?
|
||||||
let mut req = reqwest::Client::new().get(format!("{}/userinfo", openid_url));
|
let mut req = reqwest::Client::new().get(format!("{}/userinfo", openid_url));
|
||||||
|
|
||||||
// Add auth header to the request if it exists
|
// Add auth header to the request if it exists
|
Loading…
Reference in New Issue
Block a user