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

How would you suggest that I say that? I thought I did by saying:

I know I am the one wrong here, as no one understood what I was trying to do. Maybe I could have said I want the options of either area &or entity?

I dealt with that,

{{ expand ( area_entities ( lights.area_id ) ) | selectattr ( 'domain' , 'eq' , 'light' ) | selectattr ( 'state' , 'eq' , 'on' ) | list | count > 0 }}

I actually said this in my first post:
https://community.home-assistant.io/t/how-to-check-the-current-on-off-state-from-an-areas-lights-in-a-blueprint-using-selector-target-entity-domain-lights/472256/5?u=defenestrateit

@petro please don’t get annoyed with me, you have been such a huge help, & i very much appreciate your time, but I am still only a novice at this; & thus have some very stupid perceptions.

State what your endgoal is, not “how do I get the state of a device”. What do you plan on doing with the entities on the device?

So your script takes a target with lights. If any of the lights are on then it turns them off. If all of them are off it turns them on.

Sounds like a light group, why not just make one of those?

&

If the light/s are already on they transition @ one speed & if they are off @ another speed.

Because I really like using the Area selector, because it requires less maintenance & is easier to set up, but I also want the option of using the same code to target A single entity as well.

Now realise I started this project to set up a system for my parents, & tho my mum has the technical mind to understand this stuff I doubt she will ever spend the time learning it (she is 78), so I need it to be flexible enough with that in mind. & I am having heaps of fun learning this stuff…

So a template light then? You seem to be fine with making templates. Make one per area that sets it state from the lights in that area. And then you can control all the lights in that room as one light (turn on and off, transition, etc)

If you’re trying to make this easy for a non technical person to use then making scripts is going to be problematic. When you put a script on a dashboard you get a run button. That’s it. It doesn’t tell you whether the lights controlled by that script are on or off, it doesn’t let you adjust the brightness or change the color, etc. All the things a light does. Users who don’t get HA will struggle to interact with this thing. They’ll press what you tell them to press but it won’t make a lot of sense, it’s just memorization.

Additionally the easiest way for a non technical user to make things happen in HA is scenes. You go and adjust the lights and such in your room to the way you want and then you press a button in HA and basically say “memorize the lights in this room for later”. And then you get a button to press which recreates the room the way you had it. A button they made so they get it more. Lights can be put in scenes, scripts can’t.

You may like the selector but consider how the users you’re making this for are likely to interact with it. If they want to change the lights they’ll probably go look for a light.

1 Like

I’m not interested in putting the script on a dashboard, I set things up with a Automation & A script. Each room has a custom made automation, that does lots of things like object detection (with cameras) & motion detection for lights. & this will turn lights off, but to turn lights on (or change the profile) is very generic & just requires A blueprint script to handle thing like light effects & brightness, which is set by a input_select (helper) for each area.

If you look at my first post, it will probably make more sense:
https://community.home-assistant.io/t/how-to-check-the-current-on-off-state-from-an-areas-lights-in-a-blueprint-using-selector-target-entity-domain-lights/472256/5?u=defenestrateit

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