WTH! đŸ˜€ Howto: reference A Device/Area's state (On/Off/unavailable) with A "Target Selector"?

In my experience as someone who has done this (motion sensors, occupancy sensors, camera motion events, etc control all the lights in my house) there still needs to be a human override button. Which is why the template light is nice, then you can just check if the room is on with a simple is_state(room_light, 'on') and can change all the lights with a light.turn_on/turn_off/toggle with all the template complexity hidden in the template entity instead of repeated in scripts and automations.

And then if a human disagrees with the automation and wants to change it, there’s a light entity right on the dashboard for them to use to change the whole room at once.

But to each their own. Whatever works for you.

Ahh sorry I didn’t explain that well. I have two input_selects for each area for lights. One for this blueprint (lights.turn_on) [area] lights blueprint which has the profile (as I call it) Bright, Dim, night-light, adaptive, colour loop, ect. & a second for the custom automation, ([area] lights switch) that has only On, Off, Auto.

If you’re truely trying to handle multiple entities.

Requires >= 2022.11

variables:
  lights: !input lights
  entities: >
    {{ device_entities(lights.device_id | default) + area_entities(lights.area_id | default) + lights.entity_id | default([]) }}
  lights_on: >
    {{ entities | select('is_state', 'on') | list > 0 }}

< 2022.11

variables:
  lights: !input lights
  entities: >
    {{ device_entities(lights.device_id | default) + area_entities(lights.area_id | default) + lights.entity_id | default([]) }}
  lights_on: >
    {{ expand(entities) | selectattr('state', 'eq', 'on') | list > 0 }}
1 Like

:star_struck: :pray: :star_struck: :pray: :star_struck: :pray:
Thank you so much.
I think that is going to take me a few days to digest. But that is awesome about different versions. I noticed something happening to the templating data between 2022.11.0 & 2022.11.2

For anyone that uses this code, there is A “count” filter missing & an error for “lights.entity_id”. It should be:
Requires >= 2022.11

variables:
  lights: !input lights
  entities: >
    {{ device_entities(lights.device_id | default) + area_entities(lights.area_id | default) + [ lights.entity_id ] | default([]) }}
  lights_on: >
    {{ entities | select('is_state', 'on') | list | count > 0 }}

< 2022.11

variables:
  lights: !input lights
  entities: >
    {{ device_entities(lights.device_id | default) + area_entities(lights.area_id | default) + [ lights.entity_id ] | default([]) }}
  lights_on: >
    {{ expand(entities) | selectattr('state', 'eq', 'on') | list | count > 0 }}

@petro you’re my hero! :grin:
But this will thro false positive’s for both device’s & area’s because it is not filtering out entities that aren’t in the “light” domain, So:

variables:
  lights: !input lights
  entities: "{{ device_entities ( lights.device_id | default ) + area_entities ( lights.area_id | default ) + [ lights.entity_id ] | default ( [] ) }}"
  filtered: "{{ expand ( entities ) | selectattr ( 'domain' , 'eq' , 'light' ) | selectattr ( 'state' , 'eq' , 'on' ) | list }}"
  lights_on: "{{ filtered | count > 0 }}"
  transition: "{% if lights_on  %} 90 {% else %} 0 {% endif %}"

@petro If you would be so kind as to critique my work, it would be much appreciated.

looks fine, you did alter lights.entity_id for the worse though. If you need to account for single items


  light_entities: >
    {% if lights.entity_id is iterable %}
      {{ [ lights.entity_id ] if lights.entity_id is string else lights.entity_id }}
    {% else %}
      []
    {% endif %}
  entities: "{{ device_entities ( lights.device_id | default ) + area_entities ( lights.area_id | default ) + light_entities }}"
  filtered: "{{ entities | select( 'search' , '^light' ) | select('is_state', 'on') | list }}"
  lights_on: "{{ filtered | count > 0 }}"
  transition: "{{ iif(lights_on, 90, 0) }}"
1 Like

So something like:

variables:
  lights: !input lights
  entity: "{% if lights.entity_id is defined %} {{ iif ( lights.entity_id is string , [ lights.entity_id ] , lights.entity_id ) }} {% else %} [''] {% endif %}"
  entities: "{{ device_entities ( lights.device_id | default ) + area_entities ( lights.area_id | default ) + entity }}"
  filtered: "{{ entities | select( 'search' , '^light' ) | select('is_state', 'on') | list }}"
  lights_on: "{{ filtered | count > 0 }}"
  transition: "{{ iif(lights_on, 90, 0) }}"

I couldn’t figure out how to get the “iterable” command to work?

So just for intellectual purposes, & hopefully some day soon; historically/hysterical purposes (Use expand on a value set from a target selector)

:face_with_monocle: How would one deal with multiple Area_id’s?

It’s a test, not a command. Sorry forgot to add defined before hand.

  light_entities: >
    {% if lights.entity_id is defined and light.entity_id is iterable %}
      {{ [ lights.entity_id ] if lights.entity_id is string else lights.entity_id }}
    {% else %}
      []
    {% endif %}
1 Like

Sorry to critique your work, but I would like to fully understand your thinking. (there is a typo for the second “light[s].entity_id”)
1/ What is the point of the “iterable” test? It seems to return true if the variable is a list or string, & returns A error when undefined?
2/ I don’t understand this line?

{{ [ lights.entity_id ] lights.entity_id is string else lights.entity_id }}

it tests if lights.entity_id is a valid string or list. It’s just for safety incase it’s not either of those object types.

No it’s not meant to be that. It’s an inline if statement that’s built into jinja. The iif function/method is similar but it was made for beginners who don’t understand inline if statements. The difference is that an inline if statement doesn’t resolve the opposite case where as the iif function/method does. I.e. inline if statement is more efficient. You can use either, it doesn’t really make a big difference either way. I don’t bother using iif because I don’t need to. I accidentally left out the if, probably why you didn’t understand it.

         value that's returned if the test is false_______________________
                                                           /              \
{{ [ lights.entity_id ] if lights.entity_id is string else lights.entity_id }}
   \_________________/     \________test____________/
           \__value that's returned if the if test is true
1 Like

I was struggling with “if” & “iif” in “{{}}”. That all makes so much more sense now, thank you.

You have pressed that “iterable” test should be used, & i get the logic now. But is it really needed?
(I only press the matter, because unnecessary code tends to be bad code, in my limited experience).
Shouldn’t the “input selector” have filtered for invalid values to begin with? Or is it accounting for variables inputed via yaml & if so, wouldn’t the script fail to load if invalid variable were set?
I guess non existent entity_id’s could be fed into it, but they should be filtered out @ the “filtered” variable stage. & I wonder if the same logic couldn’t be applied to the “default” filter. But I except the fact that you are much more experienced than I, so please don’t take this as nit picking, i’m just curious.

Finally device_entities & area_entities don’t permit lists being fed into them (as functions {from my understanding }), so have you got any suggestions about how to deal with this challenge, because currently this Blueprint script has two bugs.

1/ It can’t handling multiple devices [lights_on always comes back as false with multiple devices].
2/ It thro’s an error when trying to handle multiple areas [TypeError: unhashable type: ‘list’]

What I have so far is enough for my purposes, but i am curious about how you would deal with these problems (or even if it can be solved [is this A limitation])? My first thought is, it would require a loop or replace function? But I don’t know how to do that yet, & then feed the results into an {{expression}} to be processed? My second thought is, maybe if device_entities & area_entities are applied as filters [ | ]? @petro Sorry, but I think my OCD is going to make me look into this further. I know you knew what a huge can of worms this was from the beginning, & i very much want to thank you for your time so far. I have bought you a couple of :coffee:'s (you very much deserve them), tho I don’t think it’s nearly enough.

What I have so far, with my very limited handling of the two bugs:

variables:
  lights: !input lights

  area: "{% if lights.area_id is defined and lights.area_id is iterable %} {{ area_entities ( lights.area_id | default ) if lights.area_id is string else area_entities ( lights.area_id | first ) }} {% else %} [''] {% endif %}"
  device: "{% if lights.device_id is defined and lights.device_id is iterable %} {{ device_entities ( lights.device_id | default ) if lights.device_id is string else device_entities ( lights.device_id | first ) }} {% else %} [''] {% endif %}"
  entity: "{% if lights.entity_id is defined and lights.entity_id is iterable %} {{ [ lights.entity_id ] if lights.entity_id is string else lights.entity_id }} {% else %} [] {% endif %}"

  entities: "{{ device + area + entity }}"
  filtered: "{{ entities | select( 'search' , '^light' ) | select('is_state', 'on') | list }}"
  lights_on: "{{ filtered | count > 0 }}"
  transition: "{{ iif ( lights_on , 90, 0 ) }}"

You’re welcome to omit the iterable. Doesn’t matter to me, if you think it’s redundant, then remove it. I personally would rather be safe then sorry.

device: >
  {% if lights.device_id is defined and lights.device_id is iterable %}
    {% set devices = [ lights.device_id ] if lights.device_id is string else lights.device_id %}
    {{ devices | map('device_entities') | sum(start=[]) }}
  {% else %}
    []
  {% endif %}
area: >
  {% if lights.area_id is defined and lights.area_id is iterable %}
    {% set devices = [ lights.area_id ] if lights.area_id is string else lights.area_id %}
    {{ devices | map('area_entities') | sum(start=[]) }}
  {% else %}
    []
  {% endif %}

you could probably simplify this to a single field to handle all 3. Allow you to avoid your iterable that you don’t like for some reason.

entities: >
  {%- set ns = namespace(ret=[]) %}
  {%- for key in ['device_id', 'area_id', 'entity_id'] %}
    {%- set items = lights.get(key, [])  %}
    {%- if items %}
      {%- set items = [ items ] if items is string else items %}
      {%- set filt = key.split('_') | first %}
      {%- set items = items if filt == 'entity' else items | map(filt ~ '_entities') | sum(start=[]) %}
      {%- set ns.ret = ns.ret + [ items ] %}
    {%- endif %}
  {%- endfor %}
  {{ ns.ret | sum(start=[]) }}
1 Like

Right, what A humbling moment. I think that is going to take me @ least a week to digest that
 (& how embarrassing, what took me an entire day to think about took you 10min’s).
@petro Any chance of a full E.g. so i don’t take things out of context, & waste a whole week on my own incompetence?
P.s. Note to self (Sometimes having extra code helps everyone else understand the objective of the code).

All you need to do is use that for the entities field and use the other ones you made that filter to lights only and count the lights that are on. Those fields don’t change. So in the end, you’d have 4 fields. The input lights, entities, filtered, lights_on, and transition.

1 Like

:grin: ok, thanks :+1:

@petro Sorry I am getting errors in single & multiple instances for areas & devices (but entity/s works great :grin: ).

TemplateRuntimeError: No filter named ‘device’.
TemplateRuntimeError: No filter named ‘area’

I’m not sure why you always do this, but you change every multiline yaml notation that I write to single line. Just use multiline notation.

Anyways, there was an error in my code, updated it.

1 Like

Because it never seem to load in my system. I think i need to use “>-”, I don’t know? I will look into it tomorrow.
But onto a much more important things! @petro I think you have don’t it (i’ll do a heap of testing tomorrow) but I think I owe you a BEEPING BEEB more :coffee:
:pray: :grinning: :smiley: :grinning_face_with_smiling_eyes: :grin: :laughing: :joy: :slightly_smiling_face: :upside_down_face: :wink: :heart_eyes: :star_struck: :yum: :stuck_out_tongue: :stuck_out_tongue_winking_eye: :crazy_face: :stuck_out_tongue_closed_eyes:

They load on every system, it’s built into yaml. Your spacing needs to be correct. Notice how my templates are indented based on the field they are in.

field: >
  {{...
1 Like

:partying_face: The Final solution! :partying_face:

@petro you’re a LEGEND!

blueprint:
  name: A Test
  description: "A Variable State Test for Target Selector input"
  domain: script

  input:
    lights:
      name: Targeted Lights
      description: The Lights this Script is Targeted at.
      selector:
        target:
          entity:
            domain: light

variables:
  lights: !input lights
  entities: >
    {%- set ns = namespace(ret=[]) %}
    {%- for key in ['device_id', 'area_id', 'entity_id'] %}
      {%- set items = lights.get(key, [])  %}
      {%- if items %}
        {%- set items = [ items ] if items is string else items %}
        {%- set filt = key.split('_') | first %}
        {%- set items = items if filt == 'entity' else items | map(filt ~ '_entities') | sum(start=[]) %}
        {%- set ns.ret = ns.ret + [ items ] %}
      {%- endif %}
    {%- endfor %}
    {{ ns.ret | sum(start=[]) }}
  filtered: "{{ entities | select( 'search' , '^light' ) | select('is_state', 'on') | list }}"
  lights_on: "{{ filtered | count > 0 }}"
  transition: "{{ iif ( lights_on , 90, 0 ) }}"

sequence:
  - service: light.turn_on
    data:
      transition: "{{ transition }}"
    target: !input lights

:grinning: :smiley: :grinning_face_with_smiling_eyes: :grin: :laughing: :sweat_smile: :rofl: :joy: :slightly_smiling_face: :upside_down_face: :blush: :innocent: :star_struck: :yum: :stuck_out_tongue: :stuck_out_tongue_winking_eye: :crazy_face: :stuck_out_tongue_closed_eyes: :exploding_head: :partying_face: :sunglasses: :cowboy_hat_face: :nerd_face:
:pleading_face: I think i’m going to cry! :persevere: :confounded:

1 Like