diff --git a/docs/home-assistant-matrix.md b/docs/home-assistant-matrix.md new file mode 100644 index 0000000..19684bc --- /dev/null +++ b/docs/home-assistant-matrix.md @@ -0,0 +1,179 @@ +# Home Assistant × Matrix integration + +Reference for wiring the Home Assistant [Matrix integration](https://www.home-assistant.io/integrations/matrix) +to the self-hosted Matrix homeserver at [`synapse.chans.xyz`](../hosts/synapse.chans.xyz.md). +Deliberately contains no Matrix passwords, access tokens, or room encryption material. + +## Purpose + +The integration lets Home Assistant send messages to Matrix rooms and react to +messages/reactions in Matrix rooms. "Reacting" is done by firing a +`matrix_command` event when one of the configured commands matches; automations +then trigger on that event. Sending is done through the `notify.matrix` platform +and the `matrix.send_message` / `matrix.react` actions. + +## Environment mapping + +| Integration setting | This deployment | +|---|---| +| `homeserver` | `https://synapse.chans.xyz` (client-server base URL) | +| `username` | full Matrix ID, e.g. `@ha_bot:chans.xyz` | +| `password` | MAS local-password account password (see below) | +| Room IDs / aliases | full forms with the identity domain, e.g. `!cUrbafjkfsMDVwdRDQ:chans.xyz` or `#room:chans.xyz` | + +- Identity domain is `chans.xyz` (not `synapse.chans.xyz`); user IDs and room + aliases carry the `:chans.xyz` suffix. +- Authentication on this homeserver is MAS (Matrix Authentication Service) with + local-password accounts. The integration logs in with `m.login.password` + (username + password), so the bot account must be a local-password account — + same as the Hermes account documented in [`hermes-matrix.md`](hermes-matrix.md). + If MAS is later switched to OAuth2/OIDC-only (no legacy password login), the + integration's password login will stop working; keep that in mind before such + a change. +- Public registration is disabled. Create/reset the dedicated bot account via + MAS / Element Admin. + +## Use a separate bot account (mandatory) + +The docs are explicit: to prevent infinite loops when reacting to commands, +the integration **must** use a separate account from any account whose messages +it reacts to. Use a dedicated account such as `@ha_bot:chans.xyz`, not a human +account. + +## configuration.yaml (example) + +```yaml +# The Matrix integration +matrix: + homeserver: https://synapse.chans.xyz + username: "@ha_bot:chans.xyz" + password: supersecurepassword + rooms: + - "#hasstest:chans.xyz" + commands: + - word: my_command + name: my_command +``` + +After changing `configuration.yaml`, restart Home Assistant to apply the +changes. The integration then shows under **Settings → Devices & services**; +its entities are on the integration card and the Entities tab. + +### Configuration variables + +| Variable | Meaning | +|---|---| +| `username` | Full Matrix ID the bot logs in as, e.g. `@ha_bot:chans.xyz`. The `@` has a special YAML meaning, so always quote it. | +| `password` | The bot account's password (MAS local password). | +| `homeserver` | Full client-server URL of the homeserver. | +| `rooms` | Rooms the bot should join and listen in. List **all** rooms commands are to be received in, even if a command scopes itself to fewer rooms. Accepts internal room ID (`!…:chans.xyz`) or alias (`#room:chans.xyz`). | +| `commands` | Commands to listen for. Each fires a `matrix_command` event when triggered. | + +### Command types + +| Key | Triggers when | +|---|---| +| `word` | A message starts with `!`. Arguments after the word are captured as a list in the event's `data`. | +| `expression` | A message matches the Python regexp. The regexp group dictionary is captured in the event's `data`. | +| `reaction` | A message is reacted to with the given emoji. | +| `name` | The command name, exposed as an attribute of the fired event. | + +A command can be scoped to specific rooms with a per-command `rooms` list (the +room must still be listed under the top-level `rooms`). + +## Event data + +When a command triggers, a `matrix_command` event fires with: + +- `name` — the command name. +- `data` — for `word` commands, a list of arguments (everything after the word, + split on spaces); for `expression` commands, the group dictionary of the + matching regexp. +- `event_id` — the received message's identifier. +- `thread_parent` — the root message ID of the thread; equals `event_id` when + the message is not inside a thread. + +## Notifications (notify.matrix) + +Deliver notifications from Home Assistant to a Matrix room (direct or group): + +```yaml +notify: + - name: matrix_notify + platform: matrix + default_room: "#hasstest:chans.xyz" +``` + +- The target room must already exist; get its canonical ID from the room + settings dialog (`!:chans.xyz`) or an alias (`#roomname:chans.xyz`). + Quote the room ID/alias in YAML to escape the `!` / `#` characters. +- The notifying account may need to be invited to the room, depending on room + policy. + +Message formats (`data.format`): `text` (default) and `html`. Images can be +attached via `data.images` (list of file paths); files from outside allowed +folders require `homeassistant.allowlist_external_dirs` to list the source +folder. + +Reply inside a thread by passing the root message ID into `data.thread_id`: + +```yaml +action: notify.matrix_notify +data: + message: "Reply message goes here" + data: + thread_id: "{{ trigger.event.data.thread_parent }}" +``` + +## Actions + +- `matrix.react` — send a reaction to a message in a Matrix room + (`reaction`, `room`, `message_id`). +- `matrix.send_message` — send a message to one or more Matrix rooms. + +## Comprehensive example (adapted) + +```yaml +matrix: + homeserver: https://synapse.chans.xyz + username: "@ha_bot:chans.xyz" + password: supersecurepassword + rooms: + - "#hasstest:chans.xyz" + - "#someothertest:chans.xyz" + commands: + - word: testword + name: testword + rooms: + - "#someothertest:chans.xyz" + - expression: "My name is (?P.*)" + name: introduction + - reaction: 👍 + name: thumbsup + +notify: + - name: matrix_notify + platform: matrix + default_room: "#hasstest:chans.xyz" + +automation: + - alias: "Respond to !testword" + triggers: + - trigger: event + event_type: matrix_command + event_data: + command: testword + actions: + - action: notify.matrix_notify + data: + message: "It looks like you wrote !testword" +``` + +## References + +- Home Assistant Matrix integration: +- Matrix host facts: [`hosts/synapse.chans.xyz.md`](../hosts/synapse.chans.xyz.md) +- Matrix deployment and upstream index: [`matrix-upstream.md`](matrix-upstream.md) +- Hermes Agent Matrix channel (MAS local-password + access-token pattern): [`hermes-matrix.md`](hermes-matrix.md) +- HA host facts: [`hosts/hass.windy.lan.md`](../hosts/hass.windy.lan.md) +- HA maintenance runbook: [`runbooks/home-assistant-maintenance.md`](../runbooks/home-assistant-maintenance.md) diff --git a/docs/matrix-upstream.md b/docs/matrix-upstream.md index 4782e8e..e17505b 100644 --- a/docs/matrix-upstream.md +++ b/docs/matrix-upstream.md @@ -108,3 +108,4 @@ Steps: - Matrix Authentication Service: - Matrix spec: - Federation tester: +- Home Assistant Matrix integration: [home-assistant-matrix.md](home-assistant-matrix.md) diff --git a/hosts/dns.windy.lan.md b/hosts/dns.windy.lan.md index 63bc9ad..882dea3 100644 --- a/hosts/dns.windy.lan.md +++ b/hosts/dns.windy.lan.md @@ -48,9 +48,11 @@ space before increasing retention. DNSSEC is disabled because the selected upstream path did not pass the known-bad-signature validation check; do not enable it without re-testing validated upstreams. -The compatible names `hass.windy.lan` and legacy `hass.local` currently point -to the same Home Assistant address. Migrate clients to `hass.windy.lan`; keep -the legacy rewrite until its planned retirement. +`hass.windy.lan` points to Home Assistant via this rewrite. The legacy +`hass.local` rewrite was removed on 2026-08-14; `hass.local` now resolves only +via HAOS mDNS/LLMNR (`hostname: hass`), not via AdGuard Home. The +`nas.windy.local` rewrite was likewise removed on 2026-08-14; `.local` names +are now left to mDNS only. Remaining rewrites all use `.windy.lan`. ## Mihomo and routing boundary