Struggling with to_state.last_changed / from_state.last_changed

Hello,

I come from iobroker, am still new to HA and am currently playing around with the automations.
For no particular reason, I have added a window contact to our pets’ running wheel to measure the revolutions (let’s see how sporty the little ones are).
Unfortunately, the measurement doesn’t yet work the way I want it to.
I have built the following automation:

alias: Degus_Laufrad_RPM
description: ""
trigger:
  - platform: state
    entity_id:
      - binary_sensor.kontakt_degu_laufrad_contact
    from: "on"
    to: "off"
    for:
      hours: 0
      minutes: 0
      seconds: 0
condition: []
action:
  - service: input_text.set_value
    target:
      entity_id: input_text.degus_laufrad_timediff
    data:
      value: "{{ trigger.to_state.last_changed - trigger.from_state.last_changed }}"
  - alias: Kontakt entprellen
    delay:
      hours: 0
      minutes: 0
      seconds: 0
      milliseconds: 250
mode: single

This automation should save the time difference of a revolution in seconds in a helper.
This helper is then used to calculate the RPM using a template:

  - sensor:
      - name: Degus_Laufrad_RPM
        unique_id: "degus_laufrad_rpm"
        unit_of_measurement: "rpm"
        device_class: "speed"
        state: >-
          {% if is_state("input_text.degus_laufrad_timediff", "0") -%}
            {{"0"}}
          {%- else -%}
            {{ (60 / as_timedelta(states('input_text.degus_laufrad_timediff')).total_seconds()) | round(0) }}
          {%- endif %}

A further automation sets the value back to 0 after 10s of standstill.
This works fine, but unfortunately I get the wrong times saved in the first helper for the time difference, which is then used to calculate the wrong RPM.

This is most likely caused by bouncing of the window contact.
The little animals are definitely not that fast.
That’s why I’ve already built a debounce time of 250ms into the automation.
(tested it also with 1s - makes no difference. Nevertheless, very short times are saved again and again)
As I understand it, the automation should never trigger for less than 250ms, so the value in the helper should never be less than 0.25s.
This is not the case.
Why? What am I doing wrong?

Are the little ones too fast for HA?
With iobroker it actually worked quite well and plausibly, despite the fact that it is a Zigbee-Aqara contact

Use the history stats sensor instead. Use it to count the number of on pulses every minute. This is RPM.

sensor:
  - platform: history_stats
    name: Pet RPM
    entity_id: binary_sensor.kontakt_degu_laufrad_contact
    state: "on"
    type: count
    end: "{{ now() }}"
    duration: 
      minutes: 1

You can add a unit_of_measurement using customize:

Did you add a counterweight to the other side of the wheel to balance it?

Hi,

That sounds pretty good, thank you.
I see two small problems here

  1. if the window contact bounces, false-positive pulses are also measured - can this be prevented? (e.g. through a forced delay of 250ms after each detection)
  2. 60s will pass between the last pulse and the actual standstill.
    This means that the speedometer displays a speed for another 60 seconds even though the wheel has already come to a standstill.

When I think about it again, it’s not the same.

The history_stats sensor does not measure speed.
You can turn the wheel 10 times at a speed of 40RPM, than standstill (1,5s from contact to contact → 60s / 1,5s = 40 RPM … 10 revolutions in 15s), but it will still only have turned 10 times within the last 60s.
What I want to measure here is the speed, not the distance or the amount of revolutions.
I calculate the distance in kilometres using a counter and convert this to kilometres. (Daily value, weekly value, monthly value and annual value)

Probably not the most elegant solution, but provides reasonably plausible values

just used a second helper to save the last trigger-time

alias: Degus_Laufrad_RPM
description: ""
trigger:
  - platform: state
    entity_id:
      - binary_sensor.kontakt_degu_laufrad_contact
    from: "on"
    to: "off"
    for:
      hours: 0
      minutes: 0
      seconds: 0
condition: []
action:
  - service: input_text.set_value
    target:
      entity_id: input_text.degus_laufrad_timediff
    data:
      value: >-
        {{ (now() -
        as_datetime(states('input_text.degus_laufrad_timediff_lastfire'))).total_seconds()
        }}
    enabled: true
  - service: input_text.set_value
    target:
      entity_id: input_text.degus_laufrad_timediff_lastfire
    data:
      value: "{{ now() }}"
    enabled: true
  - alias: Kontakt entprellen
    delay:
      hours: 0
      minutes: 0
      seconds: 0
      milliseconds: 500
mode: single