postUpdate and then sendCommand

I’m almost certainly sure it was discussed few times, but i can’t really find any straight answer.

I do have something like

var Number dimmer = 0
        if(Kitchen_StripeLED.state != "NULL" && Kitchen_StripeLED.state != "UNDEF") dimmer = (Kitchen_StripeLED.state as Number).intValue


Which saves current state of the dimmer and then lighten LED up to 70%

and then under some conditions I do have


Which should return dimmer to previous state when lights go off
Which now does not work unless I change that to


So question is
Why this does not work when hKitchenDimmer contains number 20


hKitchenDimmer is defined as

Number      hKitchenDimmer

but .toString saves the day


I’m sometimes bit lost when to use state and when to use state.toString

It’s a subtle issue of type. When you call .state you get back an Object of type State. sendCommand requires a String, a Number, or a Command type Object. So you need to cast or convert that State Object into one of those in order for it to work as an argument to sendCommand.

By calling toString you are converting the State to a String. Other options could be:

  • MyOtherItem.sendCommand(MyItem.state as Number)
  • MyOtherItem.sendCommand(MyItem.state as Command) // NOTE: not all States are Commands and not all Commands are States, for example StringType is only a State, PLAY is only a Command
  • `sendCommand(, MyItem.state.toString) // NOTE: the postUpdate and sendCommand actions only supports two Strings as arguments

With Number Items there is one gotcha. There is a method of sendCommand that accepts Number and another one that accepts DecimalType. For some reason, when you cast the state to DecimalType it will get confused and throw an ambiguous method call exception because a DecimalType is also of type Number. It doesn’t throw an exception when cast to Number though so I always recommend avoiding casting to DecimalType.

Finally, one more passing comment. dimmer is a Number. You cast Kitchen_StringLED.state to Number which is what dimmer is expecting. So why make the call to intValue? All it’s going to do is convert the state to a primitive int only to convert it back to a Number. Not only is it redundant, at least on OH 2, forcing variables to be a certain type unnecessarily can add huge amounts of time to the loading and parsing of .rules files. I have no expectation that this would change in OH 3. So avoid forcing stuff to be a type unless necessary and definitely avoid forcing stuff to be primitives unless absolutely necessary.

There is a lot of magic that the language does to make it kinda sorta typeless. When it works it’s great (i.e. when you can just call .state and it’s able to convert the State to what ever it needs to be in that context) but often times it can’t. For example logInfo("Test", "Some state " + MyItem.state) works because the "Some state " is a String so the language is smart enough to coerce MyItem.state to a String by calling toString for you. However, logInfo("Test", MyItem.state + " Some state") wont work because the first argument is a State/Number and it’s not smart enough to realize that if the second argument is a String so you want to use the String + operator, not the Number + operator.

So use the following rules to thumb.

  • By default don’t specify the type
  • When it doesn’t work cast it to Number (if you are in a calculations/comparison context) or call toString
  • Only ever convert a Number to a primitive when necessary and do so at the last moment (e.g. in calling now.plusSeconds((MyDelay.state as Number).intValue))

Thanks @rlkoshak as always for great explanation !

most probably some kind of leftofvers from the past when I created those rules… you know "if it ain’t broke, don’t fix it"
But I very often sadly do try to fix it for no good reason :smiley: