Moved most config structs to be in the same file as what they are for
This commit is contained in:
parent
2aa13e7706
commit
f4c1ac5c9b
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -29,17 +29,6 @@ version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-recursion"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.105",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-stream"
|
name = "async-stream"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
|
@ -84,7 +73,6 @@ name = "automation"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-recursion",
|
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
|
@ -27,7 +27,6 @@ bytes = "1.3.0"
|
||||||
pollster = "0.2.5"
|
pollster = "0.2.5"
|
||||||
regex = "1.7.0"
|
regex = "1.7.0"
|
||||||
async-trait = "0.1.61"
|
async-trait = "0.1.61"
|
||||||
async-recursion = "1.0.0"
|
|
||||||
futures = "0.3.25"
|
futures = "0.3.25"
|
||||||
eui48 = { version = "1.1.0", default-features = false, features = [
|
eui48 = { version = "1.1.0", default-features = false, features = [
|
||||||
"disp_hexstring",
|
"disp_hexstring",
|
||||||
|
|
10
src/auth.rs
10
src/auth.rs
|
@ -5,10 +5,12 @@ use axum::{
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::error::{ApiError, ApiErrorJson};
|
||||||
config::OpenIDConfig,
|
|
||||||
error::{ApiError, ApiErrorJson},
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
};
|
pub struct OpenIDConfig {
|
||||||
|
pub base_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
|
|
256
src/config.rs
256
src/config.rs
|
@ -5,16 +5,21 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use async_recursion::async_recursion;
|
|
||||||
use eui48::MacAddress;
|
|
||||||
use regex::{Captures, Regex};
|
use regex::{Captures, Regex};
|
||||||
use rumqttc::{has_wildcards, AsyncClient, MqttOptions, Transport};
|
use rumqttc::{AsyncClient, MqttOptions, Transport};
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
use tracing::{debug, trace};
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
devices::{self, AudioSetup, ContactSensor, IkeaOutlet, KasaOutlet, WakeOnLAN},
|
auth::OpenIDConfig,
|
||||||
error::{ConfigParseError, DeviceCreationError, MissingEnv, MissingWildcard},
|
debug_bridge::DebugBridgeConfig,
|
||||||
|
devices::{
|
||||||
|
self, AudioSetup, AudioSetupConfig, ContactSensor, ContactSensorConfig, IkeaOutlet,
|
||||||
|
IkeaOutletConfig, KasaOutlet, KasaOutletConfig, WakeOnLAN, WakeOnLANConfig,
|
||||||
|
},
|
||||||
|
error::{ConfigParseError, DeviceCreateError, MissingEnv},
|
||||||
|
hue_bridge::HueBridgeConfig,
|
||||||
|
light_sensor::LightSensorConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -33,11 +38,6 @@ pub struct Config {
|
||||||
pub devices: HashMap<String, Device>,
|
pub devices: HashMap<String, Device>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct OpenIDConfig {
|
|
||||||
pub base_url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct MqttConfig {
|
pub struct MqttConfig {
|
||||||
pub host: String,
|
pub host: String,
|
||||||
|
@ -49,13 +49,6 @@ pub struct MqttConfig {
|
||||||
pub tls: bool,
|
pub tls: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_mqtt_options<'de, D>(deserializer: D) -> Result<MqttOptions, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
Ok(MqttOptions::from(MqttConfig::deserialize(deserializer)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<MqttConfig> for MqttOptions {
|
impl From<MqttConfig> for MqttOptions {
|
||||||
fn from(value: MqttConfig) -> Self {
|
fn from(value: MqttConfig) -> Self {
|
||||||
let mut mqtt_options = MqttOptions::new(value.client_name, value.host, value.port);
|
let mut mqtt_options = MqttOptions::new(value.client_name, value.host, value.port);
|
||||||
|
@ -70,6 +63,13 @@ impl From<MqttConfig> for MqttOptions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deserialize_mqtt_options<'de, D>(deserializer: D) -> Result<MqttOptions, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Ok(MqttOptions::from(MqttConfig::deserialize(deserializer)?))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct FullfillmentConfig {
|
pub struct FullfillmentConfig {
|
||||||
#[serde(default = "default_fullfillment_ip")]
|
#[serde(default = "default_fullfillment_ip")]
|
||||||
|
@ -112,32 +112,6 @@ fn default_ntfy_url() -> String {
|
||||||
"https://ntfy.sh".into()
|
"https://ntfy.sh".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct LightSensorConfig {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub mqtt: MqttDeviceConfig,
|
|
||||||
pub min: isize,
|
|
||||||
pub max: isize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct Flags {
|
|
||||||
pub presence: isize,
|
|
||||||
pub darkness: isize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct HueBridgeConfig {
|
|
||||||
pub ip: Ipv4Addr,
|
|
||||||
pub login: String,
|
|
||||||
pub flags: Flags,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct DebugBridgeConfig {
|
|
||||||
pub topic: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub struct InfoConfig {
|
pub struct InfoConfig {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
@ -149,102 +123,14 @@ pub struct MqttDeviceConfig {
|
||||||
pub topic: String,
|
pub topic: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Copy)]
|
|
||||||
pub enum OutletType {
|
|
||||||
Outlet,
|
|
||||||
Kettle,
|
|
||||||
Charger,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct KettleConfig {
|
|
||||||
pub timeout: Option<u64>, // Timeout in seconds
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
pub struct PresenceDeviceConfig {
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub mqtt: Option<MqttDeviceConfig>,
|
|
||||||
// TODO: Maybe make this an option? That way if no timeout is set it will immediately turn the
|
|
||||||
// device off again?
|
|
||||||
pub timeout: u64, // Timeout in seconds
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PresenceDeviceConfig {
|
|
||||||
/// Set the mqtt topic to an appropriate value if it is not already set
|
|
||||||
fn generate_topic(
|
|
||||||
mut self,
|
|
||||||
class: &str,
|
|
||||||
identifier: &str,
|
|
||||||
config: &Config,
|
|
||||||
) -> Result<PresenceDeviceConfig, MissingWildcard> {
|
|
||||||
if self.mqtt.is_none() {
|
|
||||||
if !has_wildcards(&config.presence.topic) {
|
|
||||||
return Err(MissingWildcard::new(&config.presence.topic));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This is not perfect, if the topic is some/+/thing/# this will fail
|
|
||||||
let offset = config
|
|
||||||
.presence
|
|
||||||
.topic
|
|
||||||
.find('+')
|
|
||||||
.or(config.presence.topic.find('#'))
|
|
||||||
.unwrap();
|
|
||||||
let topic = format!(
|
|
||||||
"{}/{class}/{identifier}",
|
|
||||||
&config.presence.topic[..offset - 1]
|
|
||||||
);
|
|
||||||
trace!("Setting presence mqtt topic: {topic}");
|
|
||||||
self.mqtt = Some(MqttDeviceConfig { topic });
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum Device {
|
pub enum Device {
|
||||||
IkeaOutlet {
|
IkeaOutlet(IkeaOutletConfig),
|
||||||
#[serde(flatten)]
|
WakeOnLAN(WakeOnLANConfig),
|
||||||
info: InfoConfig,
|
KasaOutlet(KasaOutletConfig),
|
||||||
#[serde(flatten)]
|
AudioSetup(AudioSetupConfig),
|
||||||
mqtt: MqttDeviceConfig,
|
ContactSensor(ContactSensorConfig),
|
||||||
#[serde(default = "default_outlet_type")]
|
|
||||||
outlet_type: OutletType,
|
|
||||||
timeout: Option<u64>, // Timeout in seconds
|
|
||||||
},
|
|
||||||
WakeOnLAN {
|
|
||||||
#[serde(flatten)]
|
|
||||||
info: InfoConfig,
|
|
||||||
#[serde(flatten)]
|
|
||||||
mqtt: MqttDeviceConfig,
|
|
||||||
mac_address: MacAddress,
|
|
||||||
#[serde(default = "default_broadcast_ip")]
|
|
||||||
broadcast_ip: Ipv4Addr,
|
|
||||||
},
|
|
||||||
KasaOutlet {
|
|
||||||
ip: Ipv4Addr,
|
|
||||||
},
|
|
||||||
AudioSetup {
|
|
||||||
#[serde(flatten)]
|
|
||||||
mqtt: MqttDeviceConfig,
|
|
||||||
mixer: Box<Device>,
|
|
||||||
speakers: Box<Device>,
|
|
||||||
},
|
|
||||||
ContactSensor {
|
|
||||||
#[serde(flatten)]
|
|
||||||
mqtt: MqttDeviceConfig,
|
|
||||||
presence: Option<PresenceDeviceConfig>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_outlet_type() -> OutletType {
|
|
||||||
OutletType::Outlet
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_broadcast_ip() -> Ipv4Addr {
|
|
||||||
Ipv4Addr::new(255, 255, 255, 255)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
@ -275,94 +161,26 @@ impl Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quick helper function to box up the devices,
|
|
||||||
// passing in Box::new would be ideal, however the return type is incorrect
|
|
||||||
// Maybe there is a better way to solve this?
|
|
||||||
// fn device_box<T: devices::Device>(device: T) -> DeviceBox {
|
|
||||||
// let a: DeviceBox = Box::new(device);
|
|
||||||
// a
|
|
||||||
// }
|
|
||||||
|
|
||||||
impl Device {
|
impl Device {
|
||||||
#[async_recursion]
|
pub fn create(
|
||||||
pub async fn create(
|
|
||||||
self,
|
self,
|
||||||
identifier: &str,
|
identifier: &str,
|
||||||
config: &Config,
|
|
||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
) -> Result<Box<dyn devices::Device>, DeviceCreationError> {
|
presence_topic: &str,
|
||||||
|
) -> Result<Box<dyn devices::Device>, DeviceCreateError> {
|
||||||
let device: Box<dyn devices::Device> = match self {
|
let device: Box<dyn devices::Device> = match self {
|
||||||
Device::IkeaOutlet {
|
Device::IkeaOutlet(c) => Box::new(IkeaOutlet::create(identifier, c, client)?),
|
||||||
info,
|
Device::WakeOnLAN(c) => Box::new(WakeOnLAN::create(identifier, c)?),
|
||||||
mqtt,
|
Device::KasaOutlet(c) => Box::new(KasaOutlet::create(identifier, c)?),
|
||||||
outlet_type,
|
Device::AudioSetup(c) => {
|
||||||
timeout,
|
Box::new(AudioSetup::create(identifier, c, client, presence_topic)?)
|
||||||
} => {
|
|
||||||
trace!(
|
|
||||||
id = identifier,
|
|
||||||
"IkeaOutlet [{} in {:?}]",
|
|
||||||
info.name,
|
|
||||||
info.room
|
|
||||||
);
|
|
||||||
Box::new(IkeaOutlet::new(
|
|
||||||
identifier,
|
|
||||||
info,
|
|
||||||
mqtt,
|
|
||||||
outlet_type,
|
|
||||||
timeout,
|
|
||||||
client,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Device::WakeOnLAN {
|
|
||||||
info,
|
|
||||||
mqtt,
|
|
||||||
mac_address,
|
|
||||||
broadcast_ip,
|
|
||||||
} => {
|
|
||||||
trace!(
|
|
||||||
id = identifier,
|
|
||||||
"WakeOnLan [{} in {:?}]",
|
|
||||||
info.name,
|
|
||||||
info.room
|
|
||||||
);
|
|
||||||
Box::new(WakeOnLAN::new(
|
|
||||||
identifier,
|
|
||||||
info,
|
|
||||||
mqtt,
|
|
||||||
mac_address,
|
|
||||||
broadcast_ip,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
Device::KasaOutlet { ip } => {
|
|
||||||
trace!(id = identifier, "KasaOutlet [{}]", identifier);
|
|
||||||
Box::new(KasaOutlet::new(identifier, ip))
|
|
||||||
}
|
|
||||||
Device::AudioSetup {
|
|
||||||
mqtt,
|
|
||||||
mixer,
|
|
||||||
speakers,
|
|
||||||
} => {
|
|
||||||
trace!(id = identifier, "AudioSetup [{}]", identifier);
|
|
||||||
// Create the child devices
|
|
||||||
let mixer_id = format!("{}.mixer", identifier);
|
|
||||||
let mixer = (*mixer).create(&mixer_id, config, client.clone()).await?;
|
|
||||||
let speakers_id = format!("{}.speakers", identifier);
|
|
||||||
let speakers = (*speakers)
|
|
||||||
.create(&speakers_id, config, client.clone())
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
AudioSetup::build(identifier, mqtt, mixer, speakers)
|
|
||||||
.await
|
|
||||||
.map(Box::new)?
|
|
||||||
}
|
|
||||||
Device::ContactSensor { mqtt, presence } => {
|
|
||||||
trace!(id = identifier, "ContactSensor [{}]", identifier);
|
|
||||||
let presence = presence
|
|
||||||
.map(|p| p.generate_topic("contact", identifier, config))
|
|
||||||
.transpose()?;
|
|
||||||
|
|
||||||
Box::new(ContactSensor::new(identifier, mqtt, presence, client))
|
|
||||||
}
|
}
|
||||||
|
Device::ContactSensor(c) => Box::new(ContactSensor::create(
|
||||||
|
identifier,
|
||||||
|
c,
|
||||||
|
client,
|
||||||
|
presence_topic,
|
||||||
|
)?),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(device)
|
Ok(device)
|
||||||
|
|
|
@ -1,23 +1,28 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use rumqttc::AsyncClient;
|
use rumqttc::AsyncClient;
|
||||||
|
use serde::Deserialize;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::DebugBridgeConfig,
|
|
||||||
light_sensor::{self, OnDarkness},
|
light_sensor::{self, OnDarkness},
|
||||||
mqtt::{DarknessMessage, PresenceMessage},
|
mqtt::{DarknessMessage, PresenceMessage},
|
||||||
presence::{self, OnPresence},
|
presence::{self, OnPresence},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct DebugBridgeConfig {
|
||||||
|
pub topic: String,
|
||||||
|
}
|
||||||
|
|
||||||
struct DebugBridge {
|
struct DebugBridge {
|
||||||
topic: String,
|
topic: String,
|
||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DebugBridge {
|
impl DebugBridge {
|
||||||
pub fn new(topic: &str, client: AsyncClient) -> Self {
|
pub fn new(config: DebugBridgeConfig, client: AsyncClient) -> Self {
|
||||||
Self {
|
Self {
|
||||||
topic: topic.to_owned(),
|
topic: config.topic,
|
||||||
client,
|
client,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,10 +31,10 @@ impl DebugBridge {
|
||||||
pub fn start(
|
pub fn start(
|
||||||
mut presence_rx: presence::Receiver,
|
mut presence_rx: presence::Receiver,
|
||||||
mut light_sensor_rx: light_sensor::Receiver,
|
mut light_sensor_rx: light_sensor::Receiver,
|
||||||
config: &DebugBridgeConfig,
|
config: DebugBridgeConfig,
|
||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
) {
|
) {
|
||||||
let mut debug_bridge = DebugBridge::new(&config.topic, client);
|
let mut debug_bridge = DebugBridge::new(config, client);
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
|
|
|
@ -4,11 +4,11 @@ mod ikea_outlet;
|
||||||
mod kasa_outlet;
|
mod kasa_outlet;
|
||||||
mod wake_on_lan;
|
mod wake_on_lan;
|
||||||
|
|
||||||
pub use self::audio_setup::AudioSetup;
|
pub use self::audio_setup::{AudioSetup, AudioSetupConfig};
|
||||||
pub use self::contact_sensor::ContactSensor;
|
pub use self::contact_sensor::{ContactSensor, ContactSensorConfig};
|
||||||
pub use self::ikea_outlet::IkeaOutlet;
|
pub use self::ikea_outlet::{IkeaOutlet, IkeaOutletConfig};
|
||||||
pub use self::kasa_outlet::KasaOutlet;
|
pub use self::kasa_outlet::{KasaOutlet, KasaOutletConfig};
|
||||||
pub use self::wake_on_lan::WakeOnLAN;
|
pub use self::wake_on_lan::{WakeOnLAN, WakeOnLANConfig};
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,25 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use google_home::traits;
|
use google_home::traits;
|
||||||
use tracing::{debug, error, warn};
|
use rumqttc::AsyncClient;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use crate::config::MqttDeviceConfig;
|
use crate::config::{self, MqttDeviceConfig};
|
||||||
use crate::error::DeviceError;
|
use crate::error::DeviceCreateError;
|
||||||
use crate::mqtt::{OnMqtt, RemoteAction, RemoteMessage};
|
use crate::mqtt::{OnMqtt, RemoteAction, RemoteMessage};
|
||||||
use crate::presence::OnPresence;
|
use crate::presence::OnPresence;
|
||||||
|
|
||||||
use super::{As, Device};
|
use super::{As, Device};
|
||||||
|
|
||||||
// TODO: Ideally we store am Arc to the childern devices,
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
// that way they hook into everything just like all other devices
|
pub struct AudioSetupConfig {
|
||||||
|
#[serde(flatten)]
|
||||||
|
mqtt: MqttDeviceConfig,
|
||||||
|
mixer: Box<config::Device>,
|
||||||
|
speakers: Box<config::Device>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We need a better way to store the children devices
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AudioSetup {
|
pub struct AudioSetup {
|
||||||
identifier: String,
|
identifier: String,
|
||||||
|
@ -20,22 +29,28 @@ pub struct AudioSetup {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AudioSetup {
|
impl AudioSetup {
|
||||||
pub async fn build(
|
pub fn create(
|
||||||
identifier: &str,
|
identifier: &str,
|
||||||
mqtt: MqttDeviceConfig,
|
config: AudioSetupConfig,
|
||||||
mixer: Box<dyn Device>,
|
client: AsyncClient,
|
||||||
speakers: Box<dyn Device>,
|
// We only need to pass this in because constructing children
|
||||||
) -> Result<Self, DeviceError> {
|
presence_topic: &str, // Not a big fan of passing in the global config
|
||||||
// We expect the children devices to implement the OnOff trait
|
) -> Result<Self, DeviceCreateError> {
|
||||||
let mixer_id = mixer.get_id().to_owned();
|
trace!(id = identifier, "Setting up AudioSetup");
|
||||||
let mixer = As::consume(mixer).ok_or(DeviceError::OnOffExpected(mixer_id))?;
|
|
||||||
|
|
||||||
let speakers_id = speakers.get_id().to_owned();
|
// Create the child devices
|
||||||
let speakers = As::consume(speakers).ok_or(DeviceError::OnOffExpected(speakers_id))?;
|
let mixer_id = format!("{}.mixer", identifier);
|
||||||
|
let mixer = (*config.mixer).create(&mixer_id, client.clone(), presence_topic)?;
|
||||||
|
let mixer = As::consume(mixer).ok_or(DeviceCreateError::OnOffExpected(mixer_id))?;
|
||||||
|
|
||||||
|
let speakers_id = format!("{}.speakers", identifier);
|
||||||
|
let speakers = (*config.speakers).create(&speakers_id, client, presence_topic)?;
|
||||||
|
let speakers =
|
||||||
|
As::consume(speakers).ok_or(DeviceCreateError::OnOffExpected(speakers_id))?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
identifier: identifier.to_owned(),
|
identifier: identifier.to_owned(),
|
||||||
mqtt,
|
mqtt: config.mqtt,
|
||||||
mixer,
|
mixer,
|
||||||
speakers,
|
speakers,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,18 +1,62 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use rumqttc::AsyncClient;
|
use rumqttc::{has_wildcards, AsyncClient};
|
||||||
|
use serde::Deserialize;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{MqttDeviceConfig, PresenceDeviceConfig},
|
config::MqttDeviceConfig,
|
||||||
|
error::{DeviceCreateError, MissingWildcard},
|
||||||
mqtt::{ContactMessage, OnMqtt, PresenceMessage},
|
mqtt::{ContactMessage, OnMqtt, PresenceMessage},
|
||||||
presence::OnPresence,
|
presence::OnPresence,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Device;
|
use super::Device;
|
||||||
|
|
||||||
|
// NOTE: If we add more presence devices we might need to move this out of here
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct PresenceDeviceConfig {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub mqtt: Option<MqttDeviceConfig>,
|
||||||
|
pub timeout: u64, // Timeout in seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PresenceDeviceConfig {
|
||||||
|
/// Set the mqtt topic to an appropriate value if it is not already set
|
||||||
|
fn generate_topic(
|
||||||
|
mut self,
|
||||||
|
class: &str,
|
||||||
|
identifier: &str,
|
||||||
|
presence_topic: &str,
|
||||||
|
) -> Result<PresenceDeviceConfig, MissingWildcard> {
|
||||||
|
if self.mqtt.is_none() {
|
||||||
|
if !has_wildcards(presence_topic) {
|
||||||
|
return Err(MissingWildcard::new(presence_topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This is not perfect, if the topic is some/+/thing/# this will fail
|
||||||
|
let offset = presence_topic
|
||||||
|
.find('+')
|
||||||
|
.or(presence_topic.find('#'))
|
||||||
|
.unwrap();
|
||||||
|
let topic = format!("{}/{class}/{identifier}", &presence_topic[..offset - 1]);
|
||||||
|
trace!("Setting presence mqtt topic: {topic}");
|
||||||
|
self.mqtt = Some(MqttDeviceConfig { topic });
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct ContactSensorConfig {
|
||||||
|
#[serde(flatten)]
|
||||||
|
mqtt: MqttDeviceConfig,
|
||||||
|
presence: Option<PresenceDeviceConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ContactSensor {
|
pub struct ContactSensor {
|
||||||
identifier: String,
|
identifier: String,
|
||||||
|
@ -26,21 +70,28 @@ pub struct ContactSensor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContactSensor {
|
impl ContactSensor {
|
||||||
pub fn new(
|
pub fn create(
|
||||||
identifier: &str,
|
identifier: &str,
|
||||||
mqtt: MqttDeviceConfig,
|
config: ContactSensorConfig,
|
||||||
presence: Option<PresenceDeviceConfig>,
|
|
||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
) -> Self {
|
presence_topic: &str,
|
||||||
Self {
|
) -> Result<Self, DeviceCreateError> {
|
||||||
|
trace!(id = identifier, "Setting up ContactSensor");
|
||||||
|
|
||||||
|
let presence = config
|
||||||
|
.presence
|
||||||
|
.map(|p| p.generate_topic("contact", identifier, presence_topic))
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
identifier: identifier.to_owned(),
|
identifier: identifier.to_owned(),
|
||||||
mqtt,
|
mqtt: config.mqtt,
|
||||||
presence,
|
presence,
|
||||||
client,
|
client,
|
||||||
overall_presence: false,
|
overall_presence: false,
|
||||||
is_closed: true,
|
is_closed: true,
|
||||||
handle: None,
|
handle: None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,15 +8,39 @@ use google_home::{
|
||||||
};
|
};
|
||||||
use pollster::FutureExt as _;
|
use pollster::FutureExt as _;
|
||||||
use rumqttc::{AsyncClient, Publish};
|
use rumqttc::{AsyncClient, Publish};
|
||||||
|
use serde::Deserialize;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, trace, warn};
|
||||||
|
|
||||||
use crate::config::{InfoConfig, MqttDeviceConfig, OutletType};
|
use crate::config::{InfoConfig, MqttDeviceConfig};
|
||||||
use crate::devices::Device;
|
use crate::devices::Device;
|
||||||
|
use crate::error::DeviceCreateError;
|
||||||
use crate::mqtt::{OnMqtt, OnOffMessage};
|
use crate::mqtt::{OnMqtt, OnOffMessage};
|
||||||
use crate::presence::OnPresence;
|
use crate::presence::OnPresence;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Copy)]
|
||||||
|
pub enum OutletType {
|
||||||
|
Outlet,
|
||||||
|
Kettle,
|
||||||
|
Charger,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct IkeaOutletConfig {
|
||||||
|
#[serde(flatten)]
|
||||||
|
info: InfoConfig,
|
||||||
|
#[serde(flatten)]
|
||||||
|
mqtt: MqttDeviceConfig,
|
||||||
|
#[serde(default = "default_outlet_type")]
|
||||||
|
outlet_type: OutletType,
|
||||||
|
timeout: Option<u64>, // Timeout in seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_outlet_type() -> OutletType {
|
||||||
|
OutletType::Outlet
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct IkeaOutlet {
|
pub struct IkeaOutlet {
|
||||||
identifier: String,
|
identifier: String,
|
||||||
|
@ -31,24 +55,28 @@ pub struct IkeaOutlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IkeaOutlet {
|
impl IkeaOutlet {
|
||||||
pub fn new(
|
pub fn create(
|
||||||
identifier: &str,
|
identifier: &str,
|
||||||
info: InfoConfig,
|
config: IkeaOutletConfig,
|
||||||
mqtt: MqttDeviceConfig,
|
|
||||||
outlet_type: OutletType,
|
|
||||||
timeout: Option<u64>,
|
|
||||||
client: AsyncClient,
|
client: AsyncClient,
|
||||||
) -> Self {
|
) -> Result<Self, DeviceCreateError> {
|
||||||
Self {
|
trace!(
|
||||||
|
id = identifier,
|
||||||
|
name = config.info.name,
|
||||||
|
room = config.info.room,
|
||||||
|
"Setting up IkeaOutlet"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
identifier: identifier.to_owned(),
|
identifier: identifier.to_owned(),
|
||||||
info,
|
info: config.info,
|
||||||
mqtt,
|
mqtt: config.mqtt,
|
||||||
outlet_type,
|
outlet_type: config.outlet_type,
|
||||||
timeout,
|
timeout: config.timeout,
|
||||||
client,
|
client,
|
||||||
last_known_state: false,
|
last_known_state: false,
|
||||||
handle: None,
|
handle: None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,17 @@ use google_home::{
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
|
use crate::error::DeviceCreateError;
|
||||||
|
|
||||||
use super::Device;
|
use super::Device;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct KasaOutletConfig {
|
||||||
|
ip: Ipv4Addr,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct KasaOutlet {
|
pub struct KasaOutlet {
|
||||||
identifier: String,
|
identifier: String,
|
||||||
|
@ -21,11 +29,13 @@ pub struct KasaOutlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KasaOutlet {
|
impl KasaOutlet {
|
||||||
pub fn new(identifier: &str, ip: Ipv4Addr) -> Self {
|
pub fn create(identifier: &str, config: KasaOutletConfig) -> Result<Self, DeviceCreateError> {
|
||||||
Self {
|
trace!(id = identifier, "Setting up KasaOutlet");
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
identifier: identifier.to_owned(),
|
identifier: identifier.to_owned(),
|
||||||
addr: (ip, 9999).into(),
|
addr: (config.ip, 9999).into(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,15 +10,32 @@ use google_home::{
|
||||||
GoogleHomeDevice,
|
GoogleHomeDevice,
|
||||||
};
|
};
|
||||||
use rumqttc::Publish;
|
use rumqttc::Publish;
|
||||||
use tracing::{debug, error};
|
use serde::Deserialize;
|
||||||
|
use tracing::{debug, error, trace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{InfoConfig, MqttDeviceConfig},
|
config::{InfoConfig, MqttDeviceConfig},
|
||||||
|
error::DeviceCreateError,
|
||||||
mqtt::{ActivateMessage, OnMqtt},
|
mqtt::{ActivateMessage, OnMqtt},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Device;
|
use super::Device;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct WakeOnLANConfig {
|
||||||
|
#[serde(flatten)]
|
||||||
|
info: InfoConfig,
|
||||||
|
#[serde(flatten)]
|
||||||
|
mqtt: MqttDeviceConfig,
|
||||||
|
mac_address: MacAddress,
|
||||||
|
#[serde(default = "default_broadcast_ip")]
|
||||||
|
broadcast_ip: Ipv4Addr,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_broadcast_ip() -> Ipv4Addr {
|
||||||
|
Ipv4Addr::new(255, 255, 255, 255)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WakeOnLAN {
|
pub struct WakeOnLAN {
|
||||||
identifier: String,
|
identifier: String,
|
||||||
|
@ -29,20 +46,21 @@ pub struct WakeOnLAN {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WakeOnLAN {
|
impl WakeOnLAN {
|
||||||
pub fn new(
|
pub fn create(identifier: &str, config: WakeOnLANConfig) -> Result<Self, DeviceCreateError> {
|
||||||
identifier: &str,
|
trace!(
|
||||||
info: InfoConfig,
|
id = identifier,
|
||||||
mqtt: MqttDeviceConfig,
|
name = config.info.name,
|
||||||
mac_address: MacAddress,
|
room = config.info.room,
|
||||||
broadcast_ip: Ipv4Addr,
|
"Setting up WakeOnLAN"
|
||||||
) -> Self {
|
);
|
||||||
Self {
|
|
||||||
|
Ok(Self {
|
||||||
identifier: identifier.to_owned(),
|
identifier: identifier.to_owned(),
|
||||||
info,
|
info: config.info,
|
||||||
mqtt,
|
mqtt: config.mqtt,
|
||||||
mac_address,
|
mac_address: config.mac_address,
|
||||||
broadcast_ip,
|
broadcast_ip: config.broadcast_ip,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,15 +83,9 @@ impl MissingWildcard {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum DeviceError {
|
pub enum DeviceCreateError {
|
||||||
#[error("Expected device '{0}' to implement OnOff trait")]
|
#[error("Expected device '{0}' to implement OnOff trait")]
|
||||||
OnOffExpected(String),
|
OnOffExpected(String),
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum DeviceCreationError {
|
|
||||||
#[error(transparent)]
|
|
||||||
DeviceError(#[from] DeviceError),
|
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
MissingWildcard(#[from] MissingWildcard),
|
MissingWildcard(#[from] MissingWildcard),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
use std::net::SocketAddr;
|
use std::net::{Ipv4Addr, SocketAddr};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use serde::Serialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use tracing::{error, trace, warn};
|
use tracing::{error, trace, warn};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{Flags, HueBridgeConfig},
|
|
||||||
light_sensor::{self, OnDarkness},
|
light_sensor::{self, OnDarkness},
|
||||||
presence::{self, OnPresence},
|
presence::{self, OnPresence},
|
||||||
};
|
};
|
||||||
|
@ -15,10 +14,22 @@ pub enum Flag {
|
||||||
Darkness,
|
Darkness,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct FlagIDs {
|
||||||
|
pub presence: isize,
|
||||||
|
pub darkness: isize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct HueBridgeConfig {
|
||||||
|
pub ip: Ipv4Addr,
|
||||||
|
pub login: String,
|
||||||
|
pub flags: FlagIDs,
|
||||||
|
}
|
||||||
struct HueBridge {
|
struct HueBridge {
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
login: String,
|
login: String,
|
||||||
flags: Flags,
|
flags: FlagIDs,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
@ -27,11 +38,11 @@ struct FlagMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HueBridge {
|
impl HueBridge {
|
||||||
pub fn new(addr: SocketAddr, login: &str, flags: Flags) -> Self {
|
pub fn new(config: HueBridgeConfig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
addr,
|
addr: (config.ip, 80).into(),
|
||||||
login: login.to_owned(),
|
login: config.login,
|
||||||
flags,
|
flags: config.flags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,10 +79,9 @@ impl HueBridge {
|
||||||
pub fn start(
|
pub fn start(
|
||||||
mut presence_rx: presence::Receiver,
|
mut presence_rx: presence::Receiver,
|
||||||
mut light_sensor_rx: light_sensor::Receiver,
|
mut light_sensor_rx: light_sensor::Receiver,
|
||||||
config: &HueBridgeConfig,
|
config: HueBridgeConfig,
|
||||||
) {
|
) {
|
||||||
let mut hue_bridge =
|
let mut hue_bridge = HueBridge::new(config);
|
||||||
HueBridge::new((config.ip, 80).into(), &config.login, config.flags.clone());
|
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use rumqttc::{matches, AsyncClient};
|
use rumqttc::{matches, AsyncClient};
|
||||||
|
use serde::Deserialize;
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
use tracing::{debug, error, trace};
|
use tracing::{debug, error, trace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{LightSensorConfig, MqttDeviceConfig},
|
config::MqttDeviceConfig,
|
||||||
error::LightSensorError,
|
error::LightSensorError,
|
||||||
mqtt::{self, BrightnessMessage, OnMqtt},
|
mqtt::{self, BrightnessMessage, OnMqtt},
|
||||||
};
|
};
|
||||||
|
@ -17,6 +18,14 @@ pub trait OnDarkness: Sync + Send + 'static {
|
||||||
pub type Receiver = watch::Receiver<bool>;
|
pub type Receiver = watch::Receiver<bool>;
|
||||||
type Sender = watch::Sender<bool>;
|
type Sender = watch::Sender<bool>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct LightSensorConfig {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub mqtt: MqttDeviceConfig,
|
||||||
|
pub min: isize,
|
||||||
|
pub max: isize,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct LightSensor {
|
struct LightSensor {
|
||||||
mqtt: MqttDeviceConfig,
|
mqtt: MqttDeviceConfig,
|
||||||
|
|
44
src/main.rs
44
src/main.rs
|
@ -6,8 +6,8 @@ use axum::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use automation::{
|
use automation::{
|
||||||
auth::User,
|
auth::{OpenIDConfig, User},
|
||||||
config::{Config, OpenIDConfig},
|
config::Config,
|
||||||
debug_bridge, devices,
|
debug_bridge, devices,
|
||||||
error::ApiError,
|
error::ApiError,
|
||||||
hue_bridge, light_sensor,
|
hue_bridge, light_sensor,
|
||||||
|
@ -26,7 +26,7 @@ struct AppState {
|
||||||
pub openid: OpenIDConfig,
|
pub openid: OpenIDConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromRef<AppState> for automation::config::OpenIDConfig {
|
impl FromRef<AppState> for OpenIDConfig {
|
||||||
fn from_ref(input: &AppState) -> Self {
|
fn from_ref(input: &AppState) -> Self {
|
||||||
input.openid.clone()
|
input.openid.clone()
|
||||||
}
|
}
|
||||||
|
@ -74,12 +74,12 @@ async fn app() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the hue bridge if it is configured
|
// Start the hue bridge if it is configured
|
||||||
if let Some(config) = &config.hue_bridge {
|
if let Some(config) = config.hue_bridge {
|
||||||
hue_bridge::start(presence.clone(), light_sensor.clone(), config);
|
hue_bridge::start(presence.clone(), light_sensor.clone(), config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the debug bridge if it is configured
|
// Start the debug bridge if it is configured
|
||||||
if let Some(config) = &config.debug_bridge {
|
if let Some(config) = config.debug_bridge {
|
||||||
debug_bridge::start(
|
debug_bridge::start(
|
||||||
presence.clone(),
|
presence.clone(),
|
||||||
light_sensor.clone(),
|
light_sensor.clone(),
|
||||||
|
@ -88,27 +88,31 @@ async fn app() -> anyhow::Result<()> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let devices = devices::start(
|
// Setup the device handler
|
||||||
|
let device_handler = devices::start(
|
||||||
mqtt.subscribe(),
|
mqtt.subscribe(),
|
||||||
presence.clone(),
|
presence.clone(),
|
||||||
light_sensor.clone(),
|
light_sensor.clone(),
|
||||||
client.clone(),
|
client.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Create all the devices specified in the config
|
||||||
|
let devices = config
|
||||||
|
.devices
|
||||||
|
.into_iter()
|
||||||
|
.map(|(identifier, device_config)| {
|
||||||
|
device_config.create(&identifier, client.clone(), &config.presence.topic)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
|
// Can even add some more devices here
|
||||||
|
// devices.push(device)
|
||||||
|
|
||||||
|
// Register all the devices to the device_handler
|
||||||
join_all(
|
join_all(
|
||||||
config
|
devices
|
||||||
.devices
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(identifier, device_config)| async {
|
.map(|device| async { device_handler.add_device(device).await }),
|
||||||
// Force the async block to move identifier
|
|
||||||
let identifier = identifier;
|
|
||||||
let device = device_config
|
|
||||||
.create(&identifier, &config, client.clone())
|
|
||||||
.await?;
|
|
||||||
devices.add_device(device).await?;
|
|
||||||
// We don't need a seperate error type in main
|
|
||||||
anyhow::Ok(())
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -124,7 +128,7 @@ async fn app() -> anyhow::Result<()> {
|
||||||
post(async move |user: User, Json(payload): Json<Request>| {
|
post(async move |user: User, Json(payload): Json<Request>| {
|
||||||
debug!(username = user.preferred_username, "{payload:#?}");
|
debug!(username = user.preferred_username, "{payload:#?}");
|
||||||
let gc = GoogleHome::new(&user.preferred_username);
|
let gc = GoogleHome::new(&user.preferred_username);
|
||||||
let result = match devices.fullfillment(gc, payload).await {
|
let result = match device_handler.fullfillment(gc, payload).await {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return ApiError::new(StatusCode::INTERNAL_SERVER_ERROR, err.into())
|
return ApiError::new(StatusCode::INTERNAL_SERVER_ERROR, err.into())
|
||||||
|
|
11
src/mqtt.rs
11
src/mqtt.rs
|
@ -19,6 +19,17 @@ pub trait OnMqtt {
|
||||||
pub type Receiver = broadcast::Receiver<Publish>;
|
pub type Receiver = broadcast::Receiver<Publish>;
|
||||||
type Sender = broadcast::Sender<Publish>;
|
type Sender = broadcast::Sender<Publish>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
pub struct MqttConfig {
|
||||||
|
pub host: String,
|
||||||
|
pub port: u16,
|
||||||
|
pub client_name: String,
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tls: bool,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Mqtt {
|
pub struct Mqtt {
|
||||||
tx: Sender,
|
tx: Sender,
|
||||||
eventloop: EventLoop,
|
eventloop: EventLoop,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user