first
This commit is contained in:
80
docs/plans/2026-02-21-ha-entity-scanner-design.md
Normal file
80
docs/plans/2026-02-21-ha-entity-scanner-design.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Design — HA Entity Scanner & Manager
|
||||
|
||||
**Date** : 2026-02-21
|
||||
**Statut** : Approuvé
|
||||
|
||||
## Architecture globale
|
||||
|
||||
```
|
||||
Frontend (Vue 3 + Vuetify 3) ──► Backend (FastAPI) ──► Home Assistant
|
||||
SPA :5173 API :8000 :8123
|
||||
SQLite DB REST + WS
|
||||
```
|
||||
|
||||
- **Frontend** : SPA Vue 3 + Vite + Vuetify 3, appelle `/api/*`
|
||||
- **Backend** : FastAPI, proxy sécurisé vers HA (token jamais exposé côté client)
|
||||
- **DB** : SQLite via SQLModel, 3 tables (entities_cache, entity_flags, audit_log)
|
||||
- **Client HA** : `aiohttp` pour REST (`/api/states`) et WebSocket (`/api/websocket`)
|
||||
- **Déploiement** : Docker + docker-compose, variables d'env `HA_BASE_URL` / `HA_TOKEN`
|
||||
|
||||
## Backend — structure des modules
|
||||
|
||||
| Module | Responsabilité |
|
||||
|--------|---------------|
|
||||
| `app/main.py` | App FastAPI, CORS, lifespan |
|
||||
| `app/config.py` | Settings via pydantic-settings (env vars) |
|
||||
| `app/models.py` | SQLModel : EntityCache, EntityFlag, AuditLog |
|
||||
| `app/database.py` | Init SQLite, get_session |
|
||||
| `app/ha_client.py` | Client aiohttp : REST states + WS registry |
|
||||
| `app/routers/health.py` | `GET /api/health` |
|
||||
| `app/routers/scan.py` | `POST /api/scan` (async background task) |
|
||||
| `app/routers/entities.py` | `GET /api/entities`, `GET /api/entities/{id}` |
|
||||
| `app/routers/actions.py` | `POST /api/entities/actions` |
|
||||
| `app/routers/audit.py` | `GET /api/audit` |
|
||||
| `app/services/scanner.py` | Logique scan : fetch + normalize + upsert DB |
|
||||
| `app/services/entity_actions.py` | Logique disable/enable/hide via WS ou fallback |
|
||||
|
||||
## Frontend — composants principaux
|
||||
|
||||
| Composant | Rôle |
|
||||
|-----------|------|
|
||||
| `App.vue` | Layout principal, header avec statut HA |
|
||||
| `EntityTable.vue` | `v-data-table-server` avec tri, pagination, sélection |
|
||||
| `FilterBar.vue` | Recherche texte + dropdowns domaine/état + chips actives |
|
||||
| `EntityDetail.vue` | Panneau latéral détails + actions |
|
||||
| `AuditLog.vue` | Page journal des actions |
|
||||
| `ScanButton.vue` | Bouton scan + indicateur progression |
|
||||
|
||||
## Base de données SQLite
|
||||
|
||||
### entities_cache
|
||||
- `entity_id` (PK), `domain`, `friendly_name`, `state`
|
||||
- `attrs_json` (TEXT — attributs HA complets)
|
||||
- `device_class`, `unit_of_measurement`, `area_id`, `device_id`, `integration`
|
||||
- `is_disabled`, `is_hidden`, `is_available` (booléens déduits)
|
||||
- `last_changed`, `last_updated`, `fetched_at`
|
||||
|
||||
### entity_flags
|
||||
- `entity_id` (PK), `ignored_local` (bool), `favorite` (bool), `notes` (text)
|
||||
|
||||
### audit_log
|
||||
- `id` (PK auto), `ts` (datetime), `action` (text), `entity_ids_json` (text), `result` (text), `error` (text)
|
||||
|
||||
## Scan asynchrone
|
||||
|
||||
`POST /api/scan` lance une `BackgroundTask` FastAPI. Un état en mémoire (`idle`/`scanning`/`done`/`error` + progression) est exposé via `GET /api/health`. Le frontend poll le health pour afficher la progression.
|
||||
|
||||
## Désactivation des entités
|
||||
|
||||
1. **Méthode principale** : WS API HA `config/entity_registry/update` avec `disabled_by: "user"`
|
||||
2. **Fallback** : flag `ignored_local=true` en DB locale
|
||||
3. L'UI affiche un badge distinct selon le mode utilisé (désactivé HA vs ignoré local)
|
||||
4. Toute action journalisée dans `audit_log`
|
||||
|
||||
## Choix techniques
|
||||
|
||||
- **Python FastAPI** + **aiohttp** (client HA REST + WS)
|
||||
- **SQLModel** (SQLAlchemy + Pydantic)
|
||||
- **Vue 3** + **Vite** + **Vuetify 3**
|
||||
- **Docker** + **docker-compose**
|
||||
- UI en **français** uniquement
|
||||
Reference in New Issue
Block a user