If I understand correctly, you are looking for a way to have an Item to show the delta between the previous reading and the current reading for charting.
This is your biggest problem. If the Item updates to the same state, there is no change. You need to trigger the rule using updates, not changes.
Firstly, you almost certainly do not need a rule for this. You could use a script transformation profile instead.
Next, it’s important to understand the difference between an Update and a Change in OH, as hinted above. All Changes are Updates but not all Updates are Changes. That’s why we have two events. If you want to know when the Item was “touched” even if the state didn’t change, you need to focus on the Updates.
I don’t know if MyItem.previousState is driven by Changes only or, whether it takes account of updates. I’m not in a position to test it right now. Because you only look at it in your rule when the Item changes, you are essentially skipping the rule every time there was an update to the same state, so the rule isn’t even running when you want the 0 values.
Let’s assume MyItem.previousState works correctly. Then you can rely on the previousState. In a JS script transformation that would look something like:
(function(input, itemName) {
if(input == 'NULL' || 'UNDEF') return input;
// if the value has units
if (input.includes(' ')) {
const curr = Quantity(input);
return curr.subtract(items[itemName].previousQuantityState);
}
// just a plain number
else {
const curr = parseFloat(input);
return curr - items[itemName].previousNumericState);
}
})(input, itemName)
If previousState only gets updated on changes, you’ll have to keep track of the previous state yourself. But that’s not hard when using the cache.
The overall approach would be:
- calculate the delta using the value stored in the cache and the new value in the update
- record the new value
- return the delta
In JS the transformation would look something like this (works for both UoM updates or plain numbers):
(function(input) {
if(input == 'NULL' || 'UNDEF') return input;
// if the value has units
if (input.includes(' ')) {
const unit = input.split(' ')[1];
const prev = cache.private.get('delta', () => Quantity('0 ' + unit)); // returns 0 if no value is in the cache
const curr = Quantity(input);
cache.private.put('delta', curr);
return curr.subtract(prev);
}
// just a plain number
else {
const prev = cache.private.get('delta', () => 0); // returns 0 if no value is in the cache
const curr = parseFloat(input);
cache.private.put('delta', curr);
return curr - prev;
}
})(input)
Of course, the first time the Item is updated after OH is restarted will treat the current state as the first value which is probably not what you’d want. Therefore, you can either assume 0 for that first update or pass in the name of the Item and use it’s previousState which will be updated from persistence on startup.
(function(input) {
if(input == 'NULL' || 'UNDEF') return input;
// if the value has units
if (input.includes(' ')) {
const unit = input.split(' ')[1];
const prev = cache.private.get('delta', () => Quantity('0 ' + unit)); // returns 0 if no value is in the cache
const curr = Quantity(input);
cache.private.put('delta', curr);
return (prev.equals('0 ' + unit)) ? 0 : curr.subtract(prev);
}
// just a plain number
else {
const prev = cache.private.get('delta', () => 0); // returns 0 if no value is in the cache
const curr = parseFloat(input);
cache.private.put('delta', curr);
return (prev == 0) ? 0 : curr - prev;
}
})(input)
(function(input, itemName) {
if(input == 'NULL' || 'UNDEF') return input;
// if the value has units
if (input.includes(' ')) {
const unit = input.split(' ')[1];
const prev = cache.private.get('delta', () => Quantity('0 ' + unit)); // returns 0 if no value is in the cache
const curr = Quantity(input);
cache.private.put('delta', curr);
return (prev.equals('0 ' + unit)) ? items[itemName].previousQuantityState : curr.subtract(prev);
}
// just a plain number
else {
const prev = cache.private.get('delta', () => 0); // returns 0 if no value is in the cache
const curr = parseFloat(input);
cache.private.put('delta', curr);
return (prev == 0) ? items[itemName].previousNumericState : curr - prev;
}
})(input, itemName)
Create one of the transformations from the above. On the link between the Channel and the Item add a JS transformation and use the transformation you created. Use URL arguments to pass in the ItemName to the transformations that need it. See the JS Scripting docs for details.
Note I just typed in the above. There likely are errors.
Note 2 if you have more than one Item that needs this transformation, you’ll need to pass in a unique key to use in place of 'delta' into the transformation each place you use it. There is only one instance of the transformation so if you use it in more than one place each use will overwrite the entry in the cache for the others.