[SOLVED] Convert an item negative value to 0 (zero)

Thanks Udo…
I did try almost the same as well. But got errors as well.
But I dont understand this part in your rule… What exactly does it do?

if(!(inverter1ActivePower.state instanceof Number)) {
        return;                                           // cancel rule

You might also consider (as this is a Modbus connected Item) the next poll in a few seconds will overwrite your modified value again.

Maybe you need a transformation at the binding level.

Thats why I wanted the rule to run all the time, when the value isnt 0 (zero) or above.
(Yes, it´s modbus).

Why? The value the inverter return is static when it´s in “standby mode”…

inverter1ActivePower.state instanceof Number is boolean, true or false.

!(a) is 'NOT a'

So !(inverter1ActivePower.state instanceof Number) is true, if inverter1ActivePower.state is not an instance of Number (and Number is the type of data, like String, Boolean, Float or DateTime).

Here’s the problem you will face. I assume you are persisting these values to a database. If so then the negative number is going to be persisted as well as the 0 so at night the chart will be bounding between that large negative value and 0 until day time. So you need to convert the value before it gets to the Item, create a proxy Item, or take on the task of deciding when to persist in your Rule.

Rossko57 has a suggestion for converting the value before it gets to the Item. Create a JS transformation that returns the passed in value if it’s not negative and 0 if it is negative. This will convert the negative number to 0 before it gets to the Item, no Rule necessary.

For the Proxy Item the Rule would look something like:

rule "Process power"
when
    Item inverter1ActivePower changed
then
    if(!inverter1ActivePower.state isinstanceof Number) return;

    var newVal = inverter1ActivePower.state
    if((inverter1ActivePower.state as Number) < 0) newVal = 0

    inverter1ActivePower_Proxy.postUpdate(newVal)
end

You need to put the Proxy on your sitemap and in your .persist file.

To take on the role of persisting the values instead of relying on the strategy the rule would look something like this.

rule "Process power"
when
    Item inverter1ActivePower changed
then
    if(!inverter1ActivePower.state isinstanceof Number) return;

    if((inverter1ActivePower.state as Number) < 0) inverter1ActivePower.postUpdate(0) // this will retrigger the Rule
    else inverter1ActivePower.persist("rrd4j") // Name your desired persistence engine
end

You must remove inverter1ActivePower from your .persist file or from any Groups that get persisted in that .persist file.

If the Item is NULL or UNDEF the Rule exits. You can’t run the rest of the Rule if the Item is in either of these two states.

A Rule runs after the Item get’s set to a given state (or receives a command). You can’t intercept the negative value from the binding and convert it to a 0 using a Rule. You can do so using a transformation.

To convert the value to 0 before it gets to the Item.

But it doesn’t run all the time. The rule runs when the Item changes, and then it might change it again to something else.
Your Item will be changing twice every polling period, all night long.
There’s no harm in that, but what is the purpose? Are you just trying to hide the silly value in a display?

If you are planning to produce charts from persisted data, have a think about what happens at each poll.

EDIT - yeh, what Rich said :wink:

Exactly…
Rich is right, (as usual)…
I want to persist this item. But it´s stupid to persist a negative static number, when the inverter is in “standby mode”… (Its a rather borring chart I would say :slight_smile: ).

Ofcouse… Sorry Rossko… I was so focused on changing the item value, that I simple missed the whole point, that this wont do any good… The transfomation wil have to go into the channel (binding) before it enters the item.

So I´ll have to change my question, cause I have no idea how to make this in a js transformation… I only know how to divide values but 10,100, 1000 :slight_smile:

Show me your Thing … modbus data thing, this is where the action is.

Does this reading ever take genuine negative values? It is quite possible to screen out silly readings while still allowing reasonable ones through.

// SMA Inverter Power Total
	Bridge poller Active_Power [ start=30775, length=4, refresh=5000, type="input" ] {
	    
	    	 Thing data Active_Power [ readStart="30775", readValueType="int32" ]
	    
  	 }

(function(i) {
    if(isNaN(i)) return "UNDEF"; // I don't know if this will work
    if(i < 0) return 0;
    return i;
})(input)

The process is described in the binding docs, with examples

Take Rich’s little javascript an put it some file in /transforms folder.
Name it whatever - smalimit.js perhaps.

Now you just tell the data Thing to do a read transform, and what type it is, and what file to look at.

Thing data Active_Power [ readStart="30775", readValueType="int32", readTransform="JS(smalimit.js)" ]

I think the UNDEF scheme would work; however I don’t think the binding will ever supply a non-numeric value to the transform - it’s the nature of Modbus that all raw data is numbers. Does no harm having it there, though.

But it is possible to follow that thought some more…
Really, your “silly” value should show up as UNDEF - this is exactly what UNDEF is meant for, to represent that data can’t be calculated.

But if you are charting the data, you may prefer zero. UNDEF does not get persisted, and the last valid data may not have been zero, leading to a long time at a false reading in your chart.

Thanks RIch!!

Hmm you got a point… I honestly have no idea what the latest value is, ecept when the panels are NOT producing any power, then inverter will change state. So I assume it would be 0 (zero)…
So if I would use UNDEF insted of 0 (zero), I should change Rich´s script to:

(function(i) {
    if(i < 0) return UNDEF;
    return i;
})(input)

Right??

What if I want to divide the value by 100 (or 1000)… should it then be changed to:

(function(i) {
    if(i < 0) return UNDEF;
    return parseFloat(i) / 1000
})(input)

Or would it be a problem when i is set to UNDEF?

Yes.

Yes, this is in the docs and examples.

i doesn’t get set to UNDEF

Hmm it didnt went well… I get this error:

2019-09-17 00:43:12.384 [ERROR] [nding.modbus.internal.Transformation] - transformation throws exception [transformation=JS(smalimit.js), response=-2147483648]
org.eclipse.smarthome.core.transform.TransformationException: An error occurred while executing script. ReferenceError: "UNDEF" is not defined in <eval> at line number 2
	at org.openhab.transform.javascript.internal.JavaScriptTransformationService.transform(JavaScriptTransformationService.java:80) ~[?:?]
	at org.openhab.binding.modbus.internal.Transformation.transform(Transformation.java:144) ~[?:?]
	at org.openhab.binding.modbus.internal.Transformation.transformState(Transformation.java:190) ~[?:?]
	at org.openhab.binding.modbus.internal.handler.ModbusDataThingHandler.lambda$10(ModbusDataThingHandler.java:833) ~[?:?]
	at java.util.HashMap$KeySpliterator.forEachRemaining(HashMap.java:1556) ~[?:?]
	at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:647) ~[?:?]
	at org.openhab.binding.modbus.internal.handler.ModbusDataThingHandler.processUpdatedValue(ModbusDataThingHandler.java:801) ~[?:?]
	at org.openhab.binding.modbus.internal.handler.ModbusDataThingHandler.onRegisters(ModbusDataThingHandler.java:681) ~[?:?]
	at org.openhab.binding.modbus.internal.handler.ModbusPollerThingHandlerImpl$ReadCallbackDelegator.lambda$0(ModbusPollerThingHandlerImpl.java:95) ~[?:?]
	at java.util.concurrent.CopyOnWriteArrayList.forEach(CopyOnWriteArrayList.java:891) ~[?:?]
	at org.openhab.binding.modbus.internal.handler.ModbusPollerThingHandlerImpl$ReadCallbackDelegator.onRegisters(ModbusPollerThingHandlerImpl.java:95) ~[?:?]
	at org.openhab.io.transport.modbus.internal.ModbusLibraryWrapper.invokeCallbackWithResponse(ModbusLibraryWrapper.java:286) ~[?:?]
	at org.openhab.io.transport.modbus.internal.ModbusManagerImpl$PollOperation.lambda$1(ModbusManagerImpl.java:167) ~[?:?]
	at org.openhab.io.transport.modbus.internal.SimpleStopWatch.timeRunnable(SimpleStopWatch.java:152) ~[?:?]
	at org.openhab.io.transport.modbus.internal.ModbusManagerImpl$PollOperation.accept(ModbusManagerImpl.java:166) ~[?:?]
	at org.openhab.io.transport.modbus.internal.ModbusManagerImpl$PollOperation.accept(ModbusManagerImpl.java:1) ~[?:?]
	at org.openhab.io.transport.modbus.internal.ModbusManagerImpl.executeOperation(ModbusManagerImpl.java:575) ~[?:?]
	at org.openhab.io.transport.modbus.internal.ModbusManagerImpl.lambda$15(ModbusManagerImpl.java:723) ~[?:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:?]
	at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) [?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) [?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) [?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:?]
	at java.lang.Thread.run(Thread.java:748) [?:?]
Caused by: javax.script.ScriptException: ReferenceError: "UNDEF" is not defined in <eval> at line number 2
	at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:470) ~[?:?]
	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:426) ~[?:?]
	at jdk.nashorn.api.scripting.NashornScriptEngine.access$300(NashornScriptEngine.java:73) ~[?:?]
	at jdk.nashorn.api.scripting.NashornScriptEngine$3.eval(NashornScriptEngine.java:514) ~[?:?]
	at javax.script.CompiledScript.eval(CompiledScript.java:92) ~[?:?]
	at org.openhab.transform.javascript.internal.JavaScriptTransformationService.transform(JavaScriptTransformationService.java:77) ~[?:?]
	... 24 more
Caused by: jdk.nashorn.internal.runtime.ECMAException: ReferenceError: "UNDEF" is not defined
	at jdk.nashorn.internal.runtime.ECMAErrors.error(ECMAErrors.java:57) ~[?:?]
	at jdk.nashorn.internal.runtime.ECMAErrors.referenceError(ECMAErrors.java:319) ~[?:?]
	at jdk.nashorn.internal.runtime.ECMAErrors.referenceError(ECMAErrors.java:291) ~[?:?]
	at jdk.nashorn.internal.objects.Global.__noSuchProperty__(Global.java:1442) ~[?:?]
	at jdk.nashorn.internal.scripts.Script$Recompilation$12$13A$\^eval\_.L:1(<eval>:2) ~[?:?]
	at jdk.nashorn.internal.scripts.Script$11$\^eval\_.:program(<eval>:1) ~[?:?]
	at jdk.nashorn.internal.runtime.ScriptFunctionData.invoke(ScriptFunctionData.java:637) ~[?:?]
	at jdk.nashorn.internal.runtime.ScriptFunction.invoke(ScriptFunction.java:494) ~[?:?]
	at jdk.nashorn.internal.runtime.ScriptRuntime.apply(ScriptRuntime.java:393) ~[?:?]
	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:421) ~[?:?]
	at jdk.nashorn.api.scripting.NashornScriptEngine.access$300(NashornScriptEngine.java:73) ~[?:?]
	at jdk.nashorn.api.scripting.NashornScriptEngine$3.eval(NashornScriptEngine.java:514) ~[?:?]
	at javax.script.CompiledScript.eval(CompiledScript.java:92) ~[?:?]
	at org.openhab.transform.javascript.internal.JavaScriptTransformationService.transform(JavaScriptTransformationService.java:77) ~[?:?]
	... 24 more

This is the script saved as smalimit.js

(function(i) {
    if(i < 0) return UNDEF;
    return parseFloat(i) / 1000
})(input)

And this is the thing:

// SMA Inverter Power Total
	Bridge poller Active_Power [ start=30775, length=4, refresh=5000, type="input" ] {
	    
	    	 Thing data Active_Power [ readStart="30775", readValueType="int32", readTransform="JS(smalimit.js)"  ]
	    
  	 }

javascript isn’t rules and knowsnothing about an object UNDEF

Look more closely at Rich’s script

1 Like

I thought you said I didnt needed that line from Rich´s script.

Javascript tranforms only return strings. This is fine as long as the item accept that type of string as an input.
Your transfrom should be:

(function(i) {
    if (i < 0) return "UNDEF";
    return parseFloat(i) / 1000;
})(input)

The quotes "" around "UNDEF" are important

btw when transforming negative to 0, wouldn’t be possible just to use

Math.max(0, yourVar)

?

Ahh okay I see now… Thanks Vincent. I´ll give it a try when I get back home tonight.

EDIT- It seem to work fine with Rich´s scripts and Rossko´s/Vincents corrections of my mistakes…
Thank you very much guys!

Probably. I very rarely touch JavaScript so usually the most primitive approach ends up in the code.If Nashorne supports Math.max then you can do that.