The device create function is now standarized using a trait
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Dreaded_X 2023-04-13 05:41:32 +02:00
parent f4c1ac5c9b
commit 72ab48df42
Signed by: Dreaded_X
GPG Key ID: FA5F485356B0D2D4
9 changed files with 86 additions and 72 deletions

View File

@ -13,11 +13,8 @@ use tracing::debug;
use crate::{ use crate::{
auth::OpenIDConfig, auth::OpenIDConfig,
debug_bridge::DebugBridgeConfig, debug_bridge::DebugBridgeConfig,
devices::{ devices::{self, AudioSetup, ContactSensor, IkeaOutlet, KasaOutlet, WakeOnLAN},
self, AudioSetup, AudioSetupConfig, ContactSensor, ContactSensorConfig, IkeaOutlet, error::{ConfigParseError, CreateDeviceError, MissingEnv},
IkeaOutletConfig, KasaOutlet, KasaOutletConfig, WakeOnLAN, WakeOnLANConfig,
},
error::{ConfigParseError, DeviceCreateError, MissingEnv},
hue_bridge::HueBridgeConfig, hue_bridge::HueBridgeConfig,
light_sensor::LightSensorConfig, light_sensor::LightSensorConfig,
}; };
@ -126,11 +123,11 @@ pub struct MqttDeviceConfig {
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub enum Device { pub enum Device {
IkeaOutlet(IkeaOutletConfig), IkeaOutlet(<IkeaOutlet as CreateDevice>::Config),
WakeOnLAN(WakeOnLANConfig), WakeOnLAN(<WakeOnLAN as CreateDevice>::Config),
KasaOutlet(KasaOutletConfig), KasaOutlet(<KasaOutlet as CreateDevice>::Config),
AudioSetup(AudioSetupConfig), AudioSetup(<AudioSetup as CreateDevice>::Config),
ContactSensor(ContactSensorConfig), ContactSensor(<ContactSensor as CreateDevice>::Config),
} }
impl Config { impl Config {
@ -161,26 +158,33 @@ impl Config {
} }
} }
pub trait CreateDevice {
type Config;
fn create(
identifier: &str,
config: Self::Config,
client: AsyncClient,
presence_topic: &str, // Not a big fan of passing in the global config
) -> Result<Self, CreateDeviceError>
where
Self: Sized;
}
impl Device { impl Device {
pub fn create( pub fn create(
self, self,
identifier: &str, id: &str,
client: AsyncClient, client: AsyncClient,
presence_topic: &str, presence: &str,
) -> Result<Box<dyn devices::Device>, DeviceCreateError> { ) -> Result<Box<dyn devices::Device>, CreateDeviceError> {
let device: Box<dyn devices::Device> = match self { let device: Box<dyn devices::Device> = match self {
Device::IkeaOutlet(c) => Box::new(IkeaOutlet::create(identifier, c, client)?), // TODO: It would be nice if this would be more automatic, not sure how to do that...
Device::WakeOnLAN(c) => Box::new(WakeOnLAN::create(identifier, c)?), Device::IkeaOutlet(c) => Box::new(IkeaOutlet::create(id, c, client, presence)?),
Device::KasaOutlet(c) => Box::new(KasaOutlet::create(identifier, c)?), Device::WakeOnLAN(c) => Box::new(WakeOnLAN::create(id, c, client, presence)?),
Device::AudioSetup(c) => { Device::KasaOutlet(c) => Box::new(KasaOutlet::create(id, c, client, presence)?),
Box::new(AudioSetup::create(identifier, c, client, presence_topic)?) Device::AudioSetup(c) => Box::new(AudioSetup::create(id, c, client, presence)?),
} Device::ContactSensor(c) => Box::new(ContactSensor::create(id, c, client, presence)?),
Device::ContactSensor(c) => Box::new(ContactSensor::create(
identifier,
c,
client,
presence_topic,
)?),
}; };
Ok(device) Ok(device)

View File

@ -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, AudioSetupConfig}; pub use self::audio_setup::AudioSetup;
pub use self::contact_sensor::{ContactSensor, ContactSensorConfig}; pub use self::contact_sensor::ContactSensor;
pub use self::ikea_outlet::{IkeaOutlet, IkeaOutletConfig}; pub use self::ikea_outlet::IkeaOutlet;
pub use self::kasa_outlet::{KasaOutlet, KasaOutletConfig}; pub use self::kasa_outlet::KasaOutlet;
pub use self::wake_on_lan::{WakeOnLAN, WakeOnLANConfig}; pub use self::wake_on_lan::WakeOnLAN;
use std::collections::HashMap; use std::collections::HashMap;

View File

@ -4,8 +4,8 @@ use rumqttc::AsyncClient;
use serde::Deserialize; use serde::Deserialize;
use tracing::{debug, error, trace, warn}; use tracing::{debug, error, trace, warn};
use crate::config::{self, MqttDeviceConfig}; use crate::config::{self, CreateDevice, MqttDeviceConfig};
use crate::error::DeviceCreateError; use crate::error::CreateDeviceError;
use crate::mqtt::{OnMqtt, RemoteAction, RemoteMessage}; use crate::mqtt::{OnMqtt, RemoteAction, RemoteMessage};
use crate::presence::OnPresence; use crate::presence::OnPresence;
@ -28,25 +28,26 @@ pub struct AudioSetup {
speakers: Box<dyn traits::OnOff>, speakers: Box<dyn traits::OnOff>,
} }
impl AudioSetup { impl CreateDevice for AudioSetup {
pub fn create( type Config = AudioSetupConfig;
fn create(
identifier: &str, identifier: &str,
config: AudioSetupConfig, config: Self::Config,
client: AsyncClient, client: AsyncClient,
// We only need to pass this in because constructing children presence_topic: &str,
presence_topic: &str, // Not a big fan of passing in the global config ) -> Result<Self, CreateDeviceError> {
) -> Result<Self, DeviceCreateError> {
trace!(id = identifier, "Setting up AudioSetup"); trace!(id = identifier, "Setting up AudioSetup");
// Create the child devices // Create the child devices
let mixer_id = format!("{}.mixer", identifier); let mixer_id = format!("{}.mixer", identifier);
let mixer = (*config.mixer).create(&mixer_id, client.clone(), presence_topic)?; let mixer = (*config.mixer).create(&mixer_id, client.clone(), presence_topic)?;
let mixer = As::consume(mixer).ok_or(DeviceCreateError::OnOffExpected(mixer_id))?; let mixer = As::consume(mixer).ok_or(CreateDeviceError::OnOffExpected(mixer_id))?;
let speakers_id = format!("{}.speakers", identifier); let speakers_id = format!("{}.speakers", identifier);
let speakers = (*config.speakers).create(&speakers_id, client, presence_topic)?; let speakers = (*config.speakers).create(&speakers_id, client, presence_topic)?;
let speakers = let speakers =
As::consume(speakers).ok_or(DeviceCreateError::OnOffExpected(speakers_id))?; As::consume(speakers).ok_or(CreateDeviceError::OnOffExpected(speakers_id))?;
Ok(Self { Ok(Self {
identifier: identifier.to_owned(), identifier: identifier.to_owned(),

View File

@ -7,8 +7,8 @@ use tokio::task::JoinHandle;
use tracing::{debug, error, trace, warn}; use tracing::{debug, error, trace, warn};
use crate::{ use crate::{
config::MqttDeviceConfig, config::{CreateDevice, MqttDeviceConfig},
error::{DeviceCreateError, MissingWildcard}, error::{CreateDeviceError, MissingWildcard},
mqtt::{ContactMessage, OnMqtt, PresenceMessage}, mqtt::{ContactMessage, OnMqtt, PresenceMessage},
presence::OnPresence, presence::OnPresence,
}; };
@ -69,13 +69,15 @@ pub struct ContactSensor {
handle: Option<JoinHandle<()>>, handle: Option<JoinHandle<()>>,
} }
impl ContactSensor { impl CreateDevice for ContactSensor {
pub fn create( type Config = ContactSensorConfig;
fn create(
identifier: &str, identifier: &str,
config: ContactSensorConfig, config: Self::Config,
client: AsyncClient, client: AsyncClient,
presence_topic: &str, presence_topic: &str,
) -> Result<Self, DeviceCreateError> { ) -> Result<Self, CreateDeviceError> {
trace!(id = identifier, "Setting up ContactSensor"); trace!(id = identifier, "Setting up ContactSensor");
let presence = config let presence = config

View File

@ -13,9 +13,9 @@ use std::time::Duration;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use tracing::{debug, error, trace, warn}; use tracing::{debug, error, trace, warn};
use crate::config::{InfoConfig, MqttDeviceConfig}; use crate::config::{CreateDevice, InfoConfig, MqttDeviceConfig};
use crate::devices::Device; use crate::devices::Device;
use crate::error::DeviceCreateError; use crate::error::CreateDeviceError;
use crate::mqtt::{OnMqtt, OnOffMessage}; use crate::mqtt::{OnMqtt, OnOffMessage};
use crate::presence::OnPresence; use crate::presence::OnPresence;
@ -54,12 +54,15 @@ pub struct IkeaOutlet {
handle: Option<JoinHandle<()>>, handle: Option<JoinHandle<()>>,
} }
impl IkeaOutlet { impl CreateDevice for IkeaOutlet {
pub fn create( type Config = IkeaOutletConfig;
fn create(
identifier: &str, identifier: &str,
config: IkeaOutletConfig, config: Self::Config,
client: AsyncClient, client: AsyncClient,
) -> Result<Self, DeviceCreateError> { _presence_topic: &str, // Not a big fan of passing in the global config
) -> Result<Self, CreateDeviceError> {
trace!( trace!(
id = identifier, id = identifier,
name = config.info.name, name = config.info.name,

View File

@ -9,11 +9,12 @@ use google_home::{
errors::{self, DeviceError}, errors::{self, DeviceError},
traits, traits,
}; };
use rumqttc::AsyncClient;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
use tracing::trace; use tracing::trace;
use crate::error::DeviceCreateError; use crate::{config::CreateDevice, error::CreateDeviceError};
use super::Device; use super::Device;
@ -28,8 +29,15 @@ pub struct KasaOutlet {
addr: SocketAddr, addr: SocketAddr,
} }
impl KasaOutlet { impl CreateDevice for KasaOutlet {
pub fn create(identifier: &str, config: KasaOutletConfig) -> Result<Self, DeviceCreateError> { type Config = KasaOutletConfig;
fn create(
identifier: &str,
config: Self::Config,
_client: AsyncClient,
_presence_topic: &str,
) -> Result<Self, CreateDeviceError> {
trace!(id = identifier, "Setting up KasaOutlet"); trace!(id = identifier, "Setting up KasaOutlet");
Ok(Self { Ok(Self {

View File

@ -9,13 +9,13 @@ use google_home::{
types::Type, types::Type,
GoogleHomeDevice, GoogleHomeDevice,
}; };
use rumqttc::Publish; use rumqttc::{AsyncClient, Publish};
use serde::Deserialize; use serde::Deserialize;
use tracing::{debug, error, trace}; use tracing::{debug, error, trace};
use crate::{ use crate::{
config::{InfoConfig, MqttDeviceConfig}, config::{CreateDevice, InfoConfig, MqttDeviceConfig},
error::DeviceCreateError, error::CreateDeviceError,
mqtt::{ActivateMessage, OnMqtt}, mqtt::{ActivateMessage, OnMqtt},
}; };
@ -45,8 +45,15 @@ pub struct WakeOnLAN {
broadcast_ip: Ipv4Addr, broadcast_ip: Ipv4Addr,
} }
impl WakeOnLAN { impl CreateDevice for WakeOnLAN {
pub fn create(identifier: &str, config: WakeOnLANConfig) -> Result<Self, DeviceCreateError> { type Config = WakeOnLANConfig;
fn create(
identifier: &str,
config: Self::Config,
_client: AsyncClient,
_presence_topic: &str,
) -> Result<Self, CreateDeviceError> {
trace!( trace!(
id = identifier, id = identifier,
name = config.info.name, name = config.info.name,

View File

@ -83,7 +83,7 @@ impl MissingWildcard {
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum DeviceCreateError { pub enum CreateDeviceError {
#[error("Expected device '{0}' to implement OnOff trait")] #[error("Expected device '{0}' to implement OnOff trait")]
OnOffExpected(String), OnOffExpected(String),
#[error(transparent)] #[error(transparent)]

View File

@ -19,17 +19,6 @@ 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,