S.H.O.N.A.R./deferred/home-assistant/docs/home-assistant.md
avi 4eab1f11cf Product pivot: defer Home Assistant; provider-based server architecture (Nextcloud default)
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).
2026-09-08 18:19:57 -05:00

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):

  1. Open the HA web UI and log in.
  2. Click your profile picture (bottom-left) → Security.
  3. Scroll to Long-lived access tokens → Create Token.
  4. 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:

  1. Toggle Enable Home Assistant on.
  2. Home Assistant URL — e.g. http://homeassistant.local:8123 or http://192.168.1.50:8123.
  3. 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/.