Directly send wol packet instead of using the webhook
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Dreaded_X 2023-01-19 16:35:35 +01:00
parent a0cefa8302
commit aa8963bd4a
Signed by: Dreaded_X
GPG Key ID: 76BDEC4E165D8AD9
6 changed files with 50 additions and 26 deletions

View File

@ -30,8 +30,9 @@ steps:
- docker rm automation_rs || true
- docker create -e RUST_LOG=$RUST_LOG -e MQTT_PASSWORD=$MQTT_PASSWORD -e HUE_TOKEN=$HUE_TOKEN -e NTFY_TOPIC=$NTFY_TOPIC --name automation_rs automation_rs
- docker network connect mqtt automation_rs
# Networks need to be setup to to allow broadcasts: https://www.devwithimagination.com/2020/06/15/homebridge-docker-and-wake-on-lan/ https://github.com/dhutchison/container-images/blob/0c2d7d96bab751fb0a008cc91ba2990724bbd11f/homebridge/configure_docker_networks_for_wol.sh
# Needs to be done for ALL networks, because we can't seem to control which interface gets used to send the broadcast
- docker create -e RUST_LOG=$RUST_LOG -e MQTT_PASSWORD=$MQTT_PASSWORD -e HUE_TOKEN=$HUE_TOKEN -e NTFY_TOPIC=$NTFY_TOPIC --network mqtt --restart unless-stopped --name automation_rs automation_rs
- docker network connect web automation_rs
- docker start automation_rs

23
Cargo.lock generated
View File

@ -17,6 +17,12 @@ version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
[[package]]
name = "arrayvec"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "async-recursion"
version = "1.0.0"
@ -72,6 +78,7 @@ dependencies = [
"toml",
"tracing",
"tracing-subscriber",
"wakey",
]
[[package]]
@ -375,6 +382,12 @@ dependencies = [
"libc",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "http"
version = "0.2.8"
@ -1282,6 +1295,16 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "wakey"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dedab5a691c0d33bcfb5c1ed6bb17265e531ed3392282eed9b20063a0f23e9f5"
dependencies = [
"arrayvec",
"hex",
]
[[package]]
name = "want"
version = "0.3.0"

View File

@ -33,6 +33,7 @@ futures = "0.3.25"
eui48 = { version = "1.1.0", default-features = false, features = ["disp_hexstring", "serde"] }
thiserror = "1.0.38"
anyhow = "1.0.68"
wakey = "0.3.0"
[profile.release]
lto=true

View File

@ -46,6 +46,7 @@ name = "Zeus"
room = "Living Room"
topic = "automation/appliance/living_room/zeus"
mac_address = "30:9c:23:60:9c:13"
broadcast_ip = "10.0.0.255"
[devices.living_audio]
type = "AudioSetup"

View File

@ -165,6 +165,8 @@ pub enum Device {
#[serde(flatten)]
mqtt: MqttDeviceConfig,
mac_address: MacAddress,
#[serde(default = "default_broadcast_ip")]
broadcast_ip: Ipv4Addr,
},
KasaOutlet {
ip: Ipv4Addr,
@ -182,6 +184,10 @@ pub enum Device {
}
}
fn default_broadcast_ip() -> Ipv4Addr {
Ipv4Addr::new(255, 255, 255, 255)
}
impl Config {
pub fn parse_file(filename: &str) -> Result<Self, ConfigParseError> {
debug!("Loading config: {filename}");
@ -227,9 +233,9 @@ impl Device {
IkeaOutlet::build(&identifier, info, mqtt, kettle, client).await
.map(device_box)?
},
Device::WakeOnLAN { info, mqtt, mac_address } => {
Device::WakeOnLAN { info, mqtt, mac_address, broadcast_ip } => {
trace!(id = identifier, "WakeOnLan [{} in {:?}]", info.name, info.room);
WakeOnLAN::build(&identifier, info, mqtt, mac_address, client).await
WakeOnLAN::build(&identifier, info, mqtt, mac_address, broadcast_ip, client).await
.map(device_box)?
},
Device::KasaOutlet { ip } => {

View File

@ -1,8 +1,9 @@
use std::net::Ipv4Addr;
use async_trait::async_trait;
use google_home::{GoogleHomeDevice, types::Type, device, traits::{self, Scene}, errors::ErrorCode};
use tracing::{debug, error};
use rumqttc::{AsyncClient, Publish, matches};
use pollster::FutureExt as _;
use eui48::MacAddress;
use crate::{config::{InfoConfig, MqttDeviceConfig}, mqtt::{OnMqtt, ActivateMessage}, error::DeviceError};
@ -15,14 +16,15 @@ pub struct WakeOnLAN {
info: InfoConfig,
mqtt: MqttDeviceConfig,
mac_address: MacAddress,
broadcast_ip: Ipv4Addr,
}
impl WakeOnLAN {
pub async fn build(identifier: &str, info: InfoConfig, mqtt: MqttDeviceConfig, mac_address: MacAddress, client: AsyncClient) -> Result<Self, DeviceError> {
pub async fn build(identifier: &str, info: InfoConfig, mqtt: MqttDeviceConfig, mac_address: MacAddress, broadcast_ip: Ipv4Addr, client: AsyncClient) -> Result<Self, DeviceError> {
// @TODO Handle potential errors here
client.subscribe(mqtt.topic.clone(), rumqttc::QoS::AtLeastOnce).await?;
Ok(Self { identifier: identifier.to_owned(), info, mqtt, mac_address })
Ok(Self { identifier: identifier.to_owned(), info, mqtt, mac_address, broadcast_ip })
}
}
@ -79,26 +81,16 @@ impl GoogleHomeDevice for WakeOnLAN {
impl traits::Scene for WakeOnLAN {
fn set_active(&self, activate: bool) -> Result<(), ErrorCode> {
if activate {
// @TODO In the future send the wake on lan package directly, this is kind of annoying
// if we are inside of docker, so for now just call a webhook that does it for us
let mac_address = self.mac_address.clone();
let id = self.identifier.clone();
debug!(id = self.identifier, "Activating Computer: {} (Sending to {})", self.mac_address, self.broadcast_ip);
let wol = wakey::WolPacket::from_bytes(&self.mac_address.to_array()).map_err(|err| {
error!(id = self.identifier, "invalid mac address: {err}");
google_home::errors::DeviceError::TransientError
})?;
debug!(id, "Activating Computer: {}", mac_address);
let res = match reqwest::get(format!("http://10.0.0.2:9000/start-pc?mac={mac_address}")).block_on() {
Ok(res) => res,
Err(err) => {
error!(id, "Failed to call webhook: {err}");
return Err(google_home::errors::DeviceError::TransientError.into());
}
};
let status = res.status();
if !status.is_success() {
error!(id, "Failed to call webhook: {}", status);
}
Ok(())
wol.send_magic_to((Ipv4Addr::new(0, 0, 0, 0), 0), (self.broadcast_ip, 9)).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");
// We do not support deactivating this scene