Type Conversions

Hey guys, short reply from me. I’m currently on a surprise vacation (invited on Thursday, flight Friday morning). I think we should try to make the article a bit more systematic, as already mentioned here. Definitely something I can work on in the next couple of day (I have my laptop with me).

The rest of the changes look great! Later guys :wink:

Really great text and discussion!

What bothered me for some time - is there a way to convert string (the name of the item) to the object, so the item object further be manipulated.

If there is no way for this, it would be great to have this clearly stated as well.

No, from a programming perspective that doesn’t make any sense. We could add a statement that one cannot do this but if we start going down the path of listing everything that one cannot do the article becomes infinite in length.

Having said that, if you do a little but of setup up front you can get the Item or if a group by name. See Design Pattern: Associated Items

Thanks, fair enough. I just thought about something like FAQ where “NO” would be said to questions of dummies, even if they do not make sense from a programming perspective.

Just wanted to mention that here for everyone to be aware, the tutorial posted here is now also available at:

http://docs.openhab.org/configuration/rules-dsl.html#conversions

If you want to add improvements or additions, please do so there!

3 Likes

I don’t know if this is relevant, but I’m trying do define a Map, like this:

var Map<GenericItem, State> LightStates = null

However this does not work, as “State cannot be resolved to a Type” Is State a Type?
If I leave the parameters off, I get a warning “Map is raw type it should be parameterized”, but it works.

The discussion here https://community.openhab.org/t/storestates-restorestates/29994/3 says that this is correct.

Not a big deal, the raw Map works, just gives an error when the rules are loaded is all.

I don’t know this definitively, but I do not think that one can use Java Interfaces as Types in the Rules DSL. This is also why you can’t do Map<Item, Boolean>. Since State is an Interface rather than a proper class I think you can’t define the Map like that.

You can’t use Item either, as Item is an openhab reserved word, which was why is was using GenericItem.

I tried:

MAP<GenericItem, Object>

to see if that would work, and I get the error "can’t map Map<Item, State> to Map<GenericItem, Object>

Doesn’t really matter, I’ll just chalk it up to “openhab weirdness”.

1 Like

Hi,
the expression:

val hours = (MyDateTimeItem.state as DateTimeType).calendar.get(Calendar::HOUR_OF_DAY)

gives an error:

The name ‘Calendar’ cannot be resolved to an item or type

it is needed:

import java.util.Calendar

I am not sure about the syntax. Look here:

In 2.2 release and beyond you need to use

(MyDateTimeItem.state as DateTimeType).getZonedDateTime.getHour
1 Like

Hi guys,

casting and type coversion is something that drives me crazy as well. Maybe you can point me to the right direction:

This is the rule that should tell me when the washinmachine is done. Took the biggest part of that from annother post (I guess it was from Thom Dietrich)

val Number Waschmaschine_MODE_OFF = 0
val Number Waschmaschine_MODE_STANDBY = 1
val Number Waschmaschine_MODE_ACTIVE = 2
val Number Waschmaschine_MODE_FINISHED = 3
val Number Waschmaschine_MODE_CREASE_PROTECTION = 4

var Number Waschmaschine_Counter = 0


rule "Waschmaschine Status"
when
    Item Steckdose_Waschmaschine_Strom changed
then
  if (Steckdose_Waschmaschine_Strom.state < 1) Waschmaschine_OpState.postUpdate(Waschmaschine_MODE_OFF)
  else if (Steckdose_Waschmaschine_Strom.state > 100  && Waschmaschine_OpState.state !== (Waschmaschine_MODE_CREASE_PROTECTION) || Steckdose_Waschmaschine_Strom.state > 500){
    Waschmaschine_OpState.postUpdate(Waschmaschine_MODE_ACTIVE)
  }
  if (Waschmaschine_OpState.state == (Waschmaschine_MODE_ACTIVE) && Steckdose_Waschmaschine_Strom.state < 2){
    Waschmaschine_Counter = Waschmaschine_Counter + 1
  }
  if (Steckdose_Waschmaschine_Strom.state > 1 && Steckdose_Waschmaschine_Strom.averageSince(now.minusMinutes(2)) < 2) {
    if (Waschmaschine_OpState.state == Waschmaschine_MODE_OFF) Waschmaschine_OpState.postUpdate(Waschmaschine_MODE_STANDBY)
  }
  if (Waschmaschine_Counter > 6){
    if (Waschmaschine_OpState.state == Waschmaschine_MODE_ACTIVE){
      Waschmaschine_Counter = 0
      Waschmaschine_OpState.postUpdate(Waschmaschine_MODE_CREASE_PROTECTION)
    }
  }
  if (Steckdose_Waschmaschine_Strom.averageSince(now.minusMinutes(30)) < 150){
    if (Waschmaschine_OpState.state == (Waschmaschine_MODE_CREASE_PROTECTION)) Waschmaschine_OpState.postUpdate(Waschmaschine_MODE_FINISHED)
  }
 end  

Now when loading into VSC, it throws an error like this

file: 'file:///Volumes/openHAB-share/openhab2-conf/rules/washing_machine_state.rules'
severity: 'Fehler'
message: 'Ambiguous binary operation.
The operator declarations
	operator_lessThan(Number, Number) in NumberExtensions and
	operator_lessThan(Type, Number) in NumberExtensions
both match.'
at: '25,114'
source: ''
code: 'org.eclipse.xtext.xbase.validation.IssueCodes.ambiguous_feature_call'

The script runs and alternates between states “0” and “2”, but never actually reaches “1”, “3” or “4”.

Guess the main problem is this expression here

Steckdose_Waschmaschine_Strom.averageSince(now.minusMinutes(2)) < 2)

So I have tried “xxx as Decimal”, “(xxx as Decimal).floatValue”, replaced the figure “2” with a variable of type int or type float or type number, nothing worked. What am I doing wrong?

Thanks for any hints

You’ve hit one of the weird cases.

I think the problem is the first line of the Rule.

if (Steckdose_Waschmaschine_Strom.state < 1) Waschmaschine_OpState.postUpdate(Waschmaschine_MODE_OFF)

Steckdose_Waschmaschine_Strom.state (assuming it is a Number Item) carries a DecimalType. But DecimalType is both of type Number and of type Type. So when the Rules Engine tries to run the < operator it doesn’t know which one to run, the one that takes Number, Number or it takes Type, Number.

You can fix this by telling the Rules Engine which < operation to call by casting the state to Number.

if (Steckdose_Waschmaschine_Strom.state as Number < 1) Waschmaschine_OpState.postUpdate(Waschmaschine_MODE_OFF)
1 Like

Great, why I am always that lucky…

The line you have posted is working and not mentioned by VSC. I guess the problem is exactly what you said, but I guess i’d rather need to specify the type of this expression: ‘Steckdose_Waschmaschine_Strom.averageSince(now.minusMinutes(2)’, as the comparison to ‘>2’ fails.
…and this is where I have no clue, how to do it…

First of all, do you have persistence set up?

Secondly, log out the result of the call from averageSince. If there is a problem with persistence or the data in the database then averageSince will return null and you can’t compare null with >.

Anytime you have errors or a rule doesn’t execute as expected:

  • watch the openhab.log for errors
  • watch events.log to verify the events that should trigger the Rule are happening
  • log out everything

Yes, using rrd4j

Ok, guess this I will have to check, thanks! But as the error is already stated by the editor (VSC), I’d say we should stick with the casting problem.

no errors, ‘2018-03-15 17:53:35.324 [INFO ] [el.core.internal.ModelRepositoryImpl] - Loading model ‘washing_machine_state.rules’’
No additional line (error) after that. So should be fine.

The triggers are occuring (changes in current drawn by the machine. I can see it changing in the GUI, it is logged in persistence so I can draw a chart and the values are written in events.log. The rule itself works but never enters the states I have stated above. When editing the code in VSC, it returns the lines in question with the errors I have posted. Still, the rule itself runs and returns the states “off” and “active” when the specified conditions (current drawn by the machine) are true.

What else could I log? I guess you where spot on with your first reply, it must be a casting problem. So how do I cast this expression so the rule engine understands it as a number it can compare to the value of “< 2”

Steckdose_Waschmaschine_Strom.averageSince(now.minusMinutes(2))

Is the syntax of the type correct (xyz(now.minusMinutes(2)) ?

If you log out the result you will immediately be able to see what it is that averageSince is returning. We won’t know what we can cast it to or what we need to convert it from until we know what it is.

I can’t even log it :joy: as the editor does not accept the expression as any kind of number…
Bummr…
It throws Messages like “cannot convert from Decimal to String”, or “cannot convert from Number to decimal”…and so on. When I do NOTHING to the string, it still shows the first error. So no idea how to log it

logInfo("Waschmaschine Strom Durchschnittswert",(Steckdose_Waschmaschine_Strom.averageSince(now.minusMinutes(2))))
nope

logInfo("Waschmaschine Strom Durchschnittswert",(Steckdose_Waschmaschine_Strom.averageSince))
nope

logInfo("Waschmaschine Strom Durchschnittswert",(Steckdose_Waschmaschine_Strom.averageSince as Decimal)
nope

logInfo("Waschmaschine Strom Durchschnittswert",(Steckdose_Waschmaschine_Strom.averageSince as Number)
nope

logInfo("Waschmaschine Strom Durchschnittswert",(Steckdose_Waschmaschine_Strom.averageSince as Number).floatValue
nope

logInfo("Waschmaschine Strom Durchschnittswert",(Steckdose_Waschmaschine_Strom.averageSince as Decimal).floatValue
nope

…(don’t look at the brackets too close, I have just written the different expressions from the top of my head)

Edit: THIS I love the most:

logInfo("Waschmaschine Strom Durchschnittswert",(Steckdose_Waschmaschine_Strom.averageSince as DecimalType))

returns the error

file: 'file:///Volumes/openHAB-share/openhab2-conf/rules/washing_machine_state.rules'
severity: 'Warnung'
message: 'Unnecessary cast from DecimalType to DecimalType'
at: '18,98'
source: ''
code: 'org.eclipse.xtext.xbase.validation.IssueCodes.obsolete_cast'

So if I do nothing, it’s wrong. If I cast decimal to decimal it’s redundant. Ok, now there’s a complete clusterf** in my mind…

Ok, I guess I let that sit where it ist for today and give it a fresh try on the weekend.

Ignore the editor for now. Just because the editor marks it as wrong does not mean it won’t run (necessarily). If you save the file and openhab.log doesn’t spit out a syntax error log statement the Rule will run.

Bingo. So averageSince is returning a DecimalType.

So you should be able to log:

logInfo("Waschmaschine Strom Durchschnittswert", Steckdose_Waschmaschine_Strom.averageSince(now.minusMinutes(2)).toString)

I think I mentioned in the original posting above that OH is not good at converting numbers to strings.

That is just a warning. The code will still run.

Since it is returning a DecimalType and I already explained why DecimalType is a problem, the same solution I recommended for the first line will apply here as well.

Steckdose_Waschmaschine_Strom.averageSince(now.minusMinutes(2)) as Number < 2