Mathematical operations on item.rawState using Javascript keeping UoM

Hi,

I’m using OpenHAB 3.4 in a docker-container on an Intel NUC machine. What I’m trying to do is add or subtract the item states of type Number:Energy via JavaScript in a rule. However it does not work.

I have item A (Number:Energy) with a value of 12 Wh (watt hours) and another item B (Number:Energy) with a value of 1.2 kWh (kilowatt hours). Via javascript I do:

var value1 = items.getItem(“itemA”).rawState;
var value2 = items.getItem(“itemB”).rawState;

In console window it shows me the correct values including the UoM. But when I do:

var myresult = value1 + value2;

I loose the UoM and the result is not correct as it sums 12 + 1.2 = 13.2. Of course this is not what I want. How can a correct operation with respect of units (kWh + Wh in this case) be done? Maybe anyone can help me with this?

Regards
knilch

P.S.: If I put the 2 items in a group then the group item shows the correct result whenever I change one of the 2 values. But there is no way of subtract items with this strategy and as mentioned my aim is to do achieve it with Javascript.

You have to use the methods on the QuantityType to do the math or you need to convert them to the same unit before doing a plain number addition.

var myresult = value1.add(value2);

or

var myresult = value1 + value2.toUnit('kWh');

Though this second approach will still lose the units in myresult. If you want to keep the units you must use the methods to do math with QuantityTypes.

1 Like

FYI: We are currently working on providing UoM handling for JavaScript, see GitHub - openhab/openhab-js: openHAB JavaScript Library for JavaScript Scripting Automation. The work on the Quantity API is finished now, but has to be fully released.

3 Likes

Yes, I’m eagerly awaiting it. :smiley:

Though, having rereviewed those docs, I wonder if we need to add a little something showing how to get a Quantity from an Item’s state. That’s missing (I’m assuming you’d need to create a new Quantity using the Item’s state, but are the OH units converted to the JS units for us when we call myItem.state? Does it make sense to add a numericState to Item like there is on HistoricItem? Or maybe that and a quantityState so the end user can choose whether they want the units or not.

Note that it already should be included in the latest release, but your recommended renamings of the comparison methods aren‘t published yet and as they are breaking, I haven‘t talked much about the Quantity API yet.

Really good point!

myItem.state delivers the stringified Item state, you could use it to create a JS Quantity: Quantity(myItem.state).

You proposal of having a numericState (using parseFloat or maybe a floatState and an intState) as well as a quantityState seems reasonable and adds some convenience compared to the above code. Note that you can construct a JS Quantity by passing the QuantityType of the rawState to the Quantity function. We just need to check the type of the rawState before we pass it to avoid errors.

Pull requests are always welcome! I‘d be happy to merge improvements here.

Thx! I did as you suggested. My working code now is:

var QuantityType = Java.type(‘org.openhab.core.library.types.QuantityType’);

var object1 = items.getItem(“value1”);
var object2 = items.getItem(“value2”);
var sum_result = items.getItem(“sum_result”);

var wert1 = new QuantityType(String(objekt1.rawState));
var wert2 = new QuantityType(String(objekt2.rawState));
var ergebnis = wert1.add(wert2);

sum_result.postUpdate(String(ergebnis));

Cool!

I’ll look into that but the next couple of weeks are pretty heavy so it might be a bit before I get to it.

this is probably unnecessary. The rawState is already a QuantityType. You should be able to just use

var ergebnis = object1.add(object2);
sum_result.postUpdate(ergebnis.toString());

Issue opened so I don’t forget: Add methods to Item to get the state as Quantity or Number · Issue #231 · openhab/openhab-js · GitHub

1 Like

Working as you suggested. Thanks again!

let’s say I have also a bunch of items working as “Number:Energy”. one with Wh and one with kWh.
How would I use the .toUnit(UoM) part with historic item states? appending “.toUnit()” there doesn’t seem to work?

var value1 = items.getItem("value1").history.averageSince(interval10min).toUnit("Wh");
var value2 = items.getItem("value2").history.averageSince(interval10min).toUnit("Wh");

log:
2023-02-15 17:44:56.711 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'TestRule' failed: org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (toUnit) on java.math.BigDecimal@1716db1 failed due to: Unknown identifier: toUnit

Persistence doesn’t save the units so historic state (and any of the other persistence actions) in that case will always be a DecimalType.

You are on your own when it comes to units. Your choices are:

  1. Remove the units from everything and operate without units
  2. Add units to everything after the fact by creating a new Quantity

1 would look something like this on a openhab-js 4.0

var noUnits = items.MyItem.numericState

and in the current version available in OH 3.4

var noUnits = Number.parseFloat(items.getItem('MyItem').state); // parseFloat stops on the first non-number character, ignoring the units

2 would look something like this.

var addUnits = Quantity(MyItem.averageSince(interval10min) + ' Wh');

Note you have to know ahead of time what units the values are stored as in persistence.

This might become easier in OH 4. There is work ongoing to improve how UoM are applied to Items which may make it so that Persistence can apply units to the results it pulls from the database in a sane manner. But until then you’ll have to know what units it’s stored as and apply the those units yourself when you create the new Quantity.

Once you have the Quantity, you can use toUnit() to convert to other units.

1 Like

all right, I keep forgetting that. If I remember, while changing UoMs to also change the code for historic states… or I just extract the UoM and see, what unit it uses… but I hope, I don’t change Units so often! :wink:

I‘ve seen that you used Quantity as a constructor (new keyword). Please note it‘s a factory, no constructor so it should be used without the new keyword.

I am wondering if it possible to support both patterns …

I’m still learning myself with how to manipulate the new Quantity API.

I need to do a simple calculation - it worked with OH 3.4.5, but stopped in OH4 due to UoM.

When I use a simple math operations, the result is not what I would expect.

0 mm + 0 mm should be equal to 0 mm, however it results in 0 mm0

I can use parseFloat to get this corrected, however this will remove the unit as well.
I’m not sure this is the correct approach. :thinking:

I tried Quantity(), .numericState but this didn’t work at all.

I’m wondering what’s the correct approach to do this math?

    var newTime = fullHours.minusDays(i);
    var rainPer24Hour = this.PersistenceExtensions.historicState(rainToday_Netatmo, newTime); 
    log.debug(">>> 1: sumRainInterval: {}", sumRainInterval);
    last24hours =  (rainPer24Hour !== null) ? rainPer24Hour.state : 0 ;
    log.debug(">>> 2: ... last24hours: {} - rainPer24Hour.state {}", last24hours, rainPer24Hour.state);
    sumRainInterval =  (sumRainInterval) + (last24hours);
    log.debug(">>> 3: sumRainInterval: {} ", sumRainInterval);
    
2023-08-21 12:58:16.221 [DEBUG] [penhab.model.script.Rules.RegenCheck] - The rainToday_Netatmo payload is  0 mm , rainToday_Netatmo.rawState 0 mm  
2023-08-21 12:58:16.222 [DEBUG] [penhab.model.script.Rules.RegenCheck] - >>> 1: sumRainInterval: 0 mm
2023-08-21 12:58:16.222 [DEBUG] [penhab.model.script.Rules.RegenCheck] - >>> 2: ... last24hours: 0 mm - rainPer24Hour.state 0 mm
2023-08-21 12:58:16.222 [DEBUG] [penhab.model.script.Rules.RegenCheck] - >>> 3: sumRainInterval: 0 mm0 

Any hint how to code this in OH4 is a proper way is welcome :slight_smile:

Stefan

You can always remove the unit, do the math and add it back.

When something you expect to work doesn’t, always look back at the reference docs. A Quantity is not an Item. It doesn’t have a state, numeric or otherwise.

var qty = Quantity('10.2 °C');

qty = qty.toUnit('°F');
var intValue = qty.int;
var floatValue = qty.float;

Again, referencing the docs:

add(value) ⇒ Quantity: value can be a string or a Quantity
divide(value) ⇒ Quantity: value can be a number, a string or a Quantity
multiply(value) ⇒ Quantity: value can be a number, a string or a Quantity
subtract(value) ⇒ Quantity: value can be a string or a Quantity

JS doesn’t support operator overloading so we cannot use the standard +-*/ operators.

Because you are using the + operator and as far as JS is concerned Quantity is not a number, it reverts to string concatenation.

sumRainInterval = sunRainInterval.add(last24hours);
1 Like