3 Commits
Author SHA1 Message Date
wenilandClaude Opus 4.8 1c64386996 v2 host: portable desktop app (tray + FastAPI + WebView2 panel), unified config, HA control
Fold the Windows tray/daemon into one self-contained Clawdmeter.exe with a
settings UI, and wire the host side of the watch features:

- config.py: single %LOCALAPPDATA%\Clawdmeter\config.json (ha/buttons/settings),
  atomic writes, auto-migration from the old ha_config.json.
- server.py: local FastAPI (127.0.0.1:8723) — GET/PUT /api/config (token masked),
  POST /api/ha/test, GET /api/ha/entities, GET /api/status.
- web/index.html: brand-styled settings panel (Status/HA/Buttons/Settings tabs).
- panel.py: pywebview/WebView2 window, launched as its own process (pywebview and
  pystray both want the main thread); tray "Settings" opens it via --panel.
- daemon: HA command dispatch (toggle/bri/ct), dynamic button labels + index→
  action mapping, watch-battery low warning toast, and the dimmer "dim" snapshot
  (dimreq → light_snapshot of the first entity) so the watch dial seeds from HA.
- clawdmeter.spec / requirements: bundle fastapi+uvicorn+pywebview+webview backend.
- build-exe.ps1: ASCII-only (Windows PowerShell 5.1 mangles em-dashes under cp1251).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 20:20:44 +03:00
wenilandClaude Opus 4.8 650af4221b v2: real-time Now Playing cadence + robust long-title BLE writes
Decouple the two data sources that share the BLE link:
- Anthropic usage / rate-limit: still polled every 60s.
- Windows media session: read every 3s and pushed the moment the track or
  play/pause state changes, so a song change reaches the watch in seconds
  instead of at the next 60s poll. The last usage payload is cached and merged
  into each now-playing write, so the firmware always gets one complete JSON
  object and the usage screens never blank between polls.

Fix: long media titles (a 44-char Cyrillic title -> ~256 B once json escapes
each char to \uXXXX) overflowed the ATT MTU, so every write-without-response
failed with E_INVALIDARG and tripped the zombie-link break in a reconnect loop.
Switch the RX write to response=True (WinRT does a reliable long write; the RX
char already advertises WRITE and NimBLE reassembles into its 512 B buffer) and
serialize with ensure_ascii=False so Cyrillic goes as 2-byte UTF-8 instead of
6-byte escapes. Verified on hardware: stable link, writes succeed across the
60s heartbeat.

Firmware: only re-set the Now Playing labels when the track / state actually
changed, so the faster cadence does not restart the circular title-scroll
animation on every (often identical) payload.

requirements-windows.txt: add winrt-Windows.Media[.Control]. Phase 5 imports
them but they were never declared, so now-playing silently degraded to
"nothing playing" on a fresh machine.

tests: fix two stale poll_api assertions that expected an "ok" key poll_api has
not emitted since that flag moved to the caller (connect_and_run).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 09:43:31 +03:00
kvenanzi eb70456755 Add native Windows host daemon (bleak/WinRT)
Adds a Windows port of the host daemon so the Clawdmeter stays connected
on Windows independent of WSL. Mirrors the macOS daemon and speaks the
existing GATT data service unchanged — no firmware changes.

- claude_usage_daemon_windows.py: Windows-local OAuth token read +
  Anthropic poll + BLE scan/connect/write, with auto-reconnect
  (connect-retry wrapper, zombie-link break, split fast/slow backoff)
- tray_windows.py: pystray login-startup tray app (status icon + Quit)
- autostart_windows.py: winreg HKCU\Run autostart via pythonw.exe
- icon_assets.py: per-state tray icons composited from logo.h
- install-windows.ps1 + daemon/README-windows.md: turnkey setup
- pytest suite: token / poll / reconnect / tray / autostart / no-WSL guard
2026-06-02 16:39:07 -05:00