How to store a persistent global list/array/dictionary

I am looking for a way that an automation can store an array / list / dictionary and read it later – so it needs to be persistent.

In my use case I want to store pairs of light entities and their state recorded at a particular time. In JSON, it would look something like:

[
	{
		entity_id: "light.hall_ceiling_light",
		state: "on"
	},
	{
		entity_id: "light.kitchen_ceiling_light",
		state: "off"
	},
	{
		entity_id: "light.kitchen_wall_light",
		state: "unavailable"
	}
]

It does not have to be a json list, but that seems the obvious way to go as there are easy ways of handling such lists in templates. I just need somewhere to save it that is persistent over subsequent triggers of the automation, and I need to be able to declare it and pass a reference to it to a blueprint.

Home Assistant OS must do this all the time, but how can I do it?

  • I have read a lot about arrays in template sensors, but they are read-only. Is thee a way of writing attributes to a template sensor?
  • I usually use input_xxx helpers to save read/write data but none provide for an array of any kind. I don’t know whether I can create my own attributes?
  • I could write the json code as text, but a text helper only takes 255 characters, which I am likely to exceed.
  • HASS has some integrations that provide global variables, but I found none that support arrays / lists / dictionaries. Did I miss one?

Any other ideas?

Would “creating a scene on the fly” be a solution?

If so: https://www.home-assistant.io/integrations/scene/#video-tutorial

1 Like

Trigger based template sensor to store global variables.

2 Likes

That seems like a great idea! I would need to create the list of entities and their states dynamically using a list of entities provided as input to the blueprint and their current states.

Can I write something like …

service: scene.create
    data:
      scene_id: my_scene
      entities: >
        {% set ns=namespace(scene_list="what exactly?") %} 
        {% for each entity in input_lights ... %} 
                  {% ... #put values in scene_list# ... %}
        {{ ns.scene_list }} 

How would I need to format scene_list so that it provides what the scene needs, i.e.

e1:
  state: s1 
  brightness: b1 
e2:
  state: s2
  brightness: b2 
e3:
  state: s3
  brightness: b3
... etc. 

Can one simply mix JSON with YAML?!?

Also looks like a very creative solution, but I need to fully get my head around it! I cannot immediately see

  • How to set up a structure representing the list of entities and their states?
  • How to link one such structure to each automation created by the blueprint?

I’m sure if you have examples the author of that post would be willing to help in that post.
He is one of the best around with templates.

1 Like

Yes, I have now tried this and it does 90% of what I want. I am able to snapshot the state when motion is detected and restore it when motion stops and the timer has expired. The only caveat is that there is no way to read the status from the scene using a template, so I cannot fade them back to the original state with a stagger interval as I do when I fade them up. You can use a transition, but it applies to all the lights in parallel. However, that was just a bit of fun. Maybe I will experiment in future with other methods, but this is acceptable and the simplest solution for now.

Some observations that are not clear in the documentation:

  1. The scene_id appears in the scene attributes as “friendly name”, however, using a friendly name here does not work – you get ‘invalid slug’.
  2. If you ‘create’ the same scene twice it overwrites the existing one; there is no error message, so no need to delete it first.
  3. You do get an error message if you attempt to create a scene with no entities, so in the blueprint context I first have to check that there are any. In my use-case the list is fixed (as an input to the blueprint) but if it were variable, then one would have to specifically delete the scene when it becomes empty.
  4. Though created with a scene_id:, the scene incongruously has to be activated (turned on) with target: / entity_id:. The difference is that the entity_id needs ‘scene.’ prepended to the scene_id used at 1 above.
  5. To create scenes within a blueprint I have to manufacture a unique name. After trying several options I used the entity id of the current automation with the prefix ‘automation.’ stripped off – if you don’t you get ‘invalid slug’. I create separate scenes for the dimmable and non-dimmable lights.

Here are the relevant code extracts:

blueprint:
  name: Night Walk
  description:
    Fade up lights slowly when motion detected, but only if dark and if not
    already turned on manually or by voice assistant.
    Converse for turning off and only after a minimum on time.
  domain: automation

  input:

[...]

    dimmable_lights:
      name: Dimmable light(s)
      description: Light(s) to be faded up and down
      selector:
        entity:
          filter:
            domain: light
          multiple: true
      default: []

    non_dimmable_lights:
      name: Non-dimmable light(s)
      description: Light(s) to be simply turned on and off
      selector:
        entity:
          filter:
            - domain: light
          multiple: true
      default: []

 [...]

    fade_up_time:
      name: Fade up time
      description: Time to fade up from 0 to 100%
      selector:
        number:
          min: 0
          max: 30
          unit_of_measurement: seconds
      default: 20

    fade_down_time:
      name: Fade down time
      description: Time to fade down from 100 to 0%
      selector:
        number:
          min: 0
          max: 30
          unit_of_measurement: seconds
      default: 15

[...]

mode: queued
max_exceeded: silent

trigger:

  # motion detected
  - platform: state
    id: motion_detected
    entity_id: !input motion_sensors
    to: "on"
    for: !input on_delay

  # motion clear
  - platform: state
    id: motion_clear
    entity_id: !input motion_sensors
    to: "off"
    for: !input off_delay

  # On timer end
  - platform: state
    id: timer_ended
    entity_id: !input on_timer
    to: idle

variables:
  dimmable_lights_scene_id: >
    {{ this.entity_id | string | replace("automation.", "") + "_dimmable_lights_state" }}
  non_dimmable_lights_scene_id: >
    {{ this.entity_id | string | replace("automation.", "") + "_non_dimmable_lights_state" }}
  input_dimmable_lights:     !input dimmable_lights
  input_non_dimmable_lights: !input non_dimmable_lights
  there_are_any_dimmable_lights: >
    {{ input_dimmable_lights | list | count > 0  }}
  there_are_any_non_dimmable_lights: >
    {{ input_non_dimmable_lights | list | count > 0  }}

[...]

action:
 
  - choose:
      #
      # ACTION[3]CHOOSE[0] Motion detected and it is within the operating time range AND dark OR in test mode 
      #
      - conditions:
        - alias: "Motion detected"
          condition: trigger
          id: motion_detected

[...]

        sequence:
          # Record the initial state of the dimmable lights 
          - if:
              - condition: template 
                value_template: "{{ there_are_any_dimmable_lights  }}"
            then:
              - service: scene.create
                data:
                  scene_id: >-
                    {{ dimmable_lights_scene_id }}
                  snapshot_entities: !input dimmable_lights

          # Record the initial state of the non-dimmable lights 
          - if:
              - condition: template 
                value_template: "{{ there_are_any_non_dimmable_lights }}"
            then:
              - service: scene.create
                data:
                  scene_id: >-
                    {{ non_dimmable_lights_scene_id }}
                  snapshot_entities: !input non_dimmable_lights

          # Fade up dimmable lights
[...]

          # Turn on any non-dimmable lights
[...]


      - conditions: 
        - alias: "Motion clear"
          condition: trigger
          id: motion_clear
[...]
          # Set non-dimmable lights back to previous state  
          - if: 
            - condition: template 
              value_template: "{{ there_are_any_non_dimmable_lights }}"
            then:
              - service: scene.turn_on
                target:
                  entity_id: >
                    {{ "scene." + non_dimmable_lights_scene_id }}
                data: {}

          # Fade dimmable lights back to previous state  
          - if: 
            - condition: template 
              value_template: "{{ there_are_any_dimmable_lights }}"
            then:
              - service: scene.turn_on
                target:
                  entity_id: >
                    {{ "scene." + dimmable_lights_scene_id }}
                data:
                  transition: !input fade_down_time

      [...]

The full blueprint code is in a Github gist.