The device create function is now standarized using a trait
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
f4c1ac5c9b
commit
72ab48df42
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
11
src/mqtt.rs
11
src/mqtt.rs
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user