Adding more information to a list for speaking it

Did you create the group in the UI or in a file like configuration.yaml or groups.yaml?

It shouldn’t be possible to combine switches and binary sensors in the UI…

in the UI… (shame on me)

nope i couldnt combine, that was a problem for later… means 20 questions later, hahahaha

Ok, do you want to keep the sensors defined as switches?

do i have a choice?

Yes, you can modify the domain in a couple ways…

If they are zigbee devices (Sonoff’s zigbee devices often are mis-classified) you can change their domain in your zigbee integration.

You can create a “mirror” entity in Helpers > Change device type of a switch, but binary sensor is not an option as an output.

You can create a template binary sensor for each entity mis-classified as a switch. This could be tedious.


If none of those appeal to you, it is still possible to create a mixed-domain group, but it must be done in the configuration.yaml. Old-Style Groups

My suggestion is to ‘fix that after’ in the zigbee integration… But at least get this tts accomplished for now so you feel accomplished. You can always just change the group again after.

they are wifi ones… because i initially needed wifi ones for the washing machine and the dryer, because the room is far away and no chance to get cable there to mount a zigbee hub…

and then of 2 ordered, one came broken. then i ordered more and now i got 5.

but one is different, he identifies as binary. the rest as switch.

Drew’s code will work with any of the above for now… It’ll expand any of them i believe…

Strange…

Option 4: We work around the strangeness… replace the all caps part with your binary sensor’s actual object id.

service: notify.alexa_media_echo_studio_2
data:
  message: >
    {%- set ow = expand('switch.fenster', 'binary_sensor.YOUR_BINARY_SENSOR')
    | selectattr('state','eq','on')
    | map(attribute='entity_id') | list %}
    {%- set ns = namespace(open_time=[], name=[]) %}
    {%- for w in expand(ow)|list %}
    {%- set open_time = relative_time(expand(w)
    | map(attribute='last_changed')|join|as_datetime) %}
    {%- set name = w.name%}
    {% set ns.open_time = ns.open_time + [name~" has been open for about "~open_time]%}
    {%- endfor %}
    {% set qty = ow | count %} 
    {% if qty != 0 %}
    There {{'is an' if qty==1 else 'are '~qty}} open window{{' ' if qty==1
    else 's '}} 
    {% if ns.open_time not in [[], None, 'unknown'] %}
    {{ns.open_time|join(", \n")}}
    {% endif %}
    {% else %}
    There are no open windows
    {% endif %}
  data:
    type: tts 

i was using some google and typed some text:

if i now can do a text for “no windows open” when there is no object coming back, i guess i am happy then…

{% for entity_id in expand('switch.fenster')|selectattr('state','eq','on')|map(attribute='entity_id')|list %}
{% set opensinceseconds = as_timestamp(now()) - (as_timestamp(states[entity_id].last_changed))%}
{% set opensincedays = (opensinceseconds / 86400)|round|string%}
{% set opensincehours = (opensinceseconds % 86400 / 3600)|round|string%}
{% set opensincehourstext = "Stunden"%}
{% if opensincehours|int == 1 %}
{% set opensincehourstext = "Stunde" %}
{% endif %}
{% set opensinceminutes = (opensinceseconds % 3600 / 60)|round|string%}
{% set opensinceminutestext = "Minuten"%}
{% if opensinceminutes|int == 1 %}
{% set opensinceminutestext = "Minute" %}
{% endif %}
{% if opensincedays|int > 0 %} 
{% set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensincedays + " Tagen " + opensincehours + " " + opensincehourstext + " und " + opensinceminutes + " " + opensinceminutestext + " " %}
{% elif opensincehours|int > 0 %}
{% set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensincehours + " " + opensincehourstext + " und " + opensinceminutes + " " + opensinceminutestext + " " %}
{% else %}
{% set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensinceminutes + " " + opensinceminutestext + " " %}
{% endif %}
{{sentence}}
{%endfor%}

what do you think of this solution?

EDIT: just saw i got an error in my timestamp to seconds conversion…

Looks like it works. Personally I liked Drew’s example that uses a relative time which equates to ‘about’ 5 minutes or whatever. That way you know roughly how long and is less verbose.

i am struggling to get the timestamp to seconds…

Here’s mine with more granular relative times… google translated, and with some breaks to make it easier to understand.

    {% macro input(time) -%} {%- set td_object = now() - time %} {%-  set
    seconds = td_object.total_seconds() | abs | round(0) | int(0) %}
    {%-  set periods = { 604800: 'Woche', 86400: 'Tage', 3600: 'Stunde', 60: 'Minute'} %}
    {%- set ns = namespace(seconds=seconds)%}
    {%- for p_sec in periods.keys() %}
      {%- if seconds >= p_sec -%}
        {% set p_name = periods.get(p_sec) -%}
        {%  set period_value = ns.seconds // p_sec -%}
        {% set plural = "n" if period_value != 1 %}
        {{- period_value ~" "~ p_name ~ plural~" "-}}
        {% set dif = period_value * p_sec %}
        {%-  set ns.seconds = (ns.seconds - dif) % p_sec %} 
      {%- endif%}
    {%- endfor %}
    {%- endmacro %}

    {%- set ow = expand('switch.fenster') |
    selectattr('state','eq','on') | map(attribute='entity_id') | list %}
    {%- set ns = namespace(open_time=[]) %}
    {%- for w in expand(ow)|list %}
    {%-set open_time = input(expand(w) | map(attribute='last_changed')|join|as_datetime) %}
    {%- set name = w.name%}
    {% if open_time == ""%}
    {% set open_time = "weniger als einer Minute" %}{% endif %}
    {% set ns.open_time = ns.open_time + [name~" ist offen seit
    "~open_time~"<break time='400ms'/>"]%}
    {%- endfor %}
    {% set qty = ow | count%}
    {% if qty != 0 %}
    Es gibt {{'ein' if qty==1 else qty}} offene{{' ' if qty==1 else 's '}}fenster <break time="750ms"/> 
    {% if ns.open_time not in [[], None, 'unknown'] %} 
    {{ns.open_time|join(", \n")}}
    {% endif %} 
    {% else %} Im Moment sind keine Fenster offen {% endif %}

perfect, that helped me to get mine finished:

{% for entity_id in expand('switch.fenster')|selectattr('state','eq','on')|map(attribute='entity_id')|list %}
{% set opensince = (now()) - ((states[entity_id].last_changed)) %}
{% set opensinceseconds = opensince.total_seconds()|round%}
{% set opensincedays = (opensinceseconds / 86400)|round|string%}
{% set opensincehours = (opensinceseconds % 86400 / 3600)|round|string%}
{% set opensincehourstext = "Stunden"%}
{% if opensincehours|int == 1 %}
{% set opensincehourstext = "Stunde" %}
{% endif %}
{% set opensinceminutes = (opensinceseconds % 3600 / 60)|round|string%}
{% set opensinceminutestext = "Minuten"%}
{% if opensinceminutes|int == 1 %}
{% set opensinceminutestext = "Minute" %}
{% endif %}
{% if opensincedays|int > 0 %} 
{% set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensincedays + " Tagen " + opensincehours + " " + opensincehourstext + " und " + opensinceminutes + " " + opensinceminutestext + " " %}
{% elif opensincehours|int > 0 %}
{% set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensincehours + " " + opensincehourstext + " und " + opensinceminutes + " " + opensinceminutestext + " " %}
{% else %}
{% set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensinceminutes + " " + opensinceminutestext + " " %}
{% endif %}
{{sentence}}
{%endfor%}

image

closed and reopened my Bürofenster:

image

Just for ease of viewing. It wont matter much in the TTS… but you can add dashes and it’ll look better in the pane. to remove all the spacing.

{%- for entity_id in expand('switch.fenster')|selectattr('state','eq','on')|map(attribute='entity_id')|list %}
{%- set opensince = (now()) - ((states[entity_id].last_changed)) %}
{%- set opensinceseconds = opensince.total_seconds()|round%}
{%- set opensincedays = (opensinceseconds / 86400)|round|string%}
{%- set opensincehours = (opensinceseconds % 86400 / 3600)|round|string%}
{%- set opensincehourstext = "Stunden"%}
{%- if opensincehours|int == 1 %}
{%- set opensincehourstext = "Stunde" %}
{%- endif %}
{%- set opensinceminutes = (opensinceseconds % 3600 / 60)|round|string%}
{%- set opensinceminutestext = "Minuten"%}
{%- if opensinceminutes|int == 1 %}
{%- set opensinceminutestext = "Minute" %}
{%- endif %}
{%- if opensincedays|int > 0 %} 
{%- set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensincedays + " Tagen " + opensincehours + " " + opensincehourstext + " und " + opensinceminutes + " " + opensinceminutestext + " " %}
{%- elif opensincehours|int > 0 %}
{%- set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensincehours + " " + opensincehourstext + " und " + opensinceminutes + " " + opensinceminutestext + " " %}
{%- else %}
{%- set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensinceminutes + " " + opensinceminutestext + " " %}
{%- endif %}
{{sentence}}
{%-endfor%}

now with the 0 windows are open message:

{%- set counter = namespace(value=0)  %}
{%- for entity_id in expand('switch.fenster')|selectattr('state','eq','on')|map(attribute='entity_id')|list %}
{%- set counter.value = counter.value + 1 %}
{%- set opensince = (now()) - ((states[entity_id].last_changed)) %}
{%- set opensinceseconds = opensince.total_seconds()|round%}
{%- set opensincedays = (opensinceseconds / 86400)|round|string%}
{%- set opensincehours = (opensinceseconds % 86400 / 3600)|round|string%}
{%- set opensincehourstext = "Stunden"%}
{%- if opensincehours|int == 1 %}
{%- set opensincehourstext = "Stunde" %}
{%- endif %}
{%- set opensinceminutes = (opensinceseconds % 3600 / 60)|round|string%}
{%- set opensinceminutestext = "Minuten"%}
{%- if opensinceminutes|int == 1 %}
{%- set opensinceminutestext = "Minute" %}
{%- endif %}
{%- if opensincedays|int > 0 %} 
{%- set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensincedays + " Tagen " + opensincehours + " " + opensincehourstext + " und " + opensinceminutes + " " + opensinceminutestext + " " %}
{%- elif opensincehours|int > 0 %}
{%- set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensincehours + " " + opensincehourstext + " und " + opensinceminutes + " " + opensinceminutestext + " " %}
{%- else %}
{%- set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensinceminutes + " " + opensinceminutestext + " " %}
{%- endif %}
{{  sentence }}
{%- endfor %}
{%- if counter.value == 0 %}
{{  'Im Moment sind keine Fenster offen' }}
{%- endif %}

now is it possible to trigger my automation with an alexa command?

Do you have HA exposed to Alexa, either through Home Assistant Cloud or a custom skill?

i am in the one month trial of HA Cloud since today.

If that works, i am willing to pay.

So there are a few ways to do the next step. The method I use is to use scripts…

You need to create a script in HA. Settings > Automations & Scene > Scripts > Add Script. Then click on the three dot menu at the top right and select “Edit in YAML”. Copy/paste the following and make any changes you want. FYI, the script’s entity_id will be generated from the alias so make sure it makes sense for your normal naming convention.

alias: Alexa Offen Fenster
sequence:
  - service: notify.alexa_media_last_called
    data:
      data:
        type: tts
      message: '{{fenster}}'
variables:
  fenster: |-
    {%- set counter = namespace(value=0)  %}
    {%- for entity_id in expand('switch.fenster')|selectattr('state','eq','on')|map(attribute='entity_id')|list %}
    {%- set counter.value = counter.value + 1 %}
    {%- set opensince = (now()) - ((states[entity_id].last_changed)) %}
    {%- set opensinceseconds = opensince.total_seconds()|round%}
    {%- set opensincedays = (opensinceseconds / 86400)|round|string%}
    {%- set opensincehours = (opensinceseconds % 86400 / 3600)|round|string%}
    {%- set opensincehourstext = "Stunden"%}
    {%- if opensincehours|int == 1 %}
    {%- set opensincehourstext = "Stunde" %}
    {%- endif %}
    {%- set opensinceminutes = (opensinceseconds % 3600 / 60)|round|string%}
    {%- set opensinceminutestext = "Minuten"%}
    {%- if opensinceminutes|int == 1 %}
    {%- set opensinceminutestext = "Minute" %}
    {%- endif %}
    {%- if opensincedays|int > 0 %} 
    {%- set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensincedays + " Tagen " + opensincehours + " " + opensincehourstext + " und " + opensinceminutes + " " + opensinceminutestext + " " %}
    {%- elif opensincehours|int > 0 %}
    {%- set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensincehours + " " + opensincehourstext + " und " + opensinceminutes + " " + opensinceminutestext + " " %}
    {%- else %}
    {%- set sentence = state_attr(entity_id,'friendly_name') + " ist offen seit " + opensinceminutes + " " + opensinceminutestext + " " %}
    {%- endif %}
    {{  sentence }}
    {%- endfor %}
    {%- if counter.value == 0 %}
    {{  'Im Moment sind keine Fenster offen' }}
    {%- endif %}

Once that is saved, HA will refresh the scripts file automatically. If you tell Alexa to discover devices, she should says something like “1 new scene discovered” and then read out the name of the script.