After changing the unit of measurement on several time-based sensors (e.g., from seconds to minutes), I started encountering inconsistent behavior across automations and templates. One example is a Jinja template used in a push notification message:
{{ (states('sensor.ups_battery_runtime') | int) | timestamp_custom('%H:%M:%S', local = false) }}
Originally, this returned 00:54:00 (seconds). After the unit was changed to minutes, it returned 00:00:54, causing a misleading notification (54 seconds instead of 54 minutes). This silent behavioral change led to other issues as well, such as with utility meters (see my report here).
To improve robustness and prevent such breakages, please consider allowing templates to read sensor state in a fixed unit, e.g.:
That’s expected. The device_class you set on the entity translates the states value in the frontend. For device_class duration sensors, the visual output on the frontend is always D days, HH:MM:SS. However the under the hood state is a float specified by your unit_of_measurement. If you change the unit of measurement with that device_class, it will change the frontend visual output. This functionality will not change.
If you don’t want the visual output to change, do not use device_class: duration on the upstream entity and use something else.
@petro , thanks for the response. Could you please elaborate a bit on that? My problem is that the value returned by states() is non-deterministic now. Today you can assume it will returns seconds and base your template or automation on it. The next day you change the unit of the entity and your template/automation breaks or misbehaves.
Below is my UPS’s battery runtime sensor state expressed in minutes:
This makes any utility meter, template sensor or automation fragile. They will break as soon as you change the unit of the sensor they are based on. The change in the UI is natural and seemingly harmless, but you end up with utility meters showing crazy values, similarly to templates/automations.
The only idea that comes to my mind now as a workaround is to generate a very long set of if-else statements where you cover every unit of measurement for that sensor and do your calculations per every case (this isn’t, however, possible for utility meters). It makes any code very hard to maintain and takes much time to test, I hope you get what I mean.
So my idea here is that there should be a way to return the state of a sensor in a common base unit default per device class or in a manually specified unit.
Please also note that it refers to any device class. It doesn’t have to be duration, it can be power or whatever. As soon as you change the unit of measurement, the output value of the sensor gets adjusted to the new unit, so the value returned by states() gets converted as well.
This could be a workaround for templates, but let’s be frank – that’s shitty code that nobody would want to have repeated multiple times in dozens of templates/automations
{% set value = states('sensor.ups_battery_runtime') | float %}
{% set unit = state_attr('sensor.ups_battery_runtime', 'unit_of_measurement') %}
{% set seconds =
value * (
86400 if unit == 'd' else
3600 if unit == 'h' else
60 if unit == 'min' else
1 if unit == 's' else
0.001 if unit == 'ms' else
0.000001 if unit == 'μs' else
0
)
%}
{{ seconds | int }}
But that’s exactly what the states() function could do under the hood if you specify an expected unit as its argument. But that’s just an idea. I’d also be totally happy if there was a function that just returns the value in a fixed, default unit, regardless of the actual unit of the sensor.