Label_entities(): allow multiple labels selection (intersection)

Proposition: allow getting entities having multiple labels on them (intersection).

Example:

{{ label_entities(['bedroom', 'evening_lights']) }}

It should return entities that have BOTH the bedroom and evening_lights labels at the same time.

  • some_light1 with a label bedroom – NO, would not match
  • some_light2 with labels bedroom and daytime – NO, would not match
  • some_light3 with labels bedroom, evening_lights - YES, would match

In other worlds, it would be a logical AND of the passed in label names.

This is a counter-proposition to Allow for multiple labels/areas in label_entities/area_entities – which would be a logical OR on the passed in label names.


Original reply, as this FR is extracted from that one:

I’m always hesitant with this kind of API. Two aspects…

Api Extension
What if we wanted to add some kind of extra parameter to the function?

  • Let’s say we’d want to optionally include disabled entities. It could be label_entities('kitchen', 'bedroom', true) (the true would be include_disabled=true parameter). It’s kinda possible, but…
  • …it’s ok to add a boolean parameter, but impossible to add a string parameter: like label_entities('kitchen', 'bedroom', 'on') → that’s with an example as if we wanted to add a parameter that would allow us to quickly filter by value (the value_filter='on' parameter).

Solution: do not use variadic parameters, pass an array for a list instead: label_entities(['bedroom', 'kitchen'], 'on').

Intersection vs merge
With the proposed API, we have to decide what is more important: to get entities from both labels (“merge”, “sum of both”) vs find entities that have both both labels at the same time (“intersection”).

  • You’re proposing a merge, which is good for use cases like finding entities from both bedroom and kitchen.
  • Another use case would be finding an intersection, like finding the entities that have both labels bedroom and evening_lights at the same time.

Solution: I don’t know which use case is more popular/important in this case, but for me personally, I’d go with intersection (as implementing it manually per case would be much harder).

As for merging, you can already use an operator, like like label_entities('bedroom') + label_entities('kitchen').


To sum up… If I can humbly make a slight counter-proposition:

  • don’t use variadic parameters, allow to pass an array: label_entities(['bedroom', 'evening_lights']) – that would give the entities which have both labels at the same time (intersection).
  • don’t add a function for merging results → there is already a + operator for that.

EDIT: instead of overloading the label_entities() function, they could be all separated into 3 dedicated functions, like label_entities() + label_entities_any() + label_entities_all(). See below.

1 Like

I could not agree more! I just added some labels and many have multiple. I was very surprised when trying to use the filter, that any entity that had either chosen label was listed. the expected behavior was that it would filter and display only entities that had BOTH selected labels.

1 Like

I am using these as work-arounds for the time being

Select all entities labelled ‘bedroom’ OR ‘evening_lights’

label_entities_any(['bedroom', 'evening_lights'])
{%- macro label_entities_any(label_list) -%}
  {{- label_list | map('label_entities') | sum(start=[]) | sort |join(",") -}}
{%- endmacro -%}

Select all entities labelled ‘bedroom’ AND ‘evening_lights’

label_entities_all(['bedroom', 'evening_lights'])
{%- macro label_entities_all(label_list) -%}
  {%- set ns = namespace(entities=[]) -%}
  {%- set ns.entities = label_entities(label_list.pop(0)) -%}
  {%- for l in label_list -%}
    {%- set ns.entities = ns.entities|select('in',label_entities(l)|list )  -%}
  {%- endfor -%}
  {{- ns.entities|sort|join(",") -}}
{%- endmacro -%}
1 Like

label_entities() + label_entities_any() + label_entities_all() → I like your naming + separation of functions instead of overloading → probably the best possible option for implementing both FRs :+1: