Files
ouranos/docs/hass.md

223 lines
7.7 KiB
Markdown

# Home Assistant
## Overview
[Home Assistant](https://github.com/home-assistant/core) is an open-source home automation platform. In the Ouranos sandbox it runs as a native Python application inside a virtual environment, backed by PostgreSQL for state recording and fronted by HAProxy for TLS termination.
**Host:** Oberon
**Role:** container_orchestration
**Port:** 8123
**URL:** https://hass.ouranos.helu.ca
## Architecture
```
┌──────────┐ HTTPS ┌──────────────┐ HTTP ┌──────────────┐
│ Client │────────▶│ HAProxy │────────▶│ Home │
│ │ │ (Titania) │ │ Assistant │
└──────────┘ │ :443 TLS │ │ (Oberon) │
└──────────────┘ │ :8123 │
└──────┬───────┘
┌─────────────────┼─────────────────┐
│ │ │
┌────▼─────┐ ┌──────▼──────┐ ┌─────▼─────┐
│PostgreSQL│ │ Alloy │ │ Prometheus│
│(Portia) │ │ (Oberon) │ │(Prospero) │
│ :5432 │ │ scrape │ │ remote │
│ recorder │ │ /api/prom │ │ write │
└──────────┘ └─────────────┘ └───────────┘
```
## Ansible Deployment
### Playbook
```bash
cd ansible
ansible-playbook hass/deploy.yml
```
### Files
| File | Purpose |
|------|---------|
| `hass/deploy.yml` | Main deployment playbook |
| `hass/configuration.yaml.j2` | Home Assistant configuration |
| `hass/requirements.txt.j2` | Python package pinning |
| `hass/hass.service.j2` | Systemd service unit |
### Variables
#### Host Variables (`host_vars/oberon.incus.yml`)
| Variable | Description | Value |
|----------|-------------|-------|
| `hass_user` | System user | `hass` |
| `hass_group` | System group | `hass` |
| `hass_directory` | Install directory | `/srv/hass` |
| `hass_media_directory` | Media storage | `/srv/hass/media` |
| `hass_port` | HTTP listen port | `8123` |
| `hass_version` | Pinned HA release | `2026.2.0` |
| `hass_db_host` | PostgreSQL host | `portia.incus` |
| `hass_db_port` | PostgreSQL port | `5432` |
| `hass_db_name` | Database name | `hass` |
| `hass_db_user` | Database user | `hass` |
| `hass_db_password` | Database password | `{{ vault_hass_db_password }}` |
| `hass_metrics_token` | Prometheus bearer token | `{{ vault_hass_metrics_token }}` |
#### Host Variables (`host_vars/portia.incus.yml`)
| Variable | Description |
|----------|-------------|
| `hass_db_name` | Database name on Portia |
| `hass_db_user` | Database user on Portia |
| `hass_db_password` | `{{ vault_hass_db_password }}` |
#### Vault Variables (`group_vars/all/vault.yml`)
| Variable | Description |
|----------|-------------|
| `vault_hass_db_password` | PostgreSQL password for hass database |
| `vault_hass_metrics_token` | Long-Lived Access Token for Prometheus scraping |
## Configuration
### PostgreSQL Recorder
Home Assistant uses the `recorder` integration to persist entity states and events to PostgreSQL on Portia instead of the default SQLite. Configured in `configuration.yaml.j2`:
```yaml
recorder:
db_url: "postgresql://hass:<password>@portia.incus:5432/hass"
purge_keep_days: 30
commit_interval: 1
```
The database and user are provisioned by `postgresql/deploy.yml` alongside other service databases.
### HTTP / Reverse Proxy
HAProxy on Titania terminates TLS and forwards to Oberon:8123. The `http` block in `configuration.yaml.j2` configures trusted proxies so HA correctly reads `X-Forwarded-For` headers:
```yaml
http:
server_port: 8123
use_x_forwarded_for: true
trusted_proxies:
- 10.0.0.0/8
```
### HAProxy Backend
Defined in `host_vars/titania.incus.yml` under `haproxy_backends`:
| Setting | Value |
|---------|-------|
| Subdomain | `hass` |
| Backend | `oberon.incus:8123` |
| Health path | `/api/` |
| Timeout | 300s (WebSocket support) |
The wildcard TLS certificate (`*.ouranos.helu.ca`) covers `hass.ouranos.helu.ca` automatically — no certificate changes required.
## Authentication
Home Assistant uses its **native `homeassistant` auth provider** (built-in username/password). HA does not support OIDC/OAuth2 natively, so Casdoor SSO integration is not available.
On first deployment, HA will present an onboarding wizard to create the initial admin user.
## Monitoring
### Prometheus Metrics
Home Assistant exposes Prometheus metrics at `/api/prometheus`. The Alloy agent on Oberon scrapes this endpoint with bearer token authentication and remote-writes to Prometheus on Prospero.
| Setting | Value |
|---------|-------|
| Metrics path | `/api/prometheus` |
| Scrape interval | 60s |
| Auth | Bearer token (Long-Lived Access Token) |
**⚠️ Two-Phase Metrics Bootstrapping:**
The `vault_hass_metrics_token` must be a Home Assistant **Long-Lived Access Token**, which can only be generated from the HA web UI after the initial deployment:
1. Deploy Home Assistant: `ansible-playbook hass/deploy.yml`
2. Complete the onboarding wizard at `https://hass.ouranos.helu.ca`
3. Navigate to **Profile → Security → Long-Lived Access Tokens → Create Token**
4. Store the token in vault: `vault_hass_metrics_token: "<token>"`
5. Redeploy Alloy to pick up the token: `ansible-playbook alloy/deploy.yml`
Until the token is created, the Alloy hass scrape will fail silently.
### Loki Logs
Systemd journal logs are collected by Alloy's `loki.source.journal` and shipped to Loki on Prospero.
```bash
# Query in Grafana Explore
{job="systemd", hostname="oberon"} |= "hass"
```
## Operations
### Start / Stop
```bash
sudo systemctl start hass
sudo systemctl stop hass
sudo systemctl restart hass
```
### Health Check
```bash
curl http://localhost:8123/api/
```
### Logs
```bash
journalctl -u hass -f
```
### Version Upgrade
1. Update `hass_version` in `host_vars/oberon.incus.yml`
2. Run: `ansible-playbook hass/deploy.yml`
The playbook will reinstall the pinned version via pip and restart the service.
## Troubleshooting
### Common Issues
| Symptom | Cause | Resolution |
|---------|-------|------------|
| Service won't start | Missing Python deps | Check `pip install` output in deploy log |
| Database connection error | Portia unreachable | Verify PostgreSQL is running: `ansible-playbook postgresql/deploy.yml` |
| 502 via HAProxy | HA not listening | Check `systemctl status hass` on Oberon |
| Metrics scrape failing | Missing/invalid token | Generate Long-Lived Access Token from HA UI (see Monitoring section) |
### Debug Mode
```bash
# Check service status
sudo systemctl status hass
# View recent logs
journalctl -u hass --since "5 minutes ago"
# Test database connectivity from Oberon
psql -h portia.incus -U hass -d hass -c "SELECT 1"
```
## References
- [Home Assistant Documentation](https://www.home-assistant.io/docs/)
- [Home Assistant GitHub](https://github.com/home-assistant/core)
- [Recorder Integration](https://www.home-assistant.io/integrations/recorder/)
- [Prometheus Integration](https://www.home-assistant.io/integrations/prometheus/)
- [HTTP Integration](https://www.home-assistant.io/integrations/http/)