Find last state before state iteration identified by a context

I’m using some self referencing in template sensors, namely I calculate the state from an attribute. So, every time the attribute changes, the state changes, too.

Now, I want to trigger an automation on the state change and it does matter, what is the from_state. But, in my automation, the from_state already has the attribute set as it is used to calculate the to_state.

Obvious, because the calculation is

  1. Change attribute
  2. Change state

And only the second step causes the automation to trigger. Yet, in my head, the two steps actually are part of the same state change. Let’s say in an iterative way.

My question is: can I somehow find out the last state of my template sensors before this iteration? Maybe with contexts?

Please post your sensor and automation configs to clarify what you are talking about.

It’s quite some sensor…

  - name: Runway 14/32
    << : &runway_sensor
      icon: >
        {%- set runway = states(this.entity_id) | int(-9999) %}
        mdi:{{ 'arrow-top-thin-circle-outline'          if runway > 33.75 else
               'arrow-top-left-thin-circle-outline'     if runway > 29.25 else
               'arrow-left-thin-circle-outline'         if runway > 24.75 else
               'arrow-bottom-left-thin-circle-outline'  if runway > 20.25 else
               'arrow-bottom-thin-circle-outline'       if runway > 15.75 else
               'arrow-bottom-right-thin-circle-outline' if runway > 11.25 else
               'arrow-right-thin-circle-outline'        if runway >  6.75 else
               'arrow-top-right-thin-circle-outline'    if runway >  2.25 else
               'arrow-top-thin-circle-outline'          if runway > -999  else
               'crosshairs-question' }}
      state: >
        {%- set data = state_attr(this.entity_id, 'data') %}
        {{- data.runway if data and states(this.entity_id) == 'unknown' else None }}
    attributes:
      direction: 316
      << : &runway_attributes
        data: >
          {%- set value_json = {
               "time":   state_attr('sensor.opensky', 'time')   | default(None, true),
               "states": state_attr('sensor.opensky', 'states') | default([],   true),
          } %}

          {%- set direction = state_attr(this.entity_id, 'direction') | int(0) %}
          {%- set name      = (direction/10) | round %}

          {%- set ns = namespace({'runway': None}) %}
          {%- for aircraft in value_json.states %}

            {#- Calculate speed in runway direction #}
            {%- set callsign   = aircraft[1]  | trim %}
            {%- set altitude   = aircraft[7]  | float(0) %}
            {%- set latitude   = aircraft[6]  | float(0) %}
            {%- set longitude  = aircraft[5]  | float(0) %}
            {%- set vertical   = aircraft[11] | float(0) %}
            {%- set velocity   = aircraft[9]  | float(0) %}
            {%- set heading    = aircraft[10] | float(0) %}
            {%- set lateral    = velocity * cos(heading/180*pi - direction/180*pi) %}

            {%- if true
                   and (latitude-50.87)   |abs < 0.05
                   and (longitude-7.14)   |abs < 0.1
                   and (heading-direction)|abs < 5
                   and altitude > 0
                   and altitude < 150
                   and lateral | abs > 50 %}
              {%- set ns.callsign = callsign %}
              {%- set ns.vertical = vertical %}
              {%- set ns.lateral  = lateral %}
              {%- set ns.velocity = velocity %}
              {%- set ns.altitude = altitude %}
              {%- set ns.heading  = heading %}
              {%- set ns.runway   = name|string if lateral > 50 else ((name+18)%36)|string %}
            {%- endif %}
          {%- endfor %}

          {{- {
            'timestamp':  value_json.time,
            'datetime':   value_json.time | as_datetime | as_local | string,
            'callsign':   ns.callsign,
            'altitude':   ns.altitude,
            'heading':    ns.heading,
            'velocity':   ns.velocity,
            'vertical':   ns.vertical,
            'lateral':    ns.lateral,
            'runway':     ns.runway,
          } if ns.runway else state_attr(this.entity_id, 'data')}}

The idea is to find out, which runway direction is in use, based on a list of aircraft in the area from an opensky sensor.

I am suspecting that the sensor reacts to single helicopters flying in the opposite direction. So, I send mails to myself, when the runway changes:

automation:
- alias: Runway change
  id: runway
  trigger:
  - platform: state
    entity_id: sensor.runway_14_32
    to: '14'
    to: '32'
  action:
  - service: notify.email
    data_template:
      title: Runway changed
      message: >
        {{trigger}}

Ideally, I would see the same callsign twice: first in the erroneous change as the to_state, and second as the correcting change in the from_state. But the from_state of the second change already hold the new callsign in its attribute.

I think, I could achieve my goal, if I trigger not on the state but on the attribute data.runway itself. And I will try that.

However, that led me to the general issue of: what is the from_state really? I worked with a modelling language called Modelica quite some time. It features an operator pre(), which returns the state prior to something called the state iteration: when a state changes and triggers immediate change of another state, all those changes are considered as one change altogether. Obviously, pre and from_state are currently different things. But I would like to know, if there is a way to achieve the same result. After all, the from_state in my case is something very artificial and actually just a relict of the way my sensor is technically implemented.

I’m not saying, the semantics of from_state should be changed. Maybe they should. But I’m interested, if there is a way to get the last state before a series of instantaneous state changes started.