rulesDSL maths

Hello,
I want do do some maths on my RulesDSL

import org.openhab.core.library.types.DecimalType

rule "solaredge.selfconsumption"
//self consumption calculation
when
    Item SolarEdge_Live_Consumption changed
then
    if ( SolarEdge_Live_Production.state > 0|kW ) {
        SolarEdge_Live_Self_Consumption.postUpdate((SolarEdge_Live_Production.state as DecimalType) - (SolarEdge_Live_Consumption.state as DecimalType))
    }
    else if ( SolarEdge_Live_Production.state <= 0|kW ) {
        SolarEdge_Live_Self_Consumption.postUpdate(0)
        logInfo("solaredge.selfconsumption", "self consumption nula")
    }
end

but I have all the time error:

[ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'solaredge-1' failed: Could not cast 0.07 kW to org.openhab.core.library.types.DecimalType; line 9, column 53, length 46 in solaredge

what is wrong with my code?

SolarEdge_Live_Production.state.toString.split(" kW").get(0) as Number

you find an excellent insight here

Number

works nicely, except the decimal number - needs to be -0,35kW instead of -350W. Is there a way how to reduce negative numbers?
Thanks!

don‘t know. I never used UoM. but try

(calculation stuff…).toUnit("kW")

it makes sense now to start working with variables

sorry, my post makes no sense - I misunderstood your question. It is better now to use the method of rossko in the link I provided

SolarEdge_Live_Production.state as QuantityType<Power>).toBigDecimal

you can even omit the conversion to Big Decimal and do the following:

(SolarEdge_Live_Production.state as QuantityType<Power>) - (SolarEdge_Live_Consumption.state as QuantityType<Power>)

I am so glad I no longer need to do such type gymnastics. In jruby:

rule "solaredge.selfconsumption" do
  changed SolarEdge_Live_Consumption
  run do
    self_consumption = [SolarEdge_Live_Production - SolarEdge_Live_Consumption, 0].max | 'kW'   
    SolarEdge_Live_Self_Consumption.update(self_consumption) 

    logger.info("self consumption: #{self_consumption}")
  end
end

Basically you just use the item name. It is automatically inferred that they’re a numeric type and you can perform arithmetic as if they’re a number, but you can also treat them like a first class openhab items, with so many capabilities, such as:

  • SolarEdge_Live_Self_Consumption.meta['namespace'] to access its metadata,
  • SolarEdge_Live_Self_Consumption.persist to tell it to persist to the database,
  • SolarEdge_Live_Self_Consumption.average_since(24.hours, :influxdb) to get the persistence average data,
  • SolarEdge_Live_Self_Consumption.update '12kW' to update it with a string with UoM, and many more.

Note if your Production is less than Consumption, your code will give you a negative self consumption. I’m not sure that’s what you wanted? My code above will set the self consumption to zero in this case.

A bit of note if you’re not familiar with ruby:
self_consumption.zero? is just a preferred way of writing self_consumption == 0 or self_consumption == '0 W'. You can use any of the 3 variations.

[x, y, z] is an array, in this case [SolarEdge_Live_Production - SolarEdge_Live_Consumption, 0] is an array containing two elements: the calculation result and zero. Then you call [array].max to get the max of the elements. It’s the same as the traditional way of doing max(x, y) in other languages.

Well, I have never worked with ruby :slight_smile: I am an mechanical engineer. Too less time and too less experiences, but I like it.

You are right with code, inbetween i have changed the code so it no longer returns negative values. However, I am still fighting with kW vs W values.

rule "solaredge.selfconsumption"
//self consumption calculation
when
    Item SolarEdge_Live_Consumption changed
then
    if ( SolarEdge_Live_Production.state as Number > SolarEdge_Live_Consumption.state as Number ) {
        logInfo("solaredge.selfconsumption", "produkcia vacsia ako spotreba")
        SolarEdge_Live_Self_Consumption.postUpdate((SolarEdge_Live_Production.state as QuantityType<Power>) - (SolarEdge_Live_Consumption.state as Number))
        //        SolarEdge_Live_Self_Consumption.postUpdate((SolarEdge_Live_Production.state as DecimalType) - (SolarEdge_Live_Consumption.state as DecimalType))
        logInfo("solaredge.selfconsumption", "self consumption update")
    }
    else if ( SolarEdge_Live_Production.state <= 0|kW ) {
        SolarEdge_Live_Self_Consumption.postUpdate(0)
        logInfo("solaredge.selfconsumption", "self consumption nula")
    }
end

That’s exactly why the jruby library was developed. You just want to write rules to do what you want and don’t have to remember all the gory details about types, conversions, casting, etc. The syntax is very straight forward just like how you would “expect” it to work.

You can mix and match your calculation and don’t have to care whether A is in kW and B is in W, that’s the whole point of UoM. You can do A + B and get the result, and if you want to force the resulting unit to your preference you can do that too.

In what sense?

This looks dodgy.

(SolarEdge_Live_Production.state as QuantityType<Power>)
That’s a Quantity type object, a numeric with units.
You can do maths with Quantity types and units are sorted automatically e.g. 50kW + 100W, or 1500W > 1kW etc.

(SolarEdge_Live_Consumption.state as Number)
This is not a Quantity type. It’s just numeric. It may even have units ‘cosmetically’ tagged on to the state, but the Quantity functions do not work.
Mix these types at your peril.

I would guess you’d need to compare them as state, and not convert to Number, i.e.

    if ( SolarEdge_Live_Production.state > SolarEdge_Live_Consumption.state) {

I haven’t used RulesDSL in a while.

this is not working, at least VSCode underscores it.

ok, but when I got it like this:

SolarEdge_Live_Self_Consumption.postUpdate((SolarEdge_Live_Production.state as QuantityType<Power>) - (SolarEdge_Live_Consumption.state as QuantityType<Power>))

VSCode underscores postUpdate

Ambiguous feature call.
The extension methods
	postUpdate(Item, State) in BusEvent and
	postUpdate(Item, Number) in BusEvent
both match.(org.eclipse.xtext.xbase.validation.IssueCodes.ambiguous_feature_call

finally realized how it should looks like!

rule "solaredge.selfconsumption"
//self consumption calculation
when
    Item SolarEdge_Live_Consumption changed
then
    if ( SolarEdge_Live_Production.state as QuantityType<Power> > SolarEdge_Live_Consumption.state as QuantityType<Power> ) {
        SolarEdge_Live_SelfConsumption.postUpdate(SolarEdge_Live_Consumption.state as QuantityType<Power>)
        logInfo("solaredge.selfconsumption", "production more than consumption")
    }    
    else if ( SolarEdge_Live_Production.state as QuantityType<Power> <= SolarEdge_Live_Consumption.state as QuantityType<Power> ) {
        SolarEdge_Live_SelfConsumption.postUpdate(SolarEdge_Live_Production.state as QuantityType<Power>)
        logInfo("solaredge.selfconsumption", "production less than consumption")
    }
end

Thanks everyone, sometimes VSC is showing bugs where they aren’t
have a nice day,

Michal, Slovakia

What do you think are not bugs?

There is a subtlety at work in validating the postUpdate argument.
As the message told you, it looks for a state type or a Number type. A Quantity type does not meet the requirement.

As you seem to be just re-posting state anyway,
SolarEdge_Live_SelfConsumption.postUpdate(SolarEdge_Live_Consumption.state)
should satisfy the validator.

The ultimate cure-all for postUpdate pickyness is posting a string, it then (tries to) parse it into whatever is needed by the Item.

1 Like