# ============================================================================= # 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.