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).
This commit is contained in:
avi 2026-09-08 18:19:57 -05:00
commit 4eab1f11cf
22 changed files with 289 additions and 87 deletions

View file

@ -0,0 +1,106 @@
# 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/>.