# LG TV Bridge — Decisions Format: `YYYY-MM-DD: — ` - **2026-06-01: Single bridge, single host — runs on steel141, pve173 retired.** The bridge had been running on pve173 (always-on node) since 2026-03-23, SSHing to steel141 for every TV op. Consolidated everything onto steel141. *Why:* the WebOS pairing key (`/home/seth/.aiopylgtv.sqlite`) lives only on steel141, so `power_off` always routes through it regardless of bridge host — pve173 added an SSH hop and a second host for no real availability gain (the TV is uncontrollable whenever steel141 is down anyway). One host, no SSH hop. - **2026-06-01: Cached-IP discovery, sweep only on miss.** Persist last-known TV IP in `~/.cache/lgtv/last_ip`; probe it directly (TCP :3000/:3001). Full /24 ping-sweep runs only on a cache miss, then re-caches. *Why:* the old code ping-swept all 254 hosts over SSH on every 30s HA state poll — slow, network spam, and racy (stale ARP) → the chronic "flaky state in HA" symptom. - **2026-06-01: Robust WoL — unicast + broadcast, ports 9/7, 3 bursts.** *Why:* old code sent broadcast-only on ports 9/1235 once. Unreliable wakes. NOTE: the remaining wake failures are TV-side (Quick Start+ / Mobile TV On must be ON; wired Ethernet far more reliable than Wi-Fi for LG WoL) — not fixable in code. - **2026-06-01: Deps via `pip --user --break-system-packages` for `seth`.** paho + bscpylgtv live in `/home/seth/.local` (PEP 668 box). Matches how bscpylgtv was already installed; the systemd unit runs `User=seth`. - **2026-06-01: power_off must pass an absolute `key_file_path`.** bscpylgtv's `StorageSqliteDict` defaults the key DB to `os.getcwd()/.aiopylgtv.sqlite` (its `$USER_HOME` fallback is not `$HOME`). The old pve173 setup worked only because it SSHed in as seth, landing CWD in `/home/seth`. Under systemd (cwd=/) it threw `sqlite3.OperationalError: unable to open database file`. Fix: `WebOsClient.create(ip, key_file_path="~/.aiopylgtv.sqlite")`. - **2026-06-01: power_off retries (2×, 10s apart).** A TV woken seconds earlier answers TCP :3000 but refuses a WebSocket control session for ~20-30s, so an immediate OFF-after-ON can fail. Retry covers it; normal case (TV already up) succeeds first try in ~3s. Bounded under the bridge's 60s subprocess timeout. - **2026-06-06: Google Home via the free manual `google_assistant` integration** (not Nabu Casa). Exposed only `switch.big_tv_lg_tv`. Linked end-to-end and verified. Three non-obvious requirements that were each a separate failure mode: account-linking **Scopes must include `email` + `name`** (blank → no login page); the **"HTTP basic auth header" checkbox must be OFF** (HA OAuth is secretless → checked = `/auth/token` 401); and the Google **Test must be activated by the same account used in the Home app** (slingshooter08). The GCP OAuth consent screen is *not* part of smart-home linking — don't touch it (shared project runs WP Mail SMTP). Full detail + security-cleanup list in `.claude/handoffs/2026-06-06-*.md`. - 2026-10-22: HA VM 706 pinned to 192.168.0.146 twice over — Omada DHCP reservation (MAC BC:24:11:3F:DC:7B) AND HAOS static IPv4 (`ha network update enp0s18 --ipv4-method static`) — HA had silently lost its IPv4 lease (~Aug 2026; IPv6-only, uptime 57d), cutting it off from MQTT/Caddy/Google: the "TV hasn't worked in months" outage. Static removes the DHCP dependency; reservation keeps the router from handing .146 to anyone else. - 2026-10-23: TV DHCP-reserved at 192.168.0.20 (Omada) — bscpylgtv keys pairings by IP, so every DHCP drift (7 IPs so far) re-triggered the on-TV auth prompt, and a missed prompt hung `off` past the 60s timeout. New IP seeded with the newest stored key (.31) so no re-prompt. Also: on_message now catches all exceptions — one escaping killed paho's thread while the poll loop kept the process alive (silent zombie). - 2026-10-23 (later): Reservation alone didn't hold — the TV came back on .45 minutes later and prompted again. lgtv.sh now falls back to the newest stored key when there's no key for the current IP, so IP drift is harmless. Bridge commands run on a single worker thread so a 60s `off` can't starve paho's keepalive and drop queued commands. ## Deferred / Rejected - **DHCP reservation for the TV (static IP):** not done — Pi-hole doesn't run DHCP (router does), so it's Seth's action on the router. The cached-IP probe makes it unnecessary for correctness; a reservation would just let us drop the sweep-fallback entirely. Add if convenient. - **Unique per-instance MQTT client_id:** rejected — keeping a fixed `lgtv-bridge` id means a duplicate instance is *loud* (reconnect war in broker logs) rather than silent. Single-instance discipline is the real guard. (A duplicate on 2026-06-01 during this very migration is what surfaced all this.)