Actually, that will only produce a result on startup and never update itself again (until the next restart). The reason is because the template contains no identifiable entities.
On startup, Home Assistant examines the template and assigns a “listener” to each identifiable entity. The listener detects when the entity changes state and causes the template the be re-evaluated (refreshed/updated).
If it cannot find entities, it assigns no listeners, evaluates the template once to produce a result, and then never evaluates the template again (until the next restart).
A common way to mitigate the issue is to specify an entity that changes its state periodically. For example, sensor.time changes state every minute and sensor.date changes state at the beginning of every day (moments after midnight). So the following Template Sensor would be evaluated once a day (and this assumes you have already configured sensor.date):
dehydrated_plants:
friendly_name: "Plants needing water"
entity_id: sensor.date
value_template: >-
{{ etc ...
The second issue is that the variable count will not report the correct value. That’s because variables inside a for-loop don’t retain their value outside the for-loop.
You can prove it to yourself by copy-pasting the following into the Template Editor:
{% set count = 0 %}
{%- for count in range(0,5) -%}
{%- set count = count+1 -%}
Inside the for-loop: {{ count }}
{% endfor %}
Outside the for-loop: {{count}}
You have to use a namespace to create a variable that retains its value after exiting the for-loop.
{% set ns = namespace(count = 0) %}
{%- for count in range(0,5) -%}
{%- set ns.count = ns.count+1 -%}
Inside the for-loop: {{ ns.count }}
{% endfor %}
Outside the for-loop: {{ns.count}}
If we combine everything I’ve described above, we get the following:
dehydrated_plants:
friendly_name: "Plants needing water"
entity_id: sensor.date
value_template: >-
{% set ns = namespace(count = 0) %}
{% for plant in states.plant if 'moisture low' in plant.attributes.problem %}
{% set ns.count = ns.count+1 %}
{% endfor %}
{{ns.count}}
Finally, to answer your original question, using a for-loop to calculate the total is the only way because there is no Jinja2 filter in Home Assistant that allows you to select entities based on a free-form sub-string match (i.e. match for sub-string ‘cat’ in string ‘bats cats hats’).
To be precise, there is a filter to match a sub-string but only if it is at the beginning ( It’s not a filter but a string method so, nope.startswith) or end (endswith) of the string. So if you know for certain that ‘moisture low’ is always at the beginning of string then I might be able to create a simpler template (maybe).
EDIT
This is a bit of a kludge but if ‘moisture low’ is always at the beginning of the value in the problem attribute, you can try this (paste it into the Template Editor; you will obviously need some plant sensors to be reporting ‘moisture low’ to get meaningful results):
{{ states.plant
| selectattr('attributes.problem', 'ge', 'moisture low')
| selectattr('attributes.problem', 'le', 'moisture lowz')
| list | count }}

