core: Derivative sensor doesn't update correctly for non-changing values
The problem
I use derivative sensor to measure the water flow through water counters. When the flow is changing, derivative shows realistic values. But when the flow becomes zero, derivative is still showing the last measured value for a very long period (about some hours). In the meanwhile, ‘change’ attribute of the statistics sensor becomes zero with zero flow after the last HA update 0.105 (the values keeping logic was changed). Below is the measurements of derivative and statistics sensors, and the historical values of the water meter itself as a prove.
Environment
Home Assistant 0.105.1 (ex. Hass.io) arch: x86_64
- Home Assistant release with the issue: 0.105
- Last working Home Assistant release (if known): new feature
- Operating environment (Hass.io/Docker/Windows/etc.): Hass.io/Docker/Synology DSM
- Integration causing this issue: Derivative
- Link to integration documentation on our website: https://www.home-assistant.io/integrations/derivative/
Problem-relevant configuration.yaml
sensor:
- name: raw_water_flow # problem-relevant sensor
platform: derivative
source: sensor.raw_water # water meter sensor, updates every 15 sec
round: 2
unit_time: min
- name: raw_water_stat # Seems to correctly working sensor
platform: statistics
entity_id: sensor.raw_water # water meter sensor, updates every 15 sec
max_age: '00:00:30'
sampling_size: 12
- platform: template
sensors:
raw_water_flow_stat:
unit_of_measurement: l/min
value_template: "{{ 2 * (state_attr('sensor.raw_water_stat', 'change') | float ) | round (2) }}"
Traceback/Error logs
No error, but incorrect behaviour.
Additional information
Here is the water meter values, which became zero at 01:18

The derivative sensor (sensor.raw_water_flow) was still showing non-zero (0.12 l/min) value after 01:18

The statistics sensor (sensor.raw_water_flow_stat) showed zero at 01:18

About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 4
- Comments: 47 (19 by maintainers)
Pretty amazing this issue goes back 2 years. It seems pretty obvious that because the source sensor doesn’t update when the value no longer changes, thus the derivative sensor, which needs more than 1 data point, doesn’t update either until another value gets sent from the source. It seems most people that “fix” this issue does so like the above, with some means to force an update. I’m no different.
In my case, I created a template sensor of the source and added an attribute that updates every minute. Then I based my derivative sensor off of this.
Hope that helps someone.
I’m so astounded that many are focusing on “how would I implement” and not starting with the obvious… A “Derivative” measures the rate of change occurring. My humidity sensor sends a reading every 60 seconds. HA DISCARDS repetitive readings. The “15 minute window” derivative of an hour of, say, 45% humidity, is 0. No question.
The “15 minute window” derivative of “no readings” is 0. No question.
I understand the concern for “non-reporting sensors”. But since HA drops the repetitive values, it seems best to work with what we have. No data within the window? Report the derivative as ‘0.0’. Only one reading within the window? Report the derivative as ‘0.0’.
It almost feels like the derivative helper is not written with HA in mind, since HA, by default, discards repeated readings, yet a derivative sensor, by its nature, needs multiple readings INCLUDING repeated readings.
What is the value of the time_window parameter in this case? In such way it looks ridiculous.
This is the point! Of course, in case no data received during last 20 seconds with 15 sec update interval in terms of approximation definitely means that flow is zero! Otherwise, it is just another realisation of old-known integration
statistics sensor.Yeah it gets to that last value and doesn’t calculate the derivative is zero until one more value is updated. So it gets close to 0 and is trending that way, but that last final step can take hours (however long it takes for the source sensor to update one more time).
It’s the same behavior as with the trend integration, which is where I stole the work around above.
https://community.home-assistant.io/t/add-force-update-support-to-template-sensor/106901/2
I presume that to get a derivative of 0, you need two consecutive values of the same number, but the way HA tends to work until specified otherwise is that it won’t send a second value from a device until the value changes. So the more I think about it, the more it seems the fault of HA as a whole and how derivatives work, and not the code or integration.
@afaucogney already posted :
I fully agree with @divanikus - the problem is that the function shows value at the current moment but the value is being calculated on the past sensor values. Ir order to automate the solution is the value out-of-date, the integration can use time window parameter.
having the same on my server
TLDR: I propose to update this integration as described in the final chapter of this loooong post.
I would like to give my view on the topic, which is based on mathematical insight:
Let me first start with a bit of explanation of how I approached this topic.
How I mathematically approach sensor values in HA
Sensor values are non-uniform (not equi-distant, i.e. not always with equal time between them) samples of the value a real-world “function”
f(t)at a specific time. Since sample values are digital numbers, these are an approximation of the real value of the functionf(t). Furthermore, consecutive equal values are ignored by HA, meaning we can interpret our samples as a list of pairs(t1,v1),(t2,v2),(t3,v3),..., where the sequencetis increasing, and any value ofvis always different from the previous one.This means that even if the method by which new values are obtained is a polling method with a regular interval, the samples may still be non-uniform because HA throws out equal values. Thus, any method we think of to process data should always assume the data is non-uniform, because even though we might know that data is checked regularly, we never know when the next different value comes in.
Now, based on the sample list
(t1,v1),(t2,v2),(t3,v3),...we want to create a “best representation” functiong(t)that resemblesf(t)as close as possible.So, how do we obtain a best representation
g(t)of the functionf(t)from its unique samples? There are multiple ways to do this:g(t)is based on only sample values that are in the past, i.e. only those samples(ti,vi)withti <= t. We call this a “causal” relation, because every change is caused by and can be computed from only those things that have already happened, for instance:last:g(t)is equal to the value of the last recorded (unique) sample.g(t)is based on previous and future values off(t), for instance:linear:g(t)is a straight line between the previous and next (unique) samples.The problem with any method that would fall in the category of item 2 is that they use future values of
f(t), which means that at timetwe cannot computeg(t)yet, because we also need at least 1 future samplef(t')for somet'> t, which we cannot know yet.Let me pose an assumption, without trying to clarify too much why I think it is true:
g(t)from the methodlast. This basically means it is a weigted average of the previous values, where the weights depend on how long each sample was active, and how far it is in the past.The derivative integration
So back to this integration. In my opinion, there is 1 property that would need to be satisfied for this integration to be useful, and that is:
0toxof the derivativef'(t)of a functionf(t), and for allxthe outcome will be exactlyf(x) - f(0), i.e. the original function will come out, although possibly with an offset.g(t)is based on reconstruction methodlast, the only riemann sum integral method that makes sense is to useleft-integration, which is what I will assume from here on.So, based on all of the above, I can now properly explain why I personally have a couple of “issues” with the current implementation of the Derivative integration:
time_windowis configured, the integration calculates a new derivative value at the moment when the next value comes in, from the previous value(t1,v1)and the new value(t2,v2), simply by calculating the rate of change(v2 - v1) / (t2 - t1). Though this is a good calculation to calculate the average derivative in the time window[t1, t2], the problem I have with it is that the integration applies this value at timet2, whereas (assuming we use the reconstruction methodlast) it actually makes more sense to apply this value at timet1. But that would mean at timet1the valuev2at timet2would need to be known, and thus we do not have a “causal” sensor!time_windowis configured, then the following example shows that the property is not guaranteed: Assume a cumulative sensor that has values(0,0),(2,1),(5,4). Then the derivative will calculate the following derivative sensor values:(0,0),(2,0.5),(5,1), which, usingleft-integration results in the integrated values(0,0),(2,0),(5,1.5), which is not equal to the original. (Note:right-integration would actually result in the original sensor values, but that does not match the assumption that our reconstruction function is based on methodlast)time_windowis given or not, if no sensor values take place for a long time, the derivative never resets to 0 (which is the main problem of this issue, of course), so taking the example sensor values above, if then the next value of(1005, 5)comes in, the derivative integration will add sample(1000, 0.001), and the riemann sum integral integration will add sample(1005,1.5 + 1 * (1005 - 5))=(1005, 1001.5), which is not even close!Let me give an example of what the derivative samples should look like in my opinion. Assume the samples are
(0,0),(2,1),(5,4),(12,5)andtime_window=4. Then the derivative samples should be:(-inf, 0)(0,0)enters thetime_window. New derivative sample:(0,0).(2,1)enters thetime_window. New derivative sample:(2,0.25).1w.r.t the sensor valuetime_windowseconds ago, so the average derivative is1/time_window = 1/4 = 0.25.(0,0)leaves thetime_window. New derivative sample:(4,0.25).last-interpretation, its value is still applicable in the first 2 seconds of the window. This might seem like a strange thing to do, because it is the same value as the previous sample, but below we shall see that it makes sense to update the derivative also once a sample goes outside thetime_window, not just when it enters it.(5,4)enters thetime_window. New derivative sample:(5,1).4w.r.t. the sensor valuetime_windowseconds ago, which was time1, i.e. when sample(0,0)was active, this its value was 0, so the derivative should be(4-0)/4 = 1.(2,1)leaves thetime_window. New derivative sample:(6, 0.75).(4-0)/4to(4-1)/4.(5,4)leaves thetime_window. New derivative sample:(9, 0).(5,4)is active throughout its entirety, so the derivative should become 0.(12,5)enters thetime_window. New derivative sample:(12, 0.25).(12,5)leaves thetime_window. New derivative sample:(16, 0).So our derivative samples become:
(0,0),(2,0.25),(5,1),(6,0.75),(9,0),(12,0.25),(16,0)(we dropped the duplicate(4,0.25), as HA would do).Now lets ty to
left-integrate this using the riemann sum integral integration:(0,0):Integral = 0=> sample(0,0)(2,0.25):Integral += 2 * 0=> sample(2,0)(5,1):Integral += 3*0.25=> sample(5,0.75)(6,0.75):Integral += 1*1=> sample(6,1.75)(9,0):Integral += 3*0.75=> sample(9,4)(12,0.25):Integral += 3*0=> sample(12,4)(16,0):Integral += 4*0.25=> sample(16,5)Wait what?!? That is not even close to the original list of samples! Indeed, but due to the
time_windowwe apply, what we are actually doing is applying a 4-second moving average to the original sensor before calculating its derivative. Suppose we would calculate a 4-second moving average of the original sample list every second:(0,0)=> sample(0,0)(1,0)(2,1)=> sample(2,0)(3, 0.25)(4, 0.5)(5,4)=> sample(5,0.75)(0*1 + 1*3)/4=0.75.(6,1.75)(3*1 + 1*4)/4=7/4=1.75(7,2.5)(8,3.25)(9,4)(10,4)(11,4)(12,5)=> sample(12,4)(13,4.25)(14,4.5)(15,4.75)(16,5)And if we cross-check the times at which the riemann sum integral integration calculated a new value, it matches 100% with the above list.
My proposed update
So, basically I would say that this integration needs an update as follows:
time_windowis given, it should default to 1, as otherwise we are creating a non-derivative sensor.time_windowinterval. This means the derivative will have 2 times as many state changes as the original sensor, but this comes with the added benefit that it actually makes sense as a derivative!async_call_laterfunction, with a delay of exactlytime_window.@afaucogney Would you be so kind as to read the above rationale and comment on whether you think this is a good improvement of this integration? If so, let me know, I can start working on implementing it on relatively short notice.
Possibly, it is what I need. I read the manual but It’s not clear how it works. What value should I use for the time_window? Is it a time delta that derivative uses for calculation of increment? In this case I think it’s better to use the time interval that my sensor is being updated (15 sec).
I’m not agree with you, since we are talking about physical conception ‘derivative’ what means the speed of value changing versus time line, no matter what is the reason of such changes, either “no change” or “waiting for the next value”. This is the nature of any derivative. But in case you start to matter regarding the reason of changes you mean statistics but not derivative. Home Assistant already has statistics sensor which does work exactly the way you tell about.
+1
Regarding the original issue @Spirituss described: I had the same problem that the derivative integration didn’t update values when my source sensor values were constant. I use this integration for calculating the power (in kW) based on the energy (kWh) which my energy meters provide. The latter is collected by using the RESTful Sensor integration:
I guess the value updates didn’t take place because hass didn’t write values of the source sensor in the database. I haven’t verified this, I just took a look at the Prometheus metric
hass_last_updated_time_secondsof the source sensor which I collect. As you can see, the source sensor didn’t update for quite some time:I could fix it by adding
force_update: trueto the sensor specification of the rest integration. Now it seems the source sensor (sensor.verbrauch_normalstrom) values are updated regularly – even when the value doesn’t change (which can’t be seen on my screenshots):Just wanted to quickly post this solution in case someone else finds this issue and uses the restful integration. Maybe other integrations provide similar functionality.
@basnijholt @afaucogney I believe that the problem lies in the plotting. Since people usually use such sensors for plotting, it’s highly desirable to see when change stops. It means that if the time window has exceeded and no new values are present, sensor should report 0, null, undef whatever, but not the last value. The statistics integration does this by issuing a timer for time window time and resetting sensor’s value on it’s exceeding.
Config:
The sensor sensor.raw_water shows nothing for long time:
But derivative sensor which physically means flow is still showing non-zero value:
It is definitely not the problem of the approximation, but the obvious mistake in the calculation algorithm realisation.
I added time_window to my sensors and nothing has changed. Derivatives show the same value as before.
statistics sensor runs periodically regardless if there are any changes in the sensor. This also means that it doesn’t track changes during the period.
derivate sensor (and integration sensor in which it is based) tracks changes in the source sensor. That means that if the source sensor doesn’t change, the derivate sensor will keep it’s value for long periods of time.
One possible solution is to combine both methods: track changes and periodically read the source sensor to detect “no changes”.