Improving my lighting Automations and Scripts

Some folks want to see if they can improve my lighting automations and scripts. I’m going to post some of what I have here to avoid going off topic in my other thread :+1:.

Here are my basic functions:

  1. If occupancy detected, turn on lights.
  2. If occupancy cleared, turn off lights.
  3. If button up pressed, turn on lights to the daytime or nighttime setting depending on time of day and disable occupancy sensing.
  4. If button up x 2 pressed, re-enable occupancy sensing.
  5. If button up held, enable daytime lighting even if it’s nighttime.
  6. If button down pressed, turn off lights and disable occupancy sensing.
  7. If button down held, disable occupancy sensing (if you wanna keep the lights in their current state).

Lighting Automation YAMLs

Automation

All lighting automations share the same basic layout with Trigger IDs and call the same script.

I’m passing in a helper entity to the script: “are occupancy sensors controlling the lights?”.

There’s something I want to add but haven’t tested yet. Another trigger for when the lights go off. I wanna ensure occupancy sensing gets re-enabled because I could turn off lights in my dashboard or via voice, and this automation won’t run the “Button: Turn off Lights” trigger.

I could have other triggers in here for it being daytime or nighttime, but to avoid complicating things I put those into looping scripts instead.

Automation YAML
alias: Control Bedroom Hallway Lights
description: ""
triggers:
  - device_id: 359bc5a5f510d587696d3042b560c8b7
    domain: zha
    type: press
    subtype: Up
    trigger: device
    id: "Button: Turn on Lights"
  - device_id: 359bc5a5f510d587696d3042b560c8b7
    domain: zha
    type: remote_button_double_press
    subtype: Up
    trigger: device
    id: "Button: Turn on Occupancy Sensing"
  - device_id: 359bc5a5f510d587696d3042b560c8b7
    domain: zha
    type: hold
    subtype: Up
    trigger: device
    id: "Button: Turn on Daytime Lighting"
  - device_id: 359bc5a5f510d587696d3042b560c8b7
    domain: zha
    type: press
    subtype: Down
    trigger: device
    id: "Button: Turn off Lights"
  - device_id: 359bc5a5f510d587696d3042b560c8b7
    domain: zha
    type: hold
    subtype: Down
    trigger: device
    id: "Button: Turn off Occupancy Sensing"
  - entity_id:
      - binary_sensor.bedroom_hallway_occupancy_sensors
    to: "on"
    id: "Occupancy: Turn on Lights"
    trigger: state
  - entity_id:
      - binary_sensor.bedroom_hallway_occupancy_sensors
    to: "off"
    id: "Occupancy: Turn off Lights"
    trigger: state
    for:
      hours: 0
      minutes: 0
      seconds: 20
conditions: []
actions:
  - metadata: {}
    data:
      nighttime_brightness: 7
      lights:
        - light.bedroom_hallway_lights
      trigger_id: "{{ trigger.id }}"
      daytime_lighting_mode: Adaptive
      nighttime_lighting_mode: Nighttime
      occupancy_sensing: input_boolean.bedroom_hallway_occupancy_sensing
      id: Bedroom Hallway
    action: script.control_button_occupancy_day_night_lights
mode: restart

Main Script

All of the main logic for controlling lighting is in this script.

This script handles calling other scripts based on the Trigger ID.

Main Script YAML
alias: Control Button-Occupancy Day-Night Lights
fields:
  lights:
    name: Lights
    description: A list of lights to control.
    example: light.living_room_lights, light.kitchen_lights
    required: true
    selector:
      entity:
        multiple: true
        domain: light
  occupancy_sensing:
    name: Motion Sensor Toggle
    description: Toggle that controls whether motion sensing is on or off.
    example: input_boolean.living_room_occupancy_sensing
    required: false
    selector:
      entity:
        multiple: false
        domain: input_boolean
  daytime_lighting_mode:
    selector:
      select:
        options:
          - Adaptive
          - Bright
    name: Daytime Lighting Mode
    default: Adaptive
    required: true
    description: >-
      Lighting style that turns on during the daytime. Adaptive is normal and
      changes based on the weather. Bright is great for closets or rooms where
      you want always-bright lights.
  nighttime_lighting_mode:
    selector:
      select:
        options:
          - Nighttime
          - Adaptive
          - Bright
          - Scene
          - None
    name: Nighttime Lighting Mode
    default: Nighttime
    required: true
    description: >-
      Lighting style that turns on during the evening. Nighttime is a calmer
      light temperature whereas Adaptive and Bright are the same as daytime
      lights.
  nighttime_brightness:
    selector:
      number:
        min: 1
        max: 100
        step: 1
    name: Nighttime Brightness (Optional)
    description: >-
      Only used when selecting "Nighttime". The brightness setting used during
      nighttime lighting.
    required: false
    default: 20
  nighttime_scene:
    selector:
      entity:
        domain: scene
    name: Nighttime Scene (Optional)
    description: >-
      Only used for the "Scene" mode. Meant for specific rooms and lighting
      needs where only specific lights need to be turned on with specific
      brightness and color values.
  trigger_id:
    selector:
      template: {}
    name: Trigger ID
    required: true
    description: "Trigger ID used to determine which path use when controlling lights. "
    default: "{{ trigger.id }}"
  id:
    selector:
      text: null
    name: ID
    description: >-
      This is an always-running script until lights turn off, so you need to
      pass a unique ID, so its operation isn't duplicated.
    required: true
sequence:
  - data:
      name: "Control Lights:"
      message: >
        {% set light_names = lights | map('state_attr', 'friendly_name') |
        join(', ') %} "{{ trigger_id }}" ➡️ "{{ light_names }}".
    enabled: true
    alias: Log Trigger ID and Light Names
    action: logbook.log
  - choose:
      - conditions:
          - condition: or
            conditions:
              - condition: template
                value_template: "{{ trigger_id == \"Button: Turn on Lights\" }}"
                alias: "Trigger ID is \"Button: Turn on Lights\""
              - alias: "Trigger ID is \"Button: Toggle Lights\" and lights are off"
                condition: and
                conditions:
                  - condition: template
                    value_template: "{{ trigger_id == \"Button: Toggle Lights\" }}"
                    alias: "Trigger ID is \"Button: Toggle Lights\""
                  - alias: Are lights are off?
                    condition: template
                    value_template: >-
                      {{ lights | map('states') | select('eq', 'on') | list |
                      length == 0 }}
              - condition: and
                conditions:
                  - condition: template
                    value_template: "{{ trigger_id == \"Occupancy: Turn on Lights\" }}"
                    alias: "Trigger ID is \"Occupancy: Turn on Lights\""
                  - condition: template
                    value_template: >-
                      {{ is_state(occupancy_sensing, 'on') if occupancy_sensing
                      is defined else True }}
                    alias: Motion sensor toggle is "on"
                alias: >-
                  Trigger ID is "Occupancy: Turn on Lights" and motion sensor
                  toggle is on
            alias: If Button or Occupancy turn on lights
        sequence:
          - alias: Turn on lights and turn off occupancy sensing
            parallel:
              - alias: Turn off occupancy sensing if button pressed
                if:
                  - condition: template
                    value_template: >-
                      {{ trigger_id == "Button: Turn on Lights" or trigger_id ==
                      "Button: Toggle Lights" }}
                    alias: >-
                      Trigger ID is "Button: Turn on Lights" or "Button: Toggle
                      Lights"
                then:
                  - alias: Turn off Occupancy Sensing
                    metadata: {}
                    data:
                      occupancy_sensing: "{{ occupancy_sensing }}"
                      action_name: Turn off Occupancy Sensing
                    action: script.control_motion_lights
              - metadata: {}
                data:
                  daytime_lighting_mode: "{{ daytime_lighting_mode }}"
                  nighttime_lighting_mode: "{{ nighttime_lighting_mode }}"
                  nighttime_brightness: "{{ nighttime_brightness | default(0) }}"
                  nighttime_scene: "{{ nighttime_scene | default('') }}"
                  lights: "{{ lights }}"
                  id: "{{ id }}"
                action: script.turn_on_day_night_adaptive_light
                enabled: false
              - action: script.turn_on
                metadata: {}
                data:
                  variables:
                    daytime_lighting_mode: "{{ daytime_lighting_mode }}"
                    nighttime_lighting_mode: "{{ nighttime_lighting_mode }}"
                    nighttime_brightness: "{{ nighttime_brightness | default(0) }}"
                    nighttime_scene: "{{ nighttime_scene | default('') }}"
                    lights: "{{ lights }}"
                    id: "{{ id }}"
                target:
                  entity_id:
                    - script.turn_on_day_night_adaptive_light
                enabled: true
        alias: Turn on Lights
      - conditions:
          - condition: template
            value_template: "{{ trigger_id == \"Button: Turn on Daytime Lighting\" }}"
            alias: "Trigger ID is \"Button: Turn on Daytime Lighting\""
        sequence:
          - alias: Turn on lights and turn off occupancy sensing
            parallel:
              - alias: Turn off Occupancy Sensing
                metadata: {}
                data:
                  occupancy_sensing: "{{ occupancy_sensing }}"
                  action_name: Turn off Occupancy Sensing
                action: script.control_motion_lights
              - metadata: {}
                data:
                  lights: "{{ lights }}"
                  id: "{{ id }}"
                action: script.turn_on_lights_for_adaptive_lighting
                enabled: false
              - action: script.turn_on
                metadata: {}
                data:
                  variables:
                    lights: "{{ lights }}"
                    id: "{{ id }}"
                target:
                  entity_id:
                    - script.turn_on_lights_for_adaptive_lighting
                enabled: true
        alias: Turn on Daytime Lights
      - conditions:
          - condition: or
            conditions:
              - condition: template
                value_template: "{{ trigger_id == \"Button: Turn off Lights\" }}"
                alias: "Trigger ID is \"Button: Turn off Lights\""
              - condition: and
                conditions:
                  - condition: template
                    value_template: "{{ trigger_id == \"Button: Toggle Lights\" }}"
                    alias: "Trigger ID is \"Button: Toggle Lights\""
                  - condition: template
                    value_template: >-
                      {{ lights | map('states') | select('eq', 'on') | list |
                      length > 0 }}
                    alias: At least one light is "on"
                alias: "Trigger ID is \"Button: Toggle Lights\" and a light is on"
              - condition: and
                conditions:
                  - condition: template
                    value_template: "{{ trigger_id == \"Occupancy: Turn off Lights\" }}"
                    alias: "Trigger ID is \"Occupancy: Turn off Lights\""
                  - condition: template
                    value_template: >-
                      {{ is_state(occupancy_sensing, 'on') if occupancy_sensing
                      is defined else True }}
                    alias: Motion sensor toggle is "on"
                alias: >-
                  Trigger ID is "Occupancy: Turn off Lights" and motion sensor
                  toggle is on
            alias: If Button or Occupancy turn off lights
        sequence:
          - alias: Turn off lights and turn on occupancy sensing
            parallel:
              - metadata: {}
                data:
                  transition: 1.5
                  lights: "{{ lights }}"
                action: script.turn_off_lights
              - alias: Turn on Occupancy Sensing
                metadata: {}
                data:
                  occupancy_sensing: "{{ occupancy_sensing }}"
                  action_name: Turn on Occupancy Sensing
                action: script.control_motion_lights
        alias: Turn off Lights
      - conditions:
          - condition: or
            conditions:
              - condition: template
                value_template: "{{ trigger_id == \"Button: Turn on Occupancy Sensing\" }}"
                alias: "Trigger ID is \"Button: Turn on Occupancy Sensing\""
              - alias: "Trigger ID is \"Occupancy: Turn on Occupancy Sensing\""
                condition: template
                value_template: "{{ trigger_id == \"Occupancy: Turn on Occupancy Sensing\" }}"
        sequence:
          - alias: Turn on occupancy sensing
            metadata: {}
            data:
              occupancy_sensing: "{{ occupancy_sensing }}"
              action_name: Turn on Occupancy Sensing
            action: script.control_motion_lights
        alias: Turn on Occupancy Sensing
      - conditions:
          - condition: or
            conditions:
              - condition: template
                value_template: "{{ trigger_id == \"Button: Turn off Occupancy Sensing\" }}"
                alias: "Trigger ID is \"Button: Turn off Occupancy Sensing\""
              - condition: template
                value_template: "{{ trigger_id == \"Occupancy: Turn off Occupancy Sensing\" }}"
                alias: "Trigger ID is \"Occupancy: Turn off Occupancy Sensing\""
        sequence:
          - alias: Turn off occupancy sensing
            metadata: {}
            data:
              occupancy_sensing: "{{ occupancy_sensing }}"
              action_name: Turn off Occupancy Sensing
            action: script.control_motion_lights
        alias: Turn off Occupancy Sensing
description: >-
  Turns on lights with adaptive lighting during the day, but at night, it turns
  on a much dimmer version.
icon: mdi:theme-light-dark
mode: parallel
max: 100

“Turn On” Lighting Script

I have two long-running lighting scripts. This one controls the Adaptive lighting one.

This one controls whether we’re in daytime or nighttime lighting mode.

'Turn On' Lighting Script YAML
alias: Turn on Lights for Day & Night Adaptive Lighting
description: >-
  Turns on lights with adaptive lighting during the day, but at night, it turns
  on a much dimmer version.
icon: mdi:theme-light-dark
mode: parallel
fields:
  lights:
    name: Lights
    description: The list of lights to turn on.
    example: light.living_room_lights, light.kitchen_lights
    required: true
    selector:
      entity:
        multiple: true
        domain: light
  daytime_lighting_mode:
    selector:
      select:
        options:
          - Adaptive
          - Bright
    name: Daytime Lighting Mode
    default: Adaptive
    required: true
    description: >-
      Lighting style that turns on during the daytime. Adaptive is normal and
      changes based on the weather. Bright is great for closets or rooms where
      you want always-bright lights.
  nighttime_lighting_mode:
    selector:
      select:
        options:
          - Nighttime
          - Adaptive
          - Bright
          - Scene
          - None
        multiple: false
    name: Nighttime Lighting Mode
    default: Nighttime
    required: true
    description: >-
      Lighting style that turns on during the nighttime. Nighttime is a calmer
      light temperature whereas Adaptive and Bright are the same as daytime
      lights.
  nighttime_brightness:
    selector:
      number:
        min: 1
        max: 100
        step: 1
    name: Nighttime Brightness (Optional)
    description: >-
      Brightness setting used during nighttime lighting. Not used for other
      nighttime lighting modes.
    required: false
    default: 20
  nighttime_scene:
    selector:
      entity:
        domain: scene
    name: Nighttime Scene (Optional)
    description: >-
      Meant for specific rooms and lighting needs where only specific lights
      need to be turned on with specific brightness and color values.
  id:
    selector:
      text: null
    name: ID
    description: >-
      This is an always-running script until lights turn off, so you need to
      pass a unique ID, so its operation isn't duplicated.
    required: true
sequence:
  - event: turn_on_day_night_adaptive_light
    event_data:
      id: "{{ id }}"
  - alias: Repeat until lights are off
    repeat:
      sequence:
        - event: turn_on_lights_for_adaptive_lighting
          event_data:
            id: "{{ id }}"
        - parallel:
            - alias: Trigger repeat or stop script
              sequence:
                - alias: >-
                    Wait for time to be daytime, nighttime, lights off, or
                    duplicate call
                  wait_for_trigger:
                    - trigger: time
                      at: input_datetime.morning_lighting_time
                      enabled: true
                    - trigger: time
                      at: input_datetime.nighttime_lighting_time
                      enabled: true
                    - trigger: template
                      value_template: >-
                        {{ lights | map('states') | select('eq', 'on') | list |
                        length == 0 }}
                      alias: When lights are off
                    - trigger: event
                      event_type: turn_on_day_night_adaptive_light
                      enabled: true
                      event_data:
                        id: "{{ id }}"
                  continue_on_timeout: false
                - alias: Stop if duplicate call
                  if:
                    - alias: Is duplicate call
                      condition: template
                      value_template: |-
                        {{
                          wait.trigger is defined
                          and wait.trigger.platform == "event"
                          and wait.trigger.event.data.id == id
                        }}
                  then:
                    - stop: Duplicate call
                  enabled: true
                - action: logbook.log
                  metadata: {}
                  data:
                    message: "{{ id }}"
                    name: Stopped Day & Night Adaptive Lighting
            - alias: Turn on lighting mode
              sequence:
                - alias: Choose daytime or nighttime lighting mode
                  if:
                    - condition: time
                      after: input_datetime.morning_lighting_time
                      before: input_datetime.nighttime_lighting_time
                  then:
                    - alias: Run chosen daytime lighting mode
                      choose:
                        - conditions:
                            - condition: template
                              value_template: "{{ daytime_lighting_mode == \"Adaptive\" }}"
                              alias: Is adaptive lighting mode
                          sequence:
                            - metadata: {}
                              data:
                                lights: "{{ lights }}"
                                id: "{{ id }}"
                              action: script.turn_on_lights_for_adaptive_lighting
                              enabled: false
                            - action: script.turn_on
                              metadata: {}
                              data:
                                variables:
                                  lights: "{{ lights }}"
                                  id: "{{ id }}"
                              target:
                                entity_id:
                                  - script.turn_on_lights_for_adaptive_lighting
                              enabled: true
                        - conditions:
                            - condition: template
                              value_template: "{{ daytime_lighting_mode == \"Bright\" }}      "
                              alias: Is bright lighting mode
                          sequence:
                            - metadata: {}
                              data:
                                lights: "{{ lights }}"
                              enabled: true
                              action: script.turn_on_lights_for_bright_areas
                  else:
                    - alias: Run chosen nighttime lighting mode
                      choose:
                        - conditions:
                            - condition: template
                              value_template: "{{ nighttime_lighting_mode == \"Nighttime\" }}"
                              alias: Is nighttime lighting mode
                          sequence:
                            - data:
                                lights: "{{ lights }}"
                                nighttime_brightness: "{{ nighttime_brightness }}"
                              action: script.turn_on_nighttime_lights
                        - conditions:
                            - condition: template
                              value_template: "{{ nighttime_lighting_mode == \"Adaptive\" }}"
                              alias: Is adaptive lighting mode
                          sequence:
                            - metadata: {}
                              data:
                                lights: "{{ lights }}"
                                id: "{{ id }}"
                              action: script.turn_on_lights_for_adaptive_lighting
                              enabled: false
                            - action: script.turn_on
                              metadata: {}
                              data:
                                variables:
                                  lights: "{{ lights }}"
                                  id: "{{ id }}"
                              target:
                                entity_id:
                                  - script.turn_on_lights_for_adaptive_lighting
                              enabled: true
                        - conditions:
                            - condition: template
                              value_template: "{{ nighttime_lighting_mode == \"Bright\" }}      "
                              alias: Is bright lighting mode
                          sequence:
                            - metadata: {}
                              data:
                                lights: "{{ lights }}"
                              enabled: true
                              action: script.turn_on_lights_for_bright_areas
                        - conditions:
                            - condition: template
                              value_template: "{{ nighttime_lighting_mode == \"Scene\" }}"
                              alias: Is scene lighting mode
                          sequence:
                            - metadata: {}
                              target:
                                entity_id: "{{ nighttime_scene }}"
                              data:
                                transition: 2
                              action: scene.turn_on
      until:
        - alias: If lights off or duplicate call
          condition: or
          conditions:
            - alias: Lights are off
              condition: template
              value_template: >-
                {{ lights | map('states') | select('eq', 'on') | list | length
                == 0 }}
            - alias: If a duplicate call was made
              condition: template
              value_template: |-
                {{
                  wait is defined
                  and wait.trigger is defined
                  and wait.trigger.platform == "event"
                  and wait.trigger.event.data.id == id
                }}
max: 100

Adaptive (Daytime) Lighting Script

This script’s purpose is to change the lighting mode based on the value of my global light temperature and brightness helpers. Those get affected by the cloud coverage values in a separate Weather-based automation.

My purpose is changing the light color to match the color of light outside. It’s unimportant how those values get changed in this automation, only that it listens for changes in those values.

Adaptive (Daytime) Lighting Script YAML
alias: Turn on Lights for Adaptive Lighting
fields:
  lights:
    name: Lights
    description: The list of lights to turn on.
    example: light.living_room_lights, light.kitchen_lights
    required: true
    selector:
      entity:
        multiple: true
        domain: light
  id:
    selector:
      text: {}
    name: ID
    description: >-
      This is an always-running script until lights turn off, so you need to
      pass a unique ID, so its operation isn't duplicated.
    required: true
sequence:
  - event: turn_on_lights_for_adaptive_lighting
    event_data:
      id: "{{ id }}"
  - alias: Repeat until script stopped
    repeat:
      sequence:
        - alias: Turn on lights and stop or loop again
          parallel:
            - alias: Trigger repeat or stop script
              sequence:
                - alias: >-
                    Wait for color temperature, brightness, lights off, or
                    duplicate call
                  wait_for_trigger:
                    - trigger: state
                      entity_id:
                        - input_select.light_color_temperature
                    - trigger: state
                      entity_id:
                        - input_number.light_brightness_percentage
                    - trigger: template
                      value_template: >-
                        {{ lights | map('states') | select('eq', 'on') | list |
                        length == 0 }}
                      alias: When lights are off
                      enabled: true
                    - trigger: event
                      event_type: turn_on_lights_for_adaptive_lighting
                      enabled: true
                      event_data:
                        id: "{{ id }}"
                  continue_on_timeout: false
                - alias: Stop if duplicate call
                  if:
                    - alias: Is duplicate call
                      condition: template
                      value_template: |-
                        {{
                          wait.trigger is defined
                          and wait.trigger.platform == "event"
                          and wait.trigger.event.data.id == id
                        }}
                  then:
                    - stop: Duplicate call
                  enabled: true
            - action: script.turn_on_lights
              metadata: {}
              data:
                lights: "{{ lights }}"
                brightness: "{{ states('input_number.light_brightness_percentage') }}"
                kelvin: "{{ states('input_select.light_color_temperature') }}"
                transition: 1.5
            - alias: Turn on lights
              sequence:
                - alias: Log Light Names
                  data:
                    name: "Turn on Lights for Day & Night Adaptive Lighting:"
                    message: >
                      {% set light_names = lights | map('state_attr',
                      'friendly_name') | join(', ') %} "{{ light_names }}".
                  enabled: true
                  action: logbook.log
                - parallel:
                    - metadata: {}
                      data:
                        brightness_pct: >-
                          {{ states('input_number.light_brightness_percentage')
                          }}
                        kelvin: "{{ states('input_select.light_color_temperature') }}"
                        transition: 1.5
                      target:
                        entity_id: "{{ lights }}"
                      action: light.turn_on
                      alias: Set light brightness and color temperature
                    - alias: >-
                        Set white LED brightness and turn off RGB LEDs in light
                        strips
                      metadata: {}
                      data:
                        rgbw_color:
                          - 0
                          - 0
                          - 0
                          - >
                            {{
                            (states('input_number.light_brightness_percentage')
                            | int) / 100 * 255 }}  transition: 1.5
                      target:
                        entity_id: "{{ lights }}"
                      action: light.turn_on
                  alias: Configure light settings
              enabled: false
      until:
        - alias: If lights off or duplicate call
          condition: or
          conditions:
            - alias: Lights are off
              condition: template
              value_template: >-
                {{ lights | map('states') | select('eq', 'on') | list | length
                == 0 }}
            - alias: If a duplicate call was made
              condition: template
              value_template: |-
                {{
                  wait is defined
                  and wait.trigger is defined
                  and wait.trigger.platform == "event"
                  and wait.trigger.event.data.id == id
                }}
          enabled: true
description: >-
  Turn on lights to a brightness value based on the time of day and a
  temperature relative to the current weather.
icon: mdi:lightbulb-auto
mode: parallel
max: 100

Other Scripts

I left out the other scripts because they’re pretty self-explanatory:

  1. Turn off lights.
  2. Turn on Bright lights.
  3. Turn on Nighttime lighting.
  4. Stop existing adaptive lighting scripts for a lighting area.
1 Like

Sorry if I missed this above, but what sets those various datetime triggers? Are those set manually or based on another automation (trigger on sun position, for example).

Question:

triggers:
  - device_id: 359bc5a5f510d587696d3042b560c8b7
    domain: zha
    type: press
    subtype: Up
    trigger: device
    id: "Button: Turn on Lights"

I don’t use ZHA but if you go to that device are there entities for those events? Perhaps like this?

They might be under disabled entities. In my case that Scene 001 has the entity event.living_room_cans_scene_001.

Do you have those?

I can change this value, but I don’t. Although, I was thinking about changing the nighttime one because the sun is still up close to 9:00p now rather than going away at 4:30p in the winter.

ZHA is per device, and none or mostly none of mine have those scenes. I use smart lights too.

And not everything is ZHA. Many are Wi-Fi LIFX lights as well. It’s whatever works.

(You don’t need to quote everything, I can follow the context.)

You are not going to like my approach as it might feel like too much “code”, but maybe worth entertaining for now.

Remember one goal is to reduce/combine the number of automations and simplify the overall scripts and automations.

I’d first start by simplifying the triggers.

Your initial triggers list repeated device IDs (who knows what those are?) and if you combine those with other lights it just multiples as you noted before.

I was asking about the “event.” entities because it makes it a little easier to figure out what event happened and listen to only the ones you care about. You might have to watch the ZHA events and pull out the ones that link to devices you are interested in.

When I push a button on a z-wave scene controller I see something like this (trimmed)

event_type: zwave_js_value_notification
data:
  device_id: 88767d293be4ada9d65a25aa33e732bb
  property_key: "001"
  value: KeyPressed

From I can figure out what happened on which device. So, with a tiny bit of mapping you can then figure out what needs to happen to which light.

End of day for me. Will be interesting to see what others come up with.

1 Like

Have you considered using the sun integration with above_horizen or below_horizen with an offset? that way it adjusts to your local daylight hours. Some of my automations even use a light sensor value to determine this (I’m expanding)

Yes I just nested/double quoted.

I have actually. Someone was showing a video where he used an offset on the Sun. Never thought of doing that.

Sadly, there are a few places I have a hard cutoff at a specific time (kids bedroom, etc) where I disable occupancy sensing. I’d need to make sure those aren’t tied to the Nighttime Lighting helper. If so, then I can do this for sure!

Those are buttons:

I’ll update my original post with an image as well :+1:.

When I said “Your initial triggers list repeated device IDs (who knows what those are?)” that was a rhetorical question. Meaning when you look at the YAML and see “359bc5a5f510d587696d3042b560c8b7” that doesn’t really tell you “the bedroom hall light”. Sure the UI will map that for you as long as that id never changes.

And you you want some fun, remove that from ZHA and include it back in and see what happens to your triggers.

Not terribly robust, right?

2 Likes

@Sawtaytoes, is the answer to all these questions “yes”?

  • You want to simplify your multiple automations into maybe one or two and make it more scalable and (yeah) robust. For example, have a single automation that handles all the zha scene controllers and associated lights?
  • You are ok (maybe) separating out the daylight changes automation. This is because when that changes it impacts all lights that are currently on.
  • You are willing to entertain a different approach meaning abandon your current set of automations of one per scene controller and the scripts?
  • Perhaps use a naming convention on your scene controllers so that those can be mapped to the light they operate?

And to give you an idea of where I’m going (which there might be much better ideas out there) is like this.

I would like to do this, but it appears you cannot pass a list of device ids here:
(Doesn’t work!)

trigger_variables:
  device_ids: "{{ label_devices('Bedroom Scene Controller') }}"
triggers:
  - trigger: event
    event_type: zwave_js_value_notification
    event_data:
      device_id: "{{ device_ids }}"

So, have to use a condition:

triggers:
  - trigger: event
    event_type: zwave_js_value_notification
conditions:
  - condition: template
    value_template: >-
      {{ trigger.event.data.device_id in label_devices('Bedroom Scene
      Controller')  }}
actions:
  - variables:
      device_name: "{{ device_name(trigger.event.data.device_id) }}"
      button_name: "{{ trigger.event.data.property_key }}"
      button_action: "{{ trigger.event.data.value }}"
      light_entity: "{{'light.' ~ device_name.split('-')[0] }}"
  - action: system_log.write
    data:
      message: >
        Got event for device: {{ device_name }} with event:{{ button_name}}-{{
        button_action }} and will operate {{ light_entity }}

So, pressing a button on a scene controller generates this:

Got event for device: bedroom_hallway-scene_controller with event:002-KeyPressed and will operate light.bedroom_hallway

You label all your scene controllers and without having to specify any device IDs many times in an automation for each room you have one that shows you which light to control and which and how a button was pushed.

Does that make sense to you?

It became quiet here so I’ll just post (slightly tested) code I just wrote to give an example to build upon.

Initially it was a single automation (in the UI), but I didn’t want to have to create helpers for each room. So, I moved it into a package to keep an input_boolean and a sensor together. And since in a single package file the automation could probably be simplified by splitting up the automation. It’s not very long.

Wish I could pass a list of device IDs to the event trigger.

@petro, see another way to track disabled rooms (sensor at bottom) w/o having to create individual helpers for each room?

I’m sure there’s other approaches.

Example automation

I didn’t use choose: to just make it a bit less indented.

Oh, and handling HA restarts (from/to unavailable/unknow) left as an exercise…

➜  config cat packages/common_lights.yaml

#------------------------------------------------------------------------------------------
# Combine controlling lights, occupancy and daylight into single automation
#
# Naming conventions:
#
#   * Scene controller device name: "<room name> - <whatever>"
#     e.g. "Living Room - Scene controller" becomes "living_room"
#
#   * Light: "light.<room_name>
#
#   * Motion Entities:  "binary_sensor.<room_name>_motion_detected"
#
# Then label or group the devices and entities:
#
#   * Scene Controllers: Apply lable "Common Light Scene Controller"
#
#   * Lights: add all to "light.all_bedroom_lights" helper group.
#     (This could easily be a label, if you like that better)
#
#   * Motion Sensors: add all to "binary_sensor.any_bedroom_motion" helper group
#
#------------------------------------------------------------------------------------------

automation:

  - id: common_room_lights_id
    mode: queued
    alias: Bedroom Controllers

    triggers:

      # Add: Reset motion disabled at some time or after a delay

      # Change/add for ZHA, etc.
      - trigger: event
        event_type: zwave_js_value_notification
        id: button_pressed

      - trigger: state
        entity_id: binary_sensor.any_bedroom_motion
        id: motion
        to: "on"
      - trigger: state
        entity_id: binary_sensor.any_bedroom_motion
        id: motion
        to: "off"
        for: 5

      - trigger: sun
        event: sunrise
        offset: "00:30:00"
        id: day
      - trigger: sun
        event: sunset
        offset: "00:30:00"
        id: night


    conditions:
      # Because can't pass list of device IDs to event above
      - condition: template
        value_template: >
          {{
            trigger.id != 'button_pressed'
            or trigger.event.data.device_id in label_devices('Common Light Scene Controller')
          }}


    actions:

      # trigered by the day/night change
      - if:
          - "{{ trigger.id in ['day','night'] }}"
        then:
          - action: input_boolean.turn_{{ 'on' if trigger.id == 'day' else 'off' }}
            target:
              entity_id: input_boolean.common_lights_daytime_mode

          - variables:
              tasks:
                - turn_on
              room_name: All
              target: |
                {{
                 expand('light.all_bedroom_lights')
                 |selectattr('state', 'eq', 'on')
                 |map(attribute='entity_id')
                 |list
                }}


      # Scene controller pushed
      - if:
          - "{{ trigger.id == 'button_pressed' }}"
        then:
          - variables:
              device_name: "{{ device_name(trigger.event.data.device_id) }}"
              room_name: "{{ device_name.split('-')[0]|trim|lower|replace(' ','_') }}"
              target: "{{ 'light.' ~ room_name }}"

              data: "{{ trigger.event.data }}"
              action_key: "{{ [data.property_key,data.value]|join('_') }}"
              actions:
                001_KeyPressed:
                  - turn_on
                  - disable_occupancy
                001_KeyPressed2x:
                  - enable_occupancy
                001_KeyHeldDown:
                  - enable_daytime
                002_KeyPressed:
                  - turn_off
                  - disable_occupancy
                002_KeyHeldDown:
                  - disable_occupancy
              tasks: "{{ actions[action_key] if action_key in actions else 'noop' }}"


      # Motion was detected
      - if:
          - "{{ trigger.id == 'motion' }}"
        then:
          - variables:
              triggered_by: |
                {{ ( expand(trigger.to_state.attributes.entity_id)
                  | sort(reverse=true, attribute='last_changed')
                  | map(attribute='entity_id')
                  | list )[0] }}
              room_name: "{{  triggered_by.split('.')[1]|replace('_motion_detected','') }}"
              target: "{{ 'light.' ~ room_name  }}"
              disabled: "{{ state_attr('sensor.common_lights_motion_disabled','disabled') }}"
              disabled_rooms: "{{ [] if disabled is none else disabled }}"
              tasks: |
                {{ [
                  'noop' if room_name in disabled_rooms
                  else 'turn_on' if is_state(triggered_by, 'on')
                  else 'turn_off'
                ] }}

      - action: system_log.write
        enabled: true
        data:
          message: 'Common Lights: room="{{ room_name }}" tasks="{{ tasks }}", target="{{ target }}"'

      # Handle the tasks -----------------------------------------------------------

      # Handle changes in motion detection
      - if:
          - "{{ 'disable_occupancy' in tasks  }}"
        then:
          - event: common_lights_motion_disable
            event_data:
              room: "{{ room_name }}"
      - if:
          - "{{ 'enable_occupancy' in tasks }}"
        then:
          - event: common_lights_motion_enable
            event_data:
              room: "{{ room_name }}"

      - if:
          - "{{ 'turn_on' in tasks }}"
        then:
          - action: light.turn_on
            target:
              entity_id: "{{ target }}"
            data:
              brightness_pct: "{{ 100 if is_state('input_boolean.common_lights_daytime_mode','on') else 15 }}"


      - if:
          - "{{ 'turn_off' in tasks }}"
        then:
          - action: light.turn_off
            target:
              entity_id: "{{ target }}"

      - if:
          - "{{ 'enable_daytime' in tasks }}"
        then:
          - action: light.turn_on
            target:
              entity_id: "{{ target }}"
            data:
              brightness_pct: 100
          - action: input_boolean.turn_on
            target:
              entity_id: input_boolean.common_lights_daytime_mode


#------------------------------------------------------------------------------------------


input_boolean:
  common_lights_daytime_mode:
    name: Common Lights Daytime Mode
    initial: on



template:
  # ------ This template tracks room names that have motion disabled.
  - triggers:
      - trigger: event
        event_type: common_lights_motion_disable
        id: add
      - trigger: event
        event_type: common_lights_motion_enable
        id: remove
      - trigger: event
        event_type: common_lights_motion_clear
        id: clear

    variables:
        room_name: "{{ trigger.event.data.room|default('oops') }}"

    sensor:
      - name: Room Motion Disabled
        default_entity_id: sensor.common_lights_motion_disabled
        state: Placeholder
        attributes:
          disabled: |-
            {% set cur_list = this.attributes.disabled|default([]) %}
            {{
              [] if trigger.id == 'clear'
              else (cur_list + [room_name])|unique|list if trigger.id == 'add'
              else cur_list|reject('match',room_name)|list if trigger.id == 'remove'
              else cur_list
            }}

You can make a single main automation to call all your scripts with something like this:

alias: Control All zha lights
id: control_all_zha_lights
description: ""
mode: parallel
max: 25
trace:
  stored_traces: 25
triggers:
  - id: "Button"
    trigger: event
    event_type: zha_event    
  - entity_id:
      - binary_sensor.bedroom_hallway_occupancy_sensors
      ### LIST ALL ENTITY_IDs FOR MOTION DETECTION ###
      
    to: "on"
    id: "Occupancy"
    trigger: state
  - entity_id:
      - binary_sensor.bedroom_hallway_occupancy_sensors
      ### LIST ALL ENTITY_IDs FOR MOTION DETECTION ###

    to: "off"
    id: "Occupancy"
    trigger: state
    for:
      hours: 0
      minutes: 0
      seconds: 20

variables:
  config:
  
    #  Bedroom Hallway 
    - motions:
      - binary_sensor.bedroom_hallway_occupancy_sensors
      devices:
      - 359bc5a5f510d587696d3042b560c8b7
      multi_press_complete-3-2: Turn on Occupancy Sensing

      ### ADD OTHER ZHA COMBOS HERE DEPENDING ON DEVICE DIFFERENCES, MAP THEM TO YOUR SCRIPT INPUTS ###

      # OUTPUTS
      output:
        id: Bedroom Hallway
        nighttime_brightness: 7
        daytime_lighting_mode: Adaptive
        nighttime_lighting_mode: Nighttime
        occupancy_sensing: input_boolean.bedroom_hallway_occupancy_sensing
        lights:
        - light.bedroom_hallway_lights
    
    # ADD ADDITIONAL CONFIG ITEMS AS A LISTED ITEM HERE
      
    
  zha_event: >
    {% if trigger.id == "Button" %}
      {# This may change depending on your event data. #}
      {{ trigger.event.data.command }}-{{ trigger.event.data.endpoint_id }}-{{ trigger.event.data.params.get('total_number_of_presses_counted', 'x') }}
    {% else %}
      None
    {% endif %}
  target_config: >
    {% if zha_event is None %}
      {{ config | selectattr('motions', 'contains', trigger.entity_id) | list | first | default({}) }}
    {% else %}
      {{ config | selectattr('devices', 'contains', trigger.event.data.device_id) | list | first | default({}) }}
    {% endif %}
  trigger_id: >
    {% if zha_event is None %}
      Occupancy: Turn {{ trigger.to_state.state }} Lights
    {% elif target_config %}
      {% set event = target_config.get(zha_event) %}
      {{ "Button: " ~ event if event is not none else None }}
    {% else %}
      None
    {% endif %}
conditions:
- condition: template
  value_template: "{{ trigger_id is not None }}"
actions:
  - action: script.control_button_occupancy_day_night_lights
    data: >
      {{ dict(trigger_id=trigger_id, **target_config.output) }}

Understand this automation will be firing a lot.

You will need to make your script script.control_button_occupancy_day_night_lights parallel if it isn’t already.

The benefit of going this route is that everything from the zha_event variable and below never needs modifying. You only need to add items to the config. I added comments with how to manage it.

The take away here is that it will handle all zha events without needing to modify anything but the config and the entity_id’s in the occupancy triggers. When you add a new area, you just add a config and the occupancy entity_ids.

Your button configs will require you to watch zha_events in the event viewer and populate the information. Because you haven’t provided examples, I went off previous posts of yours for the one example I saw, which was in this post.

Your inovelli option in the config would be a zha combo button_2_double-3-x, where the x is in place of the number of presses which differs from the ikea events.

Lastly, if you need different for durations, that would just be separate triggers but you can bundle binary sensors with like durations. Just make sure to keep the trigger id as id: Occupancy.

Or not bother calling scripts. The scripts are light.turn_(on|off), which can be a single action.

Push a button and turn a light on or off. As things go, you add a few more triggers – like motion or day/night change and now you have an automation for each. Do that for a few rooms and things multiply – which was what the original post was about.

The scripts are not really making things simpler. As the OP pointed out, combine triggers and use trigger ID, and then use groups, labels (or config) to avoid duplicating for each room.

Is there a reason why passing a list of device id should not be possible using trigger variables? That would help a lot.

Then those pesky devices:

variables:
  config:
  
    #  Bedroom Hallway 
    - motions:
      - binary_sensor.bedroom_hallway_occupancy_sensors
     devices:
      - 359bc5a5f510d587696d3042b560c8b7

I have many automations that use a config like that, but I’ve been moving those to labels or groups. It’s unfortunate that (apparently) the Zigbee integration doesn’t create the event.* entities because you could use those w/o having to deal with excluding and including a device.

Anyway, in this case I’d still prefer labeling the devices instead of hard-coding device IDs.

it’s not “Scripts” though. It’s a single light handler script from what I can tell. I haven’t looked at anything else, I said I would just provide an example of reducing the main automation down to 1 automation from multiple.

Device triggers do not support multiple devices. From what I understand, there’s technical reasons why this is the case.

Understood. There’s seven scripts overall, I think.

Since you are the template guy, do you have any comments on using the template like I did above to avoid creating an input_boolean for every room? Is there enough of race where if two events fired quickly it could corrupt the attribute? Does wrapping the event in a queued automation solve that? Probably not, as it’s fire and forget, right?

I’m sure everything else can likely be simplified. However he has 1 script that calls all the other scripts and he duplicates the main automation multiple times. So my example was to show him how to have 1 master automation that does everything instead of him duplicating the same automation with multiple triggers per device.

I typically try to avoid helpers myself. If I need them, I’d rather use MQTT, my own integration, or template entities.

No you’ll just end up with 2 state changes quickly.

That would just add a delay between them.