JavaScript/Blockly Get State for MelCloud AC Returns number

Hi,

I am trying to create a rule that would read the operation state of my air conditioning unit connected via MelCloud add-on and would pass it to another string item. The challenge I have is that even though the state item of the air conditioner is a string with values e.g. cool, heat, etc, it returns (presumably) the index value of the item from a list of possible states as opposed to the string one. Here is the code I use (ignore the fact that some lines are commented):

The value I get in this case is “3” as opposed to “Cool”. I can of course code the rule with a lot of ifs translating these codes into values, but I am wondering if there is a better way of doing this…

Any help would be greatly appreciated.

Kind Regards,
Sergey

Get where? The rule fragment doesn’t appear to do anything with states that would make them visible.

May we see the Item definition that you are having trouble with. As shown by API Explorer would be useful; the binding has probably assigned state presentation metadata to it.
Unfortunately I do not think that metadata is available to you in Blockly rules.

Thanks for your response! Here is the item definition where I am trying get the State in text format (via itemRegistry.getItem(‘lounge_ac_OperationMode’).getState();
{
“link”: “http://172.16.2.10:8080/rest/items/lounge_ac_OperationMode”,
“state”: “3”,
“stateDescription”: {
“pattern”: “%s”,
“readOnly”: false,
“options”: [
{
“value”: “1”,
“label”: “Heat”
},
{
“value”: “2”,
“label”: “Dry”
},
{
“value”: “3”,
“label”: “Cool”
},
{
“value”: “7”,
“label”: “Fan”
},
{
“value”: “8”,
“label”: “Auto”
}
]
},

It appears that there are value-label pairs defined but I am not sure how to get Blockly and/or Javascript rule to return the label as opposed to the value. Would you be able to advise?

I think you can get at metadata in OH3 from javascript, I don’t know how.
Might be help here

That would better than using your own MAP transformation, unless you want to change the text.

Thank you! Your response pointed me in the right direction. It was actually not the metadata that I was after but the item value options and their labels. I have changed the code in this way:

if (itemRegistry.getItem('lounge_ac_Power').getState() == 'ON') {
  var curState = itemRegistry.getItem('lounge_ac_OperationMode').getState();
  for each  (var r in itemRegistry.getItem('lounge_ac_OperationMode').getStateDescription().getOptions()) {
    if (r.getValue() == curState) {
      events.postUpdate('loungeac_google_operation_mode', r.getLabel());      
      break;
    }
  }
} else {
  events.postUpdate('loungeac_google_operation_mode', 'Off');
}

So the getOptions() method returns a list of key-value pair options for the item. I can then loop through that and identify the label for the current state.

Kind Regards,
Sergey

1 Like

Thanks for posting your working result but please use code fences.

```
code goes here
```

I’ve not done a lot with StateDescription and StateOptions in rules but they look like they could be useful. I need to explore that more so thanks for the push in that direction. This came up earlier in an issue regarding why State Description isn’t working for Group Items.

It looks like getStateDescription was added to the Item class to access the State Description Item metadata without needing to go to the MetadataRegistry directly which makes it available in all the rules languages. This could be useful.

It’s king of both because that State Description stuff is actually stored as Item metadata and it is stored in the Metadata Registry. For all intents and purposes it is Item metadata. However, the devs have also made this metadata directly accessible from the Item’s Object as well.

If not using the Helper libraries here’s an example:

// Accessing Item Metadata
var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.Examples");
logger.info("Trying to extract a metadata value")
var FrameworkUtil = Java.type("org.osgi.framework.FrameworkUtil");
var _bundle = FrameworkUtil.getBundle(scriptExtension.class);
var bundle_context = _bundle.getBundleContext()
var classname = "org.openhab.core.items.MetadataRegistry"
var MetadataRegistry_Ref = bundle_context.getServiceReference("org.openhab.core.items.MetadataRegistry");
var MetadataRegistry = bundle_context.getService(MetadataRegistry_Ref);
var methods = MetadataRegistry.class.getDeclaredMethods();
logger.info("There are " + methods.length + " methods");
for each (var m in methods) {
  logger.info(m.toString());
}
var Metadata = Java.type("org.openhab.core.items.Metadata");
var MetadataKey = Java.type("org.openhab.core.items.MetadataKey");
var metadata = MetadataRegistry.get(new MetadataKey("name", "vArgus_Status"));
logger.info("vArgus_Status's name is " + metadata.value);

Some notes on your example code…

  • In Python and Nashorn JavaScript rules there is a dict with all the Items and their current states. So itemRegistry.getItem('lounge_ac_Power').getState() can be replaced with items['lounge_ac_Power'].

  • It’s frustrating that getOptions returns a List instead of a Map since it’s key/value pairs. But an alternative to implementing the loop yourself you can use the Java Streaming API. Your loop could be replaced with.

var label = itemRegistry.getItem('lounge_ac_OperationMode').getStateDesciption()
                                                           .getOptions()
                                                           .stream()
                                                           .filter(function(r) { r.getValue() == curState; })
                                                           .findFirst();
if(label.isPresent()){
    events.postUpdate('loungeac_google_operation_mode', label.get());
}
else {
    events.postUpdate('loungeac_google_operation_mode', "Off");
}

That’s not necessarily any simpler in this particular case but in other cases it could be easier, such as performing map/reduce operations on members of a Group and the like.

Note that not all Items will have Options. Some binding will supply the Options (I’m not sure how that works but I’ve seen in the Chromecast binding as well). You can also define your own Options in the State Description Metadata on the Item.

Thanks for your help, Rich! This is really useful for other stuff I am doing. I’ve updated my code to check if label is present. I’ve also updated my post with code fences.

I had to add return in the filter function to get this working:

var label = itemRegistry.getItem(this.event.itemName.toString()).getStateDescription()
                                                           .getOptions()
                                                           .stream()
                                                           .filter(function(r) { return r.getValue() == this.event.itemState; })
                                                           .findFirst();

Otherwise label.isPresent() always returns false (in my case anyway)