run() now turns subprocess.TimeoutExpired into rc=124, so the poll loop and
on_connect can no longer die on a stalled lgtv.sh (34adf28 only guarded the
command path). A failed ON/OFF publishes a fresh status probe instead of the
requested state. Payload decode uses errors="replace"; safe_handle (34adf28)
already kept bad bytes from killing paho's thread.
tests/test_bridge.py replays the reviewer's three repros.
Fixes-finding: uncaught-helper-timeout-stops-bridge
Fixes-finding: failed-power-commands-publish-success-state
Fixes-finding: malformed-mqtt-payload-kills-network-loop
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Rework the MQTT bridge into a single deployable unit:
- Drop the self-SSH hop; run locally on steel141 (on the TV's LAN, has
bscpylgtv, effectively always-on).
- Cache last-known TV IP and probe it directly; /24 ping-sweep only on a
cache miss. Stops the 30s HA state poll from flapping/spamming the net.
- WoL now unicast (last-known IP) + broadcast, ports 9/7, 3 bursts.
- systemd unit (User=seth, EnvironmentFile for MQTT_PASSWORD), install.sh,
env template, .gitignore. Removes the half-finished lgtv.sh.new.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>