This commit is contained in:
118
src/config.rs
118
src/config.rs
@@ -1,13 +1,20 @@
|
||||
use std::{fs, net::{Ipv4Addr, SocketAddr}, collections::HashMap};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
net::{Ipv4Addr, SocketAddr},
|
||||
};
|
||||
|
||||
use async_recursion::async_recursion;
|
||||
use regex::{Regex, Captures};
|
||||
use tracing::{debug, trace};
|
||||
use rumqttc::{AsyncClient, has_wildcards};
|
||||
use serde::Deserialize;
|
||||
use eui48::MacAddress;
|
||||
use regex::{Captures, Regex};
|
||||
use rumqttc::{has_wildcards, AsyncClient};
|
||||
use serde::Deserialize;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::{devices::{DeviceBox, IkeaOutlet, WakeOnLAN, AudioSetup, ContactSensor, KasaOutlet, self}, error::{MissingEnv, MissingWildcard, ConfigParseError, DeviceCreationError}};
|
||||
use crate::{
|
||||
devices::{self, AudioSetup, ContactSensor, DeviceBox, IkeaOutlet, KasaOutlet, WakeOnLAN},
|
||||
error::{ConfigParseError, DeviceCreationError, MissingEnv, MissingWildcard},
|
||||
};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Config {
|
||||
@@ -26,7 +33,7 @@ pub struct Config {
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct OpenIDConfig {
|
||||
pub base_url: String
|
||||
pub base_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
@@ -56,7 +63,10 @@ impl From<FullfillmentConfig> for SocketAddr {
|
||||
|
||||
impl Default for FullfillmentConfig {
|
||||
fn default() -> Self {
|
||||
Self { ip: default_fullfillment_ip(), port: default_fullfillment_port() }
|
||||
Self {
|
||||
ip: default_fullfillment_ip(),
|
||||
port: default_fullfillment_port(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,20 +144,33 @@ pub struct PresenceDeviceConfig {
|
||||
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
|
||||
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> {
|
||||
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]);
|
||||
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 });
|
||||
}
|
||||
@@ -183,14 +206,14 @@ pub enum Device {
|
||||
AudioSetup {
|
||||
#[serde(flatten)]
|
||||
mqtt: MqttDeviceConfig,
|
||||
mixer: Box::<Device>,
|
||||
speakers: Box::<Device>,
|
||||
mixer: Box<Device>,
|
||||
speakers: Box<Device>,
|
||||
},
|
||||
ContactSensor {
|
||||
#[serde(flatten)]
|
||||
mqtt: MqttDeviceConfig,
|
||||
presence: Option<PresenceDeviceConfig>,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
fn default_outlet_type() -> OutletType {
|
||||
@@ -239,42 +262,77 @@ fn device_box<T: devices::Device + 'static>(device: T) -> DeviceBox {
|
||||
|
||||
impl Device {
|
||||
#[async_recursion]
|
||||
pub async fn create(self, identifier: &str, config: &Config, client: AsyncClient) -> Result<DeviceBox, DeviceCreationError> {
|
||||
pub async fn create(
|
||||
self,
|
||||
identifier: &str,
|
||||
config: &Config,
|
||||
client: AsyncClient,
|
||||
) -> Result<DeviceBox, DeviceCreationError> {
|
||||
let device = match self {
|
||||
Device::IkeaOutlet { info, mqtt, outlet_type, timeout } => {
|
||||
trace!(id = identifier, "IkeaOutlet [{} in {:?}]", info.name, info.room);
|
||||
IkeaOutlet::build(identifier, info, mqtt, outlet_type, timeout, client).await
|
||||
Device::IkeaOutlet {
|
||||
info,
|
||||
mqtt,
|
||||
outlet_type,
|
||||
timeout,
|
||||
} => {
|
||||
trace!(
|
||||
id = identifier,
|
||||
"IkeaOutlet [{} in {:?}]",
|
||||
info.name,
|
||||
info.room
|
||||
);
|
||||
IkeaOutlet::build(identifier, info, mqtt, outlet_type, timeout, client)
|
||||
.await
|
||||
.map(device_box)?
|
||||
},
|
||||
Device::WakeOnLAN { info, mqtt, mac_address, broadcast_ip } => {
|
||||
trace!(id = identifier, "WakeOnLan [{} in {:?}]", info.name, info.room);
|
||||
WakeOnLAN::build(identifier, info, mqtt, mac_address, broadcast_ip, client).await
|
||||
}
|
||||
Device::WakeOnLAN {
|
||||
info,
|
||||
mqtt,
|
||||
mac_address,
|
||||
broadcast_ip,
|
||||
} => {
|
||||
trace!(
|
||||
id = identifier,
|
||||
"WakeOnLan [{} in {:?}]",
|
||||
info.name,
|
||||
info.room
|
||||
);
|
||||
WakeOnLAN::build(identifier, info, mqtt, mac_address, broadcast_ip, client)
|
||||
.await
|
||||
.map(device_box)?
|
||||
},
|
||||
}
|
||||
Device::KasaOutlet { ip } => {
|
||||
trace!(id = identifier, "KasaOutlet [{}]", identifier);
|
||||
device_box(KasaOutlet::new(identifier, ip))
|
||||
}
|
||||
Device::AudioSetup { mqtt, mixer, speakers } => {
|
||||
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?;
|
||||
let speakers = (*speakers)
|
||||
.create(&speakers_id, config, client.clone())
|
||||
.await?;
|
||||
|
||||
AudioSetup::build(identifier, mqtt, mixer, speakers, client).await
|
||||
AudioSetup::build(identifier, mqtt, mixer, speakers, client)
|
||||
.await
|
||||
.map(device_box)?
|
||||
},
|
||||
}
|
||||
Device::ContactSensor { mqtt, presence } => {
|
||||
trace!(id = identifier, "ContactSensor [{}]", identifier);
|
||||
let presence = presence
|
||||
.map(|p| p.generate_topic("contact", identifier, config))
|
||||
.transpose()?;
|
||||
|
||||
ContactSensor::build(identifier, mqtt, presence, client).await
|
||||
ContactSensor::build(identifier, mqtt, presence, client)
|
||||
.await
|
||||
.map(device_box)?
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
Ok(device)
|
||||
|
||||
Reference in New Issue
Block a user