Compare commits
2 Commits
7aab7f726c
...
63c86ab80e
Author | SHA1 | Date |
---|---|---|
Sam W | 63c86ab80e | |
Sam W | 3f789410a9 |
36
flake.lock
36
flake.lock
|
@ -6,11 +6,11 @@
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1655976588,
|
"lastModified": 1663445644,
|
||||||
"narHash": "sha256-VreHyH6ITkf/1EX/8h15UqhddJnUleb0HgbC3gMkAEQ=",
|
"narHash": "sha256-+xVlcK60x7VY1vRJbNUEAHi17ZuoQxAIH4S4iUFUGBA=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "devshell",
|
"repo": "devshell",
|
||||||
"rev": "899ca4629020592a13a46783587f6e674179d1db",
|
"rev": "e3dc3e21594fe07bdb24bdf1c8657acaa4cb8f66",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -36,11 +36,11 @@
|
||||||
},
|
},
|
||||||
"flake-utils_2": {
|
"flake-utils_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1656928814,
|
"lastModified": 1659877975,
|
||||||
"narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
|
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
|
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -51,11 +51,11 @@
|
||||||
},
|
},
|
||||||
"flake-utils_3": {
|
"flake-utils_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1656065134,
|
"lastModified": 1659877975,
|
||||||
"narHash": "sha256-oc6E6ByIw3oJaIyc67maaFcnjYOz1mMcOtHxbEf9NwQ=",
|
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "bee6a7250dd1b01844a2de7e02e4df7d8a0a206c",
|
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -69,11 +69,11 @@
|
||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1655042882,
|
"lastModified": 1662220400,
|
||||||
"narHash": "sha256-9BX8Fuez5YJlN7cdPO63InoyBy7dm3VlJkkmTt6fS1A=",
|
"narHash": "sha256-9o2OGQqu4xyLZP9K6kNe1pTHnyPz0Wr3raGYnr9AIgY=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "naersk",
|
"repo": "naersk",
|
||||||
"rev": "cddffb5aa211f50c4b8750adbec0bbbdfb26bb9f",
|
"rev": "6944160c19cb591eb85bbf9b2f2768a935623ed3",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -124,11 +124,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs_4": {
|
"nixpkgs_4": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1656401090,
|
"lastModified": 1665296151,
|
||||||
"narHash": "sha256-bUS2nfQsvTQW2z8SK7oEFSElbmoBahOPtbXPm0AL3I4=",
|
"narHash": "sha256-uOB0oxqxN9K7XGF1hcnY+PQnlQJ+3bP2vCn/+Ru/bbc=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "16de63fcc54e88b9a106a603038dd5dd2feb21eb",
|
"rev": "14ccaaedd95a488dd7ae142757884d8e125b3363",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -153,11 +153,11 @@
|
||||||
"nixpkgs": "nixpkgs_4"
|
"nixpkgs": "nixpkgs_4"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1657075953,
|
"lastModified": 1665457114,
|
||||||
"narHash": "sha256-xVgbASGI/wODPpUiMexxZau+XDVOZMBbFmw2KsZyFKA=",
|
"narHash": "sha256-Rtut6Q81u1pDS3Y44Syhy436IrESmvPCdGDtl47E4eo=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "339de634e6f19e420e81d5a6acaa39364a726999",
|
"rev": "5066af8707c4b598ad2dbfb2fdac73714bee3438",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
*/
|
*/
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
(rust-bin.stable.latest.default.override {extensions = ["rls"];})
|
(rust-bin.nightly.latest.default.override {extensions = ["rls"];})
|
||||||
udev
|
udev
|
||||||
pkgconfig
|
pkgconfig
|
||||||
mosquitto # For testing mqtt with mosquitto_pub
|
mosquitto # For testing mqtt with mosquitto_pub
|
||||||
|
|
43
src/lib.rs
43
src/lib.rs
|
@ -17,9 +17,15 @@ enum Command {
|
||||||
Monitor((usize, MonitorCommand)),
|
Monitor((usize, MonitorCommand)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MonitorBrightnessCmd {
|
||||||
|
Absolute(u8),
|
||||||
|
Relative(i8),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum MonitorCommand {
|
pub enum MonitorCommand {
|
||||||
Brightness(u8),
|
Brightness(MonitorBrightnessCmd),
|
||||||
Input(u8),
|
Input(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,17 +57,42 @@ fn run_i2c(idx: usize, mut dev: I2cDeviceDdc, command_channel: Receiver<MonitorC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
event!(Level::INFO, monitor_index = idx, ?cmd, "Sending DDC comand");
|
event!(Level::INFO, monitor_index = idx, ?cmd, "Sending DDC comand");
|
||||||
if let Err(e) = match cmd {
|
if let Err(e) = handle_monitor_cmd(&cmd, &mut dev) {
|
||||||
MonitorCommand::Brightness(b) => 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()),
|
|
||||||
} {
|
|
||||||
event!(Level::WARN, err = %e, "Error sending DDC command");
|
event!(Level::WARN, err = %e, "Error sending DDC command");
|
||||||
}
|
}
|
||||||
last_sent_command.insert(cmd.cmd_str(), Some(Instant::now()));
|
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<()> {
|
pub fn main() -> StdError<()> {
|
||||||
event!(Level::INFO, "Starting");
|
event!(Level::INFO, "Starting");
|
||||||
let displays = I2cDeviceEnumerator::new().unwrap().collect::<Vec<_>>();
|
let displays = I2cDeviceEnumerator::new().unwrap().collect::<Vec<_>>();
|
||||||
|
|
19
src/mqtt.rs
19
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::mqttbytes::v4::Packet;
|
||||||
use rumqttc::{Client, Event, MqttOptions, QoS};
|
use rumqttc::{Client, Event, MqttOptions, QoS};
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -8,6 +8,7 @@ use tracing::{event, Level};
|
||||||
|
|
||||||
pub fn run_mqtt(txes: &Vec<Sender<MonitorCommand>>) -> StdError<()> {
|
pub fn run_mqtt(txes: &Vec<Sender<MonitorCommand>>) -> StdError<()> {
|
||||||
let client_id = env::var("MQTT_CLIENT_ID").unwrap_or("ddcmqtt".into());
|
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_host = env::var("MQTT_HOST").unwrap_or("localhost".into());
|
||||||
let mqtt_port = env::var("MQTT_PORT")
|
let mqtt_port = env::var("MQTT_PORT")
|
||||||
.unwrap_or("1883".into())
|
.unwrap_or("1883".into())
|
||||||
|
@ -24,7 +25,7 @@ pub fn run_mqtt(txes: &Vec<Sender<MonitorCommand>>) -> StdError<()> {
|
||||||
mqttoptions.set_keep_alive(Duration::from_secs(5));
|
mqttoptions.set_keep_alive(Duration::from_secs(5));
|
||||||
|
|
||||||
let (mut client, mut connection) = Client::new(mqttoptions, 10);
|
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");
|
event!(Level::INFO, mqtt_host, mqtt_port, "Running MQTT client");
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ pub fn run_mqtt(txes: &Vec<Sender<MonitorCommand>>) -> StdError<()> {
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if topic[0] != "ddcmqtt" {
|
if topic[0] != topic_prefix {
|
||||||
event!(
|
event!(
|
||||||
Level::ERROR,
|
Level::ERROR,
|
||||||
?topic,
|
?topic,
|
||||||
|
@ -58,6 +59,18 @@ pub fn run_mqtt(txes: &Vec<Sender<MonitorCommand>>) -> StdError<()> {
|
||||||
&txes,
|
&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::<i8>()?)
|
||||||
|
} else {
|
||||||
|
MonitorBrightnessCmd::Absolute(brightness.parse::<u8>()?)
|
||||||
|
};
|
||||||
|
handle_cmd(
|
||||||
|
Command::Monitor((idx.into(), MonitorCommand::Brightness(cmd))),
|
||||||
|
&txes,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
event!(Level::ERROR, ?topic, "Unrecognised or invalid topic");
|
event!(Level::ERROR, ?topic, "Unrecognised or invalid topic");
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{handle_cmd, Command, MonitorCommand, StdError};
|
use crate::{handle_cmd, Command, MonitorBrightnessCmd, MonitorCommand, StdError};
|
||||||
use rosc;
|
use rosc;
|
||||||
use std::net::UdpSocket;
|
use std::net::UdpSocket;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
|
@ -36,9 +36,9 @@ fn osc_message_to_command(msg: rosc::OscMessage) -> StdError<Command> {
|
||||||
["monitor", idx, control] => {
|
["monitor", idx, control] => {
|
||||||
println!("Monitor {}, control {}, args {:?}", idx, control, msg.args);
|
println!("Monitor {}, control {}, args {:?}", idx, control, msg.args);
|
||||||
let command = match *control {
|
let command = match *control {
|
||||||
"brightness" => Some(MonitorCommand::Brightness(
|
"brightness" => Some(MonitorCommand::Brightness(MonitorBrightnessCmd::Absolute(
|
||||||
(msg.args[0].clone().float().unwrap() * 100.0) as u8,
|
(msg.args[0].clone().float().unwrap() * 100.0) as u8,
|
||||||
)),
|
))),
|
||||||
"input" => Some(MonitorCommand::Input(
|
"input" => Some(MonitorCommand::Input(
|
||||||
(msg.args[0].clone().int().unwrap()) as u8,
|
(msg.args[0].clone().int().unwrap()) as u8,
|
||||||
)),
|
)),
|
||||||
|
|
Loading…
Reference in New Issue