Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 103 additions & 13 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,78 @@ use simplelog::{
use std::env;
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
use std::process::Command;
use syslog::Facility;

mod config;
mod discrete_value;
mod kalman;
mod switch_monitor;

trait BrightnessDevice {
fn set(&self, value: u32) -> Result<(), ErrorCode>;
fn max(&self) -> u32;
}

pub struct SysfsBacklight {
path: String,
max: u32,
}

impl SysfsBacklight {
pub fn new(path: String, max: u32) -> Self {
Self { path, max }
}
}

impl BrightnessDevice for SysfsBacklight {
fn set(&self, value: u32) -> Result<(), ErrorCode> {
write_u32_to_file(&self.path, value)
.map_err(|_| ErrorCode::CannotSetBacklight)
}

fn max(&self) -> u32 {
self.max
}
}

pub struct DdcUtilDevice {
bus: u32,
max: u32,
}

impl DdcUtilDevice {
pub fn new(bus: u32) -> Self {
Self { bus, max: 100 }
}
}

impl BrightnessDevice for DdcUtilDevice {
fn set(&self, value: u32) -> Result<(), ErrorCode> {
let output = Command::new("ddcutil")
.args([
"setvcp",
"10",
&value.to_string(),
"--bus",
&self.bus.to_string(),
"--noverify",
])
.output()
.map_err(|_| ErrorCode::CannotSetBacklight)?;

if !output.status.success() {
return Err(ErrorCode::CannotSetBacklight);
}

Ok(())
}

fn max(&self) -> u32 {
self.max
}
}

#[derive(Debug)]
struct LightConvertor {
points: Vec<LightPoint>,
Expand Down Expand Up @@ -64,6 +129,10 @@ impl LightConvertor {
}
}
}

fn max_light(&self) -> f32 {
self.points.last().map(|p| p.light as f32).unwrap_or(1.0)
}
}

fn read_file_to_string(filename: &str) -> std::io::Result<String> {
Expand Down Expand Up @@ -102,7 +171,7 @@ fn write_u32_to_file(filename: &str, value: u32) -> std::io::Result<()> {
fn main_loop(
config: &Config,
light_convertor: &LightConvertor,
max_brightness: u32,
devices: Vec<Box<dyn BrightnessDevice>>,
mut switch_monitor: switch_monitor::SwitchMonitor,
illuminance_filename: &str,
) -> Result<(), ErrorCode> {
Expand All @@ -111,9 +180,11 @@ fn main_loop(
config.kalman_r(),
config.kalman_covariance(),
);
//let max_brightness = light_convertor.max_light();
let max_brightness = 255.0;
let mut stepped_brightness = discrete_value::DiscreteValue::new(
config.min_backlight(),
max_brightness,
max_brightness as u32,
config.light_steps(),
config.step_barrier(),
);
Expand All @@ -130,29 +201,33 @@ fn main_loop(
"BRIGHTNESS CHANGED: ambient light raw={} kalman={:.1} -> level={:.2} -> hardware brightness={}",
illuminance, illuminance_k, brightness, new
);
set_brightness(config, new);
let normalized = new as f32 / max_brightness;
set_brightness(&devices, normalized);
}
}
_ => error!("Cannot read illuminance"),
}
if try_process_switch(&mut switch_monitor, config, max_brightness) {
if try_process_switch(&mut switch_monitor, &devices) {
stepped_brightness.update(config.light_steps() as f32);
}
std::thread::sleep(std::time::Duration::from_millis(300));
}
}

fn set_brightness(config: &Config, value: u32) {
if let Err(e) = write_u32_to_file(config.backlight_filename(), value) {
error!("Cannot set brightness: {}", e);
fn set_brightness(devices: &[Box<dyn BrightnessDevice>], normalized: f32) {
for device in devices {
let value = (normalized * device.max() as f32) as u32;
if let Err(e) = device.set(value) {
error!("Cannot set brightness: {:?}", e);
}
}
}

fn try_process_switch(
switch_monitor: &mut switch_monitor::SwitchMonitor,
config: &Config,
max_brightness: u32,
devices: &[Box<dyn BrightnessDevice>],
) -> bool {
let mut timeout = config.check_period_in_seconds();
let mut timeout = 1; //config.check_period_in_seconds();
loop {
match switch_monitor.wait_state_update(timeout) {
(switch_monitor::State::Off, changed) => {
Expand All @@ -171,7 +246,9 @@ fn try_process_switch(
if changed {
info!("maximum by event");
}
set_brightness(config, max_brightness);
for device in devices {
let _ = device.set(device.max());
}
timeout = 3600;
}
}
Expand All @@ -189,6 +266,7 @@ pub struct LightPoint {
light: u32,
}

#[derive(Debug)]
pub enum ErrorCode {
InvalidArgs,
ConfigReadError,
Expand Down Expand Up @@ -296,9 +374,21 @@ fn run() -> Result<(), ErrorCode> {

let light_points = config.light_points()?;
let light_convertor = LightConvertor::new(light_points);
let max_brightness = read_file_to_u32(config.max_backlight_filename())

let mut devices: Vec<Box<dyn BrightnessDevice>> = Vec::new();

let sysfs_max = read_file_to_u32(config.max_backlight_filename())
.ok_or(ErrorCode::ReadMaxBrightnessError)?;

devices.push(Box::new(SysfsBacklight::new(
config.backlight_filename().to_string(),
sysfs_max,
)));

// Example DDC monitors (hardcoded for now)
devices.push(Box::new(DdcUtilDevice::new(16)));
devices.push(Box::new(DdcUtilDevice::new(18)));

let illuminance_filename = match glob::glob(config.illuminance_filename()) {
Err(e) => {
error!("Cannot glob({}): {}", config.illuminance_filename(), e);
Expand Down Expand Up @@ -344,7 +434,7 @@ fn run() -> Result<(), ErrorCode> {
main_loop(
&config,
&light_convertor,
max_brightness,
devices,
switch_monitor,
&illuminance_filename,
)
Expand Down