I’m using a script to turn off lights that are on in the house. I was originally using “groups.all_lights” for this, but since I have 40+ lights in my system, it take a long time - I assume because it’s interating through all of them. Instead, I thought I would write a script using a template to only turn off lights that are on, so I wrote the below
'48739802483908':
alias: Test turning off lights
sequence:
- service: homeassistant.turn_off
data_template:
entity_id: >
{% for state in states.light -%}
{%- if state.state_with_unit =="on" %}
{{state.entity_id}}
{% endif -%}
{%- endfor -%}
Now, when I run the script I get
Tue Jan 30 2018 21:47:33 GMT-0500 (EST)
Invalid service data for light.turn_off: Entity ID light.1st_floor_kpl__b
light.1st_floor_kpl__c
light.1st_floor_kpl__d
light.dining_room_micro
light.dining_room_switch is an invalid entity id for dictionary value @ data['entity_id']. Got ['light.1st_floor_kpl__b
Thanks, but it looks to me that it’s complaining about the whole array, no? "Got ['light.1st_floor_kpl__b ". Or is this me not understanding the error?
Weird… Its odd, because using the the jinja2 template engine, this looks to me like its working correctly
Last time I looked into something similar it was because the template doesn’t seem to actually evaluate to an array. Instead, it evaluates to a string that just looks like an array.
I posted somewhere about sending the value to a script that calls the service, which seemed to convert the value to the expected type, but I can’t find that post anymore.
Can I ask why you want to do this? To iterate over the lights this way should be no faster versus a simple homeassistant.turn_off call on the group.all_lights
To me it seems you are adding complexity that isn’t required.
They mention that it’s slow to call on 40+ lights. Some lights seem to have a fairly significant delay when calling turn on/off. If the logic for turning off group.all_lights isn’t taking into account that some are already off and skipping over them or asynchronously doing them all in parallel, it could waste a bunch of time calling turn_off on lights that are already off. Otherwise, yeah, group.all_lights would be easier.
It’s tough for me to know what is the actual issue, my assumption is that their a) the call is not being done async or b) it is async, but the receiving controller (ISY994) can’t handle all those events at once
Either way, I definitely think it is not evaluating state when sending the command to group.all_lights as it does appear to send it to all my lights - no matter the state.
The controller may very be the bottleneck- I am not certain, but that misses the point. No need to send the off to 40+ devices, when only 3-4 may be on
But if asking for the state of a device takes the same time as asking them to turn off… whats the point? It really depends on how hass deals with your controller. If hass needs to ask your controller what the state of the device is, then your controller will still be a bottle neck.
A true test would be to install appdaemon. With yaml, I’m not sure how to create a for loop that can execute on a series of entities. Jinja is the limitation there.
With appdaemon, you would create an input_boolean and listen to it’s state. When its turned off, you would then cycle through lights active lights and shut them off. You would need another listener to listen to any light state change as well (to turn the input boolean on when a light in the group turned on).
I may have a chance to check this out at home later today because these speed tests pique my interest.
No, that’s actually not how this APPEARS to work… Using off w/ group.all_lights sends the command for each entity regardless of state. The script I posted asked HASS uses jinja to query the known states inside of HASS - it doesnt query the controller.
I haven’t had time to play with it, but I will either fix this as @tboyce1 suggested, or more likely, use appdaemon just so that the script will be cleaner.
You probably don’t need appdaemon even if you want to go the coding route. A python script should be enough, assuming you already have your automation trigger and only need this for the action.
- alias: Light's Left On!!!
trigger:
platform: state
entity_id: group.trackers
to: 'not_home'
action:
- service: homeassistant.turn_off
entity_id: >
{% for device in dict(states.light|groupby("state"))["on"] %}
{%- if loop.first %}["{% else %}", "{% endif %}{{ device.entity_id | lower }}
{%- if loop.last %}"]{% endif %}
{%- endfor %}
- service: notify.pushover
data_template:
title: "Alert"
message: "No One Is Home And The Lights Were Left On. I Turned Them Off For You"
I would test this but everyone is home and I don’t think they want the lights going off.
"""Turn off lights that are on."""
lights = []
for state in hass.states.all():
if state.entity_id.startswith("light.") and state.state == "on":
lights.append(state.entity_id)
if lights:
data = { "entity_id" : lights }
hass.services.call('light', 'turn_off', data)
Save it to config/python_scripts/lights_off.py and use like this:
I was able to make this work using the following. It does work with the exception of One bulb that thinks it is on but actually turned off at the light switch( err, family).
- alias: Light's Left On!!!
trigger:
platform: state
entity_id: group.trackers
to: 'not_home'
action:
- service: light.turn_off
data_template:
entity_id: >
{%- for device in dict(states.light|groupby("state"))["on"] %}{%- if loop.first %}{% else %}, {% endif %}{{device.entity_id | lower }}{%- if loop.last %}{% endif %}{%- endfor %}
- service: notify.pushover
data_template:
title: "Alert"
message: "No One Is Home And The Lights Were Left On. I Turned Them Off For You"