summaryrefslogtreecommitdiffstats
path: root/src/device
diff options
context:
space:
mode:
Diffstat (limited to 'src/device')
-rw-r--r--src/device/on_action.rs54
-rw-r--r--src/device/set_state.rs64
2 files changed, 118 insertions, 0 deletions
diff --git a/src/device/on_action.rs b/src/device/on_action.rs
new file mode 100644
index 0000000..5425086
--- /dev/null
+++ b/src/device/on_action.rs
@@ -0,0 +1,54 @@
+use std::{
+ env::ArgsOs, ffi::OsString, process::{self, Command}, str
+};
+
+use rumqttc::{Event::Incoming, Packet::Publish, QoS};
+use serde_json::Value;
+
+use crate::config::Config;
+
+pub fn main(argv0: &str, config: Config, device_name: &str, mut args: ArgsOs) {
+ let (Some(action_filter), Some(command)) = (args.next(), args.next()) else {
+ eprintln!("Usage: {argv0} device <device-name> on-action <action-filter> <command...>");
+ process::exit(1);
+ };
+
+ let args: Box<[OsString]> = args.collect();
+
+ let Ok(action_filter) = action_filter.into_string() else {
+ eprintln!("{argv0}: error: Invalid action-filter");
+ process::exit(1);
+ };
+
+ let (client, mut connection) = config.mqtt_client();
+
+ let device_topic = format!("zigbee2mqtt/{device_name}");
+ client.subscribe(&device_topic, QoS::AtLeastOnce).unwrap();
+
+ for notification in connection.iter() {
+ match notification.unwrap() {
+ Incoming(Publish(p)) => {
+ if p.topic == device_topic {
+ let parsed: Value =
+ serde_json::from_str(str::from_utf8(&p.payload).unwrap()).unwrap();
+ if parsed["action"] == action_filter {
+ let mut c = Command::new(&command);
+ c.args(&args);
+ let status = c.status().expect("Unable to execute command");
+ match status.code() {
+ Some(code) => {
+ if code != 0 {
+ eprintln!("command exited with: {code}");
+ }
+ }
+ None => {
+ eprintln!("command was killed by a signal: {status:?}");
+ }
+ }
+ }
+ }
+ }
+ _ => (),
+ }
+ }
+}
diff --git a/src/device/set_state.rs b/src/device/set_state.rs
new file mode 100644
index 0000000..24ed99e
--- /dev/null
+++ b/src/device/set_state.rs
@@ -0,0 +1,64 @@
+use std::{env::ArgsOs, process, str};
+
+use rumqttc::{Event::Incoming, Packet::Publish, QoS};
+use serde_json::{json, Value};
+
+use crate::config::Config;
+
+pub fn main(argv0: &str, config: Config, device_name: &str, mut args: ArgsOs) {
+ let (Some(target_state), None) = (args.next(), args.next()) else {
+ eprintln!("Usage: {argv0} device <device-name> set-state <target-state>");
+ process::exit(1);
+ };
+
+ let target_state = if target_state.eq_ignore_ascii_case("on") {
+ "ON"
+ } else if target_state.eq_ignore_ascii_case("off") {
+ "OFF"
+ } else if target_state.eq_ignore_ascii_case("toggle") {
+ "TOGGLE"
+ } else {
+ eprintln!("{argv0}: error: target-state must be on/off/toggle");
+ process::exit(1);
+ };
+
+ let (client, mut connection) = config.mqtt_client();
+
+ let device_topic = format!("zigbee2mqtt/{device_name}");
+ client.subscribe(&device_topic, QoS::AtMostOnce).unwrap();
+
+ client
+ .publish(
+ &format!("{device_topic}/get"),
+ QoS::AtLeastOnce,
+ false,
+ json!({"state": "" }).to_string(),
+ )
+ .unwrap();
+ client
+ .publish(
+ &format!("{device_topic}/set"),
+ QoS::AtLeastOnce,
+ false,
+ json!({"state": target_state}).to_string(),
+ )
+ .unwrap();
+
+ for notification in connection.iter() {
+ match notification.unwrap() {
+ Incoming(Publish(p)) => {
+ if p.topic == device_topic {
+ let parsed: Value =
+ serde_json::from_str(str::from_utf8(&p.payload).unwrap()).unwrap();
+ if target_state == "TOGGLE" {
+ break;
+ }
+ if parsed["state"] == target_state {
+ break;
+ }
+ }
+ }
+ _ => (),
+ }
+ }
+}