- Package HA : 8 scripts d'effets (calmes et « boum ») et une automatisation webhook local_only avec liste blanche stricte - Page web autonome (HTML/CSS/JS ES5) tenant sur un écran de lecteur MP3 - Mini APK Android (WebView verrouillée) compilé sans Gradle - Modèles + config.local.env non versionné : aucune valeur réelle (IP, ID du webhook, entité, prénom) dans le dépôt ; deploy.sh génère et déploie, android/build.sh injecte la configuration Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
81 lines
6.7 KiB
Markdown
81 lines
6.7 KiB
Markdown
# Télécommande LED chambre — Home Assistant
|
||
|
||
## Objectif
|
||
|
||
Permettre à ma fille de piloter le ruban LED de sa chambre depuis son lecteur MP3 Android, via une page web ultra simple, **sans lui donner accès au reste de Home Assistant**.
|
||
|
||
## Matériel
|
||
|
||
- Ruban LED **RGB non adressable** : toute la longueur affiche la même couleur. Aucun effet de « vague » spatiale n'est possible, seulement des effets globaux (cycles, fondus, respiration).
|
||
- Driver **Zigbee Iderwel** (PWM), intégré à Home Assistant.
|
||
- **Dépôt public** : aucune valeur réelle (IP, ID du webhook, entité, prénom, identifiant Android) dans les fichiers versionnés. Elles sont dans `config.local.env` (non versionné, modèle : `config.example.env`). Les fichiers versionnés sont des modèles avec des marqueurs `__HA_IP__`, `__HA_PORT__`, `__WEBHOOK_ID__`, `__ENTITE__`, `__PRENOM__` ; `deploy.sh` génère `dist/` et déploie, `android/build.sh` génère `Config.java` et la config réseau. Ne jamais réintroduire de valeur réelle dans un fichier versionné.
|
||
- entity_id du ruban : `ENTITE` dans `config.local.env` (mode couleur `xy` ; `hs_color` accepté, converti par HA)
|
||
- intégration Zigbee : **Zigbee2MQTT**
|
||
- HA : HAOS sur Odroid N2, adresse `HA_IP:HA_PORT` ; accès SSH via l'alias `SSH_HOST` (**root** : lecture seule par défaut). Jeton HA de dev dans `.env` (non versionné).
|
||
- **Effets natifs du driver (`colorloop`, `breathe`…) peu fiables** : testés, ne pas les utiliser. Les effets passent par des scripts HA. Couleurs fixes, luminosité et `transition` fonctionnent bien.
|
||
- Le driver ne remonte pas l'attribut `effect` (toujours `None` dans HA).
|
||
- MP3 : **Fanvace M36, Android 13**, écran 640×1136 à 300 dpi (≈ 341 px CSS de large), WebView 108. Branché en ADB.
|
||
- **Pas de navigateur accessible** (app lock) : la page est affichée par un mini APK WebView (`android/`, code dans le paquet `app.lumiere`, identifiant installé = `APP_ID` appliqué via `--rename-manifest-package`), qui n'autorise que `http://HA_IP:HA_PORT/local/led/`. Recompiler avec `android/build.sh` (clé de debug `~/.android/debug.keystore` : garder la même pour les mises à jour), installer avec `adb install -r android/build/ma-lumiere.apk`. Si l'IP de HA change : modifier `config.local.env`, recompiler et réinstaller.
|
||
|
||
## Architecture
|
||
|
||
```
|
||
[Page HTML statique] --POST JSON--> [Webhook HA (local_only)] --> [Automatisation] --> light.* / scripts d'effets
|
||
/config/www/led/ /api/webhook/<id>
|
||
servie sur /local/led/
|
||
```
|
||
|
||
1. **Scripts d'effets** (`script.led_<nom>`) : boucles infinies (`repeat: while true`) de `light.turn_on` avec `transition`, en `mode: restart`.
|
||
2. **Automatisation webhook** : point d'entrée unique, qui ne peut agir que sur le ruban et sur une liste fermée de scripts.
|
||
3. **Page web** : gros boutons, aucune authentification, à ajouter sur l'écran d'accueil du MP3.
|
||
|
||
## Contrat du webhook (corps JSON)
|
||
|
||
| action | champs | effet |
|
||
|--------------|--------------------------|-----------------------------------------------------------------|
|
||
| `off` | — | arrête tous les effets et éteint le ruban (transition 1 s) |
|
||
| `couleur` | `teinte` (0–359) | arrête les effets, couleur fixe `hs_color: [teinte, 100]` |
|
||
| `luminosite` | `valeur` (5–100) | `brightness_pct`, valeur bornée côté HA |
|
||
| `effet` | `nom` (ex. `arc_en_ciel`) | arrête l'effet en cours et lance `script.led_<nom>` s'il figure dans la liste blanche |
|
||
|
||
## Contraintes et règles
|
||
|
||
- **Sécurité**
|
||
- Webhook en `local_only: true` et `allowed_methods: [POST]`, avec un ID aléatoire long.
|
||
- Liste blanche stricte des scripts dans l'automatisation ; ne jamais construire un entity_id arbitraire à partir du JSON.
|
||
- Toutes les valeurs numériques sont bornées et ont un `default` côté HA.
|
||
- Aucun jeton HA dans la page.
|
||
- **Zigbee** : ne pas inonder le réseau. Garder au moins 1 s entre deux commandes dans les scripts (idéalement 2–3 s avec `transition` égale au delay). Les fondus sont lissés par le driver.
|
||
- **Effets** : lancer un nouvel effet ou une couleur fixe arrête toujours l'effet en cours.
|
||
- **Pas de retour d'état** : le webhook ne renvoie rien. La page ne connaît pas l'état du ruban et l'UI ne doit pas faire semblant de le connaître (pas de toggle on/off avec un état affiché).
|
||
- **Page web**
|
||
- Un seul fichier HTML autonome (CSS et JS inline), sans dépendance externe ni CDN.
|
||
- JS compatible avec un vieux navigateur Android : ES5 de préférence, ou au minimum sans syntaxe récente (`?.`, `??`, etc.). Utiliser `fetch` s'il est disponible, sinon `XMLHttpRequest`.
|
||
- Pensée pour un petit écran tactile en portrait : grandes cibles tactiles, peu de texte, très visuelle (pastilles de couleur, icônes d'effets), retour visuel immédiat au tap.
|
||
- Balises `<meta name="viewport">` et `apple-mobile-web-app` / `manifest` minimal pour que le raccourci sur l'écran d'accueil ressemble à une appli.
|
||
- Interface en français, pour une enfant.
|
||
|
||
## Effets (en place dans `packages/led_chambre.yaml`)
|
||
|
||
- Calmes (≥ 2 s entre commandes) : `arc_en_ciel` (pas de 30°, 3 s), `respiration` (10 ↔ 100 %, 3 s), `coucher_de_soleil` (5 s), `ocean` (4 s), `veilleuse` (orange 5 %, extinction en fondu après 30 min).
|
||
- Boum (1 commande/s, le minimum) : `disco` (sauts de couleur), `boum` (flash 100 % puis fondu à 25 %), `tourbillon` (arc-en-ciel rapide). Pas de stroboscope.
|
||
- Le webhook remet 100 % quand on sort d'un effet qui baisse la luminosité (veilleuse, respiration, boum) ou quand le ruban est éteint.
|
||
- Piège : dans une palette en `variables`, une teinte `0` arrive vide dans `hs_color` et fait planter le script → utiliser `360` pour le rouge.
|
||
- Ajouter un effet = script `led_<nom>` + entrée dans `effets` et `tous_les_effets` de l'automatisation + tuile dans la page ; déployer avec `./deploy.sh` (copie, `ha core check`, `script.reload` / `automation.reload`, pas de redémarrage).
|
||
|
||
## Livrables attendus
|
||
|
||
1. `scripts.yaml` : les scripts d'effets
|
||
2. `automations.yaml` : l'automatisation webhook (ou un package `packages/led_chambre.yaml` regroupant les deux)
|
||
3. `www/led/index.html` : la page
|
||
4. Procédure de test :
|
||
- `curl -X POST -H "Content-Type: application/json" -d '{"action":"effet","nom":"arc_en_ciel"}' http://<ha>:8123/api/webhook/<id>`
|
||
- tester chaque action avec curl **avant** de toucher à la page
|
||
5. APK « Ma lumière » (`android/`) installé via ADB, à autoriser dans l'app lock
|
||
|
||
## Ordre de travail
|
||
|
||
1. Scripts d'effets, testés depuis Outils de développement → Actions
|
||
2. Automatisation webhook, testée avec curl
|
||
3. Page web, testée d'abord sur desktop en vue mobile, puis sur le MP3
|