Note: This first post contains the latest code as a summary of the entire discussion. Thanks to all the participants in this discussion who make this code better! @jhenkens, @jon102034050, @RonJ103
So, I was looking for a way to create groups by combining other groups. The use case is, e.g., to turn off all lights in my flat in case all lights with a physical switch have been turned off. Or, to turn off all lights except some dim lights in the kid’s room.
Implementing this in the actual code base would probably require some sophisticated way of determining the order in which groups are created. So, I started with a pure yaml approach and I think, I will stick to it.
The first part is a script to combine groups, which is defined as follows:
script:
group_set:
alias: Create groups with enhanced include/exclude statements
sequence:
- service: group.set
data_template:
object_id: "{{ object_id if object_id != null else name | regex_replace('[^a-z0-9]', '_', True) | lower }}"
name: "{{ name if name != null else object_id }}"
icon: "{{ icon if icon != null else '' }}"
all: "{{ all if all != null else False }}"
entities: >-
{# ENTITIES #}
{% set ns = namespace(entities =
([] if entities == null else entities.split( ',' ) if entities is string else entities or [])) %}
{# TYPES #}
{% for type in ([] if types == null else types.split ( ',' ) if types is string else types or []) %}
{% set ns.entities = ns.entities + (expand(states[type]) | map(attribute="entity_id") | list) %}
{% endfor %}
{# INCLUDE #}
{% for group in ([] if include == null else include.split ( ',' ) if include is string else include or []) %}
{%- if expand_groups == True -%}
{%- set ns.entities = ns.entities + (expand(states[group])| map(attribute="entity_id") | list)%}
{%- else -%}
{%- set ns.entities = ns.entities + states[group].attributes.entity_id | list -%}
{%- endif -%}
{% endfor %}
{# EXCLUDE #}
{% for group in ([] if exclude == null else exclude.split ( ',' ) if exclude is string else exclude or []) %}
{%- if expand_groups == True -%}
{%- set ns.entities = ns.entities | reject('in', (expand(states[group])| map(attribute="entity_id")) | list)%}
{%- else -%}
{%- set ns.entities = ns.entities | reject('in', states[group].attributes.entity_id ) | list -%}
{%- endif -%}
{% endfor %}
{# OUTPUT #}
{{ ns.entities | join (',') }}
It uses group.set
to create the groups. You can supply most usual parameters to this service. Plus, in addition to the entities
parameter, you can supply both a list of groups whose members to include
in the new group as well as a list of groups whose members to exclude
from the new group.
This script can then be called during the homeassistant.start
event as follows:
automation:
- alias: Create test group
trigger:
- platform: homeassistant
event: start
action:
- service: script.group_set
data_template:
name: Test group
# Add specific named entity ids:
entities: light.dummy,light.disco
# Add member entities of groups or whole domains
include:
- group.kids_room
- group.bedroom
- light
# Remove member entities of other groups:
exclude: group.night_lights,group.decorations,switch
# If True, recurse group members down to actual device entities:
# Required, if passing domains to the include/exclude options
expand_groups: True
Note the two different ways to provide lists to the script.
Probably, an implementation in the actual group component would be better. But until then, this workaround should work around just fine… If I come up with more ideas on this, I’ll add them here.
Hope this helps someone…
See here for a some related threads: