# Reducing Item updates (and thus log entries) caused by a Modbus TCP energy meter

**URL:** <https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516>\
**Category:** Items & Sitemaps\
**Tags:** items\
**Created:** [May 6, 2023, 7:33am UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516 "2023-05-06T07:33:33Z")\
**Posts on this page:** 20\
**Page:** 2

<div class="post-metadata">

**Author:** ![rlkoshak](https://community.openhab.org/user_avatar/community.openhab.org/rlkoshak/32/75251_2.png) [@rlkoshak](https://community.openhab.org/u/rlkoshak)\
**Post date:** [November 8, 2023, 5:18pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/21 "2023-11-08T17:18:26Z")

</div>

Doh! Another typeo. We have to pass in the key on the call to put.

Though this raises an important thing. If you don’t understand what this code does and how it works beware adopting it. You won’t be able to maintain it going into the future and at some point it’ll break and you won’t be able to fix it.

```auto
(function(i) {
  const lastValue = cache.private.get('last', () => -999999); // initialize it so if there isn't a lastValue the delta is large
  const delta = Math.abs(lastValue - parseFloat(input));
  if(delta > 1) {
    cache.private.put('last', parseFloat(input));
    return input; // pick a reasonable threshold
  }
  return null; // if the delta isn't large enough return null, suppressing the event
})(input)

```

---

<div class="post-metadata">

**Author:** ![STSC](https://community.openhab.org/letter_avatar_proxy/v4/letter/s/f14d63/32.png) [@STSC](https://community.openhab.org/u/STSC)\
**Post date:** [November 8, 2023, 5:32pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/22 "2023-11-08T17:32:33Z")

</div>

It’s working now. Actually I’m used to DSL, because even this takes half a minute to compile on my Raspberry. What an issue is, is that the transformation can be used only once due to cache.private.get(‘last’).  
Could this ‘last’ be replaced by something like the itemName? Or maybe we could directly compare it with the item value without cache.private.

I have seen that input provides a input.lastValue, but I guess a Modbus data thing has no lastValue.

---

<div class="post-metadata">

**Author:** ![rlkoshak](https://community.openhab.org/user_avatar/community.openhab.org/rlkoshak/32/75251_2.png) [@rlkoshak](https://community.openhab.org/u/rlkoshak)\
**Post date:** [November 8, 2023, 6:01pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/23 "2023-11-08T18:01:04Z")

</div>

> [@STSC](#):
>
> Actually I’m used to DSL

In Rules DSL the transform would be

```auto
val lastValue = privateCache.get('last', () => -999999)
val delta = Math.abs(lastValue - Float::parse(input))
if(delta > 1) {
    privateCache.put('last', Float::parse(input))
    input
}
null

```

That should work. I’ve never done a DSL Script transform so can’t be certain but based on the docs, and assuming no typos it should work.

> [@STSC](#):
>
> What an issue is, is that the transformation can be used only once due to cache.private.get(‘last’).

The private cache is limited to that specific instance of the transform. If you use that transform on another profile for another Item, it will get it’s own private cache so there will be no problem.

But let’s assume there was a problem…

> [@STSC](#):
>
> Could this ‘last’ be replaced by something like the itemName? Or maybe we could directly compare it with the item value without cache.private.

The item name isn’t available in the script. _Something_ would need to be hard coded.

However, the Script transformation supports passing arguments to the transform (works for any languages including Rules DSL).

Let’s pass the itemName and the threshold as variables.

Passing variables works the same as passing values to a form in a URL: `?var1=value&var2=value2`. So assuming the transform is in the file `filter.js` you’d use `filter.js?itemName=MyItem&threshold=5`. I think for Rules DSL it would be `filter.dsl?itemName=MyItem&threshold=5`. If you defined the transform in the UI just append `?itemName=MyItem&threshold=5` after selecting the transform.

 ![image](https://community-openhab-org.s3.dualstack.eu-central-1.amazonaws.com/original/3X/1/f/1fa9451fd2b8573467c3503060ba1868fe178681.png)

The variables will be injected as a String, same as input.

Then the JS transform would become:

```auto
(function(input, itemName, threshold) {
  const lastValue = cache.private.get(itemName, () => -999999); // initialize it so if there isn't a lastValue the delta is large
  const currValue = parseFloat(input);
  if(currValue === NaN) return input; // handle NULL and UNDEF
  const delta = Math.abs(lastValue - currValue);

  if(delta > parseFloat(threshold)) {
    cache.private.put(itemName, currValue);
    return input; // pick a reasonable threshold
  }
  return null; // if the delta isn't large enough return null, suppressing the event
})(input, itemName, threshold)

```

Note, I added the ability to handle NULL and UNDEF states without crashing.

The Rules DSL version would become:

```auto
try {
    val lastValue = privateCache.get(itemName, () => -999999)
    val currValue = Float:parse(input)
    val delta = Math.abs(lastValue - currValue)
    if(delta > Float.parse(threshold)) {
        privateCache.put(itemName, Float::parse(input))
        input
    }
    null;
} catch(e) { // handle NULL and UNDEF, if the parse fails it's one of these
    logInfo('filter transform', 'Failed to process update to ' + itemName + ' to ' + input + ' with threshold ' + threshold)
    input
}

```

But again, the private cache is private, limited to this one instance of the transform. So it doesn’t matter if the same key is used.

> [@STSC](#):
>
> I have seen that input provides a input.lastValue

I don’t know where you’ve seen that. Per the [docs](https://www.openhab.org/docs/configuration/transformations.html#script-transformation):

> The input value is injected into the script context as a _string_ variable `input` .

A Sting doesn’t have a `lastValue` property.

---

<div class="post-metadata">

**Author:** ![STSC](https://community.openhab.org/letter_avatar_proxy/v4/letter/s/f14d63/32.png) [@STSC](https://community.openhab.org/u/STSC)\
**Post date:** [November 8, 2023, 8:08pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/24 "2023-11-08T20:08:24Z")

</div>

Thank you very much for your support!

> [@rlkoshak](#):
>
> That should work. I’ve never done a DSL Script transform so can’t be certain but based on the docs, and assuming no typos it should work.

I think JS is better here. Due to the amount of Modbus data CPU load should be lower than with DSL.

> [@rlkoshak](#):
>
> The private cache is limited to that specific instance of the transform. If you use that transform on another profile for another Item, it will get it’s own private cache so there will be no problem.

According to my testing, I can not confirm this. I’m having one transform which I used in several Modbus data things. Having the same cache name was not working.

> [@rlkoshak](#):
>
> Let’s pass the itemName and the threshold as variables

This is how I made it working now! 😀

> [@rlkoshak](#):
>
> > [@STSC](#):
> >
> > I have seen that input provides a input.lastValue
> 
> I don’t know where you’ve seen that. Per the [docs](https://www.openhab.org/docs/configuration/transformations.html#script-transformation):

Openhab was proposing this in the transformation code editor.

---

<div class="post-metadata">

**Author:** ![jimtng](https://community.openhab.org/user_avatar/community.openhab.org/jimtng/32/98543_2.png) [@jimtng](https://community.openhab.org/u/jimtng)\
**Post date:** [November 9, 2023, 12:19am UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/25 "2023-11-09T00:19:50Z")

</div>

> [@rlkoshak](#):
>
> The private cache is limited to that specific instance of the transform. If you use that transform on another profile for another Item, it will get it’s own private cache so there will be no problem.

This is true for inline script transform, but not for file-based transform. I _believe_ that each transformation script file is a scriptengine instance, so if you are referring to the same transformation script file from multiple places, they’ll all be using that single scriptengine instance, and therefore the same private cache.

---

<div class="post-metadata">

**Author:** ![jimtng](https://community.openhab.org/user_avatar/community.openhab.org/jimtng/32/98543_2.png) [@jimtng](https://community.openhab.org/u/jimtng)\
**Post date:** [November 9, 2023, 12:30am UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/26 "2023-11-09T00:30:16Z")

</div>

Here’s an implementation using JRuby.

```auto
Number:Power MyPower { channel="xxxx" [profile="ruby:throttle_updates"]

```

Then drop this file in `conf/automation/ruby/throttle_updates_profile.rb`

```ruby

profile(:throttle_updates) do |event, callback:, item:, state:|
  next unless event == :state_from_handler

  only_every(60.seconds, id: item) { callback.send_update(state) }
  false
end

```

---

<div class="post-metadata">

**Author:** ![Udo\_Hartmann](https://community.openhab.org/user_avatar/community.openhab.org/udo_hartmann/32/70871_2.png) [@Udo\_Hartmann](https://community.openhab.org/u/Udo_Hartmann)\
**Post date:** [November 9, 2023, 6:16am UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/27 "2023-11-09T06:16:36Z")

</div>

But this is not to prevent messages based on the difference between old and new value, but is a time based filter (max once per 60 Seconds)

---

<div class="post-metadata">

**Author:** ![STSC](https://community.openhab.org/letter_avatar_proxy/v4/letter/s/f14d63/32.png) [@STSC](https://community.openhab.org/u/STSC)\
**Post date:** [November 9, 2023, 6:26am UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/28 "2023-11-09T06:26:10Z")

</div>

In my opinion there should be more standard profiles in OH4 without having additional transformation scripts, e.g.

- delta\_filter\_abs
- delta\_filter\_relative
- time\_filter\_debounce

Let’s say you have 100 Modbus values every 3 seconds. I don’t know if cache.private() is fast enough. A native implementation might be better.

---

<div class="post-metadata">

**Author:** ![jimtng](https://community.openhab.org/user_avatar/community.openhab.org/jimtng/32/98543_2.png) [@jimtng](https://community.openhab.org/u/jimtng)\
**Post date:** [November 9, 2023, 7:15am UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/29 "2023-11-09T07:15:43Z")

</div>

> [@STSC](#):
>
> In my opinion there should be more standard profiles in OH4 without having additional transformation scripts, e.g.

I tried submitting some additional profiles to core but got told that they should go into an add-on instead.

---

<div class="post-metadata">

**Author:** ![rlkoshak](https://community.openhab.org/user_avatar/community.openhab.org/rlkoshak/32/75251_2.png) [@rlkoshak](https://community.openhab.org/u/rlkoshak)\
**Post date:** [November 9, 2023, 3:54pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/30 "2023-11-09T15:54:59Z")

</div>

> [@STSC](#):
>
> I don’t know if cache.private() is fast enough

The private and shared caches _are_ native. IIRC they are implemented as a registry (e.g. same as the ItemRegistry, ThingRegistry, MetadataRegistry, etc.) and ultimately backed by a Map. If there is a performance limitation, it’s likely going to be the overhead from calling a profile at all and not the use of the cache.

But, now that we have the name of the Item passed in we don’t really need the cache. We can pull the Item itself and see what it’s currently set to.

```auto
(function(input, itemName, threshold) {
  const newValue = parseFloat(input);
  const currValue = items[itemName].numericState;
  const threshold = parseFloat(threshold);

  if(items[itemName].isUninitialized || newValue === NaN) return input; // always update if the current state or new state is NULL or UNDEF
  if(threshold === NaN) {
    console.error('Transform filter: threshold is not parsable to a number: ' + threshold);
    return null;
  }

  const delta = Math.abs(currValue - newValue);

  if(delta > parseFloat(threshold)) {
    cache.private.put(itemName, currValue);
    return input; // pick a reasonable threshold
  }
  return null; // if the delta isn't large enough return null, suppressing the event
})(input, itemName, threshold)

```

---

<div class="post-metadata">

**Author:** ![STSC](https://community.openhab.org/letter_avatar_proxy/v4/letter/s/f14d63/32.png) [@STSC](https://community.openhab.org/u/STSC)\
**Post date:** [November 9, 2023, 4:15pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/31 "2023-11-09T16:15:38Z")

</div>

Right. Is there also a way in the “Thing to Item Transformation” to get the itemName automatically? At the moment I type it in manually.

config:js:delta\_filter\_abs?itemName= **ActivePower** &threshold=2

---

<div class="post-metadata">

**Author:** ![rlkoshak](https://community.openhab.org/user_avatar/community.openhab.org/rlkoshak/32/75251_2.png) [@rlkoshak](https://community.openhab.org/u/rlkoshak)\
**Post date:** [November 9, 2023, 4:20pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/32 "2023-11-09T16:20:43Z")

</div>

> [@STSC](#):
>
> Right. Is there also a way in the “Thing to Item Transformation” to get the itemName automatically?

As I said above, no. The only way is to pass it in.

---

<div class="post-metadata">

**Author:** ![jimtng](https://community.openhab.org/user_avatar/community.openhab.org/jimtng/32/98543_2.png) [@jimtng](https://community.openhab.org/u/jimtng)\
**Post date:** [November 10, 2023, 12:52pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/33 "2023-11-10T12:52:08Z")

</div>

I thought rate-limiting it is a better solution than checking against delta value. But to check for delta value in JRuby is like this, assuming the channel and items are all dealing with quantitytype

```auto
Number:Power MyPower { channel="xxxx" [profile="ruby:filter_out_small_changes", threshold="1 W"]

```

```ruby
profile(:filter_out_small_changes) do |event, item:, state:, configuration:|
  next true unless event == :state_from_handler && item.state?

  threshold = QuantityType.new(configuration["threshold"])
  delta = item.state - state
  delta = delta.negate if delta.negative?
  delta >= threshold
end

```

---

<div class="post-metadata">

**Author:** ![STSC](https://community.openhab.org/letter_avatar_proxy/v4/letter/s/f14d63/32.png) [@STSC](https://community.openhab.org/u/STSC)\
**Post date:** [November 10, 2023, 2:23pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/34 "2023-11-10T14:23:01Z")

</div>

It’s the nature of modbus to get that many values. If Openhab can not handle it via Transformation Profile, standard features like this should be implemented natively or into the binding. My opinion.

---

<div class="post-metadata">

**Author:** ![splatch](https://community.openhab.org/user_avatar/community.openhab.org/splatch/32/33925_2.png) [@splatch](https://community.openhab.org/u/splatch)\
**Post date:** [November 10, 2023, 2:55pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/35 "2023-11-10T14:55:39Z")

</div>

The more binding do, the more complex it gets, and harder to maintain. It leads to situations where fix for one person might impact behavior expected by other.

To be clear, I agree that threshold and debounce profiles are needed, I just question if modbus binding is one which should ship these.

---

<div class="post-metadata">

**Author:** ![STSC](https://community.openhab.org/letter_avatar_proxy/v4/letter/s/f14d63/32.png) [@STSC](https://community.openhab.org/u/STSC)\
**Post date:** [November 11, 2023, 6:47am UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/36 "2023-11-11T06:47:48Z")

</div>

> [@splatch](#):
>
> To be clear, I agree that threshold and debounce profiles are needed, I just question if modbus binding is one which should ship these.

You are fully right! Threshold and debounce profiles should be part of the Openhab core. Some days ago I even didn’t know that there are Transformations available at the market. The problem here is mainly you don’t know what the are doing and what parameters they have. Best would be to have natively more standard profiles available, especially when they are performance critical.

> [@rlkoshak](#):
>
> But, now that we have the name of the Item passed in we don’t really need the cache. We can pull the Item itself and see what it’s currently set to.

I have done some corrections to your code. It should work now, can you please review.

- renamed local variable threshold to thresholdFloat ; no double definition
- Removed line cache.private.put(itemName, currValue);
- Changed “delta \> parseFloat(threshold)” to “delta \> thresholdFloat”

```javascript
(function(input, itemName, threshold) {
  const newValue = parseFloat(input);
  const currValue = items[itemName].numericState;
  const thresholdFloat = parseFloat(threshold);

  if(items[itemName].isUninitialized || newValue === NaN) return input; // always update if the current state or new state is NULL or UNDEF
  if(thresholdFloat === NaN) {
    console.error('Transform filter: threshold is not parsable to a number: ' + threshold);
    return null;
  }

  const delta = Math.abs(currValue - newValue);

  if(delta > thresholdFloat) {
    return input; // pick a reasonable threshold
  }
  return null; // if the delta isn't large enough return null, suppressing the event
})(input, itemName, threshold)

```

---

<div class="post-metadata">

**Author:** ![rlkoshak](https://community.openhab.org/user_avatar/community.openhab.org/rlkoshak/32/75251_2.png) [@rlkoshak](https://community.openhab.org/u/rlkoshak)\
**Post date:** [November 11, 2023, 2:03pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/37 "2023-11-11T14:03:25Z")

</div>

> [@STSC](#):
>
> The problem here is mainly you don’t know what the are doing and what parameters they have

All entries on the marketplace have documentation in the post. They don’t get the published tag without that. If there is one not documented point it it and we can correct that problem.

> [@STSC](#):
>
> Best would be to have natively more standard profiles available, especially when they are performance critical

Again, based on what I’ve seen, the new overhead of calling the profile is the limiting factor. But let’s assume there is a difference. If the argument is “performance” exactly what is causing that performance difference needs to be identified and measured and addressed. Making changes blindly rarely actually fixes anything.

> [@STSC](#):
>
> can you please review.

That looks good. It’s challenging to code on my phone. it’s ready to miss things. Thanks for posting a working version.

---

<div class="post-metadata">

**Author:** ![marco\_hoefle](https://community.openhab.org/user_avatar/community.openhab.org/marco_hoefle/32/79454_2.png) [@marco\_hoefle](https://community.openhab.org/u/marco_hoefle)\
**Post date:** [December 28, 2023, 9:27am UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/38 "2023-12-28T09:27:17Z")

</div>

Hi Jim,  
I like the custom profile approach but I would also prefer that these profiles are in the OH core.  
For simplicity I started with your rate limiter:

```yaml
Number:VolumetricFlowRate Lueftung_Airflow "Airflow: [%.0f %unit%]" (gLueftung, gLueftung_Chart_Airflow) {
	channel="knx:device:bridge:lueftung_fast_poll:Lueftung_Airflow" [profile="ruby:throttle_updates"],
	unit="m³/h"
}

```

I activated “JRuby Scripting” and created that file:

conf/automation/ruby/throttle\_updates\_profile.rb

```auto
profile(:throttle_updates) do |event, callback:, item:, state:|
  next unless event == :state_from_handler

  only_every(300.seconds, id: item) { callback.send_update(state) }
  false
end

```

I see in the logs:

```yaml
 No ProfileFactory found which supports profile 'ruby:throttle_updates' for link 'Lueftung_Airflow -> knx:device:bridge:lueftung_fast_poll:Lueftung_Airflow'

```

Do I miss something?

Thanks  
Marco

---

<div class="post-metadata">

**Author:** ![jimtng](https://community.openhab.org/user_avatar/community.openhab.org/jimtng/32/98543_2.png) [@jimtng](https://community.openhab.org/u/jimtng)\
**Post date:** [December 28, 2023, 1:05pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/39 "2023-12-28T13:05:26Z")

</div>

Which openhab version?

Add a comment line, e.g.

```auto
# 1

```

Then save the file. You should see a log that says the file is reloaded.

---

<div class="post-metadata">

**Author:** ![jimtng](https://community.openhab.org/user_avatar/community.openhab.org/jimtng/32/98543_2.png) [@jimtng](https://community.openhab.org/u/jimtng)\
**Post date:** [December 28, 2023, 1:24pm UTC](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516/40 "2023-12-28T13:24:59Z")

</div>

> [@marco\_hoefle](#):
>
> I like the custom profile approach but I would also prefer that these profiles are in the OH core.

Yeah, I tried submitting some more profiles to core but got told that they should go in an add-on. I didn’t want to waste any more time on it since I’m perfectly happy using jruby profile.

I’ve recently made the jruby profile visible and usable by UI items too.

[Previous page](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516.md?page=1)

[Next page](https://community.openhab.org/t/reducing-item-updates-and-thus-log-entries-caused-by-a-modbus-tcp-energy-meter/146516.md?page=3)
