Files
ha-led-remote/packages/led_chambre.yaml
T
bachirandClaude Opus 5.5 e4bd58afa7 Télécommande LED pour chambre d'enfant via Home Assistant
- 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>
2026-10-10 21:29:51 +02:00

321 lines
11 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# =============================================================================
# Télécommande LED chambre
# Ruban : __ENTITE__ (Zigbee2MQTT, driver Iderwel RGB non adressable)
#
# Généré par deploy.sh : dans le dépôt, ce fichier est un modèle dont les
# marqueurs (entre doubles soulignés) sont remplacés depuis config.local.env.
#
# Point d'entrée unique : webhook local (POST JSON), voir CLAUDE.md
# {"action": "off"}
# {"action": "couleur", "teinte": 0-359}
# {"action": "luminosite", "valeur": 5-100}
# {"action": "effet", "nom": "arc_en_ciel|respiration|coucher_de_soleil|ocean|veilleuse
# |disco|boum|tourbillon"}
#
# Règle Zigbee : au moins 2 s entre deux commandes dans les effets calmes,
# avec une transition égale au délai. Les effets « boum » (disco, boum,
# tourbillon) vont au minimum autorisé : 1 commande par seconde. Pas de
# stroboscope (réseau Zigbee, et risque photosensible).
# =============================================================================
script:
# --- Arc-en-ciel : tour complet des teintes par pas de 30°, 3 s par pas ---
# Ne touche pas à la luminosité : le curseur reste actif pendant l'effet.
led_arc_en_ciel:
alias: "LED chambre – arc-en-ciel"
icon: mdi:looks
mode: restart
sequence:
- repeat:
while: "{{ true }}"
sequence:
- repeat:
for_each: [0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330]
sequence:
- action: light.turn_on
target:
entity_id: __ENTITE__
data:
hs_color: ["{{ repeat.item }}", 100]
transition: 3
- delay: 3
# --- Respiration : garde la couleur actuelle, luminosité 100 % <-> 10 % ---
led_respiration:
alias: "LED chambre – respiration"
icon: mdi:weather-windy
mode: restart
sequence:
- repeat:
while: "{{ true }}"
sequence:
- action: light.turn_on
target:
entity_id: __ENTITE__
data:
brightness_pct: 10
transition: 3
- delay: 3
- action: light.turn_on
target:
entity_id: __ENTITE__
data:
brightness_pct: 100
transition: 3
- delay: 3
# --- Coucher de soleil : orange -> rose -> rouge -> violet, fondus de 5 s ---
led_coucher_de_soleil:
alias: "LED chambre – coucher de soleil"
icon: mdi:weather-sunset
mode: restart
sequence:
- repeat:
while: "{{ true }}"
sequence:
- repeat:
for_each:
- { h: 25, s: 100 } # orange
- { h: 340, s: 70 } # rose
- { h: 0, s: 100 } # rouge
- { h: 285, s: 90 } # violet
sequence:
- action: light.turn_on
target:
entity_id: __ENTITE__
data:
hs_color: ["{{ repeat.item.h }}", "{{ repeat.item.s }}"]
transition: 5
- delay: 5
# --- Océan : bleu -> turquoise -> cyan -> bleu profond, fondus de 4 s ---
led_ocean:
alias: "LED chambre – océan"
icon: mdi:waves
mode: restart
sequence:
- repeat:
while: "{{ true }}"
sequence:
- repeat:
for_each:
- { h: 215, s: 100 } # bleu
- { h: 172, s: 100 } # turquoise
- { h: 190, s: 80 } # cyan clair
- { h: 235, s: 100 } # bleu profond
sequence:
- action: light.turn_on
target:
entity_id: __ENTITE__
data:
hs_color: ["{{ repeat.item.h }}", "{{ repeat.item.s }}"]
transition: 4
- delay: 4
# --- Veilleuse : orange très faible, extinction en fondu après 30 min ---
led_veilleuse:
alias: "LED chambre – veilleuse"
icon: mdi:weather-night
mode: restart
sequence:
- action: light.turn_on
target:
entity_id: __ENTITE__
data:
hs_color: [28, 100]
brightness_pct: 5
transition: 3
- delay: "00:29:00"
# Dernière minute : fondu jusqu'à l'extinction (total 30 min)
- action: light.turn_off
target:
entity_id: __ENTITE__
data:
transition: 60
# --- Disco : saut de couleur vive chaque seconde ---
# Index (n x 7) mod 10 : parcourt les 10 couleurs dans un ordre qui paraît
# aléatoire, sans jamais répéter deux fois la même d'affilée.
# Ne touche pas à la luminosité.
led_disco:
alias: "LED chambre – disco"
icon: mdi:mirror-rectangle
mode: restart
# Rouge = 360 et non 0 : une teinte 0 tirée de la palette arrivait vide
# dans hs_color et faisait planter le script.
variables:
palette: [360, 30, 55, 120, 170, 200, 235, 275, 300, 320]
sequence:
- repeat:
while: "{{ true }}"
sequence:
- action: light.turn_on
target:
entity_id: __ENTITE__
data:
hs_color: ["{{ palette[(repeat.index * 7) % 10] }}", 100]
transition: 0
- delay: 1
# --- Boum : flash d'une nouvelle couleur à 100 %, puis fondu à 25 % (2 s) ---
led_boum:
alias: "LED chambre – boum"
icon: mdi:speaker
mode: restart
variables:
palette: [320, 200, 55, 275, 360, 170, 30, 235] # rouge = 360, voir disco
sequence:
- repeat:
while: "{{ true }}"
sequence:
- action: light.turn_on
target:
entity_id: __ENTITE__
data:
hs_color: ["{{ palette[repeat.index % 8] }}", 100]
brightness_pct: 100
transition: 0
- delay: 1
- action: light.turn_on
target:
entity_id: __ENTITE__
data:
brightness_pct: 25
transition: 1
- delay: 1
# --- Tourbillon : arc-en-ciel rapide, pas de 45°, fondus de 1 s ---
# Ne touche pas à la luminosité.
led_tourbillon:
alias: "LED chambre – tourbillon"
icon: mdi:tailwind
mode: restart
sequence:
- repeat:
while: "{{ true }}"
sequence:
- repeat:
for_each: [0, 45, 90, 135, 180, 225, 270, 315]
sequence:
- action: light.turn_on
target:
entity_id: __ENTITE__
data:
hs_color: ["{{ repeat.item }}", 100]
transition: 1
- delay: 1
automation:
- id: led_chambre_webhook
alias: "LED chambre – webhook télécommande"
description: >-
Point d'entrée unique de la page /local/led/. N'agit que sur
__ENTITE__ et sur la liste blanche des scripts led_*.
# La dernière commande gagne (taps rapides, curseur de luminosité)
mode: restart
max_exceeded: silent
triggers:
- trigger: webhook
webhook_id: __WEBHOOK_ID__
allowed_methods: [POST]
local_only: true
variables:
ruban: __ENTITE__
# Liste blanche : nom reçu -> script. Jamais d'entity_id construit depuis le JSON.
effets:
arc_en_ciel: script.led_arc_en_ciel
respiration: script.led_respiration
coucher_de_soleil: script.led_coucher_de_soleil
ocean: script.led_ocean
veilleuse: script.led_veilleuse
disco: script.led_disco
boum: script.led_boum
tourbillon: script.led_tourbillon
tous_les_effets:
- script.led_arc_en_ciel
- script.led_respiration
- script.led_coucher_de_soleil
- script.led_ocean
- script.led_veilleuse
- script.led_disco
- script.led_boum
- script.led_tourbillon
corps: "{{ trigger.json if trigger.json is defined and trigger.json is mapping else {} }}"
act: "{{ corps.get('action', '') | string }}"
nom: "{{ corps.get('nom', '') | string }}"
teinte: "{{ ([0, corps.get('teinte', 0) | int(0), 359] | sort)[1] }}"
valeur: "{{ ([5, corps.get('valeur', 100) | int(100), 100] | sort)[1] }}"
# Remettre 100 % si le ruban est éteint ou sort d'un effet qui
# baisse la luminosité (veilleuse 5 %, respiration 10 %, boum 25 %).
remettre_lumiere: >-
{{ is_state(ruban, 'off')
or is_state('script.led_veilleuse', 'on')
or is_state('script.led_respiration', 'on')
or is_state('script.led_boum', 'on') }}
actions:
- choose:
# --- off : arrête les effets et éteint ---
- conditions: "{{ act == 'off' }}"
sequence:
- action: script.turn_off
target:
entity_id: "{{ tous_les_effets }}"
- action: light.turn_off
target:
entity_id: "{{ ruban }}"
data:
transition: 1
# --- couleur fixe ---
- conditions: "{{ act == 'couleur' }}"
sequence:
- action: script.turn_off
target:
entity_id: "{{ tous_les_effets }}"
- if: "{{ remettre_lumiere }}"
then:
- action: light.turn_on
target:
entity_id: "{{ ruban }}"
data:
hs_color: ["{{ teinte }}", 100]
brightness_pct: 100
transition: 1
else:
- action: light.turn_on
target:
entity_id: "{{ ruban }}"
data:
hs_color: ["{{ teinte }}", 100]
transition: 1
# --- luminosité (ne coupe pas l'effet en cours) ---
- conditions: "{{ act == 'luminosite' }}"
sequence:
- action: light.turn_on
target:
entity_id: "{{ ruban }}"
data:
brightness_pct: "{{ valeur }}"
transition: 1
# --- effet de la liste blanche ---
- conditions: "{{ act == 'effet' and nom in effets }}"
sequence:
- action: script.turn_off
target:
entity_id: "{{ tous_les_effets }}"
# Allume à 100 % avant un effet qui ne règle pas la luminosité
- if: "{{ remettre_lumiere and nom not in ['veilleuse', 'respiration', 'boum'] }}"
then:
- action: light.turn_on
target:
entity_id: "{{ ruban }}"
data:
brightness_pct: 100
transition: 1
- action: script.turn_on
target:
entity_id: "{{ effets[nom] }}"
# Toute autre requête est ignorée silencieusement.