Entity state `last_changed` constantly changing, even though the state didn't

I have a Tamota device (using the Tasmota integration) that is hooked up to my doorbell and exposes it as a binary sensor. In my dashboard, I display the time it last_changed as secondary info. The problem is that this changes every time the Tasmota device sends its state update (every 300 seconds), even though the value did not change. If I look at the history of the entity, it didn’t change there.

How can I make it not update the last_changed attribute, when there was no actual change? Isn’t it supposed to just update the last_updated attribute when the value stayed the same?

I’m using Home Assistant 2022.6.7 running in a container.

If there is no confusion, here, that shouldn’t happen.
It’s even independent from the integration (tasmota, here) that “last_changed” is only modified if the state value actually changes, not if it has been updated by the integration.

Yeah, even only updating the attributes should not do that.

That’s unusual. I don’t see that behavior with my devices running Tasmota. For example, I have an energy-monitoring switch connected to an appliance that’s always on. I don’t use the switch to control the appliance, only to monitor the appliance’s energy consumption. The switch’s last_changed value only changes when I restart Home Assistant (and not every 5 minutes when telemetry is received).

Thanks for the quick replies!
So is this a bug then? Can I report this in the core repo?

Here are two screenshots showing the problematic entity:
image
image

As you can see the actual state stayed the same (Off), but the Last changed timestamp changed.

If it’s a bug but you can’t provide a way for someone to replicate it then it makes it difficult for someone to debug it. For example, I am also using the Tasmota integration and I am not experiencing the problem you reported. I would be at a disadvantage to fix the bug because I can’t see it in action.

I’m not discouraging you from reporting it but you should gather and report as much information about it as you can in order to make it easier for someone to fix it.

Post it as an Issue in the GitHub Core repository.

Is that both the same sensor? It doesn’t make sense the 2 pictures show different timings…
The picture at the bottom shows it working as intended.

That is 100% the same entity, I opened the history directly from the dev tools states page of the first screenshot.
Why do you think the timing is off? I took the screenshot at 7:39pm, so that’s why it ends there. But as you can see, last_changed changed at 7:36pm even though the actual state didn’t at that time.

Right. Can you tell what Tasmota is sending in the config topic, e.g. with MQTT Explorer (assuming you use dicovery)?
There is a MQTT option, force_update that, well, forces an update even if the state hasn’t changed.

I don’t see force_update in there:

tasmota/discovery/xxx/config
{
   "ip":"xxx",
   "dn":"Door Phone",
   "fn":[
      "Door Phone Opener",
      null,
      null,
      null,
      null,
      null,
      null,
      null
   ],
   "hn":"hallway-door-phone-5383",
   "mac":"xxx",
   "md":"Generic",
   "ty":0,
   "if":0,
   "ofln":"Offline",
   "onln":"Online",
   "state":[
      "OFF",
      "ON",
      "TOGGLE",
      "HOLD"
   ],
   "sw":"11.1.0",
   "t":"hallway_door_phone",
   "ft":"tasmota/%prefix%/%topic%/",
   "tp":[
      "cmnd",
      "stat",
      "tele"
   ],
   "rl":[
      1,
      0,
      0,
      0,
      0,
      0,
      0,
      0
   ],
   "swc":[
      -1,
      2,
      -1,
      -1,
      -1,
      -1,
      -1,
      -1
   ],
   "swn":[
      null,
      "Switch2",
      null,
      null,
      null,
      null,
      null,
      null
   ],
   "btn":[
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0
   ],
   "so":{
      "4":0,
      "11":0,
      "13":0,
      "17":1,
      "20":0,
      "30":0,
      "68":0,
      "73":0,
      "82":0,
      "114":1,
      "117":0
   },
   "lk":0,
   "lt_st":0,
   "sho":[
      0,
      0,
      0,
      0
   ],
   "ver":1
}
tasmota/discovery/xxx/sensors
{
  "sn": {
    "Time": "1970-01-01T00:00:23.614",
    "Switch2": "OFF"
  },
  "ver": 1
}

If you look at the sensor’s activity in LogBook, is there any entry in the LogBook at 7:36 pm?

Nope
image

I now managed to build a reproducible setup for this problem.

I created a completely blank Home Assistant install with basically nothing but the MQTT and Tasmota integration. I then published the same messages the Tasmota device published to the MQTT broker and the last_changed field keeps changing when I publish to the tasmota/tele/hallway_door_phone/SENSOR topic.

If anyone wants to try this, here is the K8s manifest of this setup (somewhat specific to my cluster, but you can see all the settings I used):

manifest.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ha-test
  namespace: smarthome
  labels:
    app: ha-test
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: ha-test
  template:
    metadata:
      labels:
        app: ha-test
    spec:
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: ha-test-data
        - name: configmap
          configMap:
            name: ha-test-config
      initContainers:
        - name: config-merge
          image: busybox
          volumeMounts:
            - mountPath: /config
              name: data
            - mountPath: /merge/configmap
              name: configmap
          command:
            - "/bin/sh"
            - "-c"
            - "cp -Lr /merge/*/* /config/"
      containers:
        - name: homeassistant
          image: ghcr.io/home-assistant/home-assistant:2022.6.7
          volumeMounts:
            - mountPath: /config
              name: data
          env:
            - name: TZ
              value: "Europe/Berlin"
          ports:
            - containerPort: 8123
              name: http

---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: ha-test-data
  namespace: smarthome
  labels:
    app: ha-test
spec:
  storageClassName: xxx
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: ha-test-config
  namespace: smarthome
  labels:
    app: ha-test
data:
  configuration.yaml: |-
    default_config:
    group: !include groups.yaml
    automation: !include automations.yaml
    script: !include scripts.yaml
    
    http:
      cors_allowed_origins:
        - https://ha-test.xxx.xxx
      use_x_forwarded_for: true
      trusted_proxies:
        - '10.0.0.0/8'
        - '::0/0'

---
kind: Service
apiVersion: v1
metadata:
  name: ha-test
  namespace: smarthome
  labels:
    app: ha-test
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
  selector:
    app: ha-test

---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: ha-test
  namespace: smarthome
  labels:
    app: ha-test
spec:
  routes:
    - kind: Rule
      match: Host(`ha-test.xxx.xxx`)
      services:
        - name: ha-test
          namespace: smarthome
          port: 80
  tls:
    secretName: ha-test-xxx-xxx-cert

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mqtt-test
  namespace: smarthome
  labels:
    app: mqtt-test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mqtt-test
  template:
    metadata:
      labels:
        app: mqtt-test
    spec:
      volumes:
        - name: configmap
          configMap:
            name: mqtt-test-config
      containers:
        - name: mosquitto
          image: eclipse-mosquitto:2.0.14
          volumeMounts:
            - mountPath: /mosquitto/config/mosquitto.conf
              name: configmap
              subPath: mosquitto.conf
          ports:
            - containerPort: 1883
              name: mqtt
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mqtt-test-config
  namespace: smarthome
  labels:
    app: mqtt-test
data:
  mosquitto.conf: |-
    persistence false
    allow_anonymous true
    listener 1883

---
kind: Service
apiVersion: v1
metadata:
  name: mqtt-test
  namespace: smarthome
  labels:
    app: mqtt-test
  annotations:
    external-dns.alpha.kubernetes.io/hostname: mqtt-test.xxx.xxx
spec:
  type: LoadBalancer
  ipFamilyPolicy: RequireDualStack
  externalTrafficPolicy: Local
  ports:
    - name: mqtt
      port: 1883
      targetPort: mqtt
  selector:
    app: mqtt-test

Here are the messages I published to the broker:

Messages
tasmota/discovery/AAAAAAAAAAAA/config [retained]
{"ip":"192.0.2.1","dn":"Door Phone","fn":["Door Phone Opener",null,null,null,null,null,null,null],"hn":"hallway-door-phone-5383","mac":"AAAAAAAAAAAA","md":"Generic","ty":0,"if":0,"ofln":"Offline","onln":"Online","state":["OFF","ON","TOGGLE","HOLD"],"sw":"11.1.0","t":"hallway_door_phone","ft":"tasmota/%prefix%/%topic%/","tp":["cmnd","stat","tele"],"rl":[1,0,0,0,0,0,0,0],"swc":[-1,2,-1,-1,-1,-1,-1,-1],"swn":[null,"Switch2",null,null,null,null,null,null],"btn":[0,0,0,0,0,0,0,0],"so":{"4":0,"11":0,"13":0,"17":1,"20":0,"30":0,"68":0,"73":0,"82":0,"114":1,"117":0},"lk":0,"lt_st":0,"sho":[0,0,0,0],"ver":1}

tasmota/discovery/AAAAAAAAAAAA/sensors [retained]
{"sn":{"Time":"1970-01-01T00:00:23.614","Switch2":"OFF"},"ver":1}

tasmota/tele/hallway_door_phone/LWT [retained]
Online

tasmota/tele/hallway_door_phone/STATE
{"Time":"1970-01-01T13:36:06.915","Uptime":"0T13:35:18","UptimeSec":48918,"Heap":26,"SleepMode":"Dynamic","Sleep":50,"LoadAvg":19,"MqttCount":1,"POWER":"OFF","Wifi":{"AP":1,"SSId":"IoT","BSSId":"BB:BB:BB:BB:BB:BB","Channel":1,"Mode":"11n","RSSI":100,"Signal":-50,"LinkCount":1,"Downtime":"0T00:00:09"}}

tasmota/tele/hallway_door_phone/SENSOR
{"Time":"1970-01-01T13:36:06.927","Switch2":"OFF"}

Ah, seems to be “work-as-designed” for Tasmota:

Indeed, the integration has “force update” by default, meaning each and every update will change “last_changed”, whether the state has actually changed or not.
I guess it could be considered a bug in the Tasmota integration, at least according to me.

Unless there was a specific reason that it was implemented like that, it looks like wrong behavior to me. I guess I’ll open an issue and see what happens.

EDIT: Issue: https://github.com/home-assistant/core/issues/74357

1 Like

I’m not seeing this behavior for a switch (running Tasmota 12).

Telemetry has been configured to report every 30 minutes.

Screenshot from 2022-07-04 11-40-44

According to the switch’s History, the last time it changed state was July 2 11:04.

According to its information displayed in Developer Tools > States, its last_changed is July 2 11:04.
Screenshot from 2022-07-04 11-31-52

The following template reports the same result (July 2 11:04).

If it was force-updated every time it received telemetry, then the value of last_changed should be a time today (July 4), but it isn’t.

In the console output I can see that your device isn’t sending anything to the /SENSOR topic. I have SetOption114 1 set, so that the switches are disconnected from relays, since I don’t have a second relay on that device and the sensor is not related to the first relay. I think this causes the additional message to the /SENSOR topic, which seem to cause the last_changed update.

I would add that information to the Issue you created because it means the problem may occur only for a very specific configuration.

1 Like

I don’t see on override for switches, and lights are forced to false.
The integration definitely needs some cleanup on that side :wink:

1 Like

I’m seeing this behavior on MQTT device trackers as well. The last-changed timestamp in the UI is refreshing every time a device’s topic is published instead of upon a state change as I understand it should.