Files
lgtv/.claude/handoffs/2026-06-06-070435-google-home-tv-integration.md
2026-06-06 07:08:49 -04:00

9.0 KiB
Raw Permalink Blame History

Handoff: LG TV → Home Assistant → Google Home (working end-to-end)

Session Metadata

  • Created: 2026-06-06 07:04:35
  • Project: /home/claude/bin/lgtv
  • Branch: master
  • Session duration: ~long (multi-hour, spanning the bridge consolidation + the full Google Home linking saga)

Recent Commits (for context)

  • ddfb768 chore: project-id-named icon copy (seth-freiberg.png) for Google Home upload
  • d07f334 feat: 144x144 transparent PNG app icon for Google Home action
  • 205bc15 docs: session handoff — consolidation + fixes complete and verified
  • 66b0f51 docs: record OFF-path gotchas (CWD-relative key path, freshly-woken retry)
  • 8f2bd2b fix: power_off works under systemd — absolute key path + freshly-woken retry

Handoff Chain

Current State Summary

DONE and verified. The LG TV is controllable from Google Home (account slingshooter08@gmail.com) via voice/app. Chain: Google Home → Google smart-home cloud → HA google_assistant integration (VM HA, 192.168.0.146) → MQTT broker (192.168.0.154) → lgtv-mqtt.service bridge on steel141 → WoL / WebOS power_off → TV. Confirmed live: a Google "turn on" produced Received command: ON on the bridge and GCP action logs show syncLog status: OK + executionLog isSuccess. The only remaining work is security cleanup of credentials that passed through the chat.

Codebase Understanding

Architecture Overview

  • Bridge (lgtv_mqtt.py + lgtv.sh) runs ONLY on steel141 as systemd lgtv-mqtt.service (User=seth). Single instance. pve173's old copy retired 2026-06-01. See prior handoff for the bridge internals.
  • HA google_assistant is the manual (free, non-Nabu-Casa) Cloud-to-Cloud integration. Config lives in /config/configuration.yaml on the VM HA, with /config/SERVICE_ACCOUNT.json for HomeGraph report_state.
  • HA is publicly reachable at https://hass.sethpc.xyz (Caddy CT → 192.168.0.146:8123), valid LE cert, no auth gate on that vhost — required so Google can reach /auth/authorize, /auth/token, /api/google_assistant.

Critical Files

File Purpose Relevance
/usr/local/bin/lgtv.sh (steel141) TV control (WoL/power_off/status) deployed; source in repo
/usr/local/bin/lgtv_mqtt.py (steel141) MQTT↔HA bridge systemd lgtv-mqtt.service
/config/configuration.yaml (VM HA .146) holds the google_assistant: block backed up as .bak.pre-googleassistant
/config/SERVICE_ACCOUNT.json (VM HA .146) HomeGraph key (project seth-freiberg) chmod 600
assets/seth-freiberg.png (repo) Google action icon (must be named <project_id>.png) uploaded to console

Key Patterns Discovered

  • VM HA /config access: SSH add-on on 192.168.0.146:22, user seth, passwordless sudo to root (seth in wheel). /config is root-owned; write via sudo tee.
  • HA core logs: ssh seth@.146 'bash -lc "ha core logs"' (needs login shell for the supervisor token). /config/home-assistant.log does NOT exist on this HAOS install.
  • GCP read access: minted SA access tokens via a signed-JWT OAuth assertion in Python (cryptography lib) → called Logging/ServiceUsage/IAP REST APIs.

Work Completed

Tasks Finished

  • Deployed HA google_assistant: config + SERVICE_ACCOUNT.json, validated, restarted.
  • Generated the 144×144 transparent PNG icon (scripts/make_icon.py), named it seth-freiberg.png (Google requires filename == project_id).
  • Diagnosed and fixed the entire Google linking chain (see Decisions).
  • Confirmed end-to-end: Google → HA → bridge → TV ON. GCP action logs OK.
  • Reset the debug logging that was enabled for diagnosis.

Files Modified

File Changes Rationale
repo assets/, scripts/make_icon.py added icon + generator Google action branding
VM HA /config/configuration.yaml appended google_assistant: block the integration
VM HA /config/SERVICE_ACCOUNT.json created HomeGraph report_state

Decisions Made

Decision Options Considered Rationale
Manual Google Assistant integration (free) Nabu Casa ($) already had public HTTPS via Caddy
Keep using existing project seth-freiberg dedicated project it worked once linking was fixed; see gotcha
Account-linking scopes email,name leave blank blank → Google wouldn't open the login page
HTTP basic-auth-header checkbox UNCHECKED checked HA OAuth is secretless; checked → token exchange 401

Pending Work

Immediate Next Steps

  1. SECURITY: rotate/revoke credentials that were pasted into the chat transcript — see "Important Context". This is the only outstanding task.
  2. Optional: assign the "Big TV" device to a room in Google Home for nicer voice targeting.
  3. Optional: give the action a dedicated GCP project later if seth-freiberg (shared with WP Mail SMTP + Gemini keys) ever causes trouble. Not needed now.

Blockers/Open Questions

  • None blocking. Integration is live.

Deferred Items

  • DHCP reservation for the TV (not needed; cached-IP probe handles it).
  • Dedicated GCP project for the action (works as-is in the shared project).

Context for Resuming Agent

Important Context

The integration WORKS. Do not "re-fix" it. If re-touching, preserve these exact settings that were hard-won:

  • HA config: project_id: seth-freiberg, service_account: !include SERVICE_ACCOUNT.json, report_state: true, expose_by_default: false, only switch.big_tv_lg_tv exposed.
  • Google console (Cloud-to-cloud → Setup): Client ID https://oauth-redirect.googleusercontent.com/r/seth-freiberg, Auth URL https://hass.sethpc.xyz/auth/authorize, Token URL …/auth/token, Fulfillment …/api/google_assistant, Scopes email and name, HTTP-basic-auth-header checkbox OFF.
  • HomeGraph API enabled in project seth-freiberg.

SECURITY — rotate these (their VALUES were exposed in the chat transcript):

  1. The GCP service-account key(s) for claude@seth-freiberg.iam — TWO keys were created (04:25 + 05:26 UTC). Delete unused ones, rotate the live one. The HA copy is /config/SERVICE_ACCOUNT.json; redeploy a fresh key there after rotating.
  2. The HA SSH add-on password for user seth on 192.168.0.146:22 — change it.
  3. The HA long-lived access token used for API calls — revoke it in the VM HA (profile → Security → Long-lived tokens).
  4. Remove the temporary Logs Viewer role granted to claude@seth-freiberg.iam (added only for log diagnosis).
  5. Delete the local key file seth-freiberg-4ba9cc3a2d99.json in the repo dir (gitignored, but remove it now that it's deployed).
  6. Address the GCP console banner about unrestricted Gemini API keys in this project.

Assumptions Made

  • steel141 stays effectively always-on (it does — hosts seth's desktop session for weeks).
  • The Google Home account is slingshooter08@gmail.com; GCP owner is seth@sethfreiberg.com.

Potential Gotchas

  • seth-freiberg is a shared GCP project (WP Mail SMTP consent screen "wp mail", In production). DO NOT flip its OAuth consent screen to Testing — it would break WP Mail SMTP. The GCP consent screen is NOT part of smart-home linking anyway.
  • HA http.ban logs Google's /auth/token posts as failed auth when the token exchange breaks; repeated failures can ban Google's IPs → check /config/ip_bans.yaml and delete Google (108.177.x / google.com) entries if linking suddenly fails instantly.
  • bscpylgtv key DB is CWD-relative — lgtv.sh passes an absolute key_file_path (prior handoff). Don't regress that.

Environment State

Tools/Services Used

  • VM HA 2026.2.3 at 192.168.0.146 (HAOS); SSH add-on on :22 (user seth, sudo root).
  • MQTT broker: Mosquitto CT 301 on pve241 (192.168.0.154); user lgtv.
  • Caddy CT: hass.sethpc.xyz → 192.168.0.146:8123 (no auth gate, HTTP/1.1 to backend).
  • GCP project seth-freiberg (HomeGraph + smart-home action "LG-TV").
  • External reachability test host: ssh rosy (68.100.191.36).

Active Processes

  • lgtv-mqtt.service on steel141 (enabled, running).
  • HA google_assistant integration loaded on the VM HA.

Environment Variables

  • HOMELAB_PASSWORD (used for MQTT/SSH where applicable) — name only.
  • MQTT_PASSWORD via /etc/lgtv/lgtv.env on steel141 (EnvironmentFile) — name only.

Security Reminder: Credential VALUES were intentionally omitted from this doc; the cleanup list above names them by location only. Rotate them.