How to programmatically add "the" to a list of area names?

Defining my goal

I have a conversation intent_script that identifies areas with open windows and produces a response like “Windows are open in Kitchen and Living Room.”

I would like to make this sound more natural by using appropriate articles: “Windows are open in the Kitchen and the Living Room.” However, not all of my area names take the definite article “the”, since I have areas named things like “Jason’s Room”.

I don’t want to add “the” to my area names directly in Home Assistant, since I prefer my areas to be named “Kitchen” instead of “the Kitchen”.

I also would like to do this in a piece of reusable code for the sake of simplicity and efficiency.

Current effort

I am attempting to solve this by creating a custom Jinja macro at custom_templates/add_articles_areas.jinja. Here’s what it currently holds:

{%- macro add_article(room) -%}
{%- set rooms_with_the = [
'Basement',
'Basement Bathroom',
'Utility Room',
'Kitchen',
'Living Room',
'Main Bedroom',
'Upstairs Bathroom',
'Upstairs Hallway',
'Outdoors'
] -%}
{%- if room in rooms_with_the -%}
the {{ room }}
{% else %}
{{ room }}
{%- endif -%}
{%- endmacro -%}

This is the relevant section of my configuration.yaml:

intent_script:
  getWindowState:
    speech:
      text: >
        {% from 'add_articles_areas.jinja' import add_article %}
        
        {% set open_areas = state_attr('binary_sensor.exterior_windows', 'entity_id') | select("is_state", "on") | map('area_name') | unique | list %}
        {% if is_state("binary_sensor.exterior_windows", "off") %}
        {{ [
        "All the windows I know about seem to be closed.",
        "As far as I can tell, all windows are closed.",
        "Sensors report no open windows.",
        "They all look closed to me."
        ] | random }}
        {% elif open_areas | length == 1 %}
        There are windows open in {{ add_article(open_areas[0]) }}.
        {% elif open_areas %}
        {% set formatted_areas = open_areas | map("add_article") | list %}
        There are windows open in {{ formatted_areas[:-1] | join(', ') }} and {{ formatted_areas[-1] }}.
        {% endif %}

I’ll admit this is cobbled together, and I don’t fully understand every step of what I’m doing. For reference, this is a working version of the template without any of the attempts I’ve made to programmatically add “the”.

{% set open_areas = state_attr('binary_sensor.exterior_windows', 'entity_id') | select("is_state", "on") | map('area_name') | unique | list %}
{% if is_state("binary_sensor.exterior_windows", "off") %}
{{ [
"All the windows I know about seem to be closed.",
"As far as I can tell, all windows are closed.",
"Sensors report no open windows.",
"They all look closed to me."
] | random }}
{% elif open_areas | length == 1 %}
There are windows open in {{ open_areas[0] }}.
{% elif open_areas %}
There are windows open in {{ open_areas[:-1] | join(', ') }} and {{ open_areas[-1] }}.
{% endif %}

Issue

Presently, this doesn’t work. When I ask Assist one of the trigger phrases, “Are any windows open?”, Assist responds “An unexpected error occurred”.

When I test the intent_script template in the Template Designer, I get the issue:

TemplateRuntimeError: No filter named ‘add_article’.

What am I missing here? Is it possible to do what I want, or am I barking up the wrong tree?

Personal aha moment here: I understand that the line {% set formatted_areas = open_areas | map("add_article") | list %} is attempting to use the macro as if it’s a filter. But a macro isn’t a filter.

(I recognize that’s what the RuntimeError means; I’m putting it here to document my understanding and personal troubleshooting.)

So I need to figure out how to replace this section:

{% set formatted_areas = open_areas | map("add_article") | list %}
There are windows open in {{ formatted_areas[:-1] | join(', ') }} and {{ formatted_areas[-1] }}.

Do you really want it to say “the” for every area like: “… in the Kitchen, the Living room, the Basement…”?

{% set open_areas = state_attr('binary_sensor.exterior_windows', 'entity_id') | select("is_state", "on") | map('area_name') | unique | list %}
{% if is_state("binary_sensor.all_window_sensors", "off") %}
{{ [
"All the windows I know about seem to be closed.",
"As far as I can tell, all windows are closed.",
"Sensors report no open windows.",
"They all look closed to me." 
] | random }}
{% elif open_areas %}
There are windows open in the {{' and '.join((open_areas|join(', the ')).rsplit(', ', 1))}}.
{% endif %}

Hey Drew, always appreciate your help. Thanks.

I would prefer “the” on most rooms in this sentence, yeah. However, not every room makes sense with that definite article: “There are windows open in the Jason’s Room” doesn’t fly, which is why I’m trying this hackneyed scheme. :laughing:

It looks like the code you’ve offered would just hard-prepend “the” to all room names (except maybe the last in the list?), which doesn’t quite suit my purpose; I’m not sure I can guarantee that “Jason’s Room” will be the last in the list or the only that doesn’t take “the”.

I do appreciate that you seem to have condensed my “and” issue and made my length == 1 block unnecessary, thanks. I’m gonna try to make sense of that syntax so I can reuse it in the future.