Sensor for first time binary sensor is tripped each day

I could use some guidance on trying to make a sensor to display the earliest time each day that a binary sensor was tripped. I don’t need the most recent but rather when it was first tripped in the current day. I initially thought about doing a SQL query on sqllite but I kept getting malformed data errors (even when I copied and pasted the example code from the HA page). If I need to move HA to mariadb, I suppose I could do that if this was the best path forward.

I’m using Node Red, so I thought about the get history node, but it ends up producing 35 array items. There may be a way to programmatically go through them to determine the first “on” state from today, but I have yet to figure that out. I also can’t seem to find a pattern of the first or last array item being the earliest as they often capture previous day readings.

If you have done this, I’d love some guidance. I may be overengineering this, so I am hoping someone can push me in the right direction.

You could do it with a triggered template sensor.

template:
  - trigger:
      - platform: state
        entity_id: binary_sensor.your_sensor_here
        to: 'on'
    sensor:
      - name: Sensor Daily First On Time
        state: >
          {% if has_value(this.state) %}
            {% if states.binary_sensor.your_sensor_here.last_changed.day > this.state.last_changed.day %}
              {{ states.binary_sensor.your_sensor_here.last_changed }}
            {% else %}
              {{ this.state }}
            {% endif %}
          {% else %}
            {{ states.binary_sensor.your_sensor_here.last_changed }}
          {% endif %}

This is untested. And the for the first day it will have the date and time of the last time the door was opened. The following day it should be correct. Let me know how it goes.

1 Like

Thank you for the work! It’s currently showing as unknown upon a restart, but I will update this tomorrow.

Open the door.

1 Like

:roll_eyes: of course. I see an entry now. I’ll confirm the new data shows up tomorrow as design. Thanks for your patience. I’m clearly not thinking.

1 Like

I’ve viewed it this morning, and it is showing the most current timestamp, but not the earliest in the day.

A related question: does this have anything to do with timestamps being in UTC? Likely not, but wanted to confirm. If it’s looking at a current day, and I’m five hours behind UTC time (east coast in US), would that affect the template?

has_value needs an entity_id as input, not a state.
Based on your input I came to this.

template:
  - trigger:
      - platform: state
        entity_id: binary_sensor.your_sensor_here
        to: 'on'
    sensor:
      - name: Sensor Daily First On Time
        state: >
          {% set opened = states[trigger.entity_id].last_changed %}
          {% if as_datetime(this.state) is not none and as_datetime(this.state).date() != opened.date() %}
            {{ opened }}
          {% else %}
            {{ this.state }}
          {% endif %}
        device_class: timestamp
2 Likes

Thanks Fes.

I’m getting the following error in the logs:
sensor.great_room_sensor_daily_first_on_time rendered invalid timestamp: unknown

I didn’t make the same mistake as above, and I triggered the great_room_motion_sensor after the restart, but I’m still seeing an unknown state. My code is as follows just in case I’ve screwed something up in the transfer over:

template:
  - trigger:
      - platform: state
        entity_id: binary_sensor.great_room_motion_sensor
        to: 'on'
    sensor:
      - name: Great Room Sensor Daily First On Time
        state: >
          {% set opened = states[trigger.entity_id].last_changed %}
          {% if as_datetime(this.state) is not none and as_datetime(this.state).date() != opened.date() %}
            {{ opened }}
          {% else %}
            {{ this.state }}
          {% endif %}
        device_class: timestamp

Sorry, my bad. I shouldn’t have included not in the 2nd line.
Should have been

template:
  - trigger:
      - platform: state
        entity_id: binary_sensor.your_sensor_here
        to: 'on'
    sensor:
      - name: Sensor Daily First On Time
        state: >
          {% set opened = states[trigger.entity_id].last_changed %}
          {% if as_datetime(this.state) is none or as_datetime(this.state).date() != opened.date() %}
            {{ opened }}
          {% else %}
            {{ this.state }}
          {% endif %}
        device_class: timestamp

That worked in that it captured the first trigger of the motion sensor, and it has kept that same entry even when it was tripped multiple times. Thank you so much for the help as this was beyond my capability.

If I was looking for the time of day it was first triggered (rather than number of minutes or hours ago), would I pipe ( | ) an as_local in the line where the opened variable is set?

Again, thank you so much!

        state: >
          {% set opened = states[trigger.entity_id].last_changed %}
          {% if as_datetime(this.state) is none or as_datetime(this.state).date() != opened.date() %}
            {{ (opened|as_local).strftime("%H:%M") }}
          {% else %}
            {{ this.state }}
          {% endif %}
        device_class: timestamp
1 Like

That worked! Thank you so much for your help (and patience). I’m really grateful for your time.

This won’t work. this.state will be a time string now, and you can’t apply as_datetime() to a time string. It will always return none

For example {{ as_datetime("12:00") }} will return none

So that means that every time the door opens now, it will update to the new time.

I would put the datetime in an attribute:

template:
  - trigger:
      - platform: state
        entity_id: binary_sensor.your_sensor_here
        to: 'on'
    sensor:
      - name: Sensor Daily First On Time
        state: >
          {% set opened = states[trigger.entity_id].last_changed %}
          {% set previous = this.attributes.get('datetime', 'none') %}
          {% if as_datetime(previous) is none or as_datetime(previous).date() != opened.date() %}
            {{ (opened | as_local).strftime('%H:%M') }}
          {% else %}
            {{ this.state }}
          {% endif %}
        attributes:
          datetime: >
            {% set opened = states[trigger.entity_id].last_changed %}
            {% set previous = this.attributes.get('datetime', 'none') %}
            {% if as_datetime(previous) is none or as_datetime(previous).date() != opened.date() %}
              {{ opened.isoformat() }}
            {% else %}
              {{ this.attributes.get('datetime', 'none') }}
            {% endif %}
1 Like

Ah poop. You’re right. Well that got complicated.

I feel like even this can be simplified.

template:
  - trigger:
      - platform: state
        entity_id: binary_sensor.your_sensor_here
        to: 'on'
        variables:
          time: "{{ now() }}"
    sensor:
      - name: Sensor Daily First On Time
        state: >
          {% if this.state | default %}
            {{ this.state if today_at() < this.state | as_datetime < time | as_datetime else time }}
          {% else %}
            {{ today_at() }}
          {% endif %}
        device_class: timestamp

But he wants a time string like "07:45" as the state, not a full datetime (forgot to remove the state_class from my last version, removed it now)

You could use today_at() on the state and compare it to now() but that could cause to false negatives of the door is opened later than the time it was opened first the day before. So you need the date as well

today_at has a date associated with it and it changes at midnight.

Yes, but he doesn’t want the date in his state. He only wants the time as the sensor state.
That’s why I was putting it in an attribute

alright, then you’d need to remove device_class: timestamp

edit: looks like you did