feat: vmcec.sh tests HDMI-CEC against a fake TV; vmwake.sh wakes the VM over QMP

run.sh opens a QMP socket; the skill documents the CEC test and its gotchas.
This commit is contained in:
theupriser committed 2026-09-25 11:14:00 +02:00
1 parent e04db1ec5f
commit 04204f1b9d
4 files changed
+277 -21

No files matched your search

+35 -21
View File
@@ -274,33 +274,47 @@ the environment through). fwupd correctly refuses the firmware in the VM
A successful dry run counts as staged, so `[m]`/`[r]` and the restart question A successful dry run counts as staged, so `[m]`/`[r]` and the restart question
on `q` show up too; in dry-run mode "restart" only prints. on `q` show up too; in dry-run mode "restart" only prints.
## Fake HDMI-CEC (vivid) ## Fake HDMI-CEC (vivid): `scripts/vmcec.sh`
The VM has no CEC hardware; the kernel's `vivid` test driver emulates it: a The VM has no CEC hardware; the kernel's `vivid` test driver emulates it: a
virtual HDMI input (`/dev/cec0`, plays the TV) and output (`/dev/cec1`, the virtual HDMI input (`/dev/cec0`, plays the TV via `cec-ctl`/`cec-follower`)
PC). Needs the HDMI-CEC item on (cecd installed); `cec-ctl` is in v4l-utils. and output (`/dev/cec1`, the PC, where cecd runs). Needs the HDMI-CEC item on;
the Steam-settings checks need Steam Machine support (steamos-manager).
```bash ```bash
ssh -p 2222 -o BatchMode=yes theupriser@localhost bash -s << 'EOF' scripts/vmcec.sh # all checks + a real suspend/resume (~5 min)
export XDG_RUNTIME_DIR=/run/user/1000 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus scripts/vmcec.sh --no-sleep # without suspending the VM
sudo modprobe vivid n_devs=1 num_inputs=1 input_types=0x3 num_outputs=1 output_types=0x1 scripts/vmcec.sh --sleep-only # only the suspend/resume check
cec-ctl -d /dev/cec0 --tv --phys-addr 0.0.0.0 --osd-name "Sony TV" >/dev/null scripts/vmwake.sh # wake a sleeping VM (QMP socket from run.sh)
# plug the output into the input: the PC side gets 1.0.0.0
v4l2-ctl -d /dev/video0 --set-ctrl hdmi_000_0_is_connected_to=2
# the cecd service (-e) grabs every CEC device, the TV too: run it on the output only
systemctl --user stop cecd; systemd-run --user -q --unit=cecd-test /usr/bin/cecd -d /dev/cec1
sleep 4; cec-ctl -d /dev/cec1 | grep -E "Physical Address|Logical Address " # 1.0.0.0, e.g. 8
EOF
``` ```
Then, with `T="cec-ctl -d /dev/cec0 -t 8"` (the PC's logical address): It prints PASS/FAIL/SKIP for: identity (address 1.0.0.0, name "Steam
`$T --give-osd-name` (the Steam Machine answers "Steam Machine"), remote keys Machine", Valve vendor ID), every TV remote key and the key it becomes, the
`$T --user-control-pressed ui-cmd=down` + `$T --user-control-released` Steam settings over D-Bus (remote control, WakeTv, SuspendTv, SuspendDevice
(arrive as KEY_DOWN on the `cecd vivid-000-vid-out0` input device; there's no and cecd's config), PC to TV (make active, wake, volume, audio status,
evtest, read `/dev/input/eventN` with a small python struct reader), and PC to standby), TV standby putting the PC to sleep (blocked by an inhibitor), and
TV: `sudo cec-ctl -d /dev/cec0 -M` (monitor needs root) while running sleep/wake turning the TV off and on.
`cectool -d /dev/cec1 set-active|volume-up|standby`. Afterwards
`systemctl --user stop cecd-test; sudo modprobe -r vivid; systemctl --user start cecd`. Gotchas it handles, learned the hard way:
- The cecd service (`-e`) grabs every CEC device, the fake TV too: the test
uses a temporary drop-in (`~/.config/systemd/user/cecd.service.d/vmcec.conf`)
with `-d /dev/cec1`, removed at the end.
- The TV's power button (KEY_POWER) and TV standby with SuspendDevice on
really suspend the VM (Steam Machine support makes the power button sleep):
block with `sudo systemd-inhibit --what=sleep:handle-power-key` (as user it's
refused). If the VM sleeps anyway: `scripts/vmwake.sh` (the QEMU window's
keyboard doesn't wake it; a VM started before run.sh had `-qmp` needs a hard restart).
- `rtcwake` skips logind, so cecd never sees the sleep: suspend with
`systemctl suspend` and wake over QMP.
- `steamosctl get/set-hdmi-cec-suspend-tv|suspend-device` don't work
(steamos-manager 26.4.1); Steam uses the D-Bus properties of
`com.steampowered.SteamOSManager1.HdmiCec2`, and so does the test.
- The kernel answers "give OSD name" itself, even without cecd: it's no proof
cecd runs. cecd logs "Putting TV in standby" only at `RUST_LOG=debug`.
- CecDevice1 methods take arguments (`VolumeUp y 0`, Daemon1 `Standby b true`).
- After the test (or a hard restart) CachyOS may put autologin back on
gamescope: set the session to plasma and restart the display manager.
The menu item is 7 (8 = Steam Machine support, 9 = kernel pin, 10 = BIOS with `--fremont`). The menu item is 7 (8 = Steam Machine support, 9 = kernel pin, 10 = BIOS with `--fremont`).
## Steamify shortcut ## Steamify shortcut
+1
View File
@@ -49,4 +49,5 @@ exec qemu-system-x86_64 \
-nic user,model=virtio-net-pci,hostfwd=tcp::2222-:22 \ -nic user,model=virtio-net-pci,hostfwd=tcp::2222-:22 \
-virtfs local,path="$repo",mount_tag=repo,security_model=mapped-xattr \ -virtfs local,path="$repo",mount_tag=repo,security_model=mapped-xattr \
-virtfs local,path="$PWD/share",mount_tag=vmtools,security_model=mapped-xattr,readonly=on \ -virtfs local,path="$PWD/share",mount_tag=vmtools,security_model=mapped-xattr,readonly=on \
-qmp unix:"$PWD/qmp.sock",server=on,wait=off \
"${smbios[@]}" "${cdrom[@]}" "${smbios[@]}" "${cdrom[@]}"
+230
View File
@@ -0,0 +1,230 @@
#!/bin/bash
# Test HDMI-CEC in the guest against a fake TV: the kernel's vivid driver
# gives a virtual HDMI input (/dev/cec0, plays the TV, driven with cec-ctl)
# and output (/dev/cec1, the PC, where cecd runs). Needs the wizard's HDMI-CEC
# item on (cecd, cec-ctl from v4l-utils); steamos-manager (Steam Machine
# support) for the Steam settings tests. Prints PASS/FAIL/SKIP per check.
#
# scripts/vmcec.sh everything, including a real suspend/resume
# (needs the QMP socket from run.sh to wake up)
# scripts/vmcec.sh --no-sleep everything except suspending the VM
# scripts/vmcec.sh --sleep-only only the real sleep/wake check (after a full run)
#
# Afterwards the normal cecd service runs again; vivid stays loaded.
# Env: VM_USER (theupriser), VM_PORT (2222), VM_HOST (localhost).
set -euo pipefail
. "$(dirname "$0")/common.sh"
SLEEP_TEST=true SLEEP_ONLY=false
[[ "${1:-}" == --no-sleep ]] && SLEEP_TEST=false
[[ "${1:-}" == --sleep-only ]] && SLEEP_ONLY=true
# --- in the guest: helpers, setup and all checks except the real sleep ---
$SLEEP_ONLY || vm_ssh "SLEEP_TEST=$SLEEP_TEST bash -s" << 'REMOTE'
export XDG_RUNTIME_DIR=/run/user/1000 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
W=~/.cache/vmcec; mkdir -p "$W"
pass=0 fail=0 skip=0
PASS() { echo " PASS $*"; pass=$((pass + 1)); }
FAIL() { echo " FAIL $*"; fail=$((fail + 1)); }
SKIP() { echo " SKIP $*"; skip=$((skip + 1)); }
check() { local d="$1"; shift; if "$@" >/dev/null 2>&1; then PASS "$d"; else FAIL "$d"; fi; }
TV() { cec-ctl -d /dev/cec0 "$@"; }
BUS=com.steampowered.CecDaemon1
has_smgr() { steamosctl get-hdmi-cec-state >/dev/null 2>&1; }
# Key reader for the cecd input device: prints key names while it runs.
cat > "$W/keys.py" << 'PY'
import re, select, struct, sys, time
names = {}
for line in open('/usr/include/linux/input-event-codes.h'):
m = re.match(r'#define\s+(KEY_\w+|BTN_\w+)\s+(0x[0-9a-fA-F]+|\d+)\b', line)
if m: names.setdefault(int(m.group(2), 0), m.group(1))
f = open(sys.argv[1], 'rb'); end = time.time() + float(sys.argv[2])
while time.time() < end:
if select.select([f], [], [], 0.1)[0]:
_, _, t, c, v = struct.unpack('llHHi', f.read(24))
if t == 1 and v == 1: print(names.get(c, c), flush=True)
PY
# Monitor the TV side's CEC bus into $W/mon.txt (needs root).
mon_start() { sudo pkill -f 'cec-ctl -d /dev/cec0 -M' 2>/dev/null; : > "$W/mon.txt"
sudo sh -c "exec cec-ctl -d /dev/cec0 -M > $W/mon.txt 2>&1" & sleep 1; }
mon_stop() { sleep 1; sudo pkill -f 'cec-ctl -d /dev/cec0 -M' 2>/dev/null; wait 2>/dev/null; }
seen() { grep -q "Received from.*): $1" "$W/mon.txt"; }
echo "== Setup: fake TV (vivid)"
lsmod | grep -q '^vivid' || sudo modprobe vivid n_devs=1 num_inputs=1 input_types=0x3 num_outputs=1 output_types=0x1
sleep 2
TV --tv --osd-name "Fake TV" >/dev/null 2>&1
v4l2-ctl -d /dev/video0 --set-ctrl hdmi_000_0_is_connected_to=2
# The cecd service (-e) takes every CEC device, the TV too: a temporary
# drop-in limits it to the PC side, so steamos-manager still controls it.
mkdir -p ~/.config/systemd/user/cecd.service.d
printf '[Service]\nExecStart=\nExecStart=/usr/bin/cecd -d /dev/cec1\n' > ~/.config/systemd/user/cecd.service.d/vmcec.conf
systemctl --user daemon-reload; systemctl --user restart cecd.service
sleep 4
systemctl --user is-active -q steamos-manager.service && { systemctl --user restart steamos-manager.service; sleep 3; }
LA=$(cec-ctl -d /dev/cec1 | awk '/Logical Address /{print $4; exit}')
PA=$(cec-ctl -d /dev/cec1 | awk '/Physical Address/{print $4; exit}')
EV=$(grep -A4 'N: Name="cecd' /proc/bus/input/devices | grep -oE 'event[0-9]+' | head -1)
echo " PC side: physical $PA, logical $LA, input /dev/input/${EV:-none}"
echo "== 1. Identity (TV asks the PC)"
[[ "$PA" == 1.0.0.0 ]] && PASS "physical address 1.0.0.0" || FAIL "physical address is $PA"
[[ "$LA" =~ ^(4|8|9|11)$ ]] && PASS "logical address $LA (playback device)" || FAIL "logical address is '$LA'"
name=$(TV -t "$LA" --give-osd-name 2>&1 | awk -F': ' '/name:/{print $2; exit}')
[[ "$name" == "Steam Machine" ]] && PASS "OSD name 'Steam Machine'" || FAIL "OSD name is '$name' (steamos-manager's config not applied?)"
vendor=$(TV -t "$LA" --give-device-vendor-id 2>&1 | awk '/vendor-id:/{print $2; exit}')
[[ "$vendor" == 0xe0319e* ]] && PASS "vendor ID Valve (0xe0319e)" || FAIL "vendor ID is '$vendor'"
check "answers a poll" TV -t "$LA" --poll
echo "== 2. TV remote -> PC (key presses)"
press() { TV -t "$LA" --user-control-pressed ui-cmd="$1" >/dev/null; sleep 0.15; TV -t "$LA" --user-control-released >/dev/null; sleep 0.25; }
keys_for() { # keys_for <seconds> <ui-cmd>...: keys arriving while pressing
EV=$(grep -A4 'N: Name="cecd' /proc/bus/input/devices | grep -oE 'event[0-9]+' | head -1)
: > "$W/keys.txt"; local r=
[[ -n "$EV" ]] && { sudo timeout "$1" python3 "$W/keys.py" "/dev/input/$EV" "$1" > "$W/keys.txt" & r=$!; }
sleep 1; shift
local k; for k in "$@"; do press "$k"; done; [[ -n "$r" ]] && wait "$r" 2>/dev/null; cat "$W/keys.txt"; }
if [[ -z "$EV" ]]; then FAIL "no cecd input device"
else
for k in up down left right select exit back root-menu setup-menu contents-menu \
play pause stop rewind fast-forward forward backward \
volume-up volume-down mute number-entry-mode number-0-or-number-10 number-5 \
channel-up channel-down display-information electronic-program-guide \
f1-blue f2-red f3-green f4-yellow; do
got=$(keys_for 2 "$k" | tr '\n' ' ')
if [[ -n "$got" ]]; then PASS "$(printf '%-28s -> %s' "$k" "$got")"
else SKIP "$(printf '%-28s -> nothing (not mapped by cecd)' "$k")"; fi
done
# The TV's power button arrives as KEY_POWER, and Steam Machine support
# makes the power button sleep: with sleep blocked, check the key only.
sudo systemd-inhibit --what=sleep:handle-power-key --mode=block --who=vmcec --why=test sleep 6 & inh=$!; sleep 1
got=$(keys_for 2 power-toggle-function | tr '\n' ' ')
wait $inh 2>/dev/null
[[ "$got" == *KEY_POWER* ]] && PASS "$(printf '%-28s -> %s' power-toggle-function "$got") (sleeps the PC)" ||
FAIL "power-toggle-function -> '$got'"
fi
echo "== 3. Steam settings (steamos-manager, over D-Bus like Steam)"
# steamosctl's suspend-tv/suspend-device getters and setters don't work
# (steamos-manager 26.4.1); Steam uses these D-Bus properties.
SM=com.steampowered.SteamOSManager1; SMP=/com/steampowered/SteamOSManager1; CEC2=$SM.HdmiCec2
prop() { busctl --user get-property $SM $SMP $CEC2 "$1" 2>/dev/null | awk '{print $2}'; }
setp() { busctl --user set-property $SM $SMP $CEC2 "$1" b "$2" 2>/dev/null; sleep 2; }
cfg() { grep -h "^$1 *=" ~/.config/cecd/config.d/*.toml 2>/dev/null | tail -1 | awk -F'= *' '{print $2}'; }
if ! has_smgr; then SKIP "steamos-manager has no HDMI-CEC interface (no Steam Machine support?)"
else
echo " now: $(steamosctl get-hdmi-cec-state 2>&1); EnableControl=$(prop EnableControl) WakeTv=$(prop WakeTv) SuspendTv=$(prop SuspendTv) SuspendDevice=$(prop SuspendDevice) WakeDeviceSupported=$(prop WakeDeviceSupported)"
setp EnableControl false
got=$(keys_for 2 down | tr '\n' ' ')
[[ -z "$got" && "$(cfg uinput)" == false ]] && PASS "remote control off: keys are ignored (uinput = false)" || FAIL "remote control off: keys '$got', uinput = $(cfg uinput)"
setp EnableControl true
got=$(keys_for 2 down | tr '\n' ' ')
[[ -n "$got" ]] && PASS "remote control on again: $got" || FAIL "remote control on, but no keys"
for pr in WakeTv:wake_tv SuspendTv:suspend_tv SuspendDevice:allow_standby; do
for v in false true; do
setp "${pr%%:*}" $v
[[ "$(prop "${pr%%:*}")" == "$v" && "$(cfg "${pr#*:}")" == "$v" ]] &&
PASS "${pr%%:*} $v (cecd config ${pr#*:} = $v)" || FAIL "${pr%%:*} $v: property $(prop "${pr%%:*}"), config ${pr#*:} = $(cfg "${pr#*:}")"
done
done
[[ "$(prop WakeDeviceSupported)" == true ]] && PASS "waking the PC with the TV is supported" ||
SKIP "waking the PC with the TV: not supported here (WakeDeviceSupported = false)"
steamosctl set-hdmi-cec-state control-and-wake >/dev/null 2>&1; sleep 3
fi
systemctl --user is-active -q cecd.service && PASS "cecd still running" || FAIL "cecd stopped"
echo "== 4. PC -> TV"
# A plain TV (no soundbar): volume goes to the TV itself (address 0).
TV --tv --osd-name "Fake TV" >/dev/null 2>&1; sleep 2
echo " fake TV: $(TV | awk '/Logical Address /{printf "%s%s ", $4, $5}')"
DEV=$(busctl --user tree $BUS 2>/dev/null | grep -oE '/com/steampowered/CecDaemon1/Devices/Cec[0-9]+' | head -1)
mon_start
if has_smgr; then steamosctl hdmi-cec-make-active >/dev/null 2>&1; sleep 1; else busctl --user call $BUS "$DEV" $BUS.CecDevice1 SetActiveSource >/dev/null 2>&1; sleep 1; fi
busctl --user call $BUS /com/steampowered/CecDaemon1/Daemon $BUS.Daemon1 Wake >/dev/null 2>&1; sleep 1
mon_stop
{ seen ACTIVE_SOURCE || seen IMAGE_VIEW_ON || seen TEXT_VIEW_ON; } && PASS "make active: TV switches to the PC ($(grep -oE '\): [A-Z_]+' "$W/mon.txt" | sort -u | tr -d '):' | tr '\n' ' '))" || FAIL "make active: TV got nothing"
seen IMAGE_VIEW_ON || seen TEXT_VIEW_ON && PASS "wake: TV is turned on (IMAGE_VIEW_ON)" || FAIL "wake: no IMAGE_VIEW_ON"
mon_start
for m in VolumeUp VolumeDown Mute; do busctl --user call $BUS "$DEV" $BUS.CecDevice1 $m y 0 >/dev/null 2>&1; sleep 0.5; done
echo " audio status: $(busctl --user --timeout=3 call $BUS "$DEV" $BUS.CecDevice1 GetAudioStatus y 0 2>&1)"; sleep 1
mon_stop
n=$(grep -cE 'USER_CONTROL_PRESSED' "$W/mon.txt"); ops=$(grep -A1 USER_CONTROL_PRESSED "$W/mon.txt" | grep -oiE 'volume-up|volume-down|mute' | sort -u | tr '\n' ' ')
[[ $n -ge 3 ]] && PASS "volume up/down/mute sent to the TV ($ops)" || FAIL "volume: $n key presses reached the TV ($ops)"
seen GIVE_AUDIO_STATUS && PASS "asks the TV's audio status" || FAIL "no GIVE_AUDIO_STATUS"
mon_start
busctl --user call $BUS /com/steampowered/CecDaemon1/Daemon $BUS.Daemon1 Standby b true >/dev/null 2>&1; sleep 1
mon_stop
seen STANDBY && PASS "standby: TV is turned off (STANDBY)" || FAIL "standby: no STANDBY"
systemctl --user is-active -q cec-audio-control.socket && PASS "cec-audio-control socket listening" || FAIL "cec-audio-control socket not active"
echo "== 5. TV off -> PC to sleep (sleep blocked, so the VM stays up)"
if ! has_smgr; then SKIP "needs steamos-manager"
else
TV --tv >/dev/null 2>&1; sleep 2
for want in true false; do
setp SuspendDevice $want; sleep 1
since=$(date '+%Y-%m-%d %H:%M:%S')
sudo systemd-inhibit --what=sleep --mode=block --who=vmcec --why=test sleep 7 & inh=$!; sleep 1
TV -t 15 --standby >/dev/null 2>&1; sleep 4
wait $inh 2>/dev/null
tried=$(journalctl --user -u cecd --since "$since" --no-pager | grep -c 'Failed to standby')
if [[ $want == true ]]; then
[[ $tried -gt 0 ]] && PASS "SuspendDevice on: TV standby makes the PC go to sleep (blocked here)" || FAIL "SuspendDevice on, but no sleep attempt"
else
[[ $tried -eq 0 ]] && PASS "SuspendDevice off: TV standby doesn't touch the PC" || FAIL "SuspendDevice off, but the PC tried to sleep"
fi
done
setp SuspendDevice true
fi
if ! $SLEEP_TEST; then rm -f ~/.config/systemd/user/cecd.service.d/vmcec.conf; systemctl --user daemon-reload; systemctl --user restart cecd.service; fi
echo "== Result: $pass passed, $fail failed, $skip skipped (before the sleep test)"
echo "$LA" > "$W/la"
REMOTE
$SLEEP_TEST || { echo "(sleep test skipped)"; exit 0; }
# --- real suspend/resume: the TV should go off, and on again at wake ---
# Through logind (systemctl suspend), like Steam and the power button: cecd
# acts on logind's PrepareForSleep, which rtcwake skips. cec-follower plays a
# TV that answers power status; QMP (vmwake.sh) wakes the VM.
echo "== 6. Real sleep and wake (systemctl suspend, woken over QMP)"
vm_ssh bash -s << 'REMOTE'
export XDG_RUNTIME_DIR=/run/user/1000 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
W=~/.cache/vmcec
# cecd on the PC side only, with debug logging (it logs what it does at sleep).
mkdir -p ~/.config/systemd/user/cecd.service.d
printf '[Service]\nExecStart=\nExecStart=/usr/bin/cecd -d /dev/cec1\nEnvironment=RUST_LOG=debug\n' > ~/.config/systemd/user/cecd.service.d/vmcec.conf
systemctl --user daemon-reload; systemctl --user restart cecd.service; sleep 4
SM=com.steampowered.SteamOSManager1; SMP=/com/steampowered/SteamOSManager1
busctl --user set-property $SM $SMP $SM.HdmiCec2 SuspendTv b true 2>/dev/null
busctl --user set-property $SM $SMP $SM.HdmiCec2 WakeTv b true 2>/dev/null
sudo systemctl stop vmcec-tv vmcec-mon 2>/dev/null; sudo systemctl reset-failed vmcec-tv vmcec-mon vmcec-sleep 2>/dev/null
cec-ctl -d /dev/cec0 --tv >/dev/null; sleep 1
sudo systemd-run -q --unit=vmcec-tv sh -c 'exec cec-follower -d /dev/cec0 > /dev/null 2>&1'; sleep 2
steamosctl hdmi-cec-make-active >/dev/null 2>&1; sleep 3
sudo systemd-run -q --unit=vmcec-mon sh -c "exec cec-ctl -d /dev/cec0 -M > $W/sleep-mon.txt 2>&1"; sleep 1
sudo systemd-run -q --unit=vmcec-sleep sh -c 'sleep 2; systemctl suspend'
echo " suspending..."
REMOTE
sleep 15
"$(dirname "$0")/vmwake.sh" || true
until vm_ssh -o ConnectTimeout=4 true 2>/dev/null; do sleep 3; done
sleep 8
vm_ssh bash -s << 'REMOTE'
export XDG_RUNTIME_DIR=/run/user/1000 DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
W=~/.cache/vmcec
sudo systemctl stop vmcec-mon vmcec-tv 2>/dev/null
t=$(journalctl -b --no-pager -o short-iso | grep "PM: suspend entry" | tail -1 | awk '{print $1}')
[[ -n "$t" ]] && echo " PASS the VM really slept and woke (suspend at $t)" || echo " FAIL no suspend in the kernel log"
log=$(journalctl --user -u cecd --since "$(date -d "${t:-now} -10 sec" '+%F %T')" --no-pager)
grep -q 'Putting TV in standby' <<< "$log" && echo " PASS cecd: 'Putting TV in standby'" || echo " FAIL cecd didn't act on going to sleep"
grep -q 'Waking TV' <<< "$log" && echo " PASS cecd: 'Woke from standby. Waking TV.'" || echo " FAIL cecd didn't act on waking up"
grep -q 'Received from.*: STANDBY' "$W/sleep-mon.txt" && echo " PASS going to sleep: TV turned off (STANDBY)" || echo " FAIL going to sleep: no STANDBY to the TV"
grep -qE 'Received from.*: (IMAGE_VIEW_ON|TEXT_VIEW_ON)' "$W/sleep-mon.txt" && echo " PASS waking up: TV turned on (IMAGE_VIEW_ON)" || echo " FAIL waking up: no IMAGE_VIEW_ON"
grep -oE '^(Received|Transmitted)[^:]*: [A-Z_]+' "$W/sleep-mon.txt" | sed 's/^/ /'
# Back to the normal service.
rm -f ~/.config/systemd/user/cecd.service.d/vmcec.conf; systemctl --user daemon-reload; systemctl --user restart cecd.service
REMOTE
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
# Wake the VM from sleep (suspend to RAM), e.g. after the TV's power button
# in the HDMI-CEC test. Uses the QMP socket run.sh opens in the VM directory.
# Env: VM_DIR (the repo if it holds disk.qcow2, else ~/vms/cachyos-test).
set -euo pipefail
here="$(cd "$(dirname "$0")/.." && pwd)"
VM_DIR="${VM_DIR:-$([[ -f "$here/disk.qcow2" ]] && echo "$here" || echo "$HOME/vms/cachyos-test")}"
sock="$VM_DIR/qmp.sock"
[[ -S "$sock" ]] || { echo "No QMP socket at $sock (VM not running, or started before run.sh had -qmp)." >&2; exit 1; }
printf '%s\n' '{"execute":"qmp_capabilities"}' '{"execute":"system_wakeup"}' |
socat -t 2 - UNIX-CONNECT:"$sock" 2>/dev/null | grep -c '"return"' >/dev/null && echo "Wake-up sent."