diff --git a/Cargo.lock b/Cargo.lock index 80824ce..bcc6139 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1682,11 +1682,11 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "wakey" version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dedab5a691c0d33bcfb5c1ed6bb17265e531ed3392282eed9b20063a0f23e9f5" +source = "git+https://github.com/DreadedX/wakey#a982950203aa56a93a7ba7a63062eccdd24046c5" dependencies = [ "arrayvec", "hex", + "tokio", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4bd9b82..11bfa85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,5 +39,8 @@ console-subscriber = "0.1.8" tracing-subscriber = "0.3.16" serde-tuple-vec-map = "1.0.1" +[patch.crates-io] +wakey = { git = "https://github.com/DreadedX/wakey" } + [profile.release] lto = true diff --git a/google-home/src/device.rs b/google-home/src/device.rs index a3c5277..6829fa3 100644 --- a/google-home/src/device.rs +++ b/google-home/src/device.rs @@ -104,7 +104,11 @@ pub trait GoogleHomeDevice: AsGoogleHomeDevice + Sync + Send + 'static { // OnOff if let Some(on_off) = As::::cast(self) { - device.state.on = on_off.is_on().map_err(|err| device.set_error(err)).ok(); + device.state.on = on_off + .is_on() + .await + .map_err(|err| device.set_error(err)) + .ok(); } device @@ -114,14 +118,14 @@ pub trait GoogleHomeDevice: AsGoogleHomeDevice + Sync + Send + 'static { match command { CommandType::OnOff { on } => { if let Some(on_off) = As::::cast_mut(self) { - on_off.set_on(*on)?; + on_off.set_on(*on).await?; } else { return Err(DeviceError::ActionNotAvailable.into()); } } CommandType::ActivateScene { deactivate } => { if let Some(scene) = As::::cast(self) { - scene.set_active(!deactivate)?; + scene.set_active(!deactivate).await?; } else { return Err(DeviceError::ActionNotAvailable.into()); } diff --git a/google-home/src/traits.rs b/google-home/src/traits.rs index 2a0f12f..0daaf98 100644 --- a/google-home/src/traits.rs +++ b/google-home/src/traits.rs @@ -1,3 +1,4 @@ +use async_trait::async_trait; use serde::Serialize; use crate::errors::ErrorCode; @@ -10,6 +11,7 @@ pub enum Trait { Scene, } +#[async_trait] #[impl_cast::device_trait] pub trait OnOff { fn is_command_only(&self) -> Option { @@ -21,15 +23,16 @@ pub trait OnOff { } // TODO: Implement correct error so we can handle them properly - fn is_on(&self) -> Result; - fn set_on(&mut self, on: bool) -> Result<(), ErrorCode>; + async fn is_on(&self) -> Result; + async fn set_on(&mut self, on: bool) -> Result<(), ErrorCode>; } +#[async_trait] #[impl_cast::device_trait] pub trait Scene { fn is_scene_reversible(&self) -> Option { None } - fn set_active(&self, activate: bool) -> Result<(), ErrorCode>; + async fn set_active(&self, activate: bool) -> Result<(), ErrorCode>; } diff --git a/src/devices/audio_setup.rs b/src/devices/audio_setup.rs index 2bdaff8..316d37a 100644 --- a/src/devices/audio_setup.rs +++ b/src/devices/audio_setup.rs @@ -87,21 +87,21 @@ impl OnMqtt for AudioSetup { match action { RemoteAction::On => { - if self.mixer.is_on().unwrap() { - self.speakers.set_on(false).unwrap(); - self.mixer.set_on(false).unwrap(); + if self.mixer.is_on().await.unwrap() { + self.speakers.set_on(false).await.unwrap(); + self.mixer.set_on(false).await.unwrap(); } else { - self.speakers.set_on(true).unwrap(); - self.mixer.set_on(true).unwrap(); + self.speakers.set_on(true).await.unwrap(); + self.mixer.set_on(true).await.unwrap(); } }, RemoteAction::BrightnessMoveUp => { - if !self.mixer.is_on().unwrap() { - self.mixer.set_on(true).unwrap(); - } else if self.speakers.is_on().unwrap() { - self.speakers.set_on(false).unwrap(); + if !self.mixer.is_on().await.unwrap() { + self.mixer.set_on(true).await.unwrap(); + } else if self.speakers.is_on().await.unwrap() { + self.speakers.set_on(false).await.unwrap(); } else { - self.speakers.set_on(true).unwrap(); + self.speakers.set_on(true).await.unwrap(); } }, RemoteAction::BrightnessStop => { /* Ignore this action */ }, @@ -116,8 +116,8 @@ impl OnPresence for AudioSetup { // Turn off the audio setup when we leave the house if !presence { debug!(id = self.identifier, "Turning devices off"); - self.speakers.set_on(false).unwrap(); - self.mixer.set_on(false).unwrap(); + self.speakers.set_on(false).await.unwrap(); + self.mixer.set_on(false).await.unwrap(); } } } diff --git a/src/devices/ikea_outlet.rs b/src/devices/ikea_outlet.rs index c0d8588..7e89648 100644 --- a/src/devices/ikea_outlet.rs +++ b/src/devices/ikea_outlet.rs @@ -6,7 +6,6 @@ use google_home::{ types::Type, GoogleHomeDevice, }; -use pollster::FutureExt as _; use rumqttc::{AsyncClient, Publish}; use serde::Deserialize; use std::time::Duration; @@ -170,7 +169,7 @@ impl OnPresence for IkeaOutlet { // Turn off the outlet when we leave the house (Not if it is a battery charger) if !presence && self.outlet_type != OutletType::Charger { debug!(id = self.identifier, "Turning device off"); - self.set_on(false).ok(); + self.set_on(false).await.ok(); } } } @@ -206,13 +205,14 @@ impl GoogleHomeDevice for IkeaOutlet { } } +#[async_trait] impl traits::OnOff for IkeaOutlet { - fn is_on(&self) -> Result { + async fn is_on(&self) -> Result { Ok(self.last_known_state) } - fn set_on(&mut self, on: bool) -> Result<(), ErrorCode> { - set_on(self.client.clone(), &self.mqtt.topic, on).block_on(); + async fn set_on(&mut self, on: bool) -> Result<(), ErrorCode> { + set_on(self.client.clone(), &self.mqtt.topic, on).await; Ok(()) } diff --git a/src/devices/kasa_outlet.rs b/src/devices/kasa_outlet.rs index 89034b2..df02467 100644 --- a/src/devices/kasa_outlet.rs +++ b/src/devices/kasa_outlet.rs @@ -1,9 +1,9 @@ use std::{ - io::{Read, Write}, - net::{Ipv4Addr, SocketAddr, TcpStream}, + net::{Ipv4Addr, SocketAddr}, str::Utf8Error, }; +use async_trait::async_trait; use bytes::{Buf, BufMut}; use google_home::{ errors::{self, DeviceError}, @@ -12,6 +12,10 @@ use google_home::{ use rumqttc::AsyncClient; use serde::{Deserialize, Serialize}; use thiserror::Error; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::TcpStream, +}; use tracing::trace; use crate::{config::CreateDevice, error::CreateDeviceError, event::EventChannel}; @@ -215,15 +219,18 @@ impl Response { } } +#[async_trait] impl traits::OnOff for KasaOutlet { - fn is_on(&self) -> Result { - let mut stream = - TcpStream::connect(self.addr).or::(Err(DeviceError::DeviceOffline))?; + async fn is_on(&self) -> Result { + let mut stream = TcpStream::connect(self.addr) + .await + .or::(Err(DeviceError::DeviceOffline))?; let body = Request::get_sysinfo().encrypt(); stream .write_all(&body) - .and(stream.flush()) + .await + .and(stream.flush().await) .or::(Err(DeviceError::TransientError))?; let mut received = Vec::new(); @@ -231,6 +238,7 @@ impl traits::OnOff for KasaOutlet { loop { let read = stream .read(&mut rx_bytes) + .await .or::(Err(DeviceError::TransientError.into()))?; received.extend_from_slice(&rx_bytes[..read]); @@ -247,20 +255,22 @@ impl traits::OnOff for KasaOutlet { .or(Err(DeviceError::TransientError.into())) } - fn set_on(&mut self, on: bool) -> Result<(), errors::ErrorCode> { - let mut stream = - TcpStream::connect(self.addr).or::(Err(DeviceError::DeviceOffline))?; + async fn set_on(&mut self, on: bool) -> Result<(), errors::ErrorCode> { + let mut stream = TcpStream::connect(self.addr) + .await + .or::(Err(DeviceError::DeviceOffline))?; let body = Request::set_relay_state(on).encrypt(); stream .write_all(&body) - .and(stream.flush()) + .await + .and(stream.flush().await) .or::(Err(DeviceError::TransientError))?; let mut received = Vec::new(); let mut rx_bytes = [0; 1024]; loop { - let read = match stream.read(&mut rx_bytes) { + let read = match stream.read(&mut rx_bytes).await { Ok(read) => read, Err(_) => return Err(DeviceError::TransientError.into()), }; diff --git a/src/devices/wake_on_lan.rs b/src/devices/wake_on_lan.rs index 8f5bd84..9af59e1 100644 --- a/src/devices/wake_on_lan.rs +++ b/src/devices/wake_on_lan.rs @@ -95,7 +95,7 @@ impl OnMqtt for WakeOnLAN { } }; - self.set_active(activate).ok(); + self.set_active(activate).await.ok(); } } @@ -124,8 +124,9 @@ impl GoogleHomeDevice for WakeOnLAN { } } +#[async_trait] impl traits::Scene for WakeOnLAN { - fn set_active(&self, activate: bool) -> Result<(), ErrorCode> { + async fn set_active(&self, activate: bool) -> Result<(), ErrorCode> { if activate { debug!( id = self.identifier, @@ -138,6 +139,7 @@ impl traits::Scene for WakeOnLAN { })?; 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()