Fix kiosk fan control state updates

This commit is contained in:
Mitchell R 2026-05-13 03:47:34 +02:00
parent d018b34955
commit 54d4dfefa8
3 changed files with 24 additions and 11 deletions

View file

@ -30,6 +30,8 @@ Environment=XDG_SESSION_TYPE=wayland
Environment=XDG_SESSION_CLASS=user Environment=XDG_SESSION_CLASS=user
Environment=GST_DEBUG=1 Environment=GST_DEBUG=1
Environment=BETTERFRAME_SERVER=http://localhost Environment=BETTERFRAME_SERVER=http://localhost
# Let the unprivileged kiosk process control the Pi fan PWM sysfs files.
ExecStartPre=+/bin/sh -c 'for d in /sys/class/hwmon/hwmon*; do [ -e "$d/pwm1" ] || continue; chgrp bfkiosk "$d/pwm1" "$d/pwm1_enable" 2>/dev/null || true; chmod g+w "$d/pwm1" "$d/pwm1_enable" 2>/dev/null || true; done'
ExecStart=/usr/bin/cage -s -- /opt/betterframe/kiosk/betterframe-kiosk ExecStart=/usr/bin/cage -s -- /opt/betterframe/kiosk/betterframe-kiosk
Restart=always Restart=always
RestartSec=2 RestartSec=2

View file

@ -193,7 +193,12 @@ fn activate(app: &Application) {
ServerMsg::Wake => { ServerMsg::Wake => {
let _ = tx_for_reload.send(WorkerMsg::Wake); let _ = tx_for_reload.send(WorkerMsg::Wake);
} }
ServerMsg::Fan(pwm) => { hwmon::set_fan(pwm); } ServerMsg::Fan(pwm) => {
if !hwmon::set_fan(pwm) {
warn!("fan command failed");
}
send_heartbeat_now(&server_for_reload, &key_for_reload);
}
ServerMsg::SwitchLayout(id) => { ServerMsg::SwitchLayout(id) => {
let _ = tx_for_reload.send(WorkerMsg::SwitchLayout(id)); let _ = tx_for_reload.send(WorkerMsg::SwitchLayout(id));
} }
@ -205,9 +210,7 @@ fn activate(app: &Application) {
// immediately so admin "Hardware" panel populates without waiting a // immediately so admin "Hardware" panel populates without waiting a
// full minute after boot/pair. // full minute after boot/pair.
loop { loop {
let displays = query_displays(); send_heartbeat_now(&server, &key);
let hw = hwmon::read();
server::heartbeat(&server, &key, &displays, &hw);
std::thread::sleep(std::time::Duration::from_secs(60)); std::thread::sleep(std::time::Duration::from_secs(60));
} }
}); });
@ -262,6 +265,12 @@ fn mark_activity(display_id: u32) {
}); });
} }
fn send_heartbeat_now(server_url: &str, kiosk_key: &str) {
let displays = query_displays();
let hw = hwmon::read();
server::heartbeat(server_url, kiosk_key, &displays, &hw);
}
/// Install the once-per-second watchdog that enforces idle/sleep timeouts /// Install the once-per-second watchdog that enforces idle/sleep timeouts
/// per display. Safe to call multiple times — installs at most once. /// per display. Safe to call multiple times — installs at most once.
fn install_idle_watchdog() { fn install_idle_watchdog() {

View file

@ -58,15 +58,17 @@ pub fn run(server_url: &str, kiosk_key: &str, tx: Sender<ServerMsg>) {
} }
} else if text.contains("\"type\":\"fan\"") { } else if text.contains("\"type\":\"fan\"") {
info!("ws: fan received: {text}"); info!("ws: fan received: {text}");
let pwm: Option<u32> = if text.contains("\"mode\":\"auto\"") { let Ok(msg) = serde_json::from_str::<serde_json::Value>(&text) else {
warn!("ws: fan command was not valid JSON");
continue;
};
let pwm: Option<u32> = if msg.get("mode").and_then(|v| v.as_str()) == Some("auto") {
None None
} else if let Some(value) = msg.get("pwm").and_then(|v| v.as_u64()) {
Some(value.min(255) as u32)
} else { } else {
// Parse "pwm":N warn!("ws: fan command missing mode=auto or pwm");
let v = text.split("\"pwm\":").nth(1) continue;
.and_then(|s| s.split(|c: char| !c.is_ascii_digit()).next())
.and_then(|s| s.parse::<u32>().ok());
if v.is_none() { continue; }
v
}; };
let _ = tx.send(ServerMsg::Fan(pwm)); let _ = tx.send(ServerMsg::Fan(pwm));
} else { } else {