From 63c86ab80e5a063eb20ddee8d20dc5e1ac7fa3e0 Mon Sep 17 00:00:00 2001 From: Sam Willcocks Date: Tue, 11 Oct 2022 19:42:12 +0000 Subject: [PATCH] Add monitor brightness support --- src/lib.rs | 43 +++++++++++++++++++++++++++++++++++++------ src/mqtt.rs | 19 ++++++++++++++++--- src/osc.rs | 6 +++--- 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 294537b..8b7a15d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,9 +17,15 @@ enum Command { Monitor((usize, MonitorCommand)), } +#[derive(Debug)] +pub enum MonitorBrightnessCmd { + Absolute(u8), + Relative(i8), +} + #[derive(Debug)] pub enum MonitorCommand { - Brightness(u8), + Brightness(MonitorBrightnessCmd), Input(u8), } @@ -51,17 +57,42 @@ fn run_i2c(idx: usize, mut dev: I2cDeviceDdc, command_channel: Receiver dev.set_vcp_feature(cmd.vcp(), b.into()), - // Hack - add 15 to align with DELL monitors - MonitorCommand::Input(i) => dev.set_vcp_feature(cmd.vcp(), (i + 15).into()), - } { + if let Err(e) = handle_monitor_cmd(&cmd, &mut dev) { event!(Level::WARN, err = %e, "Error sending DDC command"); } last_sent_command.insert(cmd.cmd_str(), Some(Instant::now())); } } +fn handle_monitor_cmd(cmd: &MonitorCommand, dev: &mut I2cDeviceDdc) -> StdError<()> { + match cmd { + MonitorCommand::Brightness(MonitorBrightnessCmd::Absolute(b)) => { + event!(Level::INFO, brightness = b, "Setting monitor brightness"); + dev.set_vcp_feature(cmd.vcp(), (*b).into()) + .map_err(|e| e.into()) + } + MonitorCommand::Brightness(MonitorBrightnessCmd::Relative(b)) => { + let current = dev.get_vcp_feature(cmd.vcp())?; + let (current, max) = (current.value(), current.maximum()); + event!(Level::INFO, current, max, "Got brightness of monitor"); + if max != 100 { + return Err(format!("Expected maximum brightness to be 100, got {}", max).into()); + } + let mut new = current.saturating_add_signed(*b as i16); + // Clamp to 0-100 + if new > 100 { + new = 100; + } + event!(Level::INFO, brightness = new, "Setting monitor brightness"); + dev.set_vcp_feature(cmd.vcp(), new).map_err(|e| e.into()) + } + // Hack - add 15 to align with DELL monitors + MonitorCommand::Input(i) => dev + .set_vcp_feature(cmd.vcp(), (i + 15).into()) + .map_err(|e| e.into()), + } +} + pub fn main() -> StdError<()> { event!(Level::INFO, "Starting"); let displays = I2cDeviceEnumerator::new().unwrap().collect::>(); diff --git a/src/mqtt.rs b/src/mqtt.rs index 74c31c3..2a24158 100644 --- a/src/mqtt.rs +++ b/src/mqtt.rs @@ -1,4 +1,4 @@ -use crate::{handle_cmd, Command, MonitorCommand, StdError}; +use crate::{handle_cmd, Command, MonitorBrightnessCmd, MonitorCommand, StdError}; use rumqttc::mqttbytes::v4::Packet; use rumqttc::{Client, Event, MqttOptions, QoS}; use std::env; @@ -8,6 +8,7 @@ use tracing::{event, Level}; pub fn run_mqtt(txes: &Vec>) -> StdError<()> { let client_id = env::var("MQTT_CLIENT_ID").unwrap_or("ddcmqtt".into()); + let topic_prefix = env::var("MQTT_TOPIC_PREFIX").unwrap_or("ddcmqtt".into()); let mqtt_host = env::var("MQTT_HOST").unwrap_or("localhost".into()); let mqtt_port = env::var("MQTT_PORT") .unwrap_or("1883".into()) @@ -24,7 +25,7 @@ pub fn run_mqtt(txes: &Vec>) -> StdError<()> { mqttoptions.set_keep_alive(Duration::from_secs(5)); let (mut client, mut connection) = Client::new(mqttoptions, 10); - client.subscribe("ddcmqtt/#", QoS::AtMostOnce)?; + client.subscribe([&topic_prefix, "#"].join("/"), QoS::AtMostOnce)?; event!(Level::INFO, mqtt_host, mqtt_port, "Running MQTT client"); @@ -40,7 +41,7 @@ pub fn run_mqtt(txes: &Vec>) -> StdError<()> { ); continue; } - if topic[0] != "ddcmqtt" { + if topic[0] != topic_prefix { event!( Level::ERROR, ?topic, @@ -58,6 +59,18 @@ pub fn run_mqtt(txes: &Vec>) -> StdError<()> { &txes, )?; } + (Ok(idx), "brightness") => { + let brightness = std::str::from_utf8(&p.payload)?; + let cmd = if brightness.starts_with('+') | brightness.starts_with('-') { + MonitorBrightnessCmd::Relative(brightness.parse::()?) + } else { + MonitorBrightnessCmd::Absolute(brightness.parse::()?) + }; + handle_cmd( + Command::Monitor((idx.into(), MonitorCommand::Brightness(cmd))), + &txes, + )?; + } _ => { event!(Level::ERROR, ?topic, "Unrecognised or invalid topic"); continue; diff --git a/src/osc.rs b/src/osc.rs index fb5a6d6..eb32a11 100644 --- a/src/osc.rs +++ b/src/osc.rs @@ -1,4 +1,4 @@ -use crate::{handle_cmd, Command, MonitorCommand, StdError}; +use crate::{handle_cmd, Command, MonitorBrightnessCmd, MonitorCommand, StdError}; use rosc; use std::net::UdpSocket; use std::sync::mpsc::Sender; @@ -36,9 +36,9 @@ fn osc_message_to_command(msg: rosc::OscMessage) -> StdError { ["monitor", idx, control] => { println!("Monitor {}, control {}, args {:?}", idx, control, msg.args); let command = match *control { - "brightness" => Some(MonitorCommand::Brightness( + "brightness" => Some(MonitorCommand::Brightness(MonitorBrightnessCmd::Absolute( (msg.args[0].clone().float().unwrap() * 100.0) as u8, - )), + ))), "input" => Some(MonitorCommand::Input( (msg.args[0].clone().int().unwrap()) as u8, )),