Device config is now done through lua
This commit is contained in:
@@ -2,14 +2,12 @@ use std::fs;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::time::Duration;
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use regex::{Captures, Regex};
|
||||
use rumqttc::{MqttOptions, Transport};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::auth::OpenIDConfig;
|
||||
use crate::device_manager::DeviceConfigs;
|
||||
use crate::devices::PresenceConfig;
|
||||
use crate::error::{ConfigParseError, MissingEnv};
|
||||
use crate::schedule::Schedule;
|
||||
@@ -23,7 +21,6 @@ pub struct Config {
|
||||
pub fullfillment: FullfillmentConfig,
|
||||
pub ntfy: Option<NtfyConfig>,
|
||||
pub presence: PresenceConfig,
|
||||
pub devices: IndexMap<String, DeviceConfigs>,
|
||||
pub schedule: Schedule,
|
||||
}
|
||||
|
||||
|
||||
@@ -6,16 +6,11 @@ use enum_dispatch::enum_dispatch;
|
||||
use futures::future::join_all;
|
||||
use google_home::traits::OnOff;
|
||||
use rumqttc::{matches, AsyncClient, QoS};
|
||||
use serde::Deserialize;
|
||||
use tokio::sync::{RwLock, RwLockReadGuard};
|
||||
use tokio_cron_scheduler::{Job, JobScheduler};
|
||||
use tracing::{debug, error, instrument, trace};
|
||||
|
||||
use crate::devices::{
|
||||
AirFilterConfig, AudioSetupConfig, ContactSensorConfig, DebugBridgeConfig, Device,
|
||||
HueBridgeConfig, HueGroupConfig, IkeaOutletConfig, KasaOutletConfig, LightSensorConfig,
|
||||
WakeOnLANConfig, WasherConfig,
|
||||
};
|
||||
use crate::devices::Device;
|
||||
use crate::error::DeviceConfigError;
|
||||
use crate::event::{Event, EventChannel, OnDarkness, OnMqtt, OnNotification, OnPresence};
|
||||
use crate::schedule::{Action, Schedule};
|
||||
@@ -30,27 +25,12 @@ pub struct ConfigExternal<'a> {
|
||||
#[enum_dispatch]
|
||||
pub trait DeviceConfig {
|
||||
async fn create(
|
||||
self,
|
||||
&self,
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[enum_dispatch(DeviceConfig)]
|
||||
pub enum DeviceConfigs {
|
||||
AirFilter(AirFilterConfig),
|
||||
AudioSetup(AudioSetupConfig),
|
||||
ContactSensor(ContactSensorConfig),
|
||||
DebugBridge(DebugBridgeConfig),
|
||||
IkeaOutlet(IkeaOutletConfig),
|
||||
KasaOutlet(KasaOutletConfig),
|
||||
WakeOnLAN(WakeOnLANConfig),
|
||||
Washer(WasherConfig),
|
||||
HueBridge(HueBridgeConfig),
|
||||
HueGroup(HueGroupConfig),
|
||||
LightSensor(LightSensorConfig),
|
||||
}
|
||||
impl mlua::UserData for Box<dyn DeviceConfig> {}
|
||||
|
||||
pub type WrappedDevice = Arc<RwLock<Box<dyn Device>>>;
|
||||
pub type DeviceMap = HashMap<String, WrappedDevice>;
|
||||
@@ -160,24 +140,6 @@ impl DeviceManager {
|
||||
self.devices.write().await.insert(id, device);
|
||||
}
|
||||
|
||||
pub async fn create(
|
||||
&self,
|
||||
identifier: &str,
|
||||
device_config: DeviceConfigs,
|
||||
) -> Result<(), DeviceConfigError> {
|
||||
let ext = ConfigExternal {
|
||||
client: &self.client,
|
||||
device_manager: self,
|
||||
event_channel: &self.event_channel,
|
||||
};
|
||||
|
||||
let device = device_config.create(identifier, &ext).await?;
|
||||
|
||||
self.add(device).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn event_channel(&self) -> EventChannel {
|
||||
self.event_channel.clone()
|
||||
}
|
||||
@@ -261,3 +223,32 @@ impl DeviceManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::UserData for DeviceManager {
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_async_method(
|
||||
"create",
|
||||
|_lua, this, (identifier, config): (String, mlua::Value)| async move {
|
||||
// TODO: Handle the error here properly
|
||||
let config: Box<dyn DeviceConfig> = config.as_userdata().unwrap().take()?;
|
||||
|
||||
let ext = ConfigExternal {
|
||||
client: &this.client,
|
||||
device_manager: this,
|
||||
event_channel: &this.event_channel,
|
||||
};
|
||||
|
||||
let device = config
|
||||
.create(&identifier, &ext)
|
||||
.await
|
||||
.map_err(mlua::ExternalError::into_lua_err)?;
|
||||
|
||||
let id = device.get_id().to_owned();
|
||||
|
||||
this.add(device).await;
|
||||
|
||||
Ok(id)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use automation_macro::LuaDevice;
|
||||
use google_home::device::Name;
|
||||
use google_home::errors::ErrorCode;
|
||||
use google_home::traits::{AvailableSpeeds, FanSpeed, HumiditySetting, OnOff, Speed, SpeedValues};
|
||||
@@ -15,7 +16,7 @@ use crate::error::DeviceConfigError;
|
||||
use crate::event::OnMqtt;
|
||||
use crate::messages::{AirFilterFanState, AirFilterState, SetAirFilterFanState};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct AirFilterConfig {
|
||||
#[serde(flatten)]
|
||||
info: InfoConfig,
|
||||
@@ -26,14 +27,13 @@ pub struct AirFilterConfig {
|
||||
#[async_trait]
|
||||
impl DeviceConfig for AirFilterConfig {
|
||||
async fn create(
|
||||
self,
|
||||
&self,
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
let device = AirFilter {
|
||||
identifier: identifier.into(),
|
||||
info: self.info,
|
||||
mqtt: self.mqtt,
|
||||
config: self.clone(),
|
||||
client: ext.client.clone(),
|
||||
last_known_state: AirFilterState {
|
||||
state: AirFilterFanState::Off,
|
||||
@@ -45,11 +45,11 @@ impl DeviceConfig for AirFilterConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, LuaDevice)]
|
||||
pub struct AirFilter {
|
||||
identifier: String,
|
||||
info: InfoConfig,
|
||||
mqtt: MqttDeviceConfig,
|
||||
#[config]
|
||||
config: AirFilterConfig,
|
||||
|
||||
client: AsyncClient,
|
||||
last_known_state: AirFilterState,
|
||||
@@ -59,7 +59,7 @@ impl AirFilter {
|
||||
async fn set_speed(&self, state: AirFilterFanState) {
|
||||
let message = SetAirFilterFanState::new(state);
|
||||
|
||||
let topic = format!("{}/set", self.mqtt.topic);
|
||||
let topic = format!("{}/set", self.config.mqtt.topic);
|
||||
// TODO: Handle potential errors here
|
||||
self.client
|
||||
.publish(
|
||||
@@ -83,7 +83,7 @@ impl Device for AirFilter {
|
||||
#[async_trait]
|
||||
impl OnMqtt for AirFilter {
|
||||
fn topics(&self) -> Vec<&str> {
|
||||
vec![&self.mqtt.topic]
|
||||
vec![&self.config.mqtt.topic]
|
||||
}
|
||||
|
||||
async fn on_mqtt(&mut self, message: Publish) {
|
||||
@@ -111,7 +111,7 @@ impl GoogleHomeDevice for AirFilter {
|
||||
}
|
||||
|
||||
fn get_device_name(&self) -> Name {
|
||||
Name::new(&self.info.name)
|
||||
Name::new(&self.config.info.name)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> &str {
|
||||
@@ -123,7 +123,7 @@ impl GoogleHomeDevice for AirFilter {
|
||||
}
|
||||
|
||||
fn get_room_hint(&self) -> Option<&str> {
|
||||
self.info.room.as_deref()
|
||||
self.config.info.room.as_deref()
|
||||
}
|
||||
|
||||
fn will_report_state(&self) -> bool {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use automation_macro::LuaDevice;
|
||||
use google_home::traits::OnOff;
|
||||
use serde::Deserialize;
|
||||
use tracing::{debug, error, trace, warn};
|
||||
@@ -21,7 +22,7 @@ pub struct AudioSetupConfig {
|
||||
#[async_trait]
|
||||
impl DeviceConfig for AudioSetupConfig {
|
||||
async fn create(
|
||||
self,
|
||||
&self,
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
@@ -41,7 +42,10 @@ impl DeviceConfig for AudioSetupConfig {
|
||||
{
|
||||
let mixer = mixer.read().await;
|
||||
if (mixer.as_ref().cast() as Option<&dyn OnOff>).is_none() {
|
||||
return Err(DeviceConfigError::MissingTrait(self.mixer, "OnOff".into()));
|
||||
return Err(DeviceConfigError::MissingTrait(
|
||||
self.mixer.clone(),
|
||||
"OnOff".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,13 +61,16 @@ impl DeviceConfig for AudioSetupConfig {
|
||||
{
|
||||
let speakers = speakers.read().await;
|
||||
if (speakers.as_ref().cast() as Option<&dyn OnOff>).is_none() {
|
||||
return Err(DeviceConfigError::MissingTrait(self.mixer, "OnOff".into()));
|
||||
return Err(DeviceConfigError::MissingTrait(
|
||||
self.mixer.clone(),
|
||||
"OnOff".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let device = AudioSetup {
|
||||
identifier: identifier.into(),
|
||||
mqtt: self.mqtt,
|
||||
config: self.clone(),
|
||||
mixer,
|
||||
speakers,
|
||||
};
|
||||
@@ -73,10 +80,11 @@ impl DeviceConfig for AudioSetupConfig {
|
||||
}
|
||||
|
||||
// TODO: We need a better way to store the children devices
|
||||
#[derive(Debug)]
|
||||
struct AudioSetup {
|
||||
#[derive(Debug, LuaDevice)]
|
||||
pub struct AudioSetup {
|
||||
identifier: String,
|
||||
mqtt: MqttDeviceConfig,
|
||||
#[config]
|
||||
config: AudioSetupConfig,
|
||||
mixer: WrappedDevice,
|
||||
speakers: WrappedDevice,
|
||||
}
|
||||
@@ -90,7 +98,7 @@ impl Device for AudioSetup {
|
||||
#[async_trait]
|
||||
impl OnMqtt for AudioSetup {
|
||||
fn topics(&self) -> Vec<&str> {
|
||||
vec![&self.mqtt.topic]
|
||||
vec![&self.config.mqtt.topic]
|
||||
}
|
||||
|
||||
async fn on_mqtt(&mut self, message: rumqttc::Publish) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use automation_macro::LuaDevice;
|
||||
use google_home::traits::OnOff;
|
||||
use rumqttc::AsyncClient;
|
||||
use serde::Deserialize;
|
||||
@@ -47,7 +48,7 @@ pub struct ContactSensorConfig {
|
||||
#[async_trait]
|
||||
impl DeviceConfig for ContactSensorConfig {
|
||||
async fn create(
|
||||
self,
|
||||
&self,
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
@@ -92,8 +93,7 @@ impl DeviceConfig for ContactSensorConfig {
|
||||
|
||||
let device = ContactSensor {
|
||||
identifier: identifier.into(),
|
||||
mqtt: self.mqtt,
|
||||
presence: self.presence,
|
||||
config: self.clone(),
|
||||
client: ext.client.clone(),
|
||||
overall_presence: DEFAULT_PRESENCE,
|
||||
is_closed: true,
|
||||
@@ -111,11 +111,11 @@ struct Trigger {
|
||||
timeout: Duration, // Timeout in seconds
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ContactSensor {
|
||||
#[derive(Debug, LuaDevice)]
|
||||
pub struct ContactSensor {
|
||||
identifier: String,
|
||||
mqtt: MqttDeviceConfig,
|
||||
presence: Option<PresenceDeviceConfig>,
|
||||
#[config]
|
||||
config: ContactSensorConfig,
|
||||
|
||||
client: AsyncClient,
|
||||
overall_presence: bool,
|
||||
@@ -141,7 +141,7 @@ impl OnPresence for ContactSensor {
|
||||
#[async_trait]
|
||||
impl OnMqtt for ContactSensor {
|
||||
fn topics(&self) -> Vec<&str> {
|
||||
vec![&self.mqtt.topic]
|
||||
vec![&self.config.mqtt.topic]
|
||||
}
|
||||
|
||||
async fn on_mqtt(&mut self, message: rumqttc::Publish) {
|
||||
@@ -191,7 +191,7 @@ impl OnMqtt for ContactSensor {
|
||||
|
||||
// Check if this contact sensor works as a presence device
|
||||
// If not we are done here
|
||||
let presence = match &self.presence {
|
||||
let presence = match &self.config.presence {
|
||||
Some(presence) => presence,
|
||||
None => return,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use automation_macro::LuaDevice;
|
||||
use rumqttc::AsyncClient;
|
||||
use serde::Deserialize;
|
||||
use tracing::warn;
|
||||
@@ -10,7 +11,7 @@ use crate::error::DeviceConfigError;
|
||||
use crate::event::{OnDarkness, OnPresence};
|
||||
use crate::messages::{DarknessMessage, PresenceMessage};
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct DebugBridgeConfig {
|
||||
#[serde(flatten)]
|
||||
pub mqtt: MqttDeviceConfig,
|
||||
@@ -19,13 +20,13 @@ pub struct DebugBridgeConfig {
|
||||
#[async_trait]
|
||||
impl DeviceConfig for DebugBridgeConfig {
|
||||
async fn create(
|
||||
self,
|
||||
&self,
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
let device = DebugBridge {
|
||||
identifier: identifier.into(),
|
||||
mqtt: self.mqtt,
|
||||
config: self.clone(),
|
||||
client: ext.client.clone(),
|
||||
};
|
||||
|
||||
@@ -33,10 +34,11 @@ impl DeviceConfig for DebugBridgeConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, LuaDevice)]
|
||||
pub struct DebugBridge {
|
||||
identifier: String,
|
||||
mqtt: MqttDeviceConfig,
|
||||
#[config]
|
||||
config: DebugBridgeConfig,
|
||||
client: AsyncClient,
|
||||
}
|
||||
|
||||
@@ -50,7 +52,7 @@ impl Device for DebugBridge {
|
||||
impl OnPresence for DebugBridge {
|
||||
async fn on_presence(&mut self, presence: bool) {
|
||||
let message = PresenceMessage::new(presence);
|
||||
let topic = format!("{}/presence", self.mqtt.topic);
|
||||
let topic = format!("{}/presence", self.config.mqtt.topic);
|
||||
self.client
|
||||
.publish(
|
||||
topic,
|
||||
@@ -62,7 +64,7 @@ impl OnPresence for DebugBridge {
|
||||
.map_err(|err| {
|
||||
warn!(
|
||||
"Failed to update presence on {}/presence: {err}",
|
||||
self.mqtt.topic
|
||||
self.config.mqtt.topic
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
@@ -73,7 +75,7 @@ impl OnPresence for DebugBridge {
|
||||
impl OnDarkness for DebugBridge {
|
||||
async fn on_darkness(&mut self, dark: bool) {
|
||||
let message = DarknessMessage::new(dark);
|
||||
let topic = format!("{}/darkness", self.mqtt.topic);
|
||||
let topic = format!("{}/darkness", self.config.mqtt.topic);
|
||||
self.client
|
||||
.publish(
|
||||
topic,
|
||||
@@ -85,7 +87,7 @@ impl OnDarkness for DebugBridge {
|
||||
.map_err(|err| {
|
||||
warn!(
|
||||
"Failed to update presence on {}/presence: {err}",
|
||||
self.mqtt.topic
|
||||
self.config.mqtt.topic
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use automation_macro::LuaDevice;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{error, trace, warn};
|
||||
|
||||
@@ -21,7 +22,7 @@ pub struct FlagIDs {
|
||||
pub darkness: isize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct HueBridgeConfig {
|
||||
pub ip: Ipv4Addr,
|
||||
pub login: String,
|
||||
@@ -31,27 +32,24 @@ pub struct HueBridgeConfig {
|
||||
#[async_trait]
|
||||
impl DeviceConfig for HueBridgeConfig {
|
||||
async fn create(
|
||||
self,
|
||||
&self,
|
||||
identifier: &str,
|
||||
_ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
let device = HueBridge {
|
||||
identifier: identifier.into(),
|
||||
addr: (self.ip, 80).into(),
|
||||
login: self.login,
|
||||
flag_ids: self.flags,
|
||||
config: self.clone(),
|
||||
};
|
||||
|
||||
Ok(Box::new(device))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct HueBridge {
|
||||
#[derive(Debug, LuaDevice)]
|
||||
pub struct HueBridge {
|
||||
identifier: String,
|
||||
addr: SocketAddr,
|
||||
login: String,
|
||||
flag_ids: FlagIDs,
|
||||
#[config]
|
||||
config: HueBridgeConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@@ -62,13 +60,13 @@ struct FlagMessage {
|
||||
impl HueBridge {
|
||||
pub async fn set_flag(&self, flag: Flag, value: bool) {
|
||||
let flag_id = match flag {
|
||||
Flag::Presence => self.flag_ids.presence,
|
||||
Flag::Darkness => self.flag_ids.darkness,
|
||||
Flag::Presence => self.config.flags.presence,
|
||||
Flag::Darkness => self.config.flags.darkness,
|
||||
};
|
||||
|
||||
let url = format!(
|
||||
"http://{}/api/{}/sensors/{flag_id}/state",
|
||||
self.addr, self.login
|
||||
"http://{}:80/api/{}/sensors/{flag_id}/state",
|
||||
self.config.ip, self.config.login
|
||||
);
|
||||
|
||||
trace!(?flag, flag_id, value, "Sending request to change flag");
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use automation_macro::LuaDevice;
|
||||
use google_home::errors::ErrorCode;
|
||||
use google_home::traits::OnOff;
|
||||
use rumqttc::Publish;
|
||||
@@ -31,51 +32,42 @@ pub struct HueGroupConfig {
|
||||
#[async_trait]
|
||||
impl DeviceConfig for HueGroupConfig {
|
||||
async fn create(
|
||||
self,
|
||||
&self,
|
||||
identifier: &str,
|
||||
_ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
let device = HueGroup {
|
||||
identifier: identifier.into(),
|
||||
addr: (self.ip, 80).into(),
|
||||
login: self.login,
|
||||
group_id: self.group_id,
|
||||
scene_id: self.scene_id,
|
||||
timer_id: self.timer_id,
|
||||
remotes: self.remotes,
|
||||
config: self.clone(),
|
||||
};
|
||||
|
||||
Ok(Box::new(device))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct HueGroup {
|
||||
#[derive(Debug, LuaDevice)]
|
||||
pub struct HueGroup {
|
||||
identifier: String,
|
||||
addr: SocketAddr,
|
||||
login: String,
|
||||
group_id: isize,
|
||||
timer_id: isize,
|
||||
scene_id: String,
|
||||
remotes: Vec<MqttDeviceConfig>,
|
||||
#[config]
|
||||
config: HueGroupConfig,
|
||||
}
|
||||
|
||||
// Couple of helper function to get the correct urls
|
||||
impl HueGroup {
|
||||
fn url_base(&self) -> String {
|
||||
format!("http://{}/api/{}", self.addr, self.login)
|
||||
format!("http://{}:80/api/{}", self.config.ip, self.config.login)
|
||||
}
|
||||
|
||||
fn url_set_schedule(&self) -> String {
|
||||
format!("{}/schedules/{}", self.url_base(), self.timer_id)
|
||||
format!("{}/schedules/{}", self.url_base(), self.config.timer_id)
|
||||
}
|
||||
|
||||
fn url_set_action(&self) -> String {
|
||||
format!("{}/groups/{}/action", self.url_base(), self.group_id)
|
||||
format!("{}/groups/{}/action", self.url_base(), self.config.group_id)
|
||||
}
|
||||
|
||||
fn url_get_state(&self) -> String {
|
||||
format!("{}/groups/{}", self.url_base(), self.group_id)
|
||||
format!("{}/groups/{}", self.url_base(), self.config.group_id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +80,8 @@ impl Device for HueGroup {
|
||||
#[async_trait]
|
||||
impl OnMqtt for HueGroup {
|
||||
fn topics(&self) -> Vec<&str> {
|
||||
self.remotes
|
||||
self.config
|
||||
.remotes
|
||||
.iter()
|
||||
.map(|mqtt| mqtt.topic.as_str())
|
||||
.collect()
|
||||
@@ -122,7 +115,7 @@ impl OnOff for HueGroup {
|
||||
self.stop_timeout().await.unwrap();
|
||||
|
||||
let message = if on {
|
||||
message::Action::scene(self.scene_id.clone())
|
||||
message::Action::scene(self.config.scene_id.clone())
|
||||
} else {
|
||||
message::Action::on(false)
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use automation_macro::LuaDevice;
|
||||
use google_home::errors::ErrorCode;
|
||||
use google_home::traits::{self, OnOff};
|
||||
use google_home::types::Type;
|
||||
@@ -50,7 +51,7 @@ fn default_outlet_type() -> OutletType {
|
||||
#[async_trait]
|
||||
impl DeviceConfig for IkeaOutletConfig {
|
||||
async fn create(
|
||||
self,
|
||||
&self,
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
@@ -63,11 +64,7 @@ impl DeviceConfig for IkeaOutletConfig {
|
||||
|
||||
let device = IkeaOutlet {
|
||||
identifier: identifier.into(),
|
||||
info: self.info,
|
||||
mqtt: self.mqtt,
|
||||
outlet_type: self.outlet_type,
|
||||
timeout: self.timeout,
|
||||
remotes: self.remotes,
|
||||
config: self.clone(),
|
||||
client: ext.client.clone(),
|
||||
last_known_state: false,
|
||||
handle: None,
|
||||
@@ -77,14 +74,11 @@ impl DeviceConfig for IkeaOutletConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct IkeaOutlet {
|
||||
#[derive(Debug, LuaDevice)]
|
||||
pub struct IkeaOutlet {
|
||||
identifier: String,
|
||||
info: InfoConfig,
|
||||
mqtt: MqttDeviceConfig,
|
||||
outlet_type: OutletType,
|
||||
timeout: Option<Duration>,
|
||||
remotes: Vec<MqttDeviceConfig>,
|
||||
#[config]
|
||||
config: IkeaOutletConfig,
|
||||
|
||||
client: AsyncClient,
|
||||
last_known_state: bool,
|
||||
@@ -118,19 +112,20 @@ impl Device for IkeaOutlet {
|
||||
impl OnMqtt for IkeaOutlet {
|
||||
fn topics(&self) -> Vec<&str> {
|
||||
let mut topics: Vec<_> = self
|
||||
.config
|
||||
.remotes
|
||||
.iter()
|
||||
.map(|mqtt| mqtt.topic.as_str())
|
||||
.collect();
|
||||
|
||||
topics.push(&self.mqtt.topic);
|
||||
topics.push(&self.config.mqtt.topic);
|
||||
|
||||
topics
|
||||
}
|
||||
|
||||
async fn on_mqtt(&mut self, message: Publish) {
|
||||
// Check if the message is from the deviec itself or from a remote
|
||||
if matches(&message.topic, &self.mqtt.topic) {
|
||||
if matches(&message.topic, &self.config.mqtt.topic) {
|
||||
// Update the internal state based on what the device has reported
|
||||
let state = match OnOffMessage::try_from(message) {
|
||||
Ok(state) => state.state(),
|
||||
@@ -152,7 +147,7 @@ impl OnMqtt for IkeaOutlet {
|
||||
self.last_known_state = state;
|
||||
|
||||
// If this is a kettle start a timeout for turning it of again
|
||||
if state && let Some(timeout) = self.timeout {
|
||||
if state && let Some(timeout) = self.config.timeout {
|
||||
self.start_timeout(timeout).await.unwrap();
|
||||
}
|
||||
} else {
|
||||
@@ -178,7 +173,7 @@ impl OnMqtt for IkeaOutlet {
|
||||
impl OnPresence for IkeaOutlet {
|
||||
async fn on_presence(&mut self, presence: bool) {
|
||||
// Turn off the outlet when we leave the house (Not if it is a battery charger)
|
||||
if !presence && self.outlet_type != OutletType::Charger {
|
||||
if !presence && self.config.outlet_type != OutletType::Charger {
|
||||
debug!(id = self.identifier, "Turning device off");
|
||||
self.set_on(false).await.ok();
|
||||
}
|
||||
@@ -187,7 +182,7 @@ impl OnPresence for IkeaOutlet {
|
||||
|
||||
impl GoogleHomeDevice for IkeaOutlet {
|
||||
fn get_device_type(&self) -> Type {
|
||||
match self.outlet_type {
|
||||
match self.config.outlet_type {
|
||||
OutletType::Outlet => Type::Outlet,
|
||||
OutletType::Kettle => Type::Kettle,
|
||||
OutletType::Light => Type::Light, // Find a better device type for this, ideally would like to use charger, but that needs more work
|
||||
@@ -196,7 +191,7 @@ impl GoogleHomeDevice for IkeaOutlet {
|
||||
}
|
||||
|
||||
fn get_device_name(&self) -> device::Name {
|
||||
device::Name::new(&self.info.name)
|
||||
device::Name::new(&self.config.info.name)
|
||||
}
|
||||
|
||||
fn get_id(&self) -> &str {
|
||||
@@ -208,7 +203,7 @@ impl GoogleHomeDevice for IkeaOutlet {
|
||||
}
|
||||
|
||||
fn get_room_hint(&self) -> Option<&str> {
|
||||
self.info.room.as_deref()
|
||||
self.config.info.room.as_deref()
|
||||
}
|
||||
|
||||
fn will_report_state(&self) -> bool {
|
||||
@@ -224,7 +219,7 @@ impl traits::OnOff for IkeaOutlet {
|
||||
}
|
||||
|
||||
async fn set_on(&mut self, on: bool) -> Result<(), ErrorCode> {
|
||||
set_on(self.client.clone(), &self.mqtt.topic, on).await;
|
||||
set_on(self.client.clone(), &self.config.mqtt.topic, on).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -240,7 +235,7 @@ impl crate::traits::Timeout for IkeaOutlet {
|
||||
// TODO: Impl Drop for IkeaOutlet that will abort the handle if the IkeaOutlet
|
||||
// get dropped
|
||||
let client = self.client.clone();
|
||||
let topic = self.mqtt.topic.clone();
|
||||
let topic = self.config.mqtt.topic.clone();
|
||||
let id = self.identifier.clone();
|
||||
self.handle = Some(tokio::spawn(async move {
|
||||
debug!(id, "Starting timeout ({timeout:?})...");
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::str::Utf8Error;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use automation_macro::LuaDevice;
|
||||
use bytes::{Buf, BufMut};
|
||||
use google_home::errors::{self, DeviceError};
|
||||
use google_home::traits;
|
||||
@@ -23,7 +24,7 @@ pub struct KasaOutletConfig {
|
||||
#[async_trait]
|
||||
impl DeviceConfig for KasaOutletConfig {
|
||||
async fn create(
|
||||
self,
|
||||
&self,
|
||||
identifier: &str,
|
||||
_ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
@@ -31,17 +32,18 @@ impl DeviceConfig for KasaOutletConfig {
|
||||
|
||||
let device = KasaOutlet {
|
||||
identifier: identifier.into(),
|
||||
addr: (self.ip, 9999).into(),
|
||||
config: self.clone(),
|
||||
};
|
||||
|
||||
Ok(Box::new(device))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct KasaOutlet {
|
||||
#[derive(Debug, LuaDevice)]
|
||||
pub struct KasaOutlet {
|
||||
identifier: String,
|
||||
addr: SocketAddr,
|
||||
#[config]
|
||||
config: KasaOutletConfig,
|
||||
}
|
||||
|
||||
impl Device for KasaOutlet {
|
||||
@@ -214,7 +216,7 @@ impl Response {
|
||||
#[async_trait]
|
||||
impl traits::OnOff for KasaOutlet {
|
||||
async fn is_on(&self) -> Result<bool, errors::ErrorCode> {
|
||||
let mut stream = TcpStream::connect(self.addr)
|
||||
let mut stream = TcpStream::connect::<SocketAddr>((self.config.ip, 9999).into())
|
||||
.await
|
||||
.or::<DeviceError>(Err(DeviceError::DeviceOffline))?;
|
||||
|
||||
@@ -248,7 +250,7 @@ impl traits::OnOff for KasaOutlet {
|
||||
}
|
||||
|
||||
async fn set_on(&mut self, on: bool) -> Result<(), errors::ErrorCode> {
|
||||
let mut stream = TcpStream::connect(self.addr)
|
||||
let mut stream = TcpStream::connect::<SocketAddr>((self.config.ip, 9999).into())
|
||||
.await
|
||||
.or::<DeviceError>(Err(DeviceError::DeviceOffline))?;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use automation_macro::LuaDevice;
|
||||
use rumqttc::Publish;
|
||||
use serde::Deserialize;
|
||||
use tracing::{debug, trace, warn};
|
||||
@@ -25,16 +26,14 @@ pub const DEFAULT: bool = false;
|
||||
#[async_trait]
|
||||
impl DeviceConfig for LightSensorConfig {
|
||||
async fn create(
|
||||
self,
|
||||
&self,
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
let device = LightSensor {
|
||||
identifier: identifier.into(),
|
||||
tx: ext.event_channel.get_tx(),
|
||||
mqtt: self.mqtt,
|
||||
min: self.min,
|
||||
max: self.max,
|
||||
config: self.clone(),
|
||||
is_dark: DEFAULT,
|
||||
};
|
||||
|
||||
@@ -42,13 +41,13 @@ impl DeviceConfig for LightSensorConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, LuaDevice)]
|
||||
pub struct LightSensor {
|
||||
identifier: String,
|
||||
#[config]
|
||||
config: LightSensorConfig,
|
||||
|
||||
tx: event::Sender,
|
||||
mqtt: MqttDeviceConfig,
|
||||
min: isize,
|
||||
max: isize,
|
||||
is_dark: bool,
|
||||
}
|
||||
|
||||
@@ -61,7 +60,7 @@ impl Device for LightSensor {
|
||||
#[async_trait]
|
||||
impl OnMqtt for LightSensor {
|
||||
fn topics(&self) -> Vec<&str> {
|
||||
vec![&self.mqtt.topic]
|
||||
vec![&self.config.mqtt.topic]
|
||||
}
|
||||
|
||||
async fn on_mqtt(&mut self, message: Publish) {
|
||||
@@ -74,17 +73,17 @@ impl OnMqtt for LightSensor {
|
||||
};
|
||||
|
||||
debug!("Illuminance: {illuminance}");
|
||||
let is_dark = if illuminance <= self.min {
|
||||
let is_dark = if illuminance <= self.config.min {
|
||||
trace!("It is dark");
|
||||
true
|
||||
} else if illuminance >= self.max {
|
||||
} else if illuminance >= self.config.max {
|
||||
trace!("It is light");
|
||||
false
|
||||
} else {
|
||||
trace!(
|
||||
"In between min ({}) and max ({}) value, keeping current state: {}",
|
||||
self.min,
|
||||
self.max,
|
||||
self.config.min,
|
||||
self.config.max,
|
||||
self.is_dark
|
||||
);
|
||||
self.is_dark
|
||||
|
||||
@@ -18,19 +18,19 @@ use automation_cast::Cast;
|
||||
use google_home::traits::OnOff;
|
||||
use google_home::GoogleHomeDevice;
|
||||
|
||||
pub use self::air_filter::AirFilterConfig;
|
||||
pub use self::audio_setup::AudioSetupConfig;
|
||||
pub use self::contact_sensor::ContactSensorConfig;
|
||||
pub use self::debug_bridge::DebugBridgeConfig;
|
||||
pub use self::hue_bridge::HueBridgeConfig;
|
||||
pub use self::hue_light::HueGroupConfig;
|
||||
pub use self::ikea_outlet::IkeaOutletConfig;
|
||||
pub use self::kasa_outlet::KasaOutletConfig;
|
||||
pub use self::air_filter::*;
|
||||
pub use self::audio_setup::*;
|
||||
pub use self::contact_sensor::*;
|
||||
pub use self::debug_bridge::*;
|
||||
pub use self::hue_bridge::*;
|
||||
pub use self::hue_light::*;
|
||||
pub use self::ikea_outlet::*;
|
||||
pub use self::kasa_outlet::*;
|
||||
pub use self::light_sensor::{LightSensor, LightSensorConfig};
|
||||
pub use self::ntfy::{Notification, Ntfy};
|
||||
pub use self::presence::{Presence, PresenceConfig, DEFAULT_PRESENCE};
|
||||
pub use self::wake_on_lan::WakeOnLANConfig;
|
||||
pub use self::washer::WasherConfig;
|
||||
pub use self::wake_on_lan::*;
|
||||
pub use self::washer::*;
|
||||
use crate::event::{OnDarkness, OnMqtt, OnNotification, OnPresence};
|
||||
use crate::traits::Timeout;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use automation_macro::LuaDevice;
|
||||
use eui48::MacAddress;
|
||||
use google_home::errors::ErrorCode;
|
||||
use google_home::traits::{self, Scene};
|
||||
@@ -35,7 +36,7 @@ fn default_broadcast_ip() -> Ipv4Addr {
|
||||
#[async_trait]
|
||||
impl DeviceConfig for WakeOnLANConfig {
|
||||
async fn create(
|
||||
self,
|
||||
&self,
|
||||
identifier: &str,
|
||||
_ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
@@ -48,23 +49,18 @@ impl DeviceConfig for WakeOnLANConfig {
|
||||
|
||||
let device = WakeOnLAN {
|
||||
identifier: identifier.into(),
|
||||
info: self.info,
|
||||
mqtt: self.mqtt,
|
||||
mac_address: self.mac_address,
|
||||
broadcast_ip: self.broadcast_ip,
|
||||
config: self.clone(),
|
||||
};
|
||||
|
||||
Ok(Box::new(device))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WakeOnLAN {
|
||||
#[derive(Debug, LuaDevice)]
|
||||
pub struct WakeOnLAN {
|
||||
identifier: String,
|
||||
info: InfoConfig,
|
||||
mqtt: MqttDeviceConfig,
|
||||
mac_address: MacAddress,
|
||||
broadcast_ip: Ipv4Addr,
|
||||
#[config]
|
||||
config: WakeOnLANConfig,
|
||||
}
|
||||
|
||||
impl Device for WakeOnLAN {
|
||||
@@ -76,7 +72,7 @@ impl Device for WakeOnLAN {
|
||||
#[async_trait]
|
||||
impl OnMqtt for WakeOnLAN {
|
||||
fn topics(&self) -> Vec<&str> {
|
||||
vec![&self.mqtt.topic]
|
||||
vec![&self.config.mqtt.topic]
|
||||
}
|
||||
|
||||
async fn on_mqtt(&mut self, message: Publish) {
|
||||
@@ -98,7 +94,7 @@ impl GoogleHomeDevice for WakeOnLAN {
|
||||
}
|
||||
|
||||
fn get_device_name(&self) -> device::Name {
|
||||
let mut name = device::Name::new(&self.info.name);
|
||||
let mut name = device::Name::new(&self.config.info.name);
|
||||
name.add_default_name("Computer");
|
||||
|
||||
name
|
||||
@@ -113,7 +109,7 @@ impl GoogleHomeDevice for WakeOnLAN {
|
||||
}
|
||||
|
||||
fn get_room_hint(&self) -> Option<&str> {
|
||||
self.info.room.as_deref()
|
||||
self.config.info.room.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,25 +119,31 @@ impl traits::Scene for WakeOnLAN {
|
||||
if activate {
|
||||
debug!(
|
||||
id = self.identifier,
|
||||
"Activating Computer: {} (Sending to {})", self.mac_address, self.broadcast_ip
|
||||
"Activating Computer: {} (Sending to {})",
|
||||
self.config.mac_address,
|
||||
self.config.broadcast_ip
|
||||
);
|
||||
let wol =
|
||||
wakey::WolPacket::from_bytes(&self.mac_address.to_array()).map_err(|err| {
|
||||
let wol = wakey::WolPacket::from_bytes(&self.config.mac_address.to_array()).map_err(
|
||||
|err| {
|
||||
error!(id = self.identifier, "invalid mac address: {err}");
|
||||
google_home::errors::DeviceError::TransientError
|
||||
})?;
|
||||
},
|
||||
)?;
|
||||
|
||||
wol.send_magic_to((Ipv4Addr::new(0, 0, 0, 0), 0), (self.broadcast_ip, 9))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!(id = self.identifier, "Failed to activate computer: {err}");
|
||||
google_home::errors::DeviceError::TransientError.into()
|
||||
})
|
||||
.map(|_| debug!(id = self.identifier, "Success!"))
|
||||
wol.send_magic_to(
|
||||
(Ipv4Addr::new(0, 0, 0, 0), 0),
|
||||
(self.config.broadcast_ip, 9),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!(id = self.identifier, "Failed to activate computer: {err}");
|
||||
google_home::errors::DeviceError::TransientError.into()
|
||||
})
|
||||
.map(|_| debug!(id = self.identifier, "Success!"))
|
||||
} else {
|
||||
debug!(
|
||||
id = self.identifier,
|
||||
"Trying to deactive computer, this is not currently supported"
|
||||
"Trying to deactivate computer, this is not currently supported"
|
||||
);
|
||||
// We do not support deactivating this scene
|
||||
Err(ErrorCode::DeviceError(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use async_trait::async_trait;
|
||||
use automation_macro::LuaDevice;
|
||||
use rumqttc::Publish;
|
||||
use serde::Deserialize;
|
||||
use tracing::{debug, error, warn};
|
||||
@@ -21,15 +22,14 @@ pub struct WasherConfig {
|
||||
#[async_trait]
|
||||
impl DeviceConfig for WasherConfig {
|
||||
async fn create(
|
||||
self,
|
||||
&self,
|
||||
identifier: &str,
|
||||
ext: &ConfigExternal,
|
||||
) -> Result<Box<dyn Device>, DeviceConfigError> {
|
||||
let device = Washer {
|
||||
identifier: identifier.into(),
|
||||
mqtt: self.mqtt,
|
||||
config: self.clone(),
|
||||
event_channel: ext.event_channel.clone(),
|
||||
threshold: self.threshold,
|
||||
running: 0,
|
||||
};
|
||||
|
||||
@@ -39,13 +39,13 @@ impl DeviceConfig for WasherConfig {
|
||||
|
||||
// TODO: Add google home integration
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Washer {
|
||||
#[derive(Debug, LuaDevice)]
|
||||
pub struct Washer {
|
||||
identifier: String,
|
||||
mqtt: MqttDeviceConfig,
|
||||
#[config]
|
||||
config: WasherConfig,
|
||||
|
||||
event_channel: EventChannel,
|
||||
threshold: f32,
|
||||
running: isize,
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ const HYSTERESIS: isize = 10;
|
||||
#[async_trait]
|
||||
impl OnMqtt for Washer {
|
||||
fn topics(&self) -> Vec<&str> {
|
||||
vec![&self.mqtt.topic]
|
||||
vec![&self.config.mqtt.topic]
|
||||
}
|
||||
|
||||
async fn on_mqtt(&mut self, message: Publish) {
|
||||
@@ -77,12 +77,12 @@ impl OnMqtt for Washer {
|
||||
|
||||
// debug!(id = self.identifier, power, "Washer state update");
|
||||
|
||||
if power < self.threshold && self.running >= HYSTERESIS {
|
||||
if power < self.config.threshold && self.running >= HYSTERESIS {
|
||||
// The washer is done running
|
||||
debug!(
|
||||
id = self.identifier,
|
||||
power,
|
||||
threshold = self.threshold,
|
||||
threshold = self.config.threshold,
|
||||
"Washer is done"
|
||||
);
|
||||
|
||||
@@ -102,15 +102,15 @@ impl OnMqtt for Washer {
|
||||
{
|
||||
warn!("There are no receivers on the event channel");
|
||||
}
|
||||
} else if power < self.threshold {
|
||||
} else if power < self.config.threshold {
|
||||
// Prevent false positives
|
||||
self.running = 0;
|
||||
} else if power >= self.threshold && self.running < HYSTERESIS {
|
||||
} else if power >= self.config.threshold && self.running < HYSTERESIS {
|
||||
// Washer could be starting
|
||||
debug!(
|
||||
id = self.identifier,
|
||||
power,
|
||||
threshold = self.threshold,
|
||||
threshold = self.config.threshold,
|
||||
"Washer is starting"
|
||||
);
|
||||
|
||||
|
||||
52
src/main.rs
52
src/main.rs
@@ -1,10 +1,13 @@
|
||||
#![feature(async_closure)]
|
||||
use std::process;
|
||||
use std::{fs, process};
|
||||
|
||||
use automation::auth::{OpenIDConfig, User};
|
||||
use automation::config::Config;
|
||||
use automation::device_manager::DeviceManager;
|
||||
use automation::devices::{Ntfy, Presence};
|
||||
use automation::devices::{
|
||||
AirFilter, AudioSetup, ContactSensor, DebugBridge, HueBridge, HueGroup, IkeaOutlet, KasaOutlet,
|
||||
LightSensor, Ntfy, Presence, WakeOnLAN, Washer,
|
||||
};
|
||||
use automation::error::ApiError;
|
||||
use automation::mqtt;
|
||||
use axum::extract::FromRef;
|
||||
@@ -60,10 +63,6 @@ async fn app() -> anyhow::Result<()> {
|
||||
// Setup the device handler
|
||||
let device_manager = DeviceManager::new(client.clone());
|
||||
|
||||
for (id, device_config) in config.devices {
|
||||
device_manager.create(&id, device_config).await?;
|
||||
}
|
||||
|
||||
device_manager.add_schedule(config.schedule).await;
|
||||
|
||||
let event_channel = device_manager.event_channel();
|
||||
@@ -80,6 +79,47 @@ async fn app() -> anyhow::Result<()> {
|
||||
device_manager.add(Box::new(ntfy)).await;
|
||||
}
|
||||
|
||||
// Lua testing
|
||||
{
|
||||
let lua = mlua::Lua::new();
|
||||
let automation = lua.create_table()?;
|
||||
|
||||
automation.set("device_manager", device_manager.clone())?;
|
||||
|
||||
let util = lua.create_table()?;
|
||||
let get_env = lua.create_function(|_lua, name: String| {
|
||||
std::env::var(name).map_err(mlua::ExternalError::into_lua_err)
|
||||
})?;
|
||||
util.set("get_env", get_env)?;
|
||||
automation.set("util", util)?;
|
||||
|
||||
lua.globals().set("automation", automation)?;
|
||||
|
||||
// Register all the device types
|
||||
AirFilter::register_with_lua(&lua)?;
|
||||
AudioSetup::register_with_lua(&lua)?;
|
||||
ContactSensor::register_with_lua(&lua)?;
|
||||
DebugBridge::register_with_lua(&lua)?;
|
||||
HueBridge::register_with_lua(&lua)?;
|
||||
HueGroup::register_with_lua(&lua)?;
|
||||
IkeaOutlet::register_with_lua(&lua)?;
|
||||
KasaOutlet::register_with_lua(&lua)?;
|
||||
LightSensor::register_with_lua(&lua)?;
|
||||
WakeOnLAN::register_with_lua(&lua)?;
|
||||
Washer::register_with_lua(&lua)?;
|
||||
|
||||
// TODO: Make this not hardcoded
|
||||
let filename = "config.lua";
|
||||
let file = fs::read_to_string(filename)?;
|
||||
match lua.load(file).set_name(filename).exec_async().await {
|
||||
Err(error) => {
|
||||
println!("{error}");
|
||||
Err(error)
|
||||
}
|
||||
result => result,
|
||||
}?;
|
||||
}
|
||||
|
||||
// Wrap the mqtt eventloop and start listening for message
|
||||
// NOTE: We wait until all the setup is done, as otherwise we might miss some messages
|
||||
mqtt::start(eventloop, &event_channel);
|
||||
|
||||
Reference in New Issue
Block a user