Blueprint request: 4-button Zigbee switch to save & recall light scenes (ZHA)

Hi all,

I’ve been using Home Assistant for about 12 months now and think it’s fantastic, though occasionally a bit frustrating. I’m hoping for some advice or guidance with something I’d like to do using my Zigbee scene buttons.

I’m using a 4-button Zigbee scene switch with ZHA, which I plan to mount next to the original light switches. The original switches are momentary and connected to Shelly Pro Dimmer 2PM modules, allowing direct control of the lights without Home Assistant for fail-safe operation.

What I’d like to do is:

Set the lighting state using the original wall switches.
Use the Zigbee scene buttons to save and recall that lighting state on the fly.

So, for each button:

Set lights manually.
Press and hold the button to “remember” that setup (like an old car radio preset).
Short press to restore it.
Short press again to turn it off.

I’ve tried to implement this myself with automations, but it fails to store the scene.

Does a blueprint like this already exist? I’ve not been able to find one.
If not, would anyone be willing to help build one, or suggest the best approach?

Many thanks

Hello Coops5k,

I’m sure that can be done. Also having someone or something writing it for you means the second it misbehaves, you will be struggling back to find someone to fix it for you.
The best way to learn is to just try. Run thru the docs on getting started with the automation editor. If you get stuck, come back with what you have done.

and
The Home Assistant Cookbook - Index.

I have implemented most of the pieces for want you are trying to do, although not in a single automation.

My suggestion would be to build the automation up piece by piece.

Stage 1

I would start by simply automating a short press to turn the lights on and off.

I personally favor using scenes (even when I only have one light in the scene) as it makes it possible to add and remove lights (or change settings) without needing to adjust the automations.

Therefore I would start with two scene:

  • Off scene
  • On scene

All your automation needs to do is toggle between them - you might need an input helper to remember which scene is active, since that is not a feature of scenes themselves.

Stage 2

You need to figure out what happens if someone uses the physical switch to turn the light(s) on or off - do you still want the button to activate the opposite scene or do something different.

I am calling this out, since in my automations I typically only switch off if the light is currently on and vice-versa (there are some exceptions for PIR sensors).

TL;DR - get things working how you want them to work before moving on to memorization.

Stage 3

Next add the automation to capture the current state, I think you can just use a scene create something like this:

 - action: scene.create
    data:
      scene_id: on_memorized
      snapshot_entities:
        - light.main_room1

Run that whenever the long press happens I think create just “updates” the second time you run it.

Then you can switch out your “on” scene for the memorized scene in your existing toggle automation.

Stage 4 (Optional)

Stage 1-3 should do what you want, but you can make it cleaner by moving the logic to a template and/or by using the “off” scene as a list of lights to capture when you memorize the state.

None of this is really necessary it’s just handy if you want to use the automation for more than one room/switch.

Thanks for the responses guys, I have been trying to put it together in stages and so far have successfully created 4 scenes, mapped those scenes to the ZigBee buttons which I can recall correctly.

I have also got the triggering working for tap, double tap and hold working, so I can have different actions based on the button press type.

The problem I’m having is to do with the dynamic scene.create. It just doesn’t overwrite the original scene with the new values when called. I’ve tried this directly with the button and by triggering it manually in the automation.

action: scene.create
data:
  scene_id: lounge_1
  snapshot_entities:
    - light.lounge_centre_spots
    - light.lounge_corner_spots
    - light.lounge_lamps

From what I understand this should overwrite the scene “lounge 1” with the current values of the listed entities.

Am I missing something else?

Many thanks

scene.create will only overwrite scenes previously created by scene.create… and the created scene will not survive restart of HA or reload of the Scene integration.

Effectively there are two types of scenes.

  1. Scenes created using scene.create.
  2. Scenes manually created from the UI.

The first type can be created (and updated) using scripts - but cannot be edited in the UI.

The second type can be created (and updated) using the UI, but cannot be modified using scene.create

However both types can be applied (scene.turn_on) in the same way - effectively they are interchangeable when used.

Thanks for the tips guys. I have finally got it working after some debugging with Co-Pilot and Chat GPT. These are great tools, but they do also throw you off in the wrong direction by writing completely incorrect code sometimes.

The current example doesn’t keep the scenes after a reload or reboot of HA, but I have found this HACS integration which looks like it will solve the problem.

ha_res_scenes/README.md at main · nao-pon/ha_res_scenes · GitHub

Here is the full automation. Its a bit messy / repetitive so I will try and clean it up, and migrate to persistent scenes next.

alias: Lounge Scenes
description: ""
triggers:
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_short_press
    subtype: button_1
    id: b1_short
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_long_press
    subtype: button_1
    id: b1_long
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_short_press
    subtype: button_2
    id: b2_short
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_long_press
    subtype: button_2
    id: b2_long
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_short_press
    subtype: button_3
    id: b3_short
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_long_press
    subtype: button_3
    id: b3_long
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_short_press
    subtype: button_4
    id: b4_short
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_long_press
    subtype: button_4
    id: b4_long
    trigger: device
actions:
  - choose:
      - conditions:
          - condition: trigger
            id: b1_short
        sequence:
          - action: scene.turn_on
            target:
              entity_id: scene.lounge1
            data:
              transition: 1
      - conditions:
          - condition: trigger
            id: b1_long
        sequence:
          - action: scene.create
            data:
              scene_id: lounge1
              snapshot_entities:
                - light.lounge_lamps
                - light.lounge_centre_spots
                - light.lounge_corner_spots
      - conditions:
          - condition: trigger
            id: b2_short
        sequence:
          - action: scene.turn_on
            target:
              entity_id: scene.lounge2
            data:
              transition: 1
      - conditions:
          - condition: trigger
            id: b2_long
        sequence:
          - action: scene.create
            data:
              scene_id: lounge2
              snapshot_entities:
                - light.lounge_lamps
                - light.lounge_centre_spots
                - light.lounge_corner_spots
      - conditions:
          - condition: trigger
            id: b3_short
        sequence:
          - action: scene.turn_on
            target:
              entity_id: scene.lounge3
            data:
              transition: 1
      - conditions:
          - condition: trigger
            id: b3_long
        sequence:
          - action: scene.create
            data:
              scene_id: lounge3
              snapshot_entities:
                - light.lounge_lamps
                - light.lounge_centre_spots
                - light.lounge_corner_spots
      - conditions:
          - condition: trigger
            id: b4_short
        sequence:
          - action: scene.turn_on
            target:
              entity_id: scene.lounge4
            data:
              transition: 1
      - conditions:
          - condition: trigger
            id: b4_long
        sequence:
          - action: scene.create
            data:
              scene_id: lounge4
              snapshot_entities:
                - light.lounge_lamps
                - light.lounge_centre_spots
                - light.lounge_corner_spots
mode: single

Updated to make the scene toggle if the scene is active and the same scene button is pressed. If a different scene is triggered, it crossfades to the new scene. I am using a text helper to store the active scene.

alias: Lounge Scenes v2
description: ""
triggers:
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_short_press
    subtype: button_1
    id: b1_short
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_long_press
    subtype: button_1
    id: b1_long
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_short_press
    subtype: button_2
    id: b2_short
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_long_press
    subtype: button_2
    id: b2_long
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_short_press
    subtype: button_3
    id: b3_short
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_long_press
    subtype: button_3
    id: b3_long
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_short_press
    subtype: button_4
    id: b4_short
    trigger: device
  - device_id: dfc95dff38a7feca4fe2164c55767b57
    domain: zha
    type: remote_button_long_press
    subtype: button_4
    id: b4_long
    trigger: device
actions:
  - choose:
      - conditions:
          - condition: trigger
            id: b1_short
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: >-
                      {{ states('input_text.active_lounge_scene') == 'lounge1'
                      }}
                sequence:
                  - action: input_text.set_value
                    target:
                      entity_id: input_text.active_lounge_scene
                    data:
                      value: ""
                  - action: light.turn_off
                    target:
                      entity_id:
                        - light.lounge_lamps
                        - light.lounge_centre_spots
                        - light.lounge_corner_spots
                    data:
                      transition: 2
          - action: input_text.set_value
            target:
              entity_id: input_text.active_lounge_scene
            data:
              value: lounge1
          - action: scene.turn_on
            target:
              entity_id: scene.lounge1
            data:
              transition: 2
      - conditions:
          - condition: trigger
            id: b1_long
        sequence:
          - action: scene.create
            data:
              scene_id: lounge1
              snapshot_entities:
                - light.lounge_lamps
                - light.lounge_centre_spots
                - light.lounge_corner_spots
      - conditions:
          - condition: trigger
            id: b2_short
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: >-
                      {{ states('input_text.active_lounge_scene') == 'lounge2'
                      }}
                sequence:
                  - action: input_text.set_value
                    target:
                      entity_id: input_text.active_lounge_scene
                    data:
                      value: ""
                  - action: light.turn_off
                    target:
                      entity_id:
                        - light.lounge_lamps
                        - light.lounge_centre_spots
                        - light.lounge_corner_spots
                    data:
                      transition: 2
          - action: input_text.set_value
            target:
              entity_id: input_text.active_lounge_scene
            data:
              value: lounge2
          - action: scene.turn_on
            target:
              entity_id: scene.lounge2
            data:
              transition: 2
      - conditions:
          - condition: trigger
            id: b2_long
        sequence:
          - action: scene.create
            data:
              scene_id: lounge2
              snapshot_entities:
                - light.lounge_lamps
                - light.lounge_centre_spots
                - light.lounge_corner_spots
      - conditions:
          - condition: trigger
            id: b3_short
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: >-
                      {{ states('input_text.active_lounge_scene') == 'lounge3'
                      }}
                sequence:
                  - action: input_text.set_value
                    target:
                      entity_id: input_text.active_lounge_scene
                    data:
                      value: ""
                  - action: light.turn_off
                    target:
                      entity_id:
                        - light.lounge_lamps
                        - light.lounge_centre_spots
                        - light.lounge_corner_spots
                    data:
                      transition: 2
          - action: input_text.set_value
            target:
              entity_id: input_text.active_lounge_scene
            data:
              value: lounge3
          - action: scene.turn_on
            target:
              entity_id: scene.lounge3
            data:
              transition: 2
      - conditions:
          - condition: trigger
            id: b3_long
        sequence:
          - action: scene.create
            data:
              scene_id: lounge3
              snapshot_entities:
                - light.lounge_lamps
                - light.lounge_centre_spots
                - light.lounge_corner_spots
      - conditions:
          - condition: trigger
            id: b4_short
        sequence:
          - choose:
              - conditions:
                  - condition: template
                    value_template: >-
                      {{ states('input_text.active_lounge_scene') == 'lounge4'
                      }}
                sequence:
                  - action: input_text.set_value
                    target:
                      entity_id: input_text.active_lounge_scene
                    data:
                      value: ""
                  - action: light.turn_off
                    target:
                      entity_id:
                        - light.lounge_lamps
                        - light.lounge_centre_spots
                        - light.lounge_corner_spots
                    data:
                      transition: 2
          - action: input_text.set_value
            target:
              entity_id: input_text.active_lounge_scene
            data:
              value: lounge4
          - action: scene.turn_on
            target:
              entity_id: scene.lounge4
            data:
              transition: 2
      - conditions:
          - condition: trigger
            id: b4_long
        sequence:
          - action: scene.create
            data:
              scene_id: lounge4
              snapshot_entities:
                - light.lounge_lamps
                - light.lounge_centre_spots
                - light.lounge_corner_spots
mode: single

Persistent scenes are the next task.

I have found Claude and DeepSeek far more accurate than than anything else i’ve tried. Grok has been good also.

Thanks, I will look at an account.

There is a weird toggle bug with the version above where if I activate a scene and manually turn off a light in that scene while it is active. When I run the scene again the expected result is for all lights to go off, because the manual process of turning off the light didnt update the text helper, so it assumes the scene is still active.

What actually happens is it turns off the remaining lights in the scene, but it also turns on the light I manually turned off.

Subsequent button pushes cause the remaining lights in the scene to alternate on / off. The manually turned off light also does this, but the opposite of the ones in the scene. The only way to recover from this is to select another scene, or turn on the original light so the live state is what the scene should be.

Very strange…

After multiple iterations, it is possible to refine the automation to something like this.

The idea is that you create a “Light Group” (find it under “Settings” → “Devices and Services” → “Helpers”) containing all the lights you want to group together (in my example I have called it lounge_group_1)

Then you take the IEEE id from the Scene switch (I use MOES 4 Gang Scene switches), if you use something else you might need to adapt the automation. In my case I need to lower case any letters in the IEEE id.

Simply add the IEEE id and the light group name to the switch_map

mode: parallel
triggers:
  - trigger: event
    event_type: zha_event
    event_data:
      cluster_id: 6
variables:
  switch_map:
    a4:c1:38:88:34:12:34:56:
      - lounge_group_1
      - none
      - none
      - none
  event_data: "{{ trigger.event.data }}"
  button_index: "{{ event_data.endpoint_id - 1 }}"
  switch_setting: "{{ switch_map.get( event_data.device_ieee, 'unknown' ) }}"
  light_group: "{{ switch_setting[button_index] }}"
  light_group_id: light.{{light_group}}
  scene_name: "{{light_group}}_memorized"
conditions:
  - condition: template
    value_template: "{{ switch_setting != 'unknown' }}"
  - condition: template
    value_template: "{{ light_group != 'none' }}"
actions:
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ event_data.command == 'remote_button_short_press' }}"
        sequence:
          - if:
              - condition: template
                value_template: "{{ states(light_group_id) == 'on' }}"
            then:
              - action: light.turn_off
                target:
                  entity_id: "{{ light_group_id }}"
            else:
              - action: scene.turn_on
                target:
                  entity_id: scene.{{ scene_name }}
      - conditions:
          - condition: template
            value_template: "{{ event_data.command == 'remote_button_long_press' }}"
        sequence:
          - action: scene.create
            data:
              scene_id: "{{ scene_name }}"
              snapshot_entities: "{{ expand(light_group_id) | map(attribute='entity_id') | list }}"

The automation has three functions.

  1. If you short press when any lights in the group are on, it uses the group to turn all the lights in the group off.
  2. If you short press when ALL the lights in the group are off, it activates the scene to return them to the memorized state.
  3. If you long press it updates the memorized state, however it uses the members of the light group to figure out which lights it should memorize.

Hence to add additional lights / switches you only need to create groups and add the names to the switch_map - one automation can handle the entire house.

I’m doing the same thing with a Loratap six-button remote right now, did you get the reboot-persistent scenes working?

It’s been a couple of weeks since I got this working. I’ve not got the persistent scene storage working yet. I figured I’d just try and avoid frequent reboots while it was being tested.

Hopefully I will get some time to look at it this weekend and I can finish implementing it.

I’ve managed to get ha_res_scenes sorted out (turns out after installing it via HACS you’ve gotta enable it in Settings → Integration → Add Integration) but any scenes it saves will have transitions ignored when restoring them, so two steps forward one step back I guess

Sorry guys, in my own automations I only use manually defined scenes (which are persisted over reboots) I didn’t realize that temporary scenes weren’t persisted when I posted my replies.

I checked and it appears it’s not a bug (its by design) as stated here:

The best work around I can think of is to define a “default” (manually created) scene then check if the dynamic one exists, that way after a reboot you will have “something” to activate.

Obviously if you save a dynamic scene, that will override until the next time you reboot. To be clear you will need different names for the dynamic and manual scenes - you can’t just use the same name for both.

EDIT:
I know this is a ZHA thread - but I have found that you can also create scenes at the Zigbee2MQTT level, there is no distinction when using Z2M scenes. You can create/update the scenes through the UI or MQTT messages and those are persisted, so if you are using Z2M you can just switch to Z2M scenes.

In order to use them you just define the light groups in Z2M, then define any scenes you want (Off, Memorized, etc), the integration is clean the groups and Scenes show up in HA and you simply need to send a MQTT message to update the Memorized scene when necessary.