Improved the way devices are instantiated from their respective configs
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
ab5e47d1ff
commit
b1506f8e63
217
Cargo.lock
generated
217
Cargo.lock
generated
|
@ -26,6 +26,21 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.72"
|
||||
|
@ -65,6 +80,7 @@ dependencies = [
|
|||
"bytes",
|
||||
"console-subscriber",
|
||||
"dotenvy",
|
||||
"enum_dispatch",
|
||||
"eui48",
|
||||
"futures",
|
||||
"google-home",
|
||||
|
@ -78,6 +94,7 @@ dependencies = [
|
|||
"serde-tuple-vec-map",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"serde_with",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"toml",
|
||||
|
@ -201,6 +218,19 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console-api"
|
||||
version = "0.5.0"
|
||||
|
@ -281,6 +311,50 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
|
@ -302,6 +376,24 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum_dispatch"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f33313078bb8d4d05a2733a94ac4c2d8a0df9a2b84424ebf4f33bfc224a890e"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "eui48"
|
||||
version = "1.1.0"
|
||||
|
@ -483,7 +575,7 @@ dependencies = [
|
|||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
|
@ -496,6 +588,12 @@ version = "0.12.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
|
||||
[[package]]
|
||||
name = "hdrhistogram"
|
||||
version = "7.5.2"
|
||||
|
@ -611,6 +709,35 @@ dependencies = [
|
|||
"tokio-io-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.4.0"
|
||||
|
@ -636,7 +763,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1280,6 +1419,35 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1402f54f9a3b9e2efe71c1cea24e648acce55887983553eeb858cf3115acfd49"
|
||||
dependencies = [
|
||||
"base64 0.21.2",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.0.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with_macros",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9197f1ad0e3c173a0222d3c4404fb04c3afe87e962bcb327af73e8301fa203c7"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sharded-slab"
|
||||
version = "0.1.4"
|
||||
|
@ -1339,6 +1507,12 @@ dependencies = [
|
|||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
|
@ -1397,6 +1571,34 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb71511c991639bb078fd5bf97757e03914361c48100d52878b8e52b46fb92cd"
|
||||
dependencies = [
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
|
@ -1542,7 +1744,7 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
|||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"indexmap",
|
||||
"indexmap 1.9.3",
|
||||
"pin-project",
|
||||
"pin-project-lite",
|
||||
"rand",
|
||||
|
@ -1821,6 +2023,15 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
|
|
@ -38,6 +38,8 @@ wakey = "0.3.0"
|
|||
console-subscriber = "0.1.8"
|
||||
tracing-subscriber = "0.3.16"
|
||||
serde-tuple-vec-map = "1.0.1"
|
||||
serde_with = "3.2.0"
|
||||
enum_dispatch = "0.3.12"
|
||||
|
||||
[patch.crates-io]
|
||||
wakey = { git = "https://github.com/DreadedX/wakey" }
|
||||
|
|
|
@ -73,7 +73,7 @@ pub fn device(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
|
||||
let prefix = quote! {
|
||||
pub trait #name<T: ?Sized + 'static> {
|
||||
fn consume(self: Box<Self>) -> Option<Box<T>>;
|
||||
fn is(&self) -> bool;
|
||||
fn cast(&self) -> Option<&T>;
|
||||
fn cast_mut(&mut self) -> Option<&mut T>;
|
||||
}
|
||||
|
@ -95,8 +95,8 @@ pub fn device(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
where
|
||||
T: #interface_ident + 'static,
|
||||
{
|
||||
default fn consume(self: Box<Self>) -> Option<Box<dyn #device_trait>> {
|
||||
None
|
||||
default fn is(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
default fn cast(&self) -> Option<&(dyn #device_trait + 'static)> {
|
||||
|
@ -114,8 +114,8 @@ pub fn device(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
where
|
||||
T: #interface_ident + #device_trait + 'static,
|
||||
{
|
||||
fn consume(self: Box<Self>) -> Option<Box<dyn #device_trait>> {
|
||||
Some(self)
|
||||
fn is(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn cast(&self) -> Option<&(dyn #device_trait + 'static)> {
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::{
|
|||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use enum_dispatch::enum_dispatch;
|
||||
use regex::{Captures, Regex};
|
||||
use rumqttc::{AsyncClient, MqttOptions, Transport};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
|
@ -14,10 +15,11 @@ use crate::{
|
|||
auth::OpenIDConfig,
|
||||
device_manager::DeviceManager,
|
||||
devices::{
|
||||
AudioSetup, ContactSensor, DebugBridgeConfig, Device, HueBridgeConfig, HueLight,
|
||||
IkeaOutlet, KasaOutlet, LightSensorConfig, PresenceConfig, WakeOnLAN, Washer,
|
||||
AudioSetupConfig, ContactSensorConfig, DebugBridgeConfig, Device, HueBridgeConfig,
|
||||
HueLightConfig, IkeaOutletConfig, KasaOutletConfig, LightSensorConfig, PresenceConfig,
|
||||
WakeOnLANConfig, WasherConfig,
|
||||
},
|
||||
error::{ConfigParseError, CreateDeviceError, MissingEnv},
|
||||
error::{ConfigParseError, DeviceConfigError, MissingEnv},
|
||||
event::EventChannel,
|
||||
};
|
||||
|
||||
|
@ -34,7 +36,7 @@ pub struct Config {
|
|||
pub hue_bridge: Option<HueBridgeConfig>,
|
||||
pub debug_bridge: Option<DebugBridgeConfig>,
|
||||
#[serde(default, with = "tuple_vec_map")]
|
||||
pub devices: Vec<(String, DeviceConfig)>,
|
||||
pub devices: Vec<(String, DeviceConfigs)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
|
@ -122,18 +124,6 @@ pub struct MqttDeviceConfig {
|
|||
pub topic: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum DeviceConfig {
|
||||
AudioSetup(<AudioSetup as CreateDevice>::Config),
|
||||
ContactSensor(<ContactSensor as CreateDevice>::Config),
|
||||
IkeaOutlet(<IkeaOutlet as CreateDevice>::Config),
|
||||
KasaOutlet(<KasaOutlet as CreateDevice>::Config),
|
||||
WakeOnLAN(<WakeOnLAN as CreateDevice>::Config),
|
||||
Washer(<Washer as CreateDevice>::Config),
|
||||
HueLight(<HueLight as CreateDevice>::Config),
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn parse_file(filename: &str) -> Result<Self, ConfigParseError> {
|
||||
debug!("Loading config: {filename}");
|
||||
|
@ -162,50 +152,32 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ConfigExternal<'a> {
|
||||
pub client: &'a AsyncClient,
|
||||
pub device_manager: &'a DeviceManager,
|
||||
pub presence_topic: &'a str,
|
||||
pub event_channel: &'a EventChannel,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait CreateDevice {
|
||||
type Config;
|
||||
|
||||
#[enum_dispatch]
|
||||
pub trait DeviceConfig {
|
||||
async fn create(
|
||||
identifier: &str,
|
||||
config: Self::Config,
|
||||
event_channel: &EventChannel,
|
||||
client: &AsyncClient,
|
||||
// TODO: Not a big fan of passing in the global config
|
||||
presence_topic: &str,
|
||||
devices: &DeviceManager,
|
||||
) -> Result<Self, CreateDeviceError>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
macro_rules! create {
|
||||
(($self:ident, $id:ident, $event_channel:ident, $client:ident, $presence_topic:ident, $device_manager:ident), [ $( $Variant:ident ),* ]) => {
|
||||
match $self {
|
||||
$(DeviceConfig::$Variant(c) => Box::new($Variant::create($id, c, $event_channel, $client, $presence_topic, $device_manager).await?),)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl DeviceConfig {
|
||||
pub async fn create(
|
||||
self,
|
||||
id: &str,
|
||||
event_channel: &EventChannel,
|
||||
client: &AsyncClient,
|
||||
presence_topic: &str,
|
||||
device_manager: &DeviceManager,
|
||||
) -> Result<Box<dyn Device>, CreateDeviceError> {
|
||||
Ok(create! {
|
||||
(self, id, event_channel, client, presence_topic, device_manager), [
|
||||
AudioSetup,
|
||||
ContactSensor,
|
||||
IkeaOutlet,
|
||||
KasaOutlet,
|
||||
WakeOnLAN,
|
||||
Washer,
|
||||
HueLight
|
||||
]
|
||||
})
|
||||
}
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
#[enum_dispatch(DeviceConfig)]
|
||||
pub enum DeviceConfigs {
|
||||
AudioSetup(AudioSetupConfig),
|
||||
ContactSensor(ContactSensorConfig),
|
||||
IkeaOutlet(IkeaOutletConfig),
|
||||
KasaOutlet(KasaOutletConfig),
|
||||
WakeOnLAN(WakeOnLANConfig),
|
||||
Washer(WasherConfig),
|
||||
HueLight(HueLightConfig),
|
||||
}
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
use async_trait::async_trait;
|
||||
use google_home::traits::OnOff;
|
||||
use rumqttc::AsyncClient;
|
||||
use serde::Deserialize;
|
||||
use tracing::{debug, error, trace, warn};
|
||||
|
||||
use crate::{
|
||||
config::{CreateDevice, MqttDeviceConfig},
|
||||
device_manager::{DeviceManager, WrappedDevice},
|
||||
config::{ConfigExternal, DeviceConfig, MqttDeviceConfig},
|
||||
device_manager::WrappedDevice,
|
||||
devices::As,
|
||||
error::CreateDeviceError,
|
||||
event::EventChannel,
|
||||
error::DeviceConfigError,
|
||||
event::OnMqtt,
|
||||
event::OnPresence,
|
||||
messages::{RemoteAction, RemoteMessage},
|
||||
|
@ -25,63 +23,66 @@ pub struct AudioSetupConfig {
|
|||
speakers: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl DeviceConfig for AudioSetupConfig {
|
||||
async fn create(
|
||||
self,
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
trace!(id = identifier, "Setting up AudioSetup");
|
||||
|
||||
// TODO: Make sure they implement OnOff?
|
||||
let mixer = ext
|
||||
.device_manager
|
||||
.get(&self.mixer)
|
||||
.await
|
||||
// NOTE: We need to clone to make the compiler happy, how ever if this clone happens the next one can never happen...
|
||||
.ok_or(DeviceConfigError::MissingChild(
|
||||
identifier.into(),
|
||||
self.mixer.clone(),
|
||||
))?;
|
||||
|
||||
if !As::<dyn OnOff>::is(mixer.read().await.as_ref()) {
|
||||
return Err(DeviceConfigError::MissingTrait(self.mixer, "OnOff".into()));
|
||||
}
|
||||
|
||||
let speakers =
|
||||
ext.device_manager
|
||||
.get(&self.speakers)
|
||||
.await
|
||||
.ok_or(DeviceConfigError::MissingChild(
|
||||
identifier.into(),
|
||||
self.speakers.clone(),
|
||||
))?;
|
||||
|
||||
if !As::<dyn OnOff>::is(speakers.read().await.as_ref()) {
|
||||
return Err(DeviceConfigError::MissingTrait(
|
||||
self.speakers,
|
||||
"OnOff".into(),
|
||||
));
|
||||
}
|
||||
|
||||
let device = AudioSetup {
|
||||
identifier: identifier.to_owned(),
|
||||
mqtt: self.mqtt,
|
||||
mixer,
|
||||
speakers,
|
||||
};
|
||||
|
||||
Ok(Box::new(device))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: We need a better way to store the children devices
|
||||
#[derive(Debug)]
|
||||
pub struct AudioSetup {
|
||||
struct AudioSetup {
|
||||
identifier: String,
|
||||
mqtt: MqttDeviceConfig,
|
||||
mixer: WrappedDevice,
|
||||
speakers: WrappedDevice,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CreateDevice for AudioSetup {
|
||||
type Config = AudioSetupConfig;
|
||||
|
||||
async fn create(
|
||||
identifier: &str,
|
||||
config: Self::Config,
|
||||
_event_channel: &EventChannel,
|
||||
_client: &AsyncClient,
|
||||
_presence_topic: &str,
|
||||
device_manager: &DeviceManager,
|
||||
) -> Result<Self, CreateDeviceError> {
|
||||
trace!(id = identifier, "Setting up AudioSetup");
|
||||
|
||||
// TODO: Make sure they implement OnOff?
|
||||
let mixer = device_manager
|
||||
.get(&config.mixer)
|
||||
.await
|
||||
// NOTE: We need to clone to make the compiler happy, how ever if this clone happens the next one can never happen...
|
||||
.ok_or(CreateDeviceError::DeviceDoesNotExist(config.mixer.clone()))?;
|
||||
|
||||
{
|
||||
let mixer = mixer.read().await;
|
||||
if As::<dyn OnOff>::cast(mixer.as_ref()).is_none() {
|
||||
return Err(CreateDeviceError::OnOffExpected(config.mixer));
|
||||
}
|
||||
}
|
||||
|
||||
let speakers = device_manager.get(&config.speakers).await.ok_or(
|
||||
CreateDeviceError::DeviceDoesNotExist(config.speakers.clone()),
|
||||
)?;
|
||||
|
||||
{
|
||||
let speakers = speakers.read().await;
|
||||
if As::<dyn OnOff>::cast(speakers.as_ref()).is_none() {
|
||||
return Err(CreateDeviceError::OnOffExpected(config.speakers));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
identifier: identifier.to_owned(),
|
||||
mqtt: config.mqtt,
|
||||
mixer,
|
||||
speakers,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for AudioSetup {
|
||||
fn get_id(&self) -> &str {
|
||||
&self.identifier
|
||||
|
|
|
@ -4,15 +4,15 @@ use async_trait::async_trait;
|
|||
use google_home::traits::OnOff;
|
||||
use rumqttc::{has_wildcards, AsyncClient};
|
||||
use serde::Deserialize;
|
||||
use serde_with::{serde_as, DurationSeconds};
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::{debug, error, trace, warn};
|
||||
|
||||
use crate::{
|
||||
config::{CreateDevice, MqttDeviceConfig},
|
||||
device_manager::{DeviceManager, WrappedDevice},
|
||||
config::{ConfigExternal, DeviceConfig, MqttDeviceConfig},
|
||||
device_manager::WrappedDevice,
|
||||
devices::{As, DEFAULT_PRESENCE},
|
||||
error::{CreateDeviceError, MissingWildcard},
|
||||
event::EventChannel,
|
||||
error::{DeviceConfigError, MissingWildcard},
|
||||
event::OnMqtt,
|
||||
event::OnPresence,
|
||||
messages::{ContactMessage, PresenceMessage},
|
||||
|
@ -22,11 +22,13 @@ use crate::{
|
|||
use super::Device;
|
||||
|
||||
// NOTE: If we add more presence devices we might need to move this out of here
|
||||
#[serde_as]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct PresenceDeviceConfig {
|
||||
#[serde(flatten)]
|
||||
pub mqtt: Option<MqttDeviceConfig>,
|
||||
pub timeout: u64, // Timeout in seconds
|
||||
#[serde_as(as = "DurationSeconds")]
|
||||
pub timeout: Duration,
|
||||
}
|
||||
|
||||
impl PresenceDeviceConfig {
|
||||
|
@ -56,11 +58,13 @@ impl PresenceDeviceConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct LightsConfig {
|
||||
lights: Vec<String>,
|
||||
#[serde(default)]
|
||||
timeout: u64, // Timeout in seconds
|
||||
#[serde_as(as = "DurationSeconds")]
|
||||
pub timeout: Duration,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
|
@ -71,14 +75,66 @@ pub struct ContactSensorConfig {
|
|||
lights: Option<LightsConfig>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl DeviceConfig for ContactSensorConfig {
|
||||
async fn create(
|
||||
self,
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
trace!(id = identifier, "Setting up ContactSensor");
|
||||
|
||||
let presence = self
|
||||
.presence
|
||||
.map(|p| p.generate_topic("contact", identifier, ext.presence_topic))
|
||||
.transpose()?;
|
||||
|
||||
let lights =
|
||||
if let Some(lights_config) = self.lights {
|
||||
let mut lights = Vec::new();
|
||||
for name in lights_config.lights {
|
||||
let light = ext.device_manager.get(&name).await.ok_or(
|
||||
DeviceConfigError::MissingChild(name.clone(), "OnOff".into()),
|
||||
)?;
|
||||
|
||||
if !As::<dyn OnOff>::is(light.read().await.as_ref()) {
|
||||
return Err(DeviceConfigError::MissingTrait(name, "OnOff".into()));
|
||||
}
|
||||
|
||||
lights.push((light, false));
|
||||
}
|
||||
|
||||
Some(Lights {
|
||||
lights,
|
||||
timeout: lights_config.timeout,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let device = ContactSensor {
|
||||
identifier: identifier.to_owned(),
|
||||
mqtt: self.mqtt,
|
||||
presence,
|
||||
client: ext.client.clone(),
|
||||
overall_presence: DEFAULT_PRESENCE,
|
||||
is_closed: true,
|
||||
handle: None,
|
||||
lights,
|
||||
};
|
||||
|
||||
Ok(Box::new(device))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Lights {
|
||||
struct Lights {
|
||||
lights: Vec<(WrappedDevice, bool)>,
|
||||
timeout: Duration, // Timeout in seconds
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ContactSensor {
|
||||
struct ContactSensor {
|
||||
identifier: String,
|
||||
mqtt: MqttDeviceConfig,
|
||||
presence: Option<PresenceDeviceConfig>,
|
||||
|
@ -91,64 +147,6 @@ pub struct ContactSensor {
|
|||
lights: Option<Lights>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CreateDevice for ContactSensor {
|
||||
type Config = ContactSensorConfig;
|
||||
|
||||
async fn create(
|
||||
identifier: &str,
|
||||
config: Self::Config,
|
||||
_event_channel: &EventChannel,
|
||||
client: &AsyncClient,
|
||||
presence_topic: &str,
|
||||
device_manager: &DeviceManager,
|
||||
) -> Result<Self, CreateDeviceError> {
|
||||
trace!(id = identifier, "Setting up ContactSensor");
|
||||
|
||||
let presence = config
|
||||
.presence
|
||||
.map(|p| p.generate_topic("contact", identifier, presence_topic))
|
||||
.transpose()?;
|
||||
|
||||
let lights = if let Some(lights_config) = config.lights {
|
||||
let mut lights = Vec::new();
|
||||
for name in lights_config.lights {
|
||||
let light = device_manager
|
||||
.get(&name)
|
||||
.await
|
||||
.ok_or(CreateDeviceError::DeviceDoesNotExist(name.clone()))?;
|
||||
|
||||
{
|
||||
let light = light.read().await;
|
||||
if As::<dyn OnOff>::cast(light.as_ref()).is_none() {
|
||||
return Err(CreateDeviceError::OnOffExpected(name));
|
||||
}
|
||||
}
|
||||
|
||||
lights.push((light, false));
|
||||
}
|
||||
|
||||
Some(Lights {
|
||||
lights,
|
||||
timeout: Duration::from_secs(lights_config.timeout),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
identifier: identifier.to_owned(),
|
||||
mqtt: config.mqtt,
|
||||
presence,
|
||||
client: client.clone(),
|
||||
overall_presence: DEFAULT_PRESENCE,
|
||||
is_closed: true,
|
||||
handle: None,
|
||||
lights,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for ContactSensor {
|
||||
fn get_id(&self) -> &str {
|
||||
&self.identifier
|
||||
|
@ -249,7 +247,7 @@ impl OnMqtt for ContactSensor {
|
|||
// Once the door is closed again we start a timeout for removing the presence
|
||||
let client = self.client.clone();
|
||||
let id = self.identifier.clone();
|
||||
let timeout = Duration::from_secs(presence.timeout);
|
||||
let timeout = presence.timeout;
|
||||
self.handle = Some(tokio::spawn(async move {
|
||||
debug!(id, "Starting timeout ({timeout:?}) for contact sensor...");
|
||||
tokio::time::sleep(timeout).await;
|
||||
|
|
|
@ -24,14 +24,11 @@ pub struct DebugBridge {
|
|||
}
|
||||
|
||||
impl DebugBridge {
|
||||
pub fn new(
|
||||
config: DebugBridgeConfig,
|
||||
client: &AsyncClient,
|
||||
) -> Result<Self, crate::error::CreateDeviceError> {
|
||||
Ok(Self {
|
||||
pub fn new(config: DebugBridgeConfig, client: &AsyncClient) -> Self {
|
||||
Self {
|
||||
mqtt: config.mqtt,
|
||||
client: client.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,14 +5,17 @@ use std::{
|
|||
|
||||
use async_trait::async_trait;
|
||||
use google_home::{errors::ErrorCode, traits::OnOff};
|
||||
use rumqttc::AsyncClient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use tracing::{debug, error, trace, warn};
|
||||
|
||||
use crate::{
|
||||
config::CreateDevice, device_manager::DeviceManager, devices::Device, error::CreateDeviceError,
|
||||
event::EventChannel, event::OnDarkness, event::OnPresence, traits::Timeout,
|
||||
config::{ConfigExternal, DeviceConfig},
|
||||
devices::Device,
|
||||
error::DeviceConfigError,
|
||||
event::OnDarkness,
|
||||
event::OnPresence,
|
||||
traits::Timeout,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -117,8 +120,27 @@ pub struct HueLightConfig {
|
|||
pub timer_id: isize,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl DeviceConfig for HueLightConfig {
|
||||
async fn create(
|
||||
self,
|
||||
identifier: &str,
|
||||
_ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
let device = HueLight {
|
||||
identifier: identifier.to_owned(),
|
||||
addr: (self.ip, 80).into(),
|
||||
login: self.login,
|
||||
light_id: self.light_id,
|
||||
timer_id: self.timer_id,
|
||||
};
|
||||
|
||||
Ok(Box::new(device))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HueLight {
|
||||
struct HueLight {
|
||||
pub identifier: String,
|
||||
pub addr: SocketAddr,
|
||||
pub login: String,
|
||||
|
@ -126,28 +148,6 @@ pub struct HueLight {
|
|||
pub timer_id: isize,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CreateDevice for HueLight {
|
||||
type Config = HueLightConfig;
|
||||
|
||||
async fn create(
|
||||
identifier: &str,
|
||||
config: Self::Config,
|
||||
_event_channel: &EventChannel,
|
||||
_client: &AsyncClient,
|
||||
_presence_topic: &str,
|
||||
_devices: &DeviceManager,
|
||||
) -> Result<Self, CreateDeviceError> {
|
||||
Ok(Self {
|
||||
identifier: identifier.to_owned(),
|
||||
addr: (config.ip, 80).into(),
|
||||
login: config.login,
|
||||
light_id: config.light_id,
|
||||
timer_id: config.timer_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for HueLight {
|
||||
fn get_id(&self) -> &str {
|
||||
&self.identifier
|
||||
|
|
|
@ -8,15 +8,15 @@ use google_home::{
|
|||
};
|
||||
use rumqttc::{AsyncClient, Publish};
|
||||
use serde::Deserialize;
|
||||
use serde_with::serde_as;
|
||||
use serde_with::DurationSeconds;
|
||||
use std::time::Duration;
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::{debug, error, trace, warn};
|
||||
|
||||
use crate::config::{CreateDevice, InfoConfig, MqttDeviceConfig};
|
||||
use crate::device_manager::DeviceManager;
|
||||
use crate::config::{ConfigExternal, DeviceConfig, InfoConfig, MqttDeviceConfig};
|
||||
use crate::devices::Device;
|
||||
use crate::error::CreateDeviceError;
|
||||
use crate::event::EventChannel;
|
||||
use crate::error::DeviceConfigError;
|
||||
use crate::event::OnMqtt;
|
||||
use crate::event::OnPresence;
|
||||
use crate::messages::OnOffMessage;
|
||||
|
@ -30,6 +30,7 @@ pub enum OutletType {
|
|||
Light,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct IkeaOutletConfig {
|
||||
#[serde(flatten)]
|
||||
|
@ -38,15 +39,45 @@ pub struct IkeaOutletConfig {
|
|||
mqtt: MqttDeviceConfig,
|
||||
#[serde(default = "default_outlet_type")]
|
||||
outlet_type: OutletType,
|
||||
timeout: Option<u64>, // Timeout in seconds
|
||||
#[serde_as(as = "Option<DurationSeconds>")]
|
||||
timeout: Option<Duration>, // Timeout in seconds
|
||||
}
|
||||
|
||||
fn default_outlet_type() -> OutletType {
|
||||
OutletType::Outlet
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl DeviceConfig for IkeaOutletConfig {
|
||||
async fn create(
|
||||
self,
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
trace!(
|
||||
id = identifier,
|
||||
name = self.info.name,
|
||||
room = self.info.room,
|
||||
"Setting up IkeaOutlet"
|
||||
);
|
||||
|
||||
let device = IkeaOutlet {
|
||||
identifier: identifier.to_owned(),
|
||||
info: self.info,
|
||||
mqtt: self.mqtt,
|
||||
outlet_type: self.outlet_type,
|
||||
timeout: self.timeout,
|
||||
client: ext.client.clone(),
|
||||
last_known_state: false,
|
||||
handle: None,
|
||||
};
|
||||
|
||||
Ok(Box::new(device))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IkeaOutlet {
|
||||
struct IkeaOutlet {
|
||||
identifier: String,
|
||||
info: InfoConfig,
|
||||
mqtt: MqttDeviceConfig,
|
||||
|
@ -58,38 +89,6 @@ pub struct IkeaOutlet {
|
|||
handle: Option<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CreateDevice for IkeaOutlet {
|
||||
type Config = IkeaOutletConfig;
|
||||
|
||||
async fn create(
|
||||
identifier: &str,
|
||||
config: Self::Config,
|
||||
_event_channel: &EventChannel,
|
||||
client: &AsyncClient,
|
||||
_presence_topic: &str,
|
||||
_device_manager: &DeviceManager,
|
||||
) -> Result<Self, CreateDeviceError> {
|
||||
trace!(
|
||||
id = identifier,
|
||||
name = config.info.name,
|
||||
room = config.info.room,
|
||||
"Setting up IkeaOutlet"
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
identifier: identifier.to_owned(),
|
||||
info: config.info,
|
||||
mqtt: config.mqtt,
|
||||
outlet_type: config.outlet_type,
|
||||
timeout: config.timeout.map(Duration::from_secs),
|
||||
client: client.clone(),
|
||||
last_known_state: false,
|
||||
handle: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn set_on(client: AsyncClient, topic: &str, on: bool) {
|
||||
let message = OnOffMessage::new(on);
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ use google_home::{
|
|||
errors::{self, DeviceError},
|
||||
traits,
|
||||
};
|
||||
use rumqttc::AsyncClient;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use tokio::{
|
||||
|
@ -19,8 +18,8 @@ use tokio::{
|
|||
use tracing::trace;
|
||||
|
||||
use crate::{
|
||||
config::CreateDevice, device_manager::DeviceManager, error::CreateDeviceError,
|
||||
event::EventChannel,
|
||||
config::{ConfigExternal, DeviceConfig},
|
||||
error::DeviceConfigError,
|
||||
};
|
||||
|
||||
use super::Device;
|
||||
|
@ -30,33 +29,30 @@ pub struct KasaOutletConfig {
|
|||
ip: Ipv4Addr,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KasaOutlet {
|
||||
identifier: String,
|
||||
addr: SocketAddr,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CreateDevice for KasaOutlet {
|
||||
type Config = KasaOutletConfig;
|
||||
|
||||
impl DeviceConfig for KasaOutletConfig {
|
||||
async fn create(
|
||||
self,
|
||||
identifier: &str,
|
||||
config: Self::Config,
|
||||
_event_channel: &EventChannel,
|
||||
_client: &AsyncClient,
|
||||
_presence_topic: &str,
|
||||
_device_manager: &DeviceManager,
|
||||
) -> Result<Self, CreateDeviceError> {
|
||||
_ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
trace!(id = identifier, "Setting up KasaOutlet");
|
||||
|
||||
Ok(Self {
|
||||
let device = KasaOutlet {
|
||||
identifier: identifier.to_owned(),
|
||||
addr: (config.ip, 9999).into(),
|
||||
})
|
||||
addr: (self.ip, 9999).into(),
|
||||
};
|
||||
|
||||
Ok(Box::new(device))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct KasaOutlet {
|
||||
identifier: String,
|
||||
addr: SocketAddr,
|
||||
}
|
||||
|
||||
impl Device for KasaOutlet {
|
||||
fn get_id(&self) -> &str {
|
||||
&self.identifier
|
||||
|
|
|
@ -10,17 +10,17 @@ mod presence;
|
|||
mod wake_on_lan;
|
||||
mod washer;
|
||||
|
||||
pub use self::audio_setup::AudioSetup;
|
||||
pub use self::contact_sensor::ContactSensor;
|
||||
pub use self::audio_setup::AudioSetupConfig;
|
||||
pub use self::contact_sensor::ContactSensorConfig;
|
||||
pub use self::debug_bridge::{DebugBridge, DebugBridgeConfig};
|
||||
pub use self::hue_bridge::{HueBridge, HueBridgeConfig, HueLight};
|
||||
pub use self::ikea_outlet::IkeaOutlet;
|
||||
pub use self::kasa_outlet::KasaOutlet;
|
||||
pub use self::hue_bridge::{HueBridge, HueBridgeConfig, HueLightConfig};
|
||||
pub use self::ikea_outlet::IkeaOutletConfig;
|
||||
pub use self::kasa_outlet::KasaOutletConfig;
|
||||
pub use self::light_sensor::{LightSensor, LightSensorConfig};
|
||||
pub use self::ntfy::{Notification, Ntfy};
|
||||
pub use self::presence::{Presence, PresenceConfig, DEFAULT_PRESENCE};
|
||||
pub use self::wake_on_lan::WakeOnLAN;
|
||||
pub use self::washer::Washer;
|
||||
pub use self::wake_on_lan::WakeOnLANConfig;
|
||||
pub use self::washer::WasherConfig;
|
||||
|
||||
use google_home::{device::AsGoogleHomeDevice, traits::OnOff};
|
||||
|
||||
|
|
|
@ -9,15 +9,13 @@ use google_home::{
|
|||
types::Type,
|
||||
GoogleHomeDevice,
|
||||
};
|
||||
use rumqttc::{AsyncClient, Publish};
|
||||
use rumqttc::Publish;
|
||||
use serde::Deserialize;
|
||||
use tracing::{debug, error, trace};
|
||||
|
||||
use crate::{
|
||||
config::{CreateDevice, InfoConfig, MqttDeviceConfig},
|
||||
device_manager::DeviceManager,
|
||||
error::CreateDeviceError,
|
||||
event::EventChannel,
|
||||
config::{ConfigExternal, DeviceConfig, InfoConfig, MqttDeviceConfig},
|
||||
error::DeviceConfigError,
|
||||
event::OnMqtt,
|
||||
messages::ActivateMessage,
|
||||
};
|
||||
|
@ -39,8 +37,34 @@ fn default_broadcast_ip() -> Ipv4Addr {
|
|||
Ipv4Addr::new(255, 255, 255, 255)
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl DeviceConfig for WakeOnLANConfig {
|
||||
async fn create(
|
||||
self,
|
||||
identifier: &str,
|
||||
_ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
trace!(
|
||||
id = identifier,
|
||||
name = self.info.name,
|
||||
room = self.info.room,
|
||||
"Setting up WakeOnLAN"
|
||||
);
|
||||
|
||||
let device = WakeOnLAN {
|
||||
identifier: identifier.to_owned(),
|
||||
info: self.info,
|
||||
mqtt: self.mqtt,
|
||||
mac_address: self.mac_address,
|
||||
broadcast_ip: self.broadcast_ip,
|
||||
};
|
||||
|
||||
Ok(Box::new(device))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WakeOnLAN {
|
||||
struct WakeOnLAN {
|
||||
identifier: String,
|
||||
info: InfoConfig,
|
||||
mqtt: MqttDeviceConfig,
|
||||
|
@ -48,35 +72,6 @@ pub struct WakeOnLAN {
|
|||
broadcast_ip: Ipv4Addr,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CreateDevice for WakeOnLAN {
|
||||
type Config = WakeOnLANConfig;
|
||||
|
||||
async fn create(
|
||||
identifier: &str,
|
||||
config: Self::Config,
|
||||
_event_channel: &EventChannel,
|
||||
_client: &AsyncClient,
|
||||
_presence_topic: &str,
|
||||
_device_manager: &DeviceManager,
|
||||
) -> Result<Self, CreateDeviceError> {
|
||||
trace!(
|
||||
id = identifier,
|
||||
name = config.info.name,
|
||||
room = config.info.room,
|
||||
"Setting up WakeOnLAN"
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
identifier: identifier.to_owned(),
|
||||
info: config.info,
|
||||
mqtt: config.mqtt,
|
||||
mac_address: config.mac_address,
|
||||
broadcast_ip: config.broadcast_ip,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for WakeOnLAN {
|
||||
fn get_id(&self) -> &str {
|
||||
&self.identifier
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use async_trait::async_trait;
|
||||
use rumqttc::{AsyncClient, Publish};
|
||||
use rumqttc::Publish;
|
||||
use serde::Deserialize;
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
use crate::{
|
||||
config::{CreateDevice, MqttDeviceConfig},
|
||||
device_manager::DeviceManager,
|
||||
error::CreateDeviceError,
|
||||
config::{ConfigExternal, DeviceConfig, MqttDeviceConfig},
|
||||
error::DeviceConfigError,
|
||||
event::{Event, EventChannel, OnMqtt},
|
||||
messages::PowerMessage,
|
||||
};
|
||||
|
@ -20,10 +19,29 @@ pub struct WasherConfig {
|
|||
threshold: f32, // Power in Watt
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl DeviceConfig for WasherConfig {
|
||||
async fn create(
|
||||
self,
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
let device = Washer {
|
||||
identifier: identifier.to_owned(),
|
||||
mqtt: self.mqtt,
|
||||
event_channel: ext.event_channel.clone(),
|
||||
threshold: self.threshold,
|
||||
running: 0,
|
||||
};
|
||||
|
||||
Ok(Box::new(device))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add google home integration
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Washer {
|
||||
struct Washer {
|
||||
identifier: String,
|
||||
mqtt: MqttDeviceConfig,
|
||||
|
||||
|
@ -32,28 +50,6 @@ pub struct Washer {
|
|||
running: isize,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl CreateDevice for Washer {
|
||||
type Config = WasherConfig;
|
||||
|
||||
async fn create(
|
||||
identifier: &str,
|
||||
config: Self::Config,
|
||||
event_channel: &EventChannel,
|
||||
_client: &AsyncClient,
|
||||
_presence_topic: &str,
|
||||
_device_manager: &DeviceManager,
|
||||
) -> Result<Self, CreateDeviceError> {
|
||||
Ok(Self {
|
||||
identifier: identifier.to_owned(),
|
||||
mqtt: config.mqtt,
|
||||
event_channel: event_channel.clone(),
|
||||
threshold: config.threshold,
|
||||
running: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Washer {
|
||||
fn get_id(&self) -> &str {
|
||||
&self.identifier
|
||||
|
|
10
src/error.rs
10
src/error.rs
|
@ -90,11 +90,11 @@ impl MissingWildcard {
|
|||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum CreateDeviceError {
|
||||
#[error("Child device '{0}' does not exist (yet?)")]
|
||||
DeviceDoesNotExist(String),
|
||||
#[error("Expected device '{0}' to implement OnOff trait")]
|
||||
OnOffExpected(String),
|
||||
pub enum DeviceConfigError {
|
||||
#[error("Child '{1}' of device '{0}' does not exist")]
|
||||
MissingChild(String, String),
|
||||
#[error("Device '{0}' does not implement expected trait '{1}'")]
|
||||
MissingTrait(String, String),
|
||||
#[error(transparent)]
|
||||
MissingWildcard(#[from] MissingWildcard),
|
||||
}
|
||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -10,7 +10,7 @@ use tracing::{debug, error, info};
|
|||
|
||||
use automation::{
|
||||
auth::{OpenIDConfig, User},
|
||||
config::Config,
|
||||
config::{Config, ConfigExternal, DeviceConfig},
|
||||
device_manager::DeviceManager,
|
||||
devices::{DebugBridge, HueBridge, LightSensor, Ntfy, Presence},
|
||||
error::ApiError,
|
||||
|
@ -61,16 +61,15 @@ async fn app() -> anyhow::Result<()> {
|
|||
let event_channel = device_manager.start();
|
||||
|
||||
// Create all the devices specified in the config
|
||||
let ext = ConfigExternal {
|
||||
client: &client,
|
||||
device_manager: &device_manager,
|
||||
presence_topic: &config.presence.mqtt.topic,
|
||||
event_channel: &event_channel,
|
||||
};
|
||||
|
||||
for (id, device_config) in config.devices {
|
||||
let device = device_config
|
||||
.create(
|
||||
&id,
|
||||
&event_channel,
|
||||
&client,
|
||||
&config.presence.mqtt.topic,
|
||||
&device_manager,
|
||||
)
|
||||
.await?;
|
||||
let device = device_config.create(&id, &ext).await?;
|
||||
|
||||
device_manager.add(device).await;
|
||||
}
|
||||
|
@ -95,7 +94,7 @@ async fn app() -> anyhow::Result<()> {
|
|||
|
||||
// Start the debug bridge if it is configured
|
||||
if let Some(config) = config.debug_bridge {
|
||||
let debug_bridge = DebugBridge::new(config, &client)?;
|
||||
let debug_bridge = DebugBridge::new(config, &client);
|
||||
device_manager.add(Box::new(debug_bridge)).await;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user