Moved most config structs to be in the same file as what they are for

This commit is contained in:
2023-04-13 05:12:39 +02:00
parent 2aa13e7706
commit f4c1ac5c9b
16 changed files with 305 additions and 343 deletions

View File

@@ -1,16 +1,25 @@
use async_trait::async_trait;
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::error::DeviceError;
use crate::config::{self, MqttDeviceConfig};
use crate::error::DeviceCreateError;
use crate::mqtt::{OnMqtt, RemoteAction, RemoteMessage};
use crate::presence::OnPresence;
use super::{As, Device};
// TODO: Ideally we store am Arc to the childern devices,
// that way they hook into everything just like all other devices
#[derive(Debug, Clone, Deserialize)]
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)]
pub struct AudioSetup {
identifier: String,
@@ -20,22 +29,28 @@ pub struct AudioSetup {
}
impl AudioSetup {
pub async fn build(
pub fn create(
identifier: &str,
mqtt: MqttDeviceConfig,
mixer: Box<dyn Device>,
speakers: Box<dyn Device>,
) -> Result<Self, DeviceError> {
// We expect the children devices to implement the OnOff trait
let mixer_id = mixer.get_id().to_owned();
let mixer = As::consume(mixer).ok_or(DeviceError::OnOffExpected(mixer_id))?;
config: AudioSetupConfig,
client: AsyncClient,
// We only need to pass this in because constructing children
presence_topic: &str, // Not a big fan of passing in the global config
) -> Result<Self, DeviceCreateError> {
trace!(id = identifier, "Setting up AudioSetup");
let speakers_id = speakers.get_id().to_owned();
let speakers = As::consume(speakers).ok_or(DeviceError::OnOffExpected(speakers_id))?;
// Create the child devices
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 {
identifier: identifier.to_owned(),
mqtt,
mqtt: config.mqtt,
mixer,
speakers,
})

View File

@@ -1,18 +1,62 @@
use std::time::Duration;
use async_trait::async_trait;
use rumqttc::AsyncClient;
use rumqttc::{has_wildcards, AsyncClient};
use serde::Deserialize;
use tokio::task::JoinHandle;
use tracing::{debug, error, warn};
use tracing::{debug, error, trace, warn};
use crate::{
config::{MqttDeviceConfig, PresenceDeviceConfig},
config::MqttDeviceConfig,
error::{DeviceCreateError, MissingWildcard},
mqtt::{ContactMessage, OnMqtt, PresenceMessage},
presence::OnPresence,
};
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)]
pub struct ContactSensor {
identifier: String,
@@ -26,21 +70,28 @@ pub struct ContactSensor {
}
impl ContactSensor {
pub fn new(
pub fn create(
identifier: &str,
mqtt: MqttDeviceConfig,
presence: Option<PresenceDeviceConfig>,
config: ContactSensorConfig,
client: AsyncClient,
) -> Self {
Self {
presence_topic: &str,
) -> 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(),
mqtt,
mqtt: config.mqtt,
presence,
client,
overall_presence: false,
is_closed: true,
handle: None,
}
})
}
}

View File

@@ -8,15 +8,39 @@ use google_home::{
};
use pollster::FutureExt as _;
use rumqttc::{AsyncClient, Publish};
use serde::Deserialize;
use std::time::Duration;
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::error::DeviceCreateError;
use crate::mqtt::{OnMqtt, OnOffMessage};
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)]
pub struct IkeaOutlet {
identifier: String,
@@ -31,24 +55,28 @@ pub struct IkeaOutlet {
}
impl IkeaOutlet {
pub fn new(
pub fn create(
identifier: &str,
info: InfoConfig,
mqtt: MqttDeviceConfig,
outlet_type: OutletType,
timeout: Option<u64>,
config: IkeaOutletConfig,
client: AsyncClient,
) -> Self {
Self {
) -> Result<Self, DeviceCreateError> {
trace!(
id = identifier,
name = config.info.name,
room = config.info.room,
"Setting up IkeaOutlet"
);
Ok(Self {
identifier: identifier.to_owned(),
info,
mqtt,
outlet_type,
timeout,
info: config.info,
mqtt: config.mqtt,
outlet_type: config.outlet_type,
timeout: config.timeout,
client,
last_known_state: false,
handle: None,
}
})
}
}

View File

@@ -11,9 +11,17 @@ use google_home::{
};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tracing::trace;
use crate::error::DeviceCreateError;
use super::Device;
#[derive(Debug, Clone, Deserialize)]
pub struct KasaOutletConfig {
ip: Ipv4Addr,
}
#[derive(Debug)]
pub struct KasaOutlet {
identifier: String,
@@ -21,11 +29,13 @@ pub struct KasaOutlet {
}
impl KasaOutlet {
pub fn new(identifier: &str, ip: Ipv4Addr) -> Self {
Self {
pub fn create(identifier: &str, config: KasaOutletConfig) -> Result<Self, DeviceCreateError> {
trace!(id = identifier, "Setting up KasaOutlet");
Ok(Self {
identifier: identifier.to_owned(),
addr: (ip, 9999).into(),
}
addr: (config.ip, 9999).into(),
})
}
}

View File

@@ -10,15 +10,32 @@ use google_home::{
GoogleHomeDevice,
};
use rumqttc::Publish;
use tracing::{debug, error};
use serde::Deserialize;
use tracing::{debug, error, trace};
use crate::{
config::{InfoConfig, MqttDeviceConfig},
error::DeviceCreateError,
mqtt::{ActivateMessage, OnMqtt},
};
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)]
pub struct WakeOnLAN {
identifier: String,
@@ -29,20 +46,21 @@ pub struct WakeOnLAN {
}
impl WakeOnLAN {
pub fn new(
identifier: &str,
info: InfoConfig,
mqtt: MqttDeviceConfig,
mac_address: MacAddress,
broadcast_ip: Ipv4Addr,
) -> Self {
Self {
pub fn create(identifier: &str, config: WakeOnLANConfig) -> Result<Self, DeviceCreateError> {
trace!(
id = identifier,
name = config.info.name,
room = config.info.room,
"Setting up WakeOnLAN"
);
Ok(Self {
identifier: identifier.to_owned(),
info,
mqtt,
mac_address,
broadcast_ip,
}
info: config.info,
mqtt: config.mqtt,
mac_address: config.mac_address,
broadcast_ip: config.broadcast_ip,
})
}
}