remindwall / scripts / kiosk / probe-display-power.sh
Back to remindwall — Current RemindWall
#!/bin/bash
# T1.2 hardware probe v2 - run ON THE KIOSK with the external monitor attached.
# Decides which mechanism ddcd's /display endpoint uses.
#
# v2 fixes over v1 (which produced a misleading RESULTS block):
# - Test A is SKIPPED (not falsely passed) when m1ddc lacks `set power`
# (brew 1.2.0 does - it's a HEAD-only feature)
# - read-stability check: 5 luminance reads, because this LG has returned
# corrupted values (-51 / max 62) in the field
# - refuses to run while RemindWall is up (its 30s DDC polls interleave with
# the probe's transactions and corrupt both)
# - timestamped DDC timeline through the sleep transition instead of one
# 4s-later probe
# - your eyeball observations are prompted and CAPTURED in the output
#
# WILL BLANK THE MONITOR during test B. Total runtime ~3 minutes.
set -u
M1DDC="${M1DDC:-/opt/homebrew/bin/m1ddc}"
say() { printf '\n=== %s\n' "$*"; }
pause(){ read -r -p "--- Press Enter to continue..." _; }
ask() {
# ask <var> <question> - records a y/n observation into the transcript
local var=$1; shift
local answer
read -r -p "OBSERVATION: $* [y/n] " answer
printf 'RECORDED: %s = %s\n' "$var" "$answer"
eval "$var=\$answer"
}
ddc_alive() { "$M1DDC" get luminance >/dev/null 2>&1; }
# Prints a "t=<sec> ddc=<ok|dead> value=<n>" line each second for $1 seconds.
ddc_timeline() {
local secs=$1 t v
for t in $(seq 0 "$secs"); do
if v=$("$M1DDC" get luminance 2>/dev/null); then
echo " t=${t}s ddc=ok value=${v}"
else
echo " t=${t}s ddc=dead"
fi
sleep 1
done
}
say "Preflight"
[ -x "$M1DDC" ] || { echo "m1ddc not found at $M1DDC"; exit 66; }
if pgrep -x RemindWall >/dev/null 2>&1; then
echo "RemindWall is RUNNING - its DDC polls will corrupt this probe."
echo "Quit it first, then re-run. (pgrep -x RemindWall to verify)"
exit 65
fi
echo "RemindWall: not running (good)"
echo "session: SSH=${SSH_CONNECTION:+yes}${SSH_CONNECTION:-no}"
echo "displays:"; "$M1DDC" display list 2>&1
say "Read stability - 5 luminance reads (watch for corruption)"
for i in $(seq 1 5); do
echo " read $i: current=$("$M1DDC" get luminance 2>&1) max=$("$M1DDC" max luminance 2>&1)"
done
echo "Sane = current 0-100ish, max ~100, CONSISTENT across reads."
echo "Negative or wildly varying values = corrupted DDC reads on this panel."
say "TEST A - DDC VCP D6 standby (pure-DDC route)"
if "$M1DDC" set power 4 >/dev/null 2>&1; then
echo "set power accepted. WATCH: did the monitor power down (LED amber)?"
sleep 3
if ddc_alive; then A_ACK=yes; else A_ACK=no; fi
echo "DDC ACK in standby: $A_ACK"
"$M1DDC" set power 1 >/dev/null 2>&1 && A_WAKE_SENT=yes || A_WAKE_SENT=no
echo "wake command accepted: $A_WAKE_SENT"
sleep 3
ask A_VISIBLE_OFF "did the monitor VISIBLY power down during test A?"
ask A_VISIBLE_ON "did it come back on after the wake command?"
else
echo "SKIPPED: this m1ddc build has no 'set power' (brew 1.2.0 doesn't;"
echo "it's a HEAD-only feature). Route A unavailable as installed."
A_ACK=skipped; A_VISIBLE_OFF=skipped; A_VISIBLE_ON=skipped
fi
say "TEST B - OS display sleep (pmset) + user-activity wake"
echo "Sequence: displaysleepnow -> 15s DDC timeline -> caffeinate wake ->"
echo "10s DDC timeline. WATCH the monitor the whole time."
pause
pmset displaysleepnow; echo "sleep sent (exit $?)"
echo "DDC timeline during sleep transition:"
ddc_timeline 15
echo "Waking via caffeinate -u"
caffeinate -u -t 2
echo "DDC timeline after wake:"
ddc_timeline 10
ask B_VISIBLE_OFF "did the panel VISIBLY go dark / into standby (LED change)?"
ask B_VISIBLE_ON "did it VISIBLY wake after caffeinate?"
say "RESULTS (paste this whole transcript back)"
echo "A: ack-in-standby=$A_ACK visible-off=$A_VISIBLE_OFF visible-on=$A_VISIBLE_ON"
echo "B: visible-off=${B_VISIBLE_OFF:-?} visible-on=${B_VISIBLE_ON:-?}"
echo "Decision rule: B visible-off+on = use pmset/user-activity. The DDC"
echo "timeline tells us how long ddcd must wait after wake before touching"
echo "luminance."