# Ma lumière — télécommande LED pour une chambre d'enfant Une appli Android sur le lecteur MP3 d'un enfant pilote le ruban LED de sa chambre via Home Assistant. L'enfant n'a accès à rien d'autre dans HA. ``` [Lecteur MP3 Android] [Home Assistant sur le LAN] appli « Ma lumière » (WebView plein écran) --GET--> /local/led/index.html (page servie par HA) --POST--> /api/webhook/ (local_only, POST) │ ▼ automatisation (liste blanche) │ ▼ light. / script.led_* (8 effets) ``` Ce document décrit l'installation et récapitule les interventions réalisées le 10 octobre 2026. --- ## 1. Installation Ce dépôt est **public** : il ne contient aucune valeur réelle. Les fichiers versionnés sont des **modèles** avec des marqueurs, remplacés au déploiement par les valeurs de `config.local.env`. | Marqueur | Variable | Exemple | |---|---|---| | `__HA_IP__` / `__HA_PORT__` | adresse de HA sur le LAN | `192.168.1.10` / `8123` | | `__WEBHOOK_ID__` | ID du webhook, long et aléatoire | `led-chambre-$(openssl rand -hex 16)` | | `__ENTITE__` | entité du ruban | `light.ruban_led` | | `__PRENOM__` | prénom affiché dans la page | `Camille` | | — | `SSH_HOST` : alias SSH vers HA | `homeassistant` | | — | `APP_ID` : identifiant Android de l'appli | `fr.exemple.lumiere` | **Étapes :** 1. Copier `config.example.env` en `config.local.env` et le remplir. 2. Une seule fois, dans `configuration.yaml` de HA, ajouter `packages: !include_dir_named packages` sous `homeassistant:`, puis redémarrer HA. 3. Lancer `./deploy.sh`. Il génère `dist/`, copie le package et la page sur HA via SSH, lance `ha core check`, puis recharge les scripts et les automatisations. Le rechargement utilise un jeton HA placé dans `.env` (`HA_TOKEN=…`), optionnel. 4. Compiler l'appli avec `android/build.sh` (il faut un SDK Android avec build-tools 30.0.2 et la plateforme 25), puis l'installer avec `adb install -r android/build/ma-lumiere.apk`. **Fichiers jamais versionnés** (voir `.gitignore`) : `config.local.env`, `.env`, `dist/`, `android/build/`. --- ## 2. Contexte technique | Élément | Valeur | |---|---| | Home Assistant | HAOS 2026.6 sur Odroid N2, en HTTP sur le LAN, accès distant uniquement via VPN WireGuard | | Ruban | RGB non adressable, driver Zigbee Iderwel via **Zigbee2MQTT**, mode couleur `xy` | | Lecteur MP3 | Fanvace M36, Android 13, écran 640×1136 à 300 dpi (≈ 341 × 530 px utiles), WebView 108, **navigateur bloqué par l'app lock** | **Constats faits pendant les tests sur le ruban :** - Couleurs fixes, luminosité et `transition` (fondus) fonctionnent bien. - Les **effets natifs du driver** (`colorloop`, `breathe`…) **ne sont pas fiables** : ils ont été abandonnés au profit de scripts HA. - Le driver ne remonte pas l'effet en cours : l'attribut `effect` vaut toujours `None` dans HA. --- ## 3. Interventions sur Home Assistant ### 3.1 Fichiers modifiés ou créés | Fichier sur HA | Action | |---|---| | `/config/configuration.yaml` | **1 ligne ajoutée** sous `homeassistant:` : `packages: !include_dir_named packages` | | `/config/packages/led_chambre.yaml` | **créé** : 8 scripts d'effets et l'automatisation webhook | | `/config/www/led/` | **créé** : `index.html`, `manifest.json`, `icon.svg`, `icon-180/192/512.png` | Aucun autre fichier existant n'a été modifié. En particulier, `scripts.yaml` et `automations.yaml` n'ont pas été touchés. ### 3.2 Sauvegardes laissées sur HA | Sauvegarde | Contenu | |---|---| | `/config/configuration.yaml.bak-led-20261010` | `configuration.yaml` d'origine, avant l'ajout de `packages:` | | `/config/led_chambre.yaml.bak-20261010-boum` | package avant l'ajout des effets disco/boum/tourbillon | | `/config/led_chambre.yaml.bak-20261010-disco` | package avant la correction du bug disco | Ces sauvegardes sont volontairement placées **hors** de `packages/`, pour que HA ne les charge pas. ### 3.3 Opérations effectuées 1. Vérification de la configuration avec `ha core check` avant chaque mise en service. 2. **Un redémarrage de HA**, pour activer `packages:`. 3. Ensuite, uniquement des rechargements à chaud (`script.reload`, `automation.reload`), sans redémarrage. ### 3.4 Contenu du package `led_chambre.yaml` **Scripts d'effets**, tous en `mode: restart` : | Script | Comportement | Rythme Zigbee | |---|---|---| | `led_arc_en_ciel` | tour des teintes par pas de 30° | 1 commande / 3 s | | `led_respiration` | garde la couleur, luminosité 10 % ↔ 100 % | 1 / 3 s | | `led_coucher_de_soleil` | orange → rose → rouge → violet | 1 / 5 s | | `led_ocean` | bleu → turquoise → cyan → bleu profond | 1 / 4 s | | `led_veilleuse` | orange à 5 %, puis extinction en fondu au bout de 30 min | — | | `led_disco` | saut de couleur vive chaque seconde | 1 / s | | `led_boum` | flash d'une couleur à 100 %, puis fondu à 25 % | 1 / s | | `led_tourbillon` | arc-en-ciel rapide, pas de 45° | 1 / s | Il n'y a pas de stroboscope : le réseau Zigbee ne le permet pas, et il présente un risque photosensible. **Automatisation `led_chambre_webhook`** : - Webhook avec `local_only: true`, `allowed_methods: [POST]` et un ID aléatoire long. - `mode: restart` : si plusieurs commandes arrivent très vite, seule la dernière compte. - Contrat du corps JSON : | `action` | champ | effet | |---|---|---| | `off` | — | arrête tous les effets et éteint le ruban (fondu 1 s) | | `couleur` | `teinte` 0–359 | arrête les effets et applique une couleur fixe | | `luminosite` | `valeur` 5–100 | change la luminosité sans couper l'effet en cours | | `effet` | `nom` | arrête l'effet en cours et lance `script.led_` s'il est dans la liste blanche | - Liste blanche stricte : le JSON ne sert jamais à construire un entity_id. Les valeurs sont bornées et ont des valeurs par défaut, et toute autre requête est ignorée. - La luminosité **revient automatiquement à 100 %** quand le ruban était éteint, ou quand on sort de la veilleuse, de la respiration ou de Boum. --- ## 4. Interventions sur le lecteur MP3 - Installation via ADB de l'appli **« Ma lumière »**, aujourd'hui en **version 1.1**. - Aucun autre réglage n'a été modifié sur l'appareil. - Le débogage USB a été activé pour l'installation. **Ce que fait l'appli** : c'est un *wrapper* WebView de 37 Ko qui affiche en plein écran la page servie par HA. - Elle n'autorise la navigation que vers `http:///local/led/`, et bloque toute requête vers une autre adresse. - Le HTTP non chiffré n'est autorisé que vers l'IP de HA. - Si HA ne répond pas, elle affiche « La lumière ne répond pas » avec un bouton Réessayer. - Le cache est désactivé, et la page est rechargée à chaque retour dans l'appli (ajouté en v1.1). Une modification de la page sur HA apparaît donc sans réinstaller l'appli. --- ## 5. Fichiers du dépôt ``` ├── CLAUDE.md consignes du projet ├── README.md ce document ├── config.example.env modèle de configuration (à copier en config.local.env) ├── deploy.sh génère dist/ depuis les modèles et déploie sur HA ├── packages/led_chambre.yaml modèle → /config/packages/led_chambre.yaml ├── www/led/ modèles → /config/www/led/ │ ├── index.html la page (HTML/CSS/JS ES5 en un seul fichier) │ ├── manifest.json │ └── icon.svg, icon-*.png └── android/ sources de l'APK ├── build.sh compilation sans Gradle → build/ma-lumiere.apk ├── AndroidManifest.xml ├── res/ icônes, thème, network_security_config (modèle) └── src/app/lumiere/MainActivity.java ``` **La page** est pensée pour tenir sur un seul écran du lecteur : - en haut, Veilleuse et Éteindre ; - 9 pastilles de couleur ; - 4 niveaux de luminosité ; - 4 animations calmes ; - 3 animations « boum » au style néon. Elle n'affiche aucun état du ruban, puisque le webhook ne renvoie rien. Un appui réussi fait apparaître un halo blanc ; une erreur fait trembler le bouton et affiche un message rouge. --- ## 6. Tests réalisés - **Ruban, en direct** : couleurs, fondus, luminosité, puis effets natifs, qui se sont révélés peu fiables. - **Webhook avec curl** : - toutes les actions et les 8 effets fonctionnent, et un seul script tourne à la fois ; - les entrées non valides sont ignorées sans erreur : nom hors liste blanche (`../light.autre`), action inconnue, corps non JSON ; - les valeurs sont bornées (teinte 999 → 359, luminosité 1 → 5 %) ; - une requête GET est refusée (405) ; - les logs HA sont propres. - **De bout en bout sur le lecteur** : des taps simulés via ADB (couleur, arc-en-ciel, veilleuse, éteindre) ont été vérifiés dans l'état HA. - **Modèles** : les fichiers générés par `deploy.sh --render-only` sont identiques aux fichiers en service, hors commentaires. L'APK généré contient la bonne adresse et le bon identifiant. **Bug corrigé :** `led_disco` plantait au bout d'environ 10 s, parce qu'une teinte `0` tirée d'une palette en `variables` arrivait vide dans `hs_color`. Le rouge vaut maintenant `360` dans les palettes de Disco et de Boum. --- ## 7. Maintenance **Tester le webhook** (avec les valeurs de `config.local.env`) : ```bash curl -X POST -H "Content-Type: application/json" \ -d '{"action":"effet","nom":"arc_en_ciel"}' \ http://:/api/webhook/ ``` **Modifier la page ou un effet :** 1. Modifier le modèle (`www/led/index.html` ou `packages/led_chambre.yaml`). 2. Lancer `./deploy.sh`. 3. Rouvrir l'appli sur le lecteur. Pas de réinstallation. **Ajouter un effet :** 1. Ajouter le script `led_` dans le package. 2. Ajouter `` dans `effets` **et** dans `tous_les_effets` de l'automatisation. 3. Ajouter une tuile dans la page. 4. Lancer `./deploy.sh`. **Recompiler l'appli**, par exemple si l'IP de HA change : 1. Mettre à jour `config.local.env`. 2. Lancer `android/build.sh`, puis `adb install -r android/build/ma-lumiere.apk`. Il faut garder la même clé de signature, `~/.android/debug.keystore`. Sinon Android refusera la mise à jour et il faudra désinstaller l'appli d'abord. **Tout retirer :** 1. Supprimer `/config/packages/led_chambre.yaml` et `/config/www/led/`. 2. Retirer la ligne `packages:` de `configuration.yaml`, ou restaurer la sauvegarde. 3. Lancer `ha core check`, puis redémarrer HA. 4. Désinstaller l'appli : `adb uninstall `. --- ## 8. Reste à faire - [ ] Autoriser « Ma lumière » dans l'**app lock** du lecteur. - [ ] **Réserver l'IP** de HA sur la box, puisqu'elle est compilée dans l'appli. - [ ] **Supprimer le jeton** HA de développement (profil HA → Sécurité) et le fichier `.env` quand ils ne sont plus utiles. L'appli n'en a pas besoin. - [ ] Éventuellement, désactiver le **débogage USB** sur le lecteur. - [ ] Éventuellement, supprimer les sauvegardes `*.bak*` dans `/config` une fois que tout est validé. ℹ️ Le dossier `/local/` de HA est accessible sans authentification depuis le LAN. C'est voulu : la page ne contient aucun secret, et le webhook n'accepte que des commandes en liste blanche, uniquement depuis le réseau local.