JavaScript: calulate with UoMs

Using OH 4.2.0-release with openHABian.

I’m having issues with calculating item values having UoMs. I want to learn how to use UoMs in Javascripting, so I don’t want to use “rawState”, and add the Unit later.
All my items do have the same Unit (W), not even one with kW :wink:
But, I can’t add/substract using +/- so I thought I can use add(value) and substract(value), but this also throws errors…

So my script is like this:

var interval = time.toZDT().minusSeconds(120);
var grundlast = items.getItem("KOS_HomeConsumption").persistence.averageSince(interval).state;
var waschmaschine = items.getItem("EMS_WaMaAktuellerVerbrauch").persistence.averageSince(interval).state;
var spuelmaschine = items.getItem("EMS_WaMaAktuellerVerbrauch").persistence.averageSince(interval).state;
var whirlpool = items.getItem("EMS_BBAktuellerVerbrauch").persistence.averageSince(interval).state;
var wallbox = items.getItem("evcc_EinfahrtLadeleistung").persistence.averageSince(interval).state;

console.info("DEBUG Grundlast: HomeConsumption: ",  grundlast);
console.info("DEBUG Grundlast: Washing Machine: ",  waschmaschine);
console.info("DEBUG Grundlast: Spülmaschine: ",  spuelmaschine);
console.info("DEBUG Grundlast: Whirlpool: " , whirlpool);
console.info("DEBUG Grundlast: Wallbox: ",  wallbox);


// console.info("Ermittle die Grundlast: ",  grundlast+waschmaschine+spuelmaschine+whirlpool+wallbox);
// Abzüglich der bekannten Verbraucher
grundlast = grundlast.subtract(waschmaschine);
grundlast = grundlast.subtract(spuelmaschine);
grundlast = grundlast.subtract(whirlpool);
grundlast = grundlast.subtract(wallbox);
console.info("Ermittle die Grundlast: ",  grundlast);

if (grundlast != null) {
  items.getItem("EMS_Grundlast").postUpdate(grundlast.toString());
}

which brings the following error:

2024-07-22 11:31:22.280 [INFO ] [tomation.script.ui.EMS_CalcGrundlast] - DEBUG Grundlast: HomeConsumption:  3860.371527847215 W
2024-07-22 11:31:22.282 [INFO ] [tomation.script.ui.EMS_CalcGrundlast] - DEBUG Grundlast: Washing Machine:  0.4771407383953940 W
2024-07-22 11:31:22.283 [INFO ] [tomation.script.ui.EMS_CalcGrundlast] - DEBUG Grundlast: Spülmaschine:  0.4771541883987936 W
2024-07-22 11:31:22.284 [INFO ] [tomation.script.ui.EMS_CalcGrundlast] - DEBUG Grundlast: Whirlpool:  2939.975505307183 W
2024-07-22 11:31:22.285 [INFO ] [tomation.script.ui.EMS_CalcGrundlast] - DEBUG Grundlast: Wallbox:  0 W
2024-07-22 11:31:22.288 [ERROR] [.script.javascript.EMS_CalcGrundlast] - Failed to execute script: TypeError: grundlast.substract is not a function
        at <js>.:program(<eval>:18)
        at org.graalvm.polyglot.Context.eval(Context.java:399)
        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:458)
        at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.eval(GraalJSScriptEngine.java:426)
        at java.scripting/javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:262)
        ... 74 more
2024-07-22 11:31:22.289 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'EMS_CalcGrundlast' failed: org.graalvm.polyglot.PolyglotException: TypeError: grundlast.substract is not a function

You need to work with Quantity. In most cases you can access quantityState rather than state to get a Quantity.

ok, got it! thanks!

var interval = time.toZDT().minusSeconds(120);
var grundlast = Quantity(items.getItem("KOS_HomeConsumption").persistence.averageSince(interval).quantityState);
var waschmaschine = Quantity(items.getItem("EMS_WaMaAktuellerVerbrauch").persistence.averageSince(interval).quantityState);
var spuelmaschine = Quantity(items.getItem("EMS_SpMaAktuellerVerbrauch").persistence.averageSince(interval).quantityState);
var whirlpool = Quantity(items.getItem("EMS_BBAktuellerVerbrauch").persistence.averageSince(interval).quantityState);
var wallbox = Quantity(items.getItem("evcc_EinfahrtLadeleistung").persistence.averageSince(interval).quantityState);

console.debug("DEBUG Grundlast: HomeConsumption: ",  grundlast);
console.debug("DEBUG Grundlast: Washing Machine: ",  waschmaschine);
console.debug("DEBUG Grundlast: Spülmaschine: ",  spuelmaschine);
console.debug("DEBUG Grundlast: Whirlpool: " , whirlpool);
console.debug("DEBUG Grundlast: Wallbox: ",  wallbox);


// console.info("Ermittle die Grundlast: ",  grundlast+waschmaschine+spuelmaschine+whirlpool+wallbox);
// Abzüglich der bekannten Verbraucher

grundlast = grundlast.subtract(waschmaschine, spuelmaschine, whirlpool, wallbox);
console.debug("DEBUG Grundlast: Ermittle die Grundlast: ",  grundlast);


if (grundlast != null) {
  items.getItem("EMS_Grundlast").postUpdate(grundlast.toString());
}

You can even shorten to:

var grundlast = items.getItem("KOS_HomeConsumption").persistence.averageSince(interval).quantityState;
1 Like

Just to elaborate a bit, there are four(ish) ways to get the state of an Item, three of which work with PersistedState.

Method Returns Works with PersistedStates
items.MyItem.isUninitialized true if the Item is NULL or UNDEF, false otherwise
items.MyItem.state the Item’s state as a String X
items.MyItem.numericState the Item’s state as a simple number, no units, null if the state cannot be represented as a number X
items.MyItem.quantityState the Item’s state as a Quantity, null if the state cannot be represented as a Quantity X

Any time you are working with an Item or a PersistedState you have the choice to have it converted to what ever makes the most sense at the time, be that a String, a simple number, or a Quantity. You don’t have to do the conversions yourself.

You may also want to look into blocklies because in particular here the blocks make computation pretty easy: Rules Blockly - Units of Measurements | openHAB . Note that this is in particular handy if you use typed variables.

… and if it is only to use the blocks to generate the code you need and steal it from there for your pure javascript code :wink:

1 Like