Binary_sensor Templates are driving me bonkers

Been trying to specifically configure a binary sensor(not a standard sensor) with little to no luck and I can’t seem to find a rhyme or reason

Configuration.yaml - working for my other templates

# Include Templates
template: !include_dir_merge_named templates/

If I do a regular ‘sensor’ in configuration.yaml the below works

  - platform: template
    sensors:
     brad_phone_connected_car_bluetooth:
        friendly_name: "Brad Phone Connected To Car Bluetooth"
        value_template: >-
           {{(state_attr('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')!=None) 
           and ('08:76:95:92:A7:B7' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices')) 
           or ('00:32:A0:03:47:1B' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices'))}}

However I specifically need a binary sensor - as shown here for

ulm_card_person_driving_entity

https://ui-lovelace-minimalist.github.io/UI/usage/custom_cards/custom_card_person_info/#description

Inside of templates/brad_phone_car_bluetooth.yaml I have tried the following, which shows the correct results when connected to either bluetooth(have included screenshots of both true and false). However the binary_sensor never actually updates when the state changes.

binary_sensor:
  - name: "Brad Phone Connected To Car Bluetooth"
    state: >-
           {{(state_attr('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')!=None) 
           and ('08:76:95:92:A7:B7' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices')) 
           or ('00:32:A0:03:47:1B' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices'))}}

I’ve also tried the following variations.

binary_sensor:
  - name: "Brad Phone Connected To Car Bluetooth"
    state: >
      {{ (is_state('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')!=None)}}
    attributes:
      connected_paired_devices: >
        {% if (is_state('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')!=None) %}
          {{ is_state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices', '08:76:95:92:A7:B7') }}
        {% elif %}
          {{ is_state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices', '00:32:A0:03:47:1B') }}
        {% endif %}
binary_sensor:
  - name: "Brad Phone Connected To Car Bluetooth"
    state: >
      {{ (is_state('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')!=None)}}
    attributes:
      koup: >
        {% if (is_state('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')!=None) %}
          {{ '08:76:95:92:A7:B7' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices') }}
        {% endif %}
      s2000: >
        {% if (is_state('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')!=None) %}
          {{ '00:32:A0:03:47:1B' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices') }}
        {% endif %}
binary_sensor:
  - name: "Brad Phone Connected To Car Bluetooth"
    state: >
      {{ (is_state('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')!=None)}}
    attributes:
      koup: >
        {% if '08:76:95:92:A7:B7' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices')  %}
            on
        {% endif %}
      s2000: >
        {% if '00:32:A0:03:47:1B' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices') %}
            on
        {% endif %}

Am I missing something, because it doesn’t seem obvious when looking at the templating documentation as to why this binary_sensor won’t actually update when the states are clearly changing?

My android devices return None if bluetooth is turned off and [] if bluetooth is on but not connected.
So you may need to have both of those in your template.

template:
  - binary_sensor:
      - name: "Brad Phone Connected To Car Bluetooth"
        state: >
          {% set c_d = state_attr('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')%}
          {% if c_d is not none %}    
          {{ c_d | join() in ['08:76:95:92:A7:B7', '00:32:A0:03:47:1B'] }}
          {% endif%}
        availability: >
          {{ state_attr('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices') not in [none, [], 'unknown', 'unavailable'] }}

EDIT Forgot that the list would need a join before the comparison.

1 Like

You’re really close; I think this should work for you:

binary_sensor:
  - name: "Brad Phone Connected To Car Bluetooth"
    state: >
      {{ '08:76:95:92:A7:B7' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices') |string 
       or '00:32:A0:03:47:1B' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices') |string }}
    attributes:
      koup: >
        {{ '08:76:95:92:A7:B7' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices') |string }}
      s2000: >
        {{ '00:32:A0:03:47:1B' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices') |string }}

I think what tripped you up is that you seem to mix up is_state and state_attr in the state: template, maybe took the wrong end off the function name and didn’t notice.

1 Like

Unfortunately, while this works, it has the same issue as all the other iterations, it evaluates in the developer template tool, but the sensor itself never actually updates.
.
.
.
.
.

So doing the below evaluates properly

{{ '00:32:A0:03:47:1B' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices') }}

When I was switching between is_state_attr and state_attr, I noticed that the MAC address when using ‘is_state_attr’ is not treated as a string and so does not evaluate like the above expression.

{{ is_state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices', '08:76:95:92:A7:B7') }}
  • is_state_attr('device_tracker.paulus', 'battery', 40) will test if the given entity attribute is the specified state (in this case, a numeric value). Note that the attribute can be None and you want to check if it is None, you need to use state_attr('sensor.my_sensor', 'attr') == None.

The reason I used the below is to avoid running when nothing is connected, and to prevent ‘none/null’ does not exist.

{{ (is_state('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')!=None)}}

Which brings me back to the most simplest form of binary_sensor without sub attributes. Which does not actually update.

binary_sensor:
  - name: "Brad Phone Connected To Car Bluetooth"
    state: >-
           {{(state_attr('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')!=None) 
           and ('08:76:95:92:A7:B7' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices')) 
           or ('00:32:A0:03:47:1B' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices'))}}

As with your version if we look at just the ‘state’ it is the same as above without the ‘None’ check, the expression evaluates, but fails to actually update the sensor once in place, even if the underlying states change.

Thanks for your guys replies.

TLDR, many variations, that evaluate properly within the developer template tool, all of which work as a standard ‘sensor’. But do not actually function when used as a ‘binary_sensor’ even though the expressions evaluate properly as true/false within the developer template tool.

This function is intended to be used on states of binary sensors,switches, or similar entities, so its behavior is different from Python’s built-in bool conversion, which would consider e.g. "on" , "off" , and "unknown" all to be true , but "" to be false

I think there is a bug, or something missing specifically to make binary_sensors update?

Different approach, then: have you tried setting a trigger for the sensor?

This might do it:

binary_sensor:
  - name: "Brad Phone Connected To Car Bluetooth"
    trigger:
      - platform: state
        entity_id: sensor.brad_pixel_6_pro_bluetooth_connection
    state: >-
           {{(state_attr('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')!=None) 
           and ('08:76:95:92:A7:B7' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices')) 
           or ('00:32:A0:03:47:1B' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices'))}}

That should cause the binary sensor to re-render every time the Bluetooth state or attributes update.

Are you configuring the binary sensor as a “modern” style template sensor or a “legacy” style?

In the very first example you are using the legacy method but later you are using the modern method.

Those two methods aren’t the same and need to be included differently.

Look at the template sensor docs for the differences.

I have indeed tried to use a trigger just like you mentioned. It still wasn’t working. Here was the format - as trigger isn’t part of binary_sensor, its it’s own object under the template

trigger:
  - platform: state
    entity_id: sensor.brad_pixel_6_pro_bluetooth_connection
binary_sensor:
  - name: "Brad Phone Connected To Car Bluetooth"
    state: >-
           {{(state_attr('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')!=None) 
           and ('08:76:95:92:A7:B7' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices')) 
           or ('00:32:A0:03:47:1B' in state_attr('sensor.brad_pixel_6_pro_bluetooth_connection','connected_paired_devices'))}}

I was using modern, but tried both.

Ok So this is the solution. When doing a binary_sensor, the sensor won’t ever update unless you do a {% XXX %} template evaluation within the state. If there’s no {% XXX %} line nothing works, even though the expressions may evaluate properly in template developer tool. The above variations will work as a ‘Sensor’, but wont work as a ‘binary_sensor’ without the {% xxx %} expression

configuration.yaml

# Include Templates
template: !include_dir_merge_named templates/

templates/brad_phone_car_bluetooth.yaml

trigger:
  - platform: state
    entity_id: sensor.brad_pixel_6_pro_bluetooth_connection
binary_sensor:
  - name: "Brad Phone Connected To Car Bluetooth"
    state: >-
           {% set c_d = state_attr('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices')%}
           {{(c_d!=None) and ('08:76:95:92:A7:B7' in c_d) 
           or ('00:32:A0:03:47:1B' in c_d)}}

Nowhere in this thread did I see an example that referenced the attribute directly from the trigger variable.

trigger:
  - platform: state
    entity_id: sensor.brad_pixel_6_pro_bluetooth_connection
binary_sensor:
  - name: "Brad Phone Connected To Car Bluetooth"
    state: >-
      {{ trigger.to_state.attributes.connected_paired_devices is defined and
         trigger.to_state.attributes.connected_paired_devices in ['08:76:95:92:A7:B7', '00:32:A0:03:47:1B'] }}
1 Like

I had previously tried triggers but did not try directly calling the trigger’s attribute. Though I just tried it for sanity sake, it as well does not ever update.

I’ve included a screenshot of the device tracker sensor, the ‘solution’ above, and your example of using trigger attributes. (don’t mind the mac address - updated all the variants with a test device so I didn’t have to keep going out to my car). The only variant from this thread that actually updates with or without a trigger is my last post.

The implication is that the template never evaluates to true and always remains false therefore the Template Sensor’s value remains fixed at off.

So what would cause the template to report false? I think we can safely assume the first half evaluates to true because the attribute’s value during your test isn’t none. So the culprit must be the second half where it checks if the received value is contained in the list.

The main difference between what you found to work and what I (and others) proposed is how the received value is handled. What you found to fail is when the received value is used directly in a comparison. What works is when the received value is first assigned to a Jinja2 variable and then the variable is used for the comparison.

I believe this difference may be due to how Home Assistant automatically infers a value’s type based on its appearance (i.e. ‘native typing’). The value presented by trigger.to_state.attributes.connected_paired_devices is automatically assigned a type based solely on its appearance. We would like to think it’s assigned the string type but what if it isn’t? If it’s not handled as a string then it would always fail a comparison with another string value. In other words, the second half of the template I proposed would always report false.

In contrast, when you assign a value to a Jinja2 variable, its type is handled by the Jinja2 processor (which operates under a different set of rules).

If you have the time, test this version. It does what you found to work, namely assign the received value to a Jinja2 variable before comparing it with a string.

trigger:
  - platform: state
    entity_id: sensor.brad_pixel_6_pro_bluetooth_connection
binary_sensor:
  - name: "Brad Phone Connected To Car Bluetooth"
    state: >-
      {% set x = trigger.to_state.attributes.connected_paired_devices if trigger.to_state.attributes.connected_paired_devices is defined else 'nope' %}
      {{ x != 'nope' and x in ['08:76:95:92:A7:B7', '00:32:A0:03:47:1B'] }}
1 Like

So I tried it, but the below is backwards, as well the list is making it so both objects values need to be there

x in ['08:76:95:92:A7:B7', '00:32:A0:03:47:1B']

If both objects values are there, the below works

['08:76:95:92:A7:B7', '00:32:A0:03:47:1B'] in x

However, both cars would never be connected at once so, this works.

trigger:
  - platform: state
    entity_id: sensor.brad_pixel_6_pro_bluetooth_connection
binary_sensor:
  - name: "123 Car Bluetooth test"
    state: >-
      {% set x = trigger.to_state.attributes.connected_paired_devices if trigger.to_state.attributes.connected_paired_devices is defined else 'nope' %}
      {{ x != 'nope'  and ('08:76:95:92:A7:B7' in x) or ('00:32:A0:03:47:1B' in x)  }}

That’s not how it works.

In the following example, the value of the variable animal must match at least one of the items in the list.

{{ animal in ['cat', 'dog', 'rat'] }}

The question I now have is:

Is the attributes value ever a list? Because that would also definitely fail the template I and others proposed.

If the value is something like '08:76:95:92:A7:B7' then the style of comparison I proposed will work. It will fail if the value is like this ['08:76:95:92:A7:B7'] or like this ['08:76:95:92:A7:B7', '00:32:A0:03:47:1B'].

Because if it is a list then that’s why this works:

('08:76:95:92:A7:B7' in x) or ('00:32:A0:03:47:1B' in x)

However if it’s truly a string value then what I proposed will find a match if it exists. In addition, if it’s a string then this:

{{ '08:76:95:92:A7:B7' in x }}

is equivalent to this:

{{ x == '08:76:95:92:A7:B7' }}

I’m not saying you are wrong. I use jinja2 almost daily with Ansible. But something is different about how jinja2 is evaluated when used under ‘binary_sensor’

Your expressions are correct, and these expressions(pretty much all of them in the whole post) all work as a standard template ‘sensor’. But when they’re loaded up under ‘binary_sensor’ even if they are evaluating as true/false on/off properly. ‘binary_sensor’ just doesn’t do it. So there is something different about the way binary_sensor evaluates an expression.

If you’re confident of that then I recommend you report it as an Issue in the GitHub Core repository.

Ideally you should devise an easily replicated example that demonstrates the problem.

BTW, can you post a screenshot of sensor.brad_pixel_6_pro_bluetooth_connection as seen in Developer Tools → States, specifically the appearance of the value of the connected_devices attribute. You may also need it for documenting the problem in the Issue.

1 Like

Sure.

The attribute’s value appears to be a string. An additional test would be to confirm the following template reports true.

{% set x = state_attr('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices') %}
{{ x != none and x is string }}

Therefore the suggested template should work because it’s simply matching a string value within a list of string values.

x in ['08:76:95:92:A7:B7', '00:32:A0:03:47:1B']

If you select the sensor and look at the state attributes in the “Current Entity” panel it is shown more like a list.

connected_not_paired_devices: []
connected_paired_devices:
  - 34:88:5D:9C:25:E2
paired_devices:
  - C8:6C:3D:AE:DB:9E
  - 34:88:5D:9C:25:E2
  - 00:23:12:50:FF:2F
unit_of_measurement: connection(s)
icon: mdi:bluetooth
friendly_name: DJFT8 Bluetooth Connection
2 Likes

Ah! Well in that case, the template must treat the attribute’s value as a list, not a string, for comparison purposes. Therefore Brad’s alteration of my suggested template is a must.

    state: >-
      {% set x = trigger.to_state.attributes.connected_paired_devices if trigger.to_state.attributes.connected_paired_devices is defined else 'nope' %}
      {{ x != 'nope' and ('08:76:95:92:A7:B7' in x) or ('00:32:A0:03:47:1B' in x) }}
1 Like

Based on everything presented so far, I believe the template can be reduced to this:

state: >
  {{ state_attr('sensor.brad_pixel_6_pro_bluetooth_connection', 'connected_paired_devices') | default([], true) | join() in ['08:76:95:92:A7:B7', '00:32:A0:03:47:1B'] }}

The sensor’s attribute list value, containing a single item, is converted to a string by join() and then checked if it exists in the list of strings.

The default filter handles the situation where the sensor’s attribute doesn’t exist.

2 Likes