Reworked air filter integration
This commit is contained in:
parent
5af713cf8f
commit
3905df690b
180
Cargo.lock
generated
180
Cargo.lock
generated
|
@ -1,6 +1,6 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
|
@ -38,6 +38,16 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "air_filter_types"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.huizinga.dev/Dreaded_X/airfilter?tag=v0.4.4#8603d6fab4ceee29c68db1edd556d691f123842d"
|
||||
dependencies = [
|
||||
"bme280",
|
||||
"defmt",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.18"
|
||||
|
@ -79,7 +89,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -118,6 +128,7 @@ version = "0.1.0"
|
|||
name = "automation_devices"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"air_filter_types",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"automation_cast",
|
||||
|
@ -176,7 +187,7 @@ dependencies = [
|
|||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -267,6 +278,18 @@ version = "2.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||
|
||||
[[package]]
|
||||
name = "bme280"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "169ac81b9123f316fde5b0e00175294dcdcdd800c1a6c92a4b58caf42a14cf1f"
|
||||
dependencies = [
|
||||
"defmt",
|
||||
"embedded-hal",
|
||||
"embedded-hal-async",
|
||||
"maybe-async-cfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.11.0"
|
||||
|
@ -355,6 +378,38 @@ dependencies = [
|
|||
"chrono",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f6162c53f659f65d00619fe31f14556a6e9f8752ccc4a41bd177ffcf3d6130"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"defmt-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-macros"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d135dd939bad62d7490b0002602d35b358dce5fd9233a709d3c1ef467d4bde6"
|
||||
dependencies = [
|
||||
"defmt-parser",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-parser"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3983b127f13995e68c1e29071e5d115cd96f215ccb5e6812e3728cd6f92653b3"
|
||||
dependencies = [
|
||||
"thiserror 2.0.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
|
@ -373,6 +428,21 @@ version = "1.9.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal-async"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884"
|
||||
dependencies = [
|
||||
"embedded-hal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
|
@ -491,7 +561,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -564,7 +634,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -848,12 +918,49 @@ dependencies = [
|
|||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "manyhow"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587"
|
||||
dependencies = [
|
||||
"manyhow-macros",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "manyhow-macros"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495"
|
||||
dependencies = [
|
||||
"proc-macro-utils",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed1202b2a6f884ae56f04cff409ab315c5ce26b5e58d7412e484f01fd52f52ef"
|
||||
|
||||
[[package]]
|
||||
name = "maybe-async-cfg"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8dbfaa67a76e2623580df07d6bb5e7956c0a4bae4b418314083a9c619bd66627"
|
||||
dependencies = [
|
||||
"manyhow",
|
||||
"proc-macro2",
|
||||
"pulldown-cmark",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
|
@ -930,7 +1037,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -951,7 +1058,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1080,7 +1187,18 @@ dependencies = [
|
|||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-utils"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1092,6 +1210,17 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"memchr",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.6"
|
||||
|
@ -1468,7 +1597,7 @@ checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1500,7 +1629,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1570,6 +1699,17 @@ version = "2.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
|
@ -1622,7 +1762,7 @@ checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1633,7 +1773,7 @@ checksum = "995d0bbc9995d1f19d28b7215a9352b0fc3cd3a2d2ec95c2cadc485cdedbcdde"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1700,7 +1840,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1790,7 +1930,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1840,6 +1980,12 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e13db2e0ccd5e14a544e8a246ba2312cd25223f616442d7f2cb0e3db614236e"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.13"
|
||||
|
@ -1945,7 +2091,7 @@ dependencies = [
|
|||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
@ -1979,7 +2125,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
@ -2274,7 +2420,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -65,6 +65,7 @@ tracing-subscriber = "0.3.16"
|
|||
uuid = "1.8.0"
|
||||
wakey = "0.3.0"
|
||||
zigbee2mqtt-types = { version = "0.4.0", features = ["debug", "philips"] }
|
||||
air_filter_types = { git = "https://git.huizinga.dev/Dreaded_X/airfilter", tag = "v0.4.4" }
|
||||
|
||||
[dependencies]
|
||||
automation_lib = { workspace = true }
|
||||
|
|
|
@ -25,3 +25,4 @@ bytes = { workspace = true }
|
|||
thiserror = { workspace = true }
|
||||
eui48 = { workspace = true }
|
||||
wakey = { workspace = true }
|
||||
air_filter_types = { workspace = true }
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use automation_lib::config::{InfoConfig, MqttDeviceConfig};
|
||||
use automation_lib::config::InfoConfig;
|
||||
use automation_lib::device::{Device, LuaDeviceCreate};
|
||||
use automation_lib::event::OnMqtt;
|
||||
use automation_lib::messages::{AirFilterFanState, AirFilterState, SetAirFilterFanState};
|
||||
use automation_lib::mqtt::WrappedAsyncClient;
|
||||
use automation_macro::LuaDeviceConfig;
|
||||
use google_home::device::Name;
|
||||
use google_home::errors::ErrorCode;
|
||||
|
@ -14,51 +9,57 @@ use google_home::traits::{
|
|||
TemperatureUnit,
|
||||
};
|
||||
use google_home::types::Type;
|
||||
use rumqttc::Publish;
|
||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use tracing::{debug, error, trace, warn};
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
#[derive(Debug, Clone, LuaDeviceConfig)]
|
||||
pub struct Config {
|
||||
#[device_config(flatten)]
|
||||
pub info: InfoConfig,
|
||||
#[device_config(flatten)]
|
||||
pub mqtt: MqttDeviceConfig,
|
||||
#[device_config(from_lua)]
|
||||
pub client: WrappedAsyncClient,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AirFilter {
|
||||
config: Config,
|
||||
state: Arc<RwLock<AirFilterState>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("Connection error")]
|
||||
ReqwestError(#[from] reqwest::Error),
|
||||
}
|
||||
|
||||
impl From<Error> for google_home::errors::ErrorCode {
|
||||
fn from(value: Error) -> Self {
|
||||
match value {
|
||||
// Assume that if we encounter a ReqwestError the device is offline
|
||||
Error::ReqwestError(_) => {
|
||||
Self::DeviceError(google_home::errors::DeviceError::DeviceOffline)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Handle error properly
|
||||
impl AirFilter {
|
||||
async fn set_speed(&self, state: AirFilterFanState) {
|
||||
let message = SetAirFilterFanState::new(state);
|
||||
async fn set_fan_speed(&self, speed: air_filter_types::FanSpeed) -> Result<(), Error> {
|
||||
let message = air_filter_types::SetFanSpeed::new(speed);
|
||||
let url = format!("{}/state/fan", self.config.url);
|
||||
let client = reqwest::Client::new();
|
||||
client.put(url).json(&message).send().await?;
|
||||
|
||||
let topic = format!("{}/set", self.config.mqtt.topic);
|
||||
// TODO: Handle potential errors here
|
||||
self.config
|
||||
.client
|
||||
.publish(
|
||||
&topic,
|
||||
rumqttc::QoS::AtLeastOnce,
|
||||
false,
|
||||
serde_json::to_string(&message).unwrap(),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| warn!("Failed to update state on {topic}: {err}"))
|
||||
.ok();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn state(&self) -> RwLockReadGuard<AirFilterState> {
|
||||
self.state.read().await
|
||||
async fn get_fan_state(&self) -> Result<air_filter_types::FanState, Error> {
|
||||
let url = format!("{}/state/fan", self.config.url);
|
||||
Ok(reqwest::get(url).await?.json().await?)
|
||||
}
|
||||
|
||||
async fn state_mut(&self) -> RwLockWriteGuard<AirFilterState> {
|
||||
self.state.write().await
|
||||
async fn get_sensor_data(&self) -> Result<air_filter_types::SensorData, Error> {
|
||||
let url = format!("{}/state/sensor", self.config.url);
|
||||
Ok(reqwest::get(url).await?.json().await?)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,19 +71,7 @@ impl LuaDeviceCreate for AirFilter {
|
|||
async fn create(config: Self::Config) -> Result<Self, Self::Error> {
|
||||
trace!(id = config.info.identifier(), "Setting up AirFilter");
|
||||
|
||||
config
|
||||
.client
|
||||
.subscribe(&config.mqtt.topic, rumqttc::QoS::AtLeastOnce)
|
||||
.await?;
|
||||
|
||||
let state = AirFilterState {
|
||||
state: AirFilterFanState::Off,
|
||||
humidity: 0.0,
|
||||
temperature: 0.0,
|
||||
};
|
||||
let state = Arc::new(RwLock::new(state));
|
||||
|
||||
Ok(Self { config, state })
|
||||
Ok(Self { config })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,30 +82,6 @@ impl Device for AirFilter {
|
|||
}
|
||||
|
||||
#[async_trait]
|
||||
impl OnMqtt for AirFilter {
|
||||
async fn on_mqtt(&self, message: Publish) {
|
||||
if !rumqttc::matches(&message.topic, &self.config.mqtt.topic) {
|
||||
return;
|
||||
}
|
||||
|
||||
let state = match AirFilterState::try_from(message) {
|
||||
Ok(state) => state,
|
||||
Err(err) => {
|
||||
error!(id = Device::get_id(self), "Failed to parse message: {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if state == *self.state().await {
|
||||
return;
|
||||
}
|
||||
|
||||
debug!(id = Device::get_id(self), "Updating state to {state:?}");
|
||||
|
||||
*self.state_mut().await = state;
|
||||
}
|
||||
}
|
||||
|
||||
impl google_home::Device for AirFilter {
|
||||
fn get_device_type(&self) -> Type {
|
||||
Type::AirPurifier
|
||||
|
@ -130,8 +95,8 @@ impl google_home::Device for AirFilter {
|
|||
Device::get_id(self)
|
||||
}
|
||||
|
||||
fn is_online(&self) -> bool {
|
||||
true
|
||||
async fn is_online(&self) -> bool {
|
||||
self.get_sensor_data().await.is_ok()
|
||||
}
|
||||
|
||||
fn get_room_hint(&self) -> Option<&str> {
|
||||
|
@ -146,16 +111,16 @@ impl google_home::Device for AirFilter {
|
|||
#[async_trait]
|
||||
impl OnOff for AirFilter {
|
||||
async fn on(&self) -> Result<bool, ErrorCode> {
|
||||
Ok(self.state().await.state != AirFilterFanState::Off)
|
||||
Ok(self.get_fan_state().await?.speed != air_filter_types::FanSpeed::Off)
|
||||
}
|
||||
|
||||
async fn set_on(&self, on: bool) -> Result<(), ErrorCode> {
|
||||
debug!("Turning on air filter: {on}");
|
||||
|
||||
if on {
|
||||
self.set_speed(AirFilterFanState::High).await;
|
||||
self.set_fan_speed(air_filter_types::FanSpeed::High).await?;
|
||||
} else {
|
||||
self.set_speed(AirFilterFanState::Off).await;
|
||||
self.set_fan_speed(air_filter_types::FanSpeed::Off).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -201,11 +166,12 @@ impl FanSpeed for AirFilter {
|
|||
}
|
||||
|
||||
async fn current_fan_speed_setting(&self) -> Result<String, ErrorCode> {
|
||||
let speed = match self.state().await.state {
|
||||
AirFilterFanState::Off => "off",
|
||||
AirFilterFanState::Low => "low",
|
||||
AirFilterFanState::Medium => "medium",
|
||||
AirFilterFanState::High => "high",
|
||||
let speed = self.get_fan_state().await?.speed;
|
||||
let speed = match speed {
|
||||
air_filter_types::FanSpeed::Off => "off",
|
||||
air_filter_types::FanSpeed::Low => "low",
|
||||
air_filter_types::FanSpeed::Medium => "medium",
|
||||
air_filter_types::FanSpeed::High => "high",
|
||||
};
|
||||
|
||||
Ok(speed.into())
|
||||
|
@ -213,19 +179,19 @@ impl FanSpeed for AirFilter {
|
|||
|
||||
async fn set_fan_speed(&self, fan_speed: String) -> Result<(), ErrorCode> {
|
||||
let fan_speed = fan_speed.as_str();
|
||||
let state = if fan_speed == "off" {
|
||||
AirFilterFanState::Off
|
||||
let speed = if fan_speed == "off" {
|
||||
air_filter_types::FanSpeed::Off
|
||||
} else if fan_speed == "low" {
|
||||
AirFilterFanState::Low
|
||||
air_filter_types::FanSpeed::Low
|
||||
} else if fan_speed == "medium" {
|
||||
AirFilterFanState::Medium
|
||||
air_filter_types::FanSpeed::Medium
|
||||
} else if fan_speed == "high" {
|
||||
AirFilterFanState::High
|
||||
air_filter_types::FanSpeed::High
|
||||
} else {
|
||||
return Err(google_home::errors::DeviceError::TransientError.into());
|
||||
};
|
||||
|
||||
self.set_speed(state).await;
|
||||
self.set_fan_speed(speed).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -238,7 +204,7 @@ impl HumiditySetting for AirFilter {
|
|||
}
|
||||
|
||||
async fn humidity_ambient_percent(&self) -> Result<isize, ErrorCode> {
|
||||
Ok(self.state().await.humidity.round() as isize)
|
||||
Ok(self.get_sensor_data().await?.humidity().round() as isize)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,8 +219,8 @@ impl TemperatureSetting for AirFilter {
|
|||
TemperatureUnit::Celsius
|
||||
}
|
||||
|
||||
async fn temperature_ambient_celsius(&self) -> f32 {
|
||||
async fn temperature_ambient_celsius(&self) -> Result<f32, ErrorCode> {
|
||||
// HACK: Round to one decimal place
|
||||
(10.0 * self.state().await.temperature).round() / 10.0
|
||||
Ok((10.0 * self.get_sensor_data().await?.temperature()).round() / 10.0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ impl Device for ContactSensor {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl google_home::Device for ContactSensor {
|
||||
fn get_device_type(&self) -> google_home::types::Type {
|
||||
match self.config.sensor_type {
|
||||
|
@ -132,7 +133,7 @@ impl google_home::Device for ContactSensor {
|
|||
false
|
||||
}
|
||||
|
||||
fn is_online(&self) -> bool {
|
||||
async fn is_online(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,6 +127,7 @@ impl OnPresence for IkeaOutlet {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl google_home::Device for IkeaOutlet {
|
||||
fn get_device_type(&self) -> Type {
|
||||
match self.config.outlet_type {
|
||||
|
@ -144,7 +145,7 @@ impl google_home::Device for IkeaOutlet {
|
|||
Device::get_id(self)
|
||||
}
|
||||
|
||||
fn is_online(&self) -> bool {
|
||||
async fn is_online(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ impl OnMqtt for WakeOnLAN {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl google_home::Device for WakeOnLAN {
|
||||
fn get_device_type(&self) -> Type {
|
||||
Type::Scene
|
||||
|
@ -91,7 +92,7 @@ impl google_home::Device for WakeOnLAN {
|
|||
Device::get_id(self)
|
||||
}
|
||||
|
||||
fn is_online(&self) -> bool {
|
||||
async fn is_online(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
@ -191,6 +191,7 @@ impl<T: LightState> OnPresence for Light<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: LightState> google_home::Device for Light<T> {
|
||||
fn get_device_type(&self) -> Type {
|
||||
Type::Light
|
||||
|
@ -204,7 +205,7 @@ impl<T: LightState> google_home::Device for Light<T> {
|
|||
Device::get_id(self)
|
||||
}
|
||||
|
||||
fn is_online(&self) -> bool {
|
||||
async fn is_online(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
@ -241,40 +241,3 @@ impl TryFrom<Bytes> for HueMessage {
|
|||
serde_json::from_slice(&bytes).or(Err(ParseError::InvalidPayload(bytes.clone())))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Import this from the air_filter code itself instead of copying
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Copy, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AirFilterFanState {
|
||||
Off,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
|
||||
pub struct SetAirFilterFanState {
|
||||
state: AirFilterFanState,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Copy, Deserialize, Serialize)]
|
||||
pub struct AirFilterState {
|
||||
pub state: AirFilterFanState,
|
||||
pub humidity: f32,
|
||||
pub temperature: f32,
|
||||
}
|
||||
|
||||
impl SetAirFilterFanState {
|
||||
pub fn new(state: AirFilterFanState) -> Self {
|
||||
Self { state }
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Publish> for AirFilterState {
|
||||
type Error = ParseError;
|
||||
|
||||
fn try_from(message: Publish) -> Result<Self, Self::Error> {
|
||||
serde_json::from_slice(&message.payload)
|
||||
.or(Err(ParseError::InvalidPayload(message.payload.clone())))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -364,8 +364,7 @@ automation.device_manager:add(LightOnOff.new({
|
|||
local bedroom_air_filter = AirFilter.new({
|
||||
name = "Air Filter",
|
||||
room = "Bedroom",
|
||||
topic = "pico/filter/bedroom",
|
||||
client = mqtt_client,
|
||||
url = "http://airfilter.lan.huizinga.dev",
|
||||
})
|
||||
automation.device_manager:add(bedroom_air_filter)
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ pub trait Device: DeviceFulfillment {
|
|||
fn get_device_type(&self) -> Type;
|
||||
fn get_device_name(&self) -> Name;
|
||||
fn get_id(&self) -> String;
|
||||
fn is_online(&self) -> bool;
|
||||
async fn is_online(&self) -> bool;
|
||||
|
||||
// Default values that can optionally be overridden
|
||||
fn will_report_state(&self) -> bool {
|
||||
|
@ -37,29 +37,39 @@ pub trait Device: DeviceFulfillment {
|
|||
}
|
||||
device.device_info = self.get_device_info();
|
||||
|
||||
let (traits, attributes) = DeviceFulfillment::sync(self).await.unwrap();
|
||||
|
||||
// TODO: Return the appropriate error
|
||||
if let Ok((traits, attributes)) = DeviceFulfillment::sync(self).await {
|
||||
device.traits = traits;
|
||||
device.attributes = attributes;
|
||||
}
|
||||
|
||||
device
|
||||
}
|
||||
|
||||
async fn query(&self) -> response::query::Device {
|
||||
let mut device = response::query::Device::new();
|
||||
if !self.is_online() {
|
||||
if !self.is_online().await {
|
||||
device.set_offline();
|
||||
}
|
||||
|
||||
device.state = DeviceFulfillment::query(self).await.unwrap();
|
||||
// TODO: Return the appropriate error
|
||||
if let Ok(state) = DeviceFulfillment::query(self).await {
|
||||
device.state = state;
|
||||
}
|
||||
|
||||
device
|
||||
}
|
||||
|
||||
async fn execute(&self, command: Command) -> Result<(), ErrorCode> {
|
||||
DeviceFulfillment::execute(self, command.clone())
|
||||
// TODO: Do something with the return value, or just get rut of the return value?
|
||||
if DeviceFulfillment::execute(self, command.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
.is_err()
|
||||
{
|
||||
return Err(ErrorCode::DeviceError(
|
||||
crate::errors::DeviceError::TransientError,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ impl GoogleHome {
|
|||
if let Some(device) = devices.get(id.as_str())
|
||||
&& let Some(device) = device.as_ref().cast()
|
||||
{
|
||||
if !device.is_online() {
|
||||
if !device.is_online().await {
|
||||
return (id, Ok(false));
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ traits! {
|
|||
// TODO: Add rename
|
||||
temperatureUnitForUX: TemperatureUnit,
|
||||
|
||||
async fn temperature_ambient_celsius(&self) -> f32,
|
||||
async fn temperature_ambient_celsius(&self) -> Result<f32, ErrorCode>,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user