If you want to exclude the entity group.computer_room, you will have to wrap it in another group. There is no way (currently) to directly exclude single entities.
It seems like there is a ârecursion depthâ limitation. I think the script only goes down one level and finds whatever is there. This can be physical items such as lights, switches, locks, etc or non-physical items like more groups which could then contain even more groups or physical items.
If we made a list of physical items for the created group (only entities like lights, switches, etc without any groups) then when we excluded a group (or entity) it could create a list of physical items to remove from the âinclude listâ.
Thanks for the replies. Iâm starting to understand what is going on now. Iâd like to work on recursing the groups, but I am having trouble testing the code in the template editor. Maybe due to how the variables are passed in? Is there a trick to putting the code in the template editor to play with it? Are you able to execute the code in the template editor?
Thanks @akloeckner. I was leaving off the âsetâ part when using the template editor. That helped a lot. I have modified your code to allow for recursion of the groups:
group_set:
alias: Create groups with enhanced include/exclude statements
sequence:
- service: group.set
data_template:
object_id: "{{ object_id }}"
name: "{{ name or object_id }}"
icon: "{{ icon or '' }}"
all: "{{ all or False }}"
entities: >-
{# ENTITIES #}
{% set ns = namespace(entities =
(entities.split(',') if entities is string else entities or [])) %}
{# TYPES #}
{% for type in (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 (include.split(',') if include is string else include or []) %}
{% if expand_groups == True %}
{% set ns.entities = ns.entities + (expand(group) | map(attribute="entity_id") | list)%}
{% else %}
{% set ns.entities = ns.entities + states.group[group|regex_replace('^.*\.')].attributes.entity_id | list %}
{% endif %}
{% endfor %}
{# EXCLUDE #}
{% for group in (exclude.split(',') if exclude is string else exclude or []) %}
{% if expand_groups == True %}
{% set ns.entities = ns.entities | reject('in',(expand(group) | map(attribute="entity_id") | list)) | list %}
{% else %}
{% set ns.entities = ns.entities | reject('in', states.group[group|regex_replace('^.*\.')].attributes.entity_id) | list %}
{% endif %}
{% endfor %}
{# OUTPUT #}
{{ ns.entities|join(',') }}
Example automation:
- alias: Group Creation Test Case
trigger:
- platform: homeassistant
event: start
action:
- service: script.group_set
data_template:
name: Group Exclude Test Case
object_id: group_exclude_test_case
include:
- group.test_group_lights_switches
exclude: group.computer_room
expand_groups: True
This is the group that was created (works as desired):
With the new version 2021.4.6, I got warnings for undefined input variables, so here goes the update with checks against undefined variables. In this course, I also removed again the types input in favor of a slightly more greedy usage of expand(). So, if you want to use the new version, just include your types, e.g. include: light,switch.
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 (',') }}
I donât think that will work: include and exclude are basically limited to what expand() can do. And I donât think it can do wildcards (*) or the likes.
However, it would be possible to
create a specific group containing all your browser_* entities. Youâll have to name and list them explicitly for that, though.
exclude that specific group.
If that is not what you are looking for, you could craft your own template and pass it to the built-in group.setâŚ
FWIW, selecting entities based on a common characteristic becomes very easy if that characteristic is stored as a custom attribute as opposed to a sub-string within the entityâs name. Itâs also much neater because it avoids long-winded entity names.
You are free to add as many custom attributes as you wish to an entity. For example if I create a custom attribute called battery_type, I can select all sensor entities with a specific type of battery using:
If adding custom attributes is of no interest to you, you may want to consider using the recently introduced match filter which easily lets you select entities using a regular expression to match a sub-string within their name.
Thanks for the tip⌠my main issue is that browser_mod adds a âlightâ as a browser control, fortunately when I checked, it does toss a type tag in there so I should be able to filter that out.
Tried adding a mod to the script that would remove anything that had the attribute type browser_mod but a few hours later it is still just failing completely⌠sigh⌠bedtime.