diff --git a/.claude/handoffs/2026-06-11-045021-pos-google-home-button.md b/.claude/handoffs/2026-06-11-045021-pos-google-home-button.md new file mode 100644 index 0000000..7cad337 --- /dev/null +++ b/.claude/handoffs/2026-06-11-045021-pos-google-home-button.md @@ -0,0 +1,219 @@ +# Handoff: POS Briefing — Google Home on-demand print button (COMPLETE) + +## Session Metadata +- Created: 2026-06-11 04:50:21 +- Project: /home/claude/bin/POS-Automation +- Branch: master +- Session duration: long (multi-day: 2026-06-07 build + deploy, 2026-06-10/11 verify/simplify) + +### Recent Commits (for context) + - 93f6c33 feat: simplify Google switch to on-only (state push restored) + - 6607367 docs: Google Home button confirmed working (switch tile); SA key regenerated + - 5c8f2d7 chore: gitignore service-account key files + - b540d5e docs: expose POS briefing as a switch tile (scenes don't tile in Google Home) + - 2691dc4 docs: record HA deployment details + access method in DECISIONS + +## Handoff Chain + +- **Continues from**: None (fresh start) +- **Supersedes**: None + +## Current State Summary + +**DONE and verified end-to-end.** The POS daily briefing now prints **on demand from a +Google Home button** (no cron). Tapping **"Print Daily Briefing"** in the Google Home app +(account `slingshooter08`) → HA `switch.print_daily_briefing` → `button.press` → MQTT → +`pos-briefing-mqtt.service` on steel141 → `pos_briefing.py` → printer (resolved by MAC) → +Epson TM-m30. Confirmed live: a real Google tap produced a printed receipt. State push to +Google is working (HomeGraph SA key was regenerated). Nothing is in-progress; remaining +items are optional polish (see Deferred). + +The original report was "the automation is not firing" — root causes were (1) no cron was +ever installed (a prior commit only edited SESSION.md), and (2) `yfinance` was missing on +steel141 post-migration. Seth then redirected the whole approach to a Google Home button, +which is what got built. + +## Codebase Understanding + +## Architecture Overview + +Mirrors the **lgtv** project pattern (steel141 systemd MQTT bridge + HA MQTT discovery + +the free manual `google_assistant` integration). + +``` +Google Home tap → google_assistant integration (VM HA 192.168.0.146, acct slingshooter08) + → HA switch.print_daily_briefing (template switch, always-off, on-tap only) + → button.press on button.pos_briefing_print_daily_briefing (MQTT-discovery button) + → MQTT posbriefing/button/press on broker CT 301 (192.168.0.154) + → pos-briefing-mqtt.service on steel141 (systemd, User=claude) + → subprocess: /usr/bin/python3 pos_briefing.py + → discover_printer() resolves IP by MAC 50:57:9c:eb:0f:07 → ESC/POS over TCP :9100 + → status/availability published back to MQTT +``` + +## Critical Files + +| File | Purpose | Relevance | +|------|---------|-----------| +| `pos_briefing.py` | Main script: fetch FreshRSS, select+summarize, dashboard, build+print receipt | Has `discover_printer()` (MAC-based), gemma4 summary + fast-model selection | +| `pos_briefing_mqtt.py` | MQTT bridge → `/usr/local/bin/pos_briefing_mqtt.py` (deployed) | systemd `pos-briefing-mqtt.service`, User=claude | +| `pos-briefing-mqtt.service` | systemd unit (repo copy) | EnvironmentFile=/etc/pos-briefing/pos-briefing.env | +| `install.sh` | Deploys bridge+unit+env, enable | Re-run to redeploy bridge changes | +| `scripts/dryrun.py` | Full pipeline, print suppressed | Use to test without printing/marking-read | +| `tests/test_printer_discovery.py` | 5 unit tests for discovery | `python3 -m pytest tests/test_printer_discovery.py` | +| `tests/pytest.ini` | Pins pytest rootdir to tests/ | Works around dead /root/bin symlinks that crash collection | +| `docs/ha-setup.md` | The HA/Google deployment (as-built) | Read first for the Google side | +| `DECISIONS.md` | All decisions w/ rationale | Read first for the why | +| `config.json` (gitignored) | Live config incl. real creds | Has printer_mac, ollama_model, ollama_select_model | + +### Key Patterns Discovered + +- **paho-mqtt 2.1.0** is installed for `claude`; bridge pins `CallbackAPIVersion.VERSION1` + (matches lgtv; one harmless deprecation warning at startup). +- **HA scripts/scenes expose to Google as the Scene trait, which Google Home does NOT + render as a tappable tile** (voice/Routine only). A **switch** tiles. This was the key + fix — the briefing is exposed as a template switch, not a script/scene. +- HA on VM `192.168.0.146` (HAOS 2026.2.3): access via `ssh seth@192.168.0.146` (key auth), + `bash -lc "ha core check|restart|logs"`, and call core services via the supervisor token: + `curl -H "Authorization: Bearer $SUPERVISOR_TOKEN" http://supervisor/core/api/...`. + `/config` is root-owned (write via `sudo tee`); back up to `/config/.backup/` first. +- The manual google_assistant integration has a **local fulfillment webhook** that answers + SYNC/EXECUTE without Google auth — great for diagnosing what HA reports to Google: + `curl -X POST http://127.0.0.1:8123/api/webhook/ -d '{"inputs":[{"intent":"action.devices.SYNC"}],"requestId":"x"}'` + (webhook id is in `/config/.storage/google_assistant`). + +## Work Completed + +### Tasks Finished + +- [x] Diagnosed "not firing" (no cron + missing yfinance); installed yfinance on steel141. +- [x] Verified pipeline runs on steel141 (scripts/dryrun.py). +- [x] Committed the in-tree hourly-weather rewrite. +- [x] Summaries → gemma4:26b (prompt converged via ask_gemma4 MCP); selection stays on + qwen2.5:1.5b (gemma4 broke Phase-1 JSON under format:json). +- [x] Printer discovery by MAC (cache + /24 sweep); 5 unit tests; wired into print_receipt. +- [x] MQTT bridge + systemd unit + env template + installer; broker user `posbriefing` on CT 301. +- [x] Deployed HA side on VM HA: switch + google_assistant exposure; verified via local SYNC. +- [x] Regenerated the HomeGraph SA key (old one was invalid); state push now works. +- [x] Simplified switch to on-only (turn_off no-op) after state push confirmed. +- [x] Confirmed end-to-end: real Google Home tap printed a receipt. + +## Files Modified + +| File | Changes | Rationale | +|------|---------|-----------| +| `pos_briefing.py` | discovery, gemma4 summaries, fast-model selection, hourly weather | core feature work | +| `pos_briefing_mqtt.py` (new) | MQTT bridge | the trigger | +| `pos-briefing-mqtt.service`, `install.sh`, `pos-briefing.env.example` (new) | deploy | systemd install | +| `tests/test_printer_discovery.py`, `tests/pytest.ini` (new) | tests | discovery coverage | +| `config.json`/`config.example.json` | printer_mac, printer_subnet, ollama_select_model, gemma4 model | config | +| `docs/ha-setup.md`, `DECISIONS.md`, `CONTEXT.md` (new/edited) | docs | record the build | +| VM HA `/config/configuration.yaml` | added template switch + google_assistant exposure | HA side | +| VM HA `/config/scripts.yaml` | print_daily_briefing script (alt trigger) | HA side | +| VM HA `/config/SERVICE_ACCOUNT.json` | replaced with valid key | fix HomeGraph | + +## Decisions Made + +See `DECISIONS.md` (2026-06-07 entries) for the full list with rationale. Highlights: +SWITCH-tile exposure (scenes don't tile); MAC-based printer discovery (DHCP IP churn); +gemma4:26b summaries + fast selection model; on-demand MQTT trigger (no cron). + +## Pending Work + +## Immediate Next Steps + +The feature is complete. No required next steps. Optional follow-ups only (see Deferred). + +### Blockers/Open Questions + +- [ ] None. Working end-to-end. + +### Deferred Items + +- **Print latency ~1–2 min** (gemma4:26b summarization + yfinance). The button returns + immediately; `posbriefing/status` tracks `printing`→`ok`. Optimize later if it annoys + (model keep-alive, fewer/shorter summaries, smaller model). +- **Router DHCP reservation for the printer** (Seth's router action). MAC discovery makes + it optional; a reservation would let us drop the /24 sweep fallback. +- **Dashboard fields `zfs`/`reddit`/`grafana` return None on steel141** (it's not the tank + host; `cannot open 'tank'`). Out of scope; the receipt prints everything else fine. +- **SA key value passed through this session's transcript** when Seth shared the file. It's + the live key; rotate once more out-of-band later if desired (then redeploy to HA). + +## Context for Resuming Agent + +## Important Context + +- **Don't reintroduce cron.** Trigger is on-demand via Google Home → MQTT only. +- **Expose to Google as a SWITCH, never a script/scene** — scenes don't render as tappable + tiles in Google Home. +- **The printer IP is DHCP-mobile** — always resolve via `discover_printer()` (MAC + `50:57:9c:eb:0f:07`); current IP was 192.168.0.184 but expect it to change. +- **A manually admin-triggered `google_assistant.request_sync` logs a harmless 404** — it + sends the unlinked owner-context id `2c22820…`; HA's automatic reporting uses the linked + `f948247…` and succeeds. Do NOT chase that 404; it is not a real failure. +- **gemma4 under `format:json` returns reasoning objects** (`{"thought": ...}`), which is + why Phase-1 selection uses `qwen2.5:1.5b`, not gemma4. Don't point selection at gemma4. +- **lgtv project is the reference** for the HA/Google/MQTT pattern; its `.claude/handoffs/` + (2026-06-06) documents the google_assistant linking invariants — do not "re-fix" them. + +## Assumptions Made + +- steel141 stays effectively always-on (hosts seth's desktop for weeks). +- Broker CT 301 (192.168.0.154) and HA VM (192.168.0.146) stay put. +- The TM-m30 keeps the same MAC (it will; only its DHCP IP moves). + +## Potential Gotchas + +- `pytest` crashes on collection from the repo root unless `tests/pytest.ini` pins rootdir + (dead `CREATE_PROJECT.md`/`GITEA_API.md` symlinks → inaccessible /root/bin → PermissionError). + Run `python3 -m pytest tests/test_printer_discovery.py`. +- The bridge runs `pos_briefing.py` from the **repo path** `/home/claude/bin/POS-Automation/`; + editing that file takes effect immediately. The **bridge itself** is the copy at + `/usr/local/bin/pos_briefing_mqtt.py` — re-run `./install.sh` (or `sudo install`) + restart + after editing `pos_briefing_mqtt.py`. +- google_assistant config reload needs a full `ha core restart` (no targeted reload). +- Editing `/config/*` on HA requires `sudo tee` (root-owned); always back up to `/config/.backup/`. + +## Environment State + +### Tools/Services Used + +- `pos-briefing-mqtt.service` on steel141 (systemd, enabled+active, User=claude). +- MQTT broker: Mosquitto CT 301 on pve241 (192.168.0.154); user `posbriefing`. +- VM HA 2026.2.3 at 192.168.0.146 (HAOS); SSH add-on (user seth, key auth, sudo root). +- steel141 Ollama (192.168.0.141:11434): gemma4:26b (summaries), qwen2.5:1.5b (selection). +- Printer: Epson TM-m30, MAC 50:57:9c:eb:0f:07, port 9100 (DHCP IP, was .184). +- GCP project `seth-freiberg` (HomeGraph; SA claude@seth-freiberg.iam, key id 8e3e812b6913…). + +### Active Processes + +- `pos-briefing-mqtt.service` (steel141) — running. +- HA `google_assistant` integration (VM HA) — loaded; state push working. + +### Environment Variables + +- `MQTT_PASSWORD` via `/etc/pos-briefing/pos-briefing.env` (EnvironmentFile, 0600) — name only. +- `HOMELAB_PASSWORD` — name only. +- `$SUPERVISOR_TOKEN` (inside HA SSH add-on login shell) — used for HA core API calls. + +### Quick health check + +```bash +sudo systemctl is-active pos-briefing-mqtt.service +sudo bash -c 'set -a; . /etc/pos-briefing/pos-briefing.env; set +a; mosquitto_sub -h "$MQTT_HOST" -p "$MQTT_PORT" -u "$MQTT_USER" -P "$MQTT_PASSWORD" -t posbriefing/status -C 1' +python3 -c "import pos_briefing as pb; print(pb.discover_printer())" # current printer IP +``` + +## Related Resources + +- `docs/ha-setup.md` — HA/Google deployment (as-built, read first for the Google side). +- `docs/superpowers/specs/2026-06-07-ha-google-home-trigger-design.md` — design. +- `docs/superpowers/plans/2026-06-07-ha-google-home-trigger.md` — implementation plan. +- `DECISIONS.md` — all decisions with rationale. +- `../lgtv/` + its `.claude/handoffs/2026-06-06-*.md` — the reference pattern + Google linking invariants. +- Repo: `git.sethpc.xyz/Seth/POS-Automation`. + +--- + +**Security Reminder**: No secret values included — credentials referenced by location/name only.