Light snapshot into new scenes

Automating the correct lighting in your house is a complex challenge because it depends on many factors. In many cases just measuring the illuminance and then (automatically) adjusting light settings seems not a sufficient solution or isn’t accurate. Your mood, direct sunlight, covers, weather condition can be all criteria to adjust to a specific condition.

This script will save all current light settings per area or light group into a new scene with just one service call. This scene is then loaded into Home Assistant and you may later create an automation that activate this scene after you moved it into standard scene file(s).

Because the environment conditions may be important to create your new automation, a selected set of sensors is saved into the comments of the scene yaml code.

This script is similar to takes a little different approach then this project Scene_generator.py of sunnythaper by saving it per area or lightgroup. This script also only focusses on lights (or light groups), althought it save some sensors for reference.

Scene file
To save the scenes, the Home Assistant file notification (File - Home Assistant) is used. Create a file notifier:

notify:
- platform: file
  name: scene_notify_snapshot
  filename: /config/scenes/snapshot.yaml
  timestamp: false  

This saves the new scenes into the snapfile. Make sure this file exists and is not empty, else it will write a header, that will fail the scenes to load.

Include the scene file into your configuration.yaml (and keep using the scenes.yaml for UI modifications):

scene default: !include scenes.yaml
scene saved: !include scenes/snapshot.yaml

Script
To save the scenes, the Home Assistant file notification (File - Home Assistant) is used. Create a file notifier:

script:
  area_scene_snapshot:
    alias: area_scene_snapshot
    fields:
      area:
        description: Area
        example: Living room
      group:
        description: Group of lights to add to dialog
        example: light.living_room
      description:
        description: description to save with 
        example: "at evening when curtains close"
    variables:
      entities: |
        {%- set scene_entities = [] -%}
        {%- if group is not defined or group is none -%}
          {{ area_entities(area) | select('match','^light\.') | list }}
        {%- else -%}
          {{ state_attr(group, 'entity_id') | list }}
        {%- endif -%}
      mytime: |-
        {{ states('sensor.date_time_iso') | regex_replace(find='[^\\dT]', replace='') | replace('T','_') }}

      desc: |-
        {{  iif(description is not defined or description is none,states('input_text.scene_snapshot_desc'),description) }}

      snap: |-
        snap_{{ (iif(group is not defined or group is none, area, group) ) | regex_replace(find='[^\\w]', replace='_') | lower }}_{{ mytime }}_{{ desc | regex_replace(find='[^\\w]', replace='_') | lower }}

    sequence:
      - choose:
          - conditions: "{{ entities | length == 0 }}"
            sequence:
              - service: script.debug
                data_template:
                  source: script.area_scene_snapshot
                  title: Invalid group or area 
                  message: |
                    Group {{ group }} or area {{ area }} has no entities
        default:
          - service: notify.scene_notify_snapshot
            data_template: 
              message: |
                - id: {{ snap }}
                  name: {{ snap }}
                  # description: {{ desc }} 
                  # saved: {{ states('sensor.time_date') }} ({{ states('sensor.time_of_day') }})
                  # illuminance:
                  {% for item in states.sensor | selectattr('attributes.device_class', 'eq', 'illuminance') | list -%}
                  #   {{ item.entity_id }}: {{ states(item.entity_id) }}
                  {% endfor -%}
                  # covers:
                  {% for item in states.cover -%}
                  #   {{ item.entity_id }}: {{ state_attr(item.entity_id,'current_position') }}  # {{ states(item.entity_id) }} 
                  {% endfor -%}
                  # condition: 
                  #   sun: "{{ states('sun.sun') }}"
                  #   elevation: {{ state_attr('sun.sun', 'elevation') }} degrees
                  #   rain: {{ states('sensor.my_rainmeter_rain') }} mm
                  #   weather: "{{ states('sensor.buienradar_condition') }}"
                  entities: 
                    {%- set scene_entities = [] -%}
                    {%- for l in entities %}
                    {{ l }}:
                      state: "{{ states(l) }}"
                      {%- if not is_state(l,'off') -%}
                        {%- for attr in states[l].attributes -%}
                          {%- if not attr in ['friendly_name', 'effect_list', 'update', 'update_available', 'linkquality', 'supported_features', 'icon', 'min_mireds', 'max_mireds', 'supported_color_modes'] %}
                      {{ attr }}: {{ " " }} 
                            {%- if states[l].attributes[attr] is string or states[l].attributes[attr] is number -%}
                              {{ states[l].attributes[attr] }}  
                            {%- elif states[l].attributes[attr] is mapping -%}
                              {%- for value in states[l].attributes[attr] %}
                        {{ value }}: {{ states[l].attributes[attr][value] }}
                              {%- endfor -%}  
                            {%- elif states[l].attributes[attr] is iterable -%}
                              {%- for value in states[l].attributes[attr] %}
                        - {{ value }}
                              {%- endfor -%}  
                            {% endif %}                    
                          {%- endif %}
                        {%- endfor %}
                      {%- endif %}
                    {%- endfor %}
          - service: scene.reload
          - service: script.debug
            data_template:
              source: script.area_scene_snapshot
              title: Group or area saved 
              message: |
                {% if group is defined and group is not none %}
                  Group {{ group }} saved to {{ snap }}
                {% else %}
                  Area {{ area }} saved to {{ snap }}
                {% endif %}

Calling the script

type: entities
entities:
  - type: button
    name: Snapshot by Area (Living room)
    tap_action:
      action: call-service
      service: script.area_scene_snapshot
      service_data:
        area: Living room
        description: Saved by area
  - type: divider
  - type: button
    name: Snapshot by Area (light.living_room)
    tap_action:
      action: call-service
      service: script.area_scene_snapshot
      service_data:
        group: light.living_room
        description: Saved by group

Note: To use the area parameter, all lights should be assigned to an area in Home Assistant.

To use the light group you might have something like this defined in your configuration yaml:

light:
  - platform: group
    name: ling_room
    entities:
      - light.living_room_couch
      - light.living_room_tv
      - light.living_room_table
      - light.living_room_candle

  - platform: switch
    name: living_room_candle
    entity_id: switch.living_room_candle_plug

Some Lovelace helpers
control

Inputs

input_text:
  scene_snapshot_desc:
    name: scene_snapshot_desc

input_select:
  area:
    name: area
    options: 
      - empty

  light_group:
    name: light_group
    options: 
      - empty

Initialize above inputs

automation:
  - id: areas_initialize
    alias: areas_initialize
    trigger:
      - platform: homeassistant
        event: start
    action:
      - service: input_select.set_options
        data:
          entity_id: input_select.area
          options: |
            {% set ns = namespace(areas=[]) %}
              {% for elm in states.light if not area_name(elm.entity_id) in ns.areas%}
                {% if area_name(elm.entity_id) is not none %}
                  {% set ns.areas = ns.areas + [area_name(elm.entity_id)]  %}
                {% endif %}
              {%- endfor %}
            {{ ns.areas }}          
      - service: input_select.set_options
        data:
          entity_id: input_select.light_group
          options: "{{ states.light | selectattr('attributes.entity_id','defined') | map(attribute='entity_id') | list }}"

Helper scripts

script:
  area_snapshot_for_area:
    alias: area_snapshot_for_area
    sequence:
      - service: script.area_scene_snapshot
        data_template:
          area: "{{ states('input_select.area') }}"
          description: "{{ states('input_text.scene_snapshot_desc') }}" 

  area_snapshot_for_group:
    alias: area_snapshot_for_group
    sequence:
      - service: script.area_scene_snapshot
        data_template:
          group: "{{ states('input_select.light_group') }}"
          description: "{{ states('input_text.scene_snapshot_desc') }}" 

Create snapshots

type: entities
entities:
  - type: button
    name: Reload scenes
    tap_action:
      action: call-service
      service: scene.reload
  - entity: input_text.scene_snapshot_desc
  - type: divider
  - entity: input_select.area
  - type: button
    name: Snapshot Area
    tap_action:
      action: call-service
      service: script.area_snapshot_for_area
  - type: divider
  - entity: input_select.light_group
  - type: button
    name: Snapshot Group
    tap_action:
      action: call-service
      service: script.area_snapshot_for_group

Show snapped scenes

type: custom:auto-entities
card:
  type: entities
filter:
  include:
    - domain: scene
      entity_id: scene.snap_*
  exclude: []

4 Likes

This is awesome! It’s funny how a little script from 2017 still has a use case in 2022, maybe there is more to be done for the particular crowd that really relies heavily on changing complex scenes. Happy to see how this project grows in the meantime.

This is great! I’ve added this to my setup! And wanted to share a card for using mushroom-chip-cards, browser_mod, and auto-entities for organizing the scenes and showing the scene creator in a popup:

type: custom:auto-entities
card:
  type: custom:mushroom-chips-card
card_param: chips
filter:
  include:
    - domain: scene
      entity_id: scene.snap_*
      options:
        type: template
        icon: mdi:spotlight
        tap_action:
          action: toggle
        content: '{{ ('' ''.join(entity.split(''_'')[2:])) | title }}'
        use_light_color: true
    - type: template
      icon: mdi:plus
      tap_action:
        action: fire-dom-event
        browser_mod:
          command: popup
          title: Save Scene
          card:
            type: entities
            entities:
              - type: button
                name: Reload scenes
                tap_action:
                  action: call-service
                  service: scene.reload
              - entity: input_text.scene_snapshot_desc
              - type: divider
              - entity: input_select.area
              - type: button
                name: Snapshot Area
                tap_action:
                  action: call-service
                  service: script.area_snapshot_for_area
              - type: divider
              - entity: input_select.light_group
              - type: button
                name: Snapshot Group
                tap_action:
                  action: call-service
                  service: script.area_snapshot_for_group

also, one thing you forgot in the original code was the script for script.debug which I just put together as:

debug:
  alias: debug
  fields:
    source:
      description: Source of the debug
    title:
      description: Title
    message:
      description: Message to be seen
  sequence:
  - service: system_log.write
    data_template:
      message: '{{source}} | {{title}}: {{message}}'
      level: info

for future people to use

Here is how it ends up looking:

Also, if you are doing this in the future, add #SCENES to the newly created scenes/snapshot.yaml or else home assistant will try to add a comment that breaks the yaml to the file.

2 Likes

I’m having some weird issues that I’m trying to track down.

I created the notifier in configuration.yaml like below:

# This bit of code below is to allow me to save
# scene snapshots. The source came from a posting
# on the HA forums.
# https://community.home-assistant.io/t/light-snapshot-into-new-scenes/392363
notify:
  - platform: file
    name: scene_notify_snapshot
    filename: config/scenes/snapshot.yaml
    timestamp: false

When I try to call the service I see this error:

“Service notify.scene_notify_snapshot not found.”

I’ve tried calling the service directly from the Developer tools and get the same error message.

Ok, I have it saving now. I restarted Home Assistant and the notify service is now available to me.

When I updated the configuration.yaml, I was only reloading the config files which apparently isn’t enough to make the service available to the system.

1 Like