ISOLATE (nothing deleted): - moved HA module (ha/, ui/devices/, HA client tests), e2e scripts, and HA docs under deferred/home-assistant/ with a README explaining status + how to revive; complete snapshot preserved on branch deferred/home-assistant REMOVE FROM ACTIVE PRODUCT: - HomeScreen: Devices card + route gone; MainActivity nav updated - ShonarApplication: haRepository removed - BuiltInSettings: Home Assistant category/settings removed from defaults - SettingsManagerTest: secret tests rewritten around a user-created SECRET-type setting (no built-in secret ships) - Manifest + URL-validation test fixture wording neutralized - README/ROADMAP: HA marked deferred with pointer to preserved branch ADD (design, per product direction): - docs/server-providers.md: ShonarProvider interface, Room data model, auth ladder (OIDC/PKCE -> Nextcloud login-flow-v2 -> token paste), sync strategy, provider-selection UX (Nextcloud default; Start9/Umbrel as platform-probe + explicit service binding, never universal APIs; custom SHONAR server; local-only), TLS TOFU pinning policy, no-secret-logging rules, provider contract test strategy, phased plan P0-P7 VERIFY: 19 Android unit tests green, APK builds, on-device launch OK (consent dialog renders; no Devices entry). Backend unchanged (26 tests).
5.1 KiB
Home Assistant integration — guide
SHONAR can talk to a local Home Assistant server over Home Assistant's official REST and WebSocket APIs. No cloud, no third-party wrappers, no telemetry — the app talks only to the URL you configure.
1. Install Home Assistant locally
Any of these works; see https://www.home-assistant.io/installation/:
- Home Assistant OS on a Raspberry Pi / VM (recommended)
- Container:
docker run -d --name homeassistant --restart=unless-stopped \ -v ./config:/config -e TZ=UTC --network=host ghcr.io/home-assistant/home-assistant:stable - Core: Python venv install (advanced)
After first start the UI is at http://<host>:8123.
2. Create a long-lived access token
Home Assistant requires a long-lived access token for third-party apps (SHONAR cannot mint one for you — that's an HA security design):
- Open the HA web UI and log in.
- Click your profile picture (bottom-left) → Security.
- Scroll to Long-lived access tokens → Create Token.
- Name it (e.g.
SHONAR) and copy the token immediately — it is shown once.
3. Where to enter the URL and token
SHONAR app → Settings → Home Assistant category:
- Toggle Enable Home Assistant on.
- Home Assistant URL — e.g.
http://homeassistant.local:8123orhttp://192.168.1.50:8123. - Long-lived access token — paste the token. Stored encrypted on the device (AndroidKeyStore-backed EncryptedSharedPreferences); never in plaintext, never logged, never included in settings export unless you explicitly request it.
The Devices tab then shows your entities. The connection status appears there: not configured / connecting / connected / error (with a plain-language message you can act on).
4. Test the connection
- Settings → Home Assistant has the URL/token fields; the Devices screen doubles as the live connection test — hit the refresh icon.
- Or from a shell on the same network:
curl -H "Authorization: Bearer <token>" http://<ha-host>:8123/api/
5. How entity discovery works
GET /api/states returns every entity; the app lists them with friendly
names, current state, and a search box. Entities whose domain is switchable
(light, switch, fan, input_boolean, humidifier) get a toggle that
calls POST /api/services/<domain>/turn_on|turn_off with entity_id.
Toggles apply optimistically and are reconciled by the live stream.
Live updates: a WebSocket to /api/websocket authenticates and subscribes to
state_changed events. On disconnect it reconnects with exponential backoff
(2s → 60s). A configurable poll fallback (State refresh interval, used when
the WebSocket is down) keeps the list fresh.
6. How custom settings work
All settings are data-driven (SettingDefinition), not hard-coded UI. Each
has an id, name, description, category, type (boolean/string/number/select/
multi-select/color/url/secret), default, optional min/max/choices,
editability, sensitivity, restart requirement, and conditional visibility.
Settings → Add (+) creates a custom setting with any type; delete works on custom settings only (built-ins can be reset but not removed). Search filters across name/id/description. Export produces JSON with secrets omitted by default; Import validates every entry against known definitions and applies all-or-nothing, reporting per-key rejection reasons.
7. How settings are stored
| Kind | Location |
|---|---|
| Ordinary values + custom definitions | Jetpack DataStore (shonar_settings) |
| Sensitive values (HA token, secret-type settings) | EncryptedSharedPreferences, AES256-GCM values / AES256-SIV keys, master key in AndroidKeyStore (never exportable) |
8. Troubleshooting
| Symptom | Likely cause / fix |
|---|---|
| "Authentication failed…" | Token expired/revoked/typo. Create a fresh long-lived access token (step 2). |
| "Cannot reach the Home Assistant server" | Wrong URL/IP, phone on different network/VLAN, HA not running, or a firewall blocking 8123. Try the curl test above from the phone's network. |
| Cleartext HTTP warning | The app permits cleartext HTTP (like the official Home Assistant Android app) because LAN servers are plain http:// by default. Android cannot restrict cleartext to IP ranges — use https:// for any non-LAN host; certificate verification is never disabled. |
| Entities stale | WebSocket dropped; poll fallback updates after the configured interval; refresh icon forces a reload. |
| mDNS name not resolving | Use the IP address instead of homeassistant.local (phone's mDNS may be limited by the router). |
9. Endpoints used (official API only)
| Method | Path | Purpose |
|---|---|---|
| GET | /api/ |
connection/config test |
| GET | /api/states |
entity discovery |
| GET | /api/states/{entity_id} |
single state |
| POST | /api/services/{domain}/{service} |
service/action calls |
| WS | /api/websocket |
auth, subscribe_events(state_changed) |
References: REST https://developers.home-assistant.io/docs/api/rest/, WebSocket https://developers.home-assistant.io/docs/api/websocket/.