ddcmqtt/src/lib.rs

90 lines
2.5 KiB
Rust
Raw Normal View History

2022-04-07 16:23:53 +01:00
use ddc::Ddc;
2022-05-11 01:15:48 +01:00
use ddc_i2c::{I2cDeviceDdc, I2cDeviceEnumerator};
use std::collections::HashMap;
2022-07-07 01:17:14 +01:00
use std::env;
2022-05-11 01:15:48 +01:00
use std::error::Error;
2022-04-07 16:23:53 +01:00
use std::sync::mpsc::{channel, Receiver, Sender};
2022-05-11 01:15:48 +01:00
use std::thread;
2022-04-07 16:23:53 +01:00
use std::time::{Duration, Instant};
2022-07-07 01:17:14 +01:00
use tracing::{event, Level};
2022-04-07 16:23:53 +01:00
mod mqtt;
mod osc;
2022-04-07 16:23:53 +01:00
pub type StdError<T> = Result<T, Box<dyn Error>>;
enum Command {
Monitor((usize, MonitorCommand)),
}
#[derive(Debug)]
pub enum MonitorCommand {
2022-04-07 16:23:53 +01:00
Brightness(u8),
2022-05-11 01:15:48 +01:00
Input(u8),
2022-04-07 16:23:53 +01:00
}
impl MonitorCommand {
fn cmd_str(&self) -> &'static str {
match self {
Self::Brightness(_) => "brightness",
Self::Input(_) => "input",
}
}
fn vcp(&self) -> u8 {
match self {
Self::Brightness(_) => 0x10,
Self::Input(_) => 0x60,
}
}
}
// Run an i2c command handler for a specific i2c device, rate limiting each type of command
fn run_i2c(idx: usize, mut dev: I2cDeviceDdc, command_channel: Receiver<MonitorCommand>) {
2022-05-11 01:15:48 +01:00
let mut last_sent_command: HashMap<&str, Option<Instant>> = HashMap::new();
2022-04-07 16:23:53 +01:00
loop {
let cmd = command_channel.recv().unwrap();
if let Some(last) = last_sent_command.entry(cmd.cmd_str()).or_insert(None) {
if (*last + Duration::from_millis(100)) > Instant::now() {
// rate limit
continue;
}
}
event!(Level::INFO, monitor_index = idx, ?cmd, "Sending DDC comand");
2022-04-07 16:23:53 +01:00
match cmd {
2022-07-07 01:17:14 +01:00
MonitorCommand::Brightness(b) => dev.set_vcp_feature(cmd.vcp(), b.into()).unwrap(),
2022-04-07 16:23:53 +01:00
// Hack - add 15 to align with DELL monitors
2022-07-07 01:17:14 +01:00
MonitorCommand::Input(i) => dev.set_vcp_feature(cmd.vcp(), (i + 15).into()).unwrap(),
2022-04-07 16:23:53 +01:00
}
last_sent_command.insert(cmd.cmd_str(), Some(Instant::now()));
}
}
pub fn main() -> StdError<()> {
event!(Level::INFO, "Starting");
2022-04-07 16:23:53 +01:00
let displays = I2cDeviceEnumerator::new().unwrap().collect::<Vec<_>>();
event!(Level::INFO, "Enumerated {} displays", displays.len());
2022-05-11 01:15:48 +01:00
let txes: Vec<_> = displays
.into_iter()
.enumerate()
.map(|(idx, d)| {
2022-05-11 01:15:48 +01:00
let (tx, rx) = channel();
thread::spawn(move || run_i2c(idx, d, rx));
2022-05-11 01:15:48 +01:00
tx
})
.collect();
if let Ok(_) = env::var("OSC_MODE") {
osc::run_osc(&txes)?;
} else {
mqtt::run_mqtt(&txes)?;
2022-07-07 01:17:14 +01:00
}
Ok(())
}
2022-04-07 16:23:53 +01:00
fn handle_cmd(cmd: Command, txes: &Vec<Sender<MonitorCommand>>) -> StdError<()> {
match cmd {
Command::Monitor((idx, c)) => txes.get(idx).ok_or("Bad monitor index")?.send(c)?,
};
Ok(())
}