Template automation for my boiler

You didn’t say that. You only provided very little context. Therefore I think you can ignore what I said.

no I wont ignore, I’m deep printing it in my head… thank you sir.

still, don’t want to leave you out of context, so here’s the whole thing:

  - alias: Presence Tracking
    id: 'Presence Tracking'
    trigger:
      - platform: state
        entity_id:
#        - device_tracker.1
#        - device_tracker.2
#        - device_tracker.3
#        - device_tracker.4
          - device_tracker.iphone
          - device_tracker.telefoon
    condition:
      - condition: template
        value_template: >
          {{ trigger.to_state.state is not none and
             trigger.from_state.state is not none and
             trigger.to_state.state != trigger.from_state.state 
            }}
      - condition: template
        value_template: >
          {% set zones = states.zone | map(attribute='entity_id')|list %}
          {{trigger.to_state.state in ['home','not_home'] or
            trigger.zone in zones}}
      - condition: template
        value_template: >
          {{ (now() - trigger.from_state.last_changed).total_seconds() > 120 }}
      - condition: template
        value_template: >
          {{ is_state('input_boolean.notify_presence', 'on')}}
    action:
      service: notify.m
      data_template:
        title: 'Presence Tracking:'
        message: >
          {% set name = trigger.to_state.attributes.friendly_name %}
          {% set to_state = trigger.to_state.state %}
          {% set from_state = trigger.from_state.state %}

          {% if to_state == 'not_home' %}
            {{ name }} left {{from_state}}
          {% elif from_state == 'not_home' %}
            {{ friendly_name }} arrived at {{to_state}}
          {% else %}
            {{ name }} left {{from_state}} and arrived at {{to_state}}
          {% endif %}

as you can see I’ve taken your advice to heart and left the a != a out…
the second value template, I am now uncertain of. I meant it to select only the possible states it should pass for the action to fire. But since you’ve told me it only shows valid state and will always be yes, it might be superfluous here too?

So the first condition should only allow state changes where the state string changed (and didn’t change from or to None.) I think that’s fine.

I’m not sure the second condition will work, mainly because for a state trigger there’s no such thing as trigger.zone. That only exists for a zone trigger, which you’re not using here. Also, looking past that detail and what I think the intent of this condition was, I’m not sure it does anything for you. A device_tracker’s state string will probably always be ‘home’, ‘not_home’ or the name of a zone. So why have a condition that the state string is something it will probably always be? Or are you trying to filter out ‘unknown’, ‘unavailable’, etc.?

The third condition only allows changes through where the device_tracker was in a given state for at least 2 minutes. That seems fine also.

And, of course, the fourth condition is fine, too.

Looking at the message template, I see one typo - using {{ friendly_name }} when you haven’t defined a variable named friendly_name. I suspect you meant {{ name }}. And, FWIW, you can simplify:

{% set name = trigger.to_state.attributes.friendly_name %}

to

{% set name = trigger.to_state.name %}

That will be the same as attributes.friendly_name if it exists, or the entity_id (or object_id, I don’t really remember which) if the entity does not have a friendly_name defined.

So, are you saying this automation notifies too often? If so, I’m not sure how.

is that so? These are my phones iCloud device_trackers, which are Zone capable? They report leaving or entering a zone…

yep, indeed, try to do that as much as possible, only allow the states Im interested in and filter out all other possible options.

indeed , typo, missed the one, thanks. will test again with this change to see what happens.

we were talking before about the triggering behavior of binary_sensors, and you said they trigger also when the object they listen to change. I thought that woud be very cool so changed:

  - alias: 'Low light - Hallway motion sensors'
    id: 'Low light - Hallway motion sensors'
    #initial_state: on
    trigger:
      - platform: state
        entity_id: sensor.mean_living_lux
      - platform: state
        entity_id: binary_sensor.low_lux_input
      - platform: state
        entity_id: sun.sun
        to: 'below_horizon'
    condition:
      - condition: template
        value_template: >
          {{ trigger.to_state.state is not none and
             trigger.from_state.state is not none and
             trigger.to_state.state != trigger.from_state.state }}
            # - condition: template
            #   value_template: >
            #         states('binary_sensor.low_lux_input') in ['on','off'] }}
      - condition: template
        value_template: >
          {{ (as_timestamp(now()) - 
              as_timestamp(state_attr('automation.low_light__hallway_motion_sensors','last_triggered')) 
              | default(0) | int > 240) }}
    action:
      - service_template: >
          homeassistant.turn_{{ states('binary_sensor.low_lux_input') }}
        entity_id: group.philips_hallway_motion_sensor_switches
      - condition: template
        value_template: >
          {{is_state('input_boolean.notify_notify', 'on')}}
      - service: notify.notify
        data_template:
          message: >-
            {{as_timestamp(now()) | timestamp_custom("%X") }}: 
             Hallway motion detection is {{ states('binary_sensor.low_lux_input') }}, 
             Low lux threshold: {{(states('input_number.low_lux') | float)}} 
             {{'>' if is_state('binary_sensor.low_lux_input', 'on') else '<'}}
             Mean living light : {{ states('sensor.mean_living_lux')}}.
             Sun is {{states('sun.sun')}}.

in to this:

  - alias: 'Low light - Hallway motion sensors'
    id: 'Low light - Hallway motion sensors'
#    initial_state: on
    trigger:
      - platform: state
        entity_id: binary_sensor.low_lux_input
      - platform: state
        entity_id: sun.sun
        to: 'below_horizon'
    condition:
      - condition: template
        value_template: >
          {{ trigger.to_state.state is not none and
             trigger.from_state.state is not none and
             trigger.to_state.state != trigger.from_state.state }}
#      - condition: template
#        value_template: >
#         {{ states('binary_sensor.low_lux_input') in ['on','off'] }}
      - condition: template
        value_template: >
          {{ (as_timestamp(now()) - 
              as_timestamp(state_attr('automation.low_light__hallway_motion_sensors','last_triggered')) 
              | default(0) | int > 240) }}
    action:
      - service_template: >
          homeassistant.turn_{{ states('binary_sensor.low_lux_input') }}
        entity_id: group.philips_hallway_motion_sensor_switches
      - condition: template
        value_template: >
          {{is_state('input_boolean.notify_notify', 'on')}}
      - service: notify.notify
        data_template:
          message: >-
            {{as_timestamp(now()) | timestamp_custom("%X") }}: 
             Hallway motion detection is {{ states('binary_sensor.low_lux_input') }}, 
             Low lux threshold: {{(states('input_number.low_lux') | float)}} 
             {{'>' if is_state('binary_sensor.low_lux_input', 'on') else '<'}}
             Mean living light : {{ states('sensor.mean_living_lux')}}.
             Sun is {{states('sun.sun')}}.

using:

binary_sensor:
- platform: template
  sensors:
    low_lux_input:
      friendly_name: Low lux input
      device_class: light
      value_template: >
        {{(states('sensor.mean_living_lux') | float) < 
          (states('input_number.low_lux') | float)}}

thinking the binary_sensor would catch the continuous light level changes of the sensor.mean_living_lux. It doesnt. It now triggers perfectly the the actual state of the binary changes on/off or when the sun trigger triggers. no automatic changes on light level, which the first example did trigger on…

but, as the added trigger keeps triggering the automation after the set time is passed, and even though I have the condition {{trigger.to_state.state != trigger.from_state.state}} I added @lolouk44 's suggestion for condition to rule that out for good…

  - condition: template
    value_template: >
      {{ states('group.philips_hallway_motion_sensor_switches') != 
         states('binary_sensor.low_lux_input') }}

lets see what happens…

the check for the boiler was a bit more complex, since it required 2 trigger entities to check. Can this be simplified further?

  - condition: template
    value_template: >
      {{ states('switch.sw_boiler_bijkeuken_template') != 
         ('on' if is_state('binary_sensor.zp_opbrengst_threshold_input','on') or
                    is_state('sensor.huidig_tarief', '1') 
          else 'off')}}

Yes, that is so. What fields are in the trigger variable have to do with the trigger platform, not the entity. And, BTW, all device_trackers (that are GPS based) are “zone capable.”

I was only talking about the threshold binary sensor, not every binary sensor.

Ok, thx. Trying to get to the bottom of this… how come the Message displays my zones just fine then? And I don’t mean home/not_home, but the real zones I declared in the zone component configuration ? They are passed on , without setting them as trigger .

Btw the difference between the behavior of the binary_sensor is noteworthy. Glad you pointed that out.
Wonder how one has to find out without this community… :+1:

@pnbruckner @lolouk44
all seems to be working fine now, so heres the full setup for reference thank you both for your invaluable advice and suggestions:

binary_sensor:
- platform: threshold
  name: 'ZP Opbrengst threshold'
  entity_id: sensor.zp_actuele_opbrengst
  upper: 1500

- platform: template
  sensors:
    zp_opbrengst_threshold_input:
      friendly_name: 'ZP Opbrengst threshold input'
      value_template: >
        {{(states('sensor.zp_actuele_opbrengst') | float) >
          (states('input_number.zp_opbrengst_threshold') | float)}}

group:
  solar_switch_monitor:
    name: Solar switch monitor
    control: hidden
    icon: mdi:theme-light-dark
    entities:
      - switch.sw_boiler_bijkeuken_template
      - sensor.huidig_tarief
      - sensor.zp_actuele_opbrengst
      - input_number.zp_opbrengst_threshold
      - binary_sensor.zp_opbrengst_threshold_input
      - binary_sensor.zp_opbrengst_threshold
      - automation.boiler_switch

binary_sensor.zp_opbrengst_threshold_input:
  show_last_changed: true
binary_sensor.zp_opbrengst_threshold:
  show_last_changed: true

automation.boiler_switch:
  show_last_changed: true
  templates:
    icon: >
      if (state === 'on') return 'mdi:bell';
      return 'mdi:bell-off';
    icon_color: >
      if (state === 'on') return 'rgb(251, 210, 41)';
      return 'rgb(54, 95, 140)';

  - alias: Boiler switch
id: 'Boiler switch'
trigger:
  - platform: state
    entity_id: sensor.huidig_tarief
  - platform: state
    entity_id: binary_sensor.zp_opbrengst_threshold_input
condition:
  - condition: template
    value_template: >
      {{ trigger.to_state.state is not none and
         trigger.from_state.state is not none and
         trigger.to_state.state != trigger.from_state.state }}
#      - condition: template
#        value_template: >
#         {{ states('binary_sensor.zp_opbrengst_threshold_input') in ['on','off'] }}
  - condition: template
    value_template: >
      {{ (as_timestamp(now()) - 
          as_timestamp(state_attr('automation.boiler_switch','last_triggered')) 
          | default(0) | int > 240) }}
  - condition: template
    value_template: >
      {{ states('switch.sw_boiler_bijkeuken_template') != 
         ('on' if is_state('binary_sensor.zp_opbrengst_threshold_input','on') or
                    is_state('sensor.huidig_tarief', '1') 
          else 'off')}}
action:
  - service_template: >
      switch.turn_{{'on' if is_state('binary_sensor.zp_opbrengst_threshold_input','on') or
                    is_state('sensor.huidig_tarief', '1') else 'off'}}
    entity_id: switch.sw_boiler_bijkeuken_template
  - condition: template
    value_template: >
      {{ is_state('input_boolean.notify_notify', 'on')}}
  - service: notify.notify
    data_template:
      message: >
        Threshold: {{states('binary_sensor.zp_opbrengst_threshold_input')}}, ZP {{states('sensor.zp_actuele_opbrengst')}}, Tarief {{states('sensor.huidig_tarief')}}: 
        Switching Boiler: {{'on' if is_state('binary_sensor.zp_opbrengst_threshold_input','on') or
                   is_state('sensor.huidig_tarief', '1') else 'off' }}.

the sensor Badge:

sensor.levering_of_verbruik:
  state_card_mode: badges
  templates:
#    icon_color: >
#      if (state === 'Levering') return 'rgb(251, 210, 41)';
#      return 'rgb(54, 95, 140)';
    theme: >
      if (state > 0) return 'orange';
      return 'green';
    unit_of_measurement: >
      ${entities['sensor.calculated_bruto_verbruik'].state}


levering_of_verbruik:
  friendly_name_template: >
    {% if states('sensor.netto_verbruik')|int > 0 %} Verbruik
    {% else %} Levering
    {% endif %}
  icon_template: >
    {% if states('sensor.netto_verbruik')|int > 0 %} mdi:import
    {% else %} mdi:export
    {% endif %}
  value_template: >
    {{states('sensor.netto_verbruik')|int}}

based on a mqtt sensor reading from my smart meter: sensor.netto_verbruik

Thanks!

please let me get back to this for a little unclarity on my side. Since we talked about the string/number issue. Ive been rewriting all to reflect the correct format. And try to have numbers everywhere without the quotes, so reading the yaml makes it clear what I am dealing with.

However when dealing with this, i am a bit confused:
this works:

customize-ui syntax:

group.personal:
  templates:
    icon: >
      if (entities['sensor.family_home'].state === '0') return 'mdi:account-off';
      if (entities['sensor.family_home'].state === '1') return 'mdi:account';
      if (entities['sensor.family_home'].state === '2') return 'mdi:account-multiple';
      return 'mdi:account-group';

Jinja:

  templates:
    icon: >
      {% if is_state('sensor.family_home', '0') %} 'mdi:account-off'
      {% elif is_state('sensor.family_home','1') %} 'mdi:account'
      {% elif is_state('sensor.family_home', '2' ) %} 'mdi:account-multiple'
      {% else %} 'mdi:account-group'
      {% endif %}

as you see I have to put the numbers in quotes. that is, if I use the is_state() format. I can leave the quotes out with |int when using the states.sensor.family_home.state format:

      {% if states.sensor.family_home.state|int == 0 %} 'mdi:account-off'
      {% elif states.sensor.family_home.state|int == 1 %} 'mdi:account'
      {% elif states.sensor.family_home.state|int == 2 %} 'mdi:account-multiple'
      {% else %} 'mdi:account-group'
      {% endif %}

Not sure why the numbers are turned into strings and I have to make them numbers again using the |int ( in the python script which creates this sensor I can safely use the unquoted numbers.

Secondly, is here a way to use the |int in the first 2 templates above? I can t seem to find a correct format/syntax for that.
thx,
Marius

First, if you have any templates that are evaluated in the frontend (vs the backend), I can’t comment on that. I don’t use any custom UI and I’m not familiar with how any of that works.

Second, all states are strings, no matter what they were before being updated in the state machine. See this code – the first thing it does with a new state value is to turn it into a string.

yes, I understand.
I guess it doesnt make a lot of difference then, but since the numbers here should be true numbers, as in the count of people home, they shouldn’t be quoted and turned into strings, as you mentioned before. And, consequently, the sensor state should be turned into a number again by using |int
All a matter of principle of course.

question remains:
can I write the is_state() construction below:

{% if is_state('sensor.family_home', '0') %} 'mdi:account-off'
      {% elif is_state('sensor.family_home','1') %} 'mdi:account'
      {% elif is_state('sensor.family_home', '2' ) %} 'mdi:account-multiple'
      {% else %} 'mdi:account-group'
      {% endif %}

somehow, using the |int, so I don’t have to quote the numbers. Like

  {% if states.sensor.family_home.state|int == 0 %} 'mdi:account-off'
  {% elif states.sensor.family_home.state|int == 1 %} 'mdi:account'
  {% elif states.sensor.family_home.state|int == 2 %} 'mdi:account-multiple'
  {% else %} 'mdi:account-group'
  {% endif %}

or

{% if states('sensor.family_home')|int == 0 %} 'mdi:account-off'
  {% elif states('sensor.family_home')|int == 1 %} 'mdi:account'
  {% elif states('sensor.family_home')|int == 2 %} 'mdi:account-multiple'
  {% else %} 'mdi:account-group'
  {% endif %}

I can find no way to use the is_state() construction with the |int …

Any of those will probably work. And, yes, is_state takes two parameters, an entity_id, and a value to compare that entity_id’s state to. And since an entity_id’s state is a string, the second parameter should also be a string.

The problem with numbers as strings is you sometimes can’t tell exactly how they’ll be converted. E.g., ‘0’, ‘00’, ‘0.0’ are all valid string representations for the number zero (the first two being representations for integers, and the third being a representation for a float.) But, if, for a particular entity, you now how the number will be represented, then you could do a string compare for equality. Still, you’re probably better off using an expression that allows comparisons as numbers instead of strings (which means, in this case, the is_state function may not be useful.)