OH4 Blockly Calculations with items states

Hi All

So further along my path to switch over to Blockly, but now completely stuck on my “Power” rules which require calculations - the basic rules are all converted.

The first rule I am working on is to calculate the VA of my UPS using Power Factor and Watts from UPS.

I have two items:

ShellyEM_UPS_PowerFactor which is a Number
ShellyEM_UPS_Watt which is a Number:Power

My rule is:

which generates:

var upsWatt, upsWattNew, upsWattNoUoM, upsWattNoUoMNew, upsPF, upsVA;


upsWatt = items.getItem('ShellyEM_UPS_Watt').state;
console.info(('Raw Value of ShellyEM_UPS_Watt: ' + String(upsWatt)));
upsWattNew = upsWatt * 1;
console.info(('Updated Value of ShellyEM_UPS_Watt: ' + String(upsWattNew)));
upsWattNoUoM = actions.Transformation.transform('REGEX', '([0-9.]*) (\\D)', items.getItem('ShellyEM_UPS_Watt').state);
console.info(('No UoM  Value of ShellyEM_UPS_Watt: ' + String(upsWattNoUoM)));
upsWattNoUoMNew = upsWattNoUoM * 1;
console.info(('New No UoM  Value of ShellyEM_UPS_Watt: ' + String(upsWattNoUoM)));
upsPF = items.getItem('ShellyEM_UPS_PowerFactor').state;
console.info(('Raw Value of ShellyEM_UPS_PowerFactor: ' + String(upsPF)));
upsPF = upsPF * 1;
console.info(('Updated Value of ShellyEM_UPS_PowerFactor: ' + String(upsPF)));
upsVA = upsWattNoUoMNew / upsVA;
console.info(('Calculated VA value is: ' + String(upsVA)));

I eventually managed to strip the UoM using REGEX as the x1 option does not work for me. However the calculation still does not work and I now have no idea and I cannot find anything in community etc. The result does not calculate and ends up being NaN.

The log output is:

14:42:26.579 [INFO ] [openhab.event.ItemStateChangedEvent  ] - Item 'ShellyEM_UPS_Watt' changed from 369.24 W to 368.81 W
14:42:26.581 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - Raw Value of ShellyEM_UPS_Watt: 368.81 W
14:42:26.582 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - Updated Value of ShellyEM_UPS_Watt: NaN
14:42:26.584 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - No UoM  Value of ShellyEM_UPS_Watt: 368.81
14:42:26.584 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - New No UoM  Value of ShellyEM_UPS_Watt: 368.81
14:42:26.585 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - Raw Value of ShellyEM_UPS_PowerFactor: -0.81
14:42:26.586 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - Updated Value of ShellyEM_UPS_PowerFactor: -0.81
14:42:26.586 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - Calculated VA value is: NaN

Not sure if I should continue on my journey with Blockly, or is it not yet ready to perform these kinds of calculations - meaning that I would end up using “Inline Scripts”, which seems to defeat the purpose of the change since I already have these rules working in DSL.

Thanks
Mark

Typo.

In OH 4 for sure there is now support for UoM. See the “Units of Measurement” category of blocks for how to do math with Item states that have units.

Instead of fighting them, use them to your advantage.

Don’t think so. The code is generated by Blocks with dropdowns.

I thought I had tried that… Difficult to distinguish between the Match and UoM blocks.

I have added the dollowing:

image

And the generated code is now:

var upsWatt, upsWattNew, upsWattNoUoM, upsPF, upsVA, upsVA_UoM;


upsWatt = items.getItem('ShellyEM_UPS_Watt').state;
console.info(('Raw Value of ShellyEM_UPS_Watt: ' + String(upsWatt)));
upsWattNew = upsWatt * 1;
console.info(('Updated Value of ShellyEM_UPS_Watt: ' + String(upsWattNew)));
upsWattNoUoM = actions.Transformation.transform('REGEX', '([0-9.]*) (\\D)', items.getItem('ShellyEM_UPS_Watt').state);
console.info(('No UoM  Value of ShellyEM_UPS_Watt: ' + String(upsWattNoUoM)));
upsPF = items.getItem('ShellyEM_UPS_PowerFactor').state;
console.info(('Raw Value of ShellyEM_UPS_PowerFactor: ' + String(upsPF)));
upsPF = upsPF * 1;
console.info(('Updated Value of ShellyEM_UPS_PowerFactor: ' + String(upsPF)));
upsVA = upsWatt / upsVA;
console.info(('Calculated VA value is: ' + String(upsVA)));
upsVA_UoM = (upsWatt.divide(upsPF));
console.info(('Calculated VA with UoM value is: ' + String(upsVA_UoM)));

But doesn’t look right:

upsVA_UoM = (upsWatt.divide(upsPF));

And generates:

17:07:02.128 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - Raw Value of ShellyEM_UPS_Watt: 442.55 W
17:07:02.140 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - Updated Value of ShellyEM_UPS_Watt: NaN
17:07:02.140 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - No UoM  Value of ShellyEM_UPS_Watt: 442.55 W
17:07:02.141 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - Raw Value of ShellyEM_UPS_PowerFactor: 0.85
17:07:02.141 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - Updated Value of ShellyEM_UPS_PowerFactor: 0.85
17:07:02.141 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - Calculated VA value is: NaN
17:07:02.142 [ERROR] [ab.automation.script.javascript.stack] - Failed to execute script:
org.graalvm.polyglot.PolyglotException: TypeError: upsWatt.divide is not a function
        at <js>.:program(<eval>:16) ~[?:?]
        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 javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:262) ~[java.scripting:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocableAndAutocloseable.eval(DelegatingScriptEngineWithInvocableAndAutocloseable.java:53) ~[?:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.eval(InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java:78) ~[?:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocableAndAutocloseable.eval(DelegatingScriptEngineWithInvocableAndAutocloseable.java:53) ~[?:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.eval(InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java:78) ~[?:?]
        at org.openhab.core.automation.module.script.internal.handler.ScriptActionHandler.lambda$0(ScriptActionHandler.java:71) ~[?:?]
        at java.util.Optional.ifPresent(Optional.java:178) ~[?:?]
        at org.openhab.core.automation.module.script.internal.handler.ScriptActionHandler.execute(ScriptActionHandler.java:68) ~[?:?]
        at org.openhab.core.automation.internal.RuleEngineImpl.executeActions(RuleEngineImpl.java:1180) ~[?:?]
        at org.openhab.core.automation.internal.RuleEngineImpl.runRule(RuleEngineImpl.java:989) ~[?:?]
        at org.openhab.core.automation.internal.TriggerHandlerCallbackImpl$TriggerData.run(TriggerHandlerCallbackImpl.java:87) ~[?:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) ~[?:?]
        at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[?:?]
        at java.lang.Thread.run(Thread.java:833) ~[?:?]
17:07:02.144 [ERROR] [.internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Watts_to_VA' failed: org.graalvm.polyglot.PolyglotException: TypeError: upsWatt.divide is not a function

I also think I discounted these Blocks because they seem to indicate the same UoM. In my case the conversion will change W to VA.

Sure - the typo is in your Blocky block…

What doesn’t look right about that? It looks about what I would expect. I’m not sure why there are the extra parens but they are not causing any problems for being there.

I can’t remember if it was you or someone else I had this on another thread. Anyway, get state of item (either the dedicated block or the “get x of item” block) returns the state of the Item converted to a String. And of course a String isn’t a QuantityType with a divide() method.

Luckily, that first block under Units of Measurement will convert a String to a Quantity so if you drop your “get state of item → get item → item ShellyEM_UPS_Watt” into that block it will convert that String to a Quantity.

However, for the PowerFactor, since that Item doesn’t have a unit, you’ll probably want to use the “to unit” block.

I’ll file an issue to add “state as number”, “state as quantity” and “raw state” to the “get x of item” block. It may be something already considered and rejected for some reason but I think it would be worth while at least raising the issue.

Sorry, I don’t understand.

Your screen shot of the original blockly code shows “set upsVA to → upsWattNoUoMNew + upsVA” but the code you posted shows upsVA = upsWattNoUoMNew / upsVA;.

Thanks.
Bad screenshot maybe? Definitely ÷. Also / in code.

Sorry for speaking in riddles :slight_smile: - you can not divide by upsVA, upsVA is not initialized.

Thanks. I have seen this a few times while working through this. The selected variable mysteriously changes. Cannot seem to tie it down. But will fix it again now. Thanks

That has helped. So I can now get the VA if I use a fixed number for upsPF (8)


Code:

var upsWatt, upsWattNew, upsWattNoUoM, upsPF, upsVA, upsVA_UoM;


upsWatt = items.getItem('ShellyEM_UPS_Watt').state;
console.info(('Raw Value of ShellyEM_UPS_Watt: ' + String(upsWatt)));
upsWattNew = upsWatt * 1;
console.info(('Updated Value of ShellyEM_UPS_Watt: ' + String(upsWattNew)));
upsWattNoUoM = actions.Transformation.transform('REGEX', '([0-9.]*) (\\D)', items.getItem('ShellyEM_UPS_Watt').state);
console.info(('No UoM  Value of ShellyEM_UPS_Watt: ' + String(upsWattNoUoM)));
upsPF = items.getItem('ShellyEM_UPS_PowerFactor').state;
console.info(('Raw Value of ShellyEM_UPS_PowerFactor: ' + String(upsPF)));
console.info(('Updated Value of ShellyEM_UPS_PowerFactor: ' + String(upsPF)));
upsVA = upsWatt / upsPF;
console.info(('Calculated VA value is: ' + String(upsVA)));
upsVA_UoM = (Quantity(items.getItem('ShellyEM_UPS_Watt').state).divide(8).toUnit('VA'));
console.info(('Calculated VA with UoM value is: ' + String(upsVA_UoM)));

If I try and use the to Unit block I get the error:

19:10:38.091 [ERROR] [ab.automation.script.javascript.stack] - Failed to execute script:
org.graalvm.polyglot.PolyglotException: TypeError: upsPF.toUnit is not a function
        at <js>.:program(<eval>:15) ~[?:?]
        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 javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:262) ~[java.scripting:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocableAndAutocloseable.eval(DelegatingScriptEngineWithInvocableAndAutocloseable.java:53) ~[?:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.eval(InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java:78) ~[?:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.DelegatingScriptEngineWithInvocableAndAutocloseable.eval(DelegatingScriptEngineWithInvocableAndAutocloseable.java:53) ~[?:?]
        at org.openhab.automation.jsscripting.internal.scriptengine.InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.eval(InvocationInterceptingScriptEngineWithInvocableAndAutoCloseable.java:78) ~[?:?]
        at org.openhab.core.automation.module.script.internal.handler.ScriptActionHandler.lambda$0(ScriptActionHandler.java:71) ~[?:?]
        at java.util.Optional.ifPresent(Optional.java:178) ~[?:?]
        at org.openhab.core.automation.module.script.internal.handler.ScriptActionHandler.execute(ScriptActionHandler.java:68) ~[?:?]
        at org.openhab.core.automation.internal.RuleEngineImpl.executeActions(RuleEngineImpl.java:1180) ~[?:?]
        at org.openhab.core.automation.internal.RuleEngineImpl.runRule(RuleEngineImpl.java:989) ~[?:?]
        at org.openhab.core.automation.internal.TriggerHandlerCallbackImpl$TriggerData.run(TriggerHandlerCallbackImpl.java:87) ~[?:?]
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) ~[?:?]
        at java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[?:?]
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[?:?]
        at java.lang.Thread.run(Thread.java:833) ~[?:?]
19:10:38.093 [ERROR] [.internal.handler.ScriptActionHandler] - Script execution of rule with UID 'Watts_to_VA' failed: org.graalvm.polyglot.PolyglotException: TypeError: upsPF.toUnit is not a function

Code:

var upsWatt, upsWattNew, upsWattNoUoM, upsPF, upsVA, upsVA_UoM;


upsWatt = items.getItem('ShellyEM_UPS_Watt').state;
console.info(('Raw Value of ShellyEM_UPS_Watt: ' + String(upsWatt)));
upsWattNew = upsWatt * 1;
console.info(('Updated Value of ShellyEM_UPS_Watt: ' + String(upsWattNew)));
upsWattNoUoM = actions.Transformation.transform('REGEX', '([0-9.]*) (\\D)', items.getItem('ShellyEM_UPS_Watt').state);
console.info(('No UoM  Value of ShellyEM_UPS_Watt: ' + String(upsWattNoUoM)));
upsPF = items.getItem('ShellyEM_UPS_PowerFactor').state;
console.info(('Raw Value of ShellyEM_UPS_PowerFactor: ' + String(upsPF)));
console.info(('Updated Value of ShellyEM_UPS_PowerFactor: ' + String(upsPF)));
upsVA = upsWatt / upsPF;
console.info(('Calculated VA value is: ' + String(upsVA)));
upsVA_UoM = (Quantity(items.getItem('ShellyEM_UPS_Watt').state).divide(upsPF.toUnit('VA')).toUnit('VA'));
console.info(('Calculated VA with UoM value is: ' + String(upsVA_UoM)));

Also, cannot link the get state of item block to the to unit block

EDIT: Working Now using the upsPF x 1 workaround. Will clean up blocks and post for info.

Appreciate the guidance.

1 Like

Thank you for doing that. Your asking hopefully carries some weight :slight_smile:

The first operand has units. The second operand has units. Therefore the result of division is going to have units. That outer “to unit” is unnecessary.

It appears that the “to unit” block isn’t as smart as I thought it might be. It will only work if the variable passed to it is already a Quantity. We can’t use that to convert it to a Quantity. Therefore you need to use the “10 W” block and append the units using something like

I am using the outer “to unit” to convert W to VA because the result of the division is W.

Thanks, will give that a try as well. But just using the multiply by 1 workaround also works.

EDIT:
image

Gives:

20:02:34.116 [INFO ] [nhab.automation.script.ui.Watts_to_VA] - ***********NEWCalculated VA with UoM value is: null

I completely agree with @rlkoshak: use UoM whenever possible, otherwise use a Blockly function to convert the state of an item to a number:

var item, temp, temp2;

// Describe this function...
function getValue(item) {
  temp = itemRegistry.getItem(item).getState();
  temp += '';
  temp2 = temp.slice(0, temp.indexOf(' ') + 1);
  if (temp2 == '') {
    temp2 = temp;
  }
  return temp2 * 1;
}

var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);


logger.error((getValue('TibberAPI_Live_Power')));
logger.error((getValue('ZBVindstyrka1_pm25')));

Log out the intermediate steps.

  • upsWatt
  • upsWatt converted to a quantity
  • upsPF
  • result of the division
  • finally conversion to VA

I have added extra logging as suggested. To me looks like it fails on the last step Temp1 = (Quantity(upsWatt).divide(upsPF).toUnit('VA'));

The new blocks are

Which generates (full code):

var upsWatt, upsWattNoUoM, upsPF, upsVA, upsVA_UoM, Temp, Temp1;


upsWatt = items.getItem('ShellyEM_UPS_Watt').state;
console.error(('Raw Value of ShellyEM_UPS_Watt: ' + String(upsWatt)));
upsWattNoUoM = actions.Transformation.transform('REGEX', '([0-9.]*) (\\D)', items.getItem('ShellyEM_UPS_Watt').state);
console.error(('No UoM  Value of ShellyEM_UPS_Watt: ' + String(upsWattNoUoM)));
upsPF = items.getItem('ShellyEM_UPS_PowerFactor').state;
console.error(('Raw Value of ShellyEM_UPS_PowerFactor: ' + String(upsPF)));
upsPF = upsPF * 1;
console.error(('Updated Value of ShellyEM_UPS_PowerFactor: ' + String(upsPF)));
upsVA = upsWatt / upsPF;
console.error(('Calculated VA value is: ' + String(upsVA)));
upsVA_UoM = (Quantity(upsWatt).divide(upsPF).toUnit('VA'));
console.error(('Calculated VA with UoM value is: ' + String(upsVA_UoM)));
upsPF = items.getItem('ShellyEM_UPS_PowerFactor').state;
console.error(('New upsPF for last test' + String(upsPF)));
upsPF += 'W';
console.error(('New upsPF after W append ' + String(upsPF)));
upsPF = Quantity(upsPF);
console.error(('New upsPF after IOM block ' + String(upsPF)));
Temp = (Quantity(upsWatt).divide(upsPF));
console.error(('New Temp after / ' + String(Temp)));
Temp1 = (Quantity(upsWatt).divide(upsPF).toUnit('VA'));
console.error(('***********NEWCalculated VA with UoM value is Temp1 : ' + String(Temp1)));

Logs results:

09:22:05.757 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - Raw Value of ShellyEM_UPS_Watt: 397.63 W
09:22:05.772 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - No UoM  Value of ShellyEM_UPS_Watt: 397.63 W
09:22:05.772 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - Raw Value of ShellyEM_UPS_PowerFactor: -0.82
09:22:05.773 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - Updated Value of ShellyEM_UPS_PowerFactor: -0.82
09:22:05.773 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - Calculated VA value is: NaN
09:22:05.774 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - Calculated VA with UoM value is: -484.91463414634146341463414634146360860 VA
09:22:05.774 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - New upsPF for last test-0.82
09:22:05.774 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - New upsPF after W append -0.82W
09:22:05.774 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - New upsPF after IOM block -0.82 W
09:22:05.775 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - New Temp after / -484.91463414634146341463414634146360860
09:22:05.775 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - ***********NEWCalculated VA with UoM value is Temp1 : null
09:22:05.776 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - Raw Value of ShellyEM_UPS_Watt: 397.63 W
09:22:05.776 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - No UoM  Value of ShellyEM_UPS_Watt: 397.63 W
09:22:05.777 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - Raw Value of ShellyEM_UPS_PowerFactor: -0.82
09:22:05.777 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - Updated Value of ShellyEM_UPS_PowerFactor: -0.82
09:22:05.777 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - Calculated VA value is: NaN
09:22:05.778 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - Calculated VA with UoM value is: -484.91463414634146341463414634146360860 VA
09:22:05.778 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - New upsPF for last test-0.82
09:22:05.778 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - New upsPF after W append -0.82W
09:22:05.778 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - New upsPF after IOM block -0.82 W
09:22:05.779 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - New Temp after / -484.91463414634146341463414634146360860
09:22:05.779 [ERROR] [nhab.automation.script.ui.Watts_to_VA] - ***********NEWCalculated VA with UoM value is Temp1 : null

The the intermediate calculation works, but when adding the UoM for VA the result goes to null

All the help has been appreciated

Also agree. But can get a bit complex when also dealing with string to number conversion and changing UoM etc.

Tried to follow you example, but a bit above me sadly.