publishMQTT not working as advertised in OH3

Since OH-3 the following rule does not work anymore because it complains about publishMQTT like this:

2021-01-26 16:41:01.503 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'mqtt-2' failed: 'publishMQTT' is not a member of 'org.openhab.core.thing.binding.ThingActions'; line 56, column 9, length 88 in mqtt

and here’s the rule like OH3 displays it in it’s code tab:;

triggers:
  - id: "0"
    configuration:
      groupName: MQTT_GROUP
    type: core.GroupStateChangeTrigger
conditions: []
actions:
  - inputs: {}
    id: script
    configuration:
      type: application/vnd.openhab.dsl.rule
      script: >
        // context: mqtt-2

        var actions = getActions("mqtt","mqtt:broker:64e1eabf")
        actions.publishMQTT("/openHAB/state/"+triggeringItem.name,triggeringItem.state.toString)
    type: script.ScriptAction

The broker name is a local MQTT broker to mosquitto. It’s thing is showing online in OH3 so where does this error come from. FYI: I already tried using “val = action …” instead of “var = action …” cause some in some other threads some people use on or the other but neither solved the problem. And as this is a group change trigger also the “triggeringItem” should still exist (which was removed on other triggers) but I would also get another error.

Can anyone shed a light on this why this isn’t working as other people seems to have no problems with this on OH3 if I understood correctly.

Double and triple check the Thing ID that you are passing to getActions. It must match exactly including case. That’s the only obvious thing I see that could cause that error.

val means it’s a constant and not going to be reassigned to some other value later on in the rule. var means variable, it will be reassigned later in the rule.

It’s not getting that far in the code to know if that is a problem or not. But it should be fine. If the rule were not triggered by a GroupStateChangeTrigger you would have to use triggerinItemName.

I already triple checked that and furthermore the rule was migrated from OH2.5 and I didn’t modify it so it should have. But here’s the picture of the MQTT Broker (Thing) in OH3 which doesn’t work. Maybe you spot something. I’m out of ideas in this case

I don’t know what could be wrong. I’ve used the action myself so I know it can work. I can’t say why it doesn’t for you.

Is it possible that it doesn’t work on group change triggers anymore?

That error is saying that it can’t find a function called publishMQTT in the thing actions of the MQTT binding. That is the function name in the v1 binding, I believe it changed to simply publish in the v2 binding.

1 Like

That’s what I thought but I looked in the docs to be sure and it is publishMQTT.

It’s also how I call it from Python in the MQTT EventBus.

    action = actions.get("mqtt", mqtt_eb_broker)
    if action:
        action.publishMQTT(topic, msg, retained)
    else:
        init_logger.error("There is no broker Thing {}!".format(mqtt_eb_broker))

So the error is really weird.

No, the action wouldn’t care about the type of the trigger type.

Meanwhile I added logInfo statements to see what gets published to MQTT because I thought maybe it has something to do with the items.

Now, that I have these logs it turns out that actually these errors only appear when system is starting up. So it seems that when MQTT broker is not up yet you get this misleading error message. I probably have to delay/prohibit sending any messages until broker is available. I just don’t know how this could be done in a rule. I need to elaborate on that

It’s not really misleading. When the rule ran, the Broker Thing in fact did not exist.

Add a Script Condition with some code to check the status of the Thing. If it’s not ONLINE return false.

var ThingRegistry = Java.type('org.openhab.core.thing.ThingRegistry')
var Bundle = Java.type('org.osgi.framework.Bundle')
var FrameworkUtil = Java.type('org.osgi.framework.FrameworkUtil')
var thingBundle = FrameworkUtil.getBundle(ThingRegistry.class)
var bundleContext = thingBundle.getBundleContext()
var ThingStatus = Java.type('org.openhab.core.thing.ThingStatus')

// Get Service
var thingRegistryRef = bundleContext.getServiceReference(ThingRegistry.class)
var thingRegistryImpl = bundleContext.getService(thingRegistryRef)

thingRegistryImpl.get(""mqtt:broker:64e1eabf") != ThingStatus.ONLINE

It would probably be good to add some logging here so you can see that the rule tried to run but was prevented because the Thing was not online.

NOTE: I just typed the above in. I’ve not tested it. I believe most of this is encapsulated in the Helper Libraries as well which would be easier to use.

There is a Thing Registry Delegate available in the default scope for rules, I believe it is simple things that would cut down on that code a lot.

Indeed. I was on another thread and it occurred to me that the ThingsRegistry might be in ScriptServiceUtil so it even if it were not already included one ought to be get it from that.

var ScriptServiceUtil = Java.type('org.openhab.core.model.script.ScriptServiceUtil');
var ThingRegistry = ScriptServiceUtil.getThingRegistryInstance();
var ThingStatus = Java.type('org.openhab.core.thing.ThingStatus');

ThingRegistry.get("mqtt:broker:64e1eabf") !== ThingStatus.ONLINE;

That’s already better, but it won’t work. getThingRegistryInstance isn’t a static method on ScriptServiceUtil like getItemRegistry is. :frowning:

But

var ThingStatus = Java.type('org.openhab.core.thing.ThingStatus');
things.get("mqtt:broker:64e1eabf") !== ThingStatus.ONLINE;

is even better. :slight_smile: But, it turns out it’s a little more complicated than that.

Here is what it would look like (I actually tested this version):

var ThingUID = Java.type('org.openhab.core.thing.ThingUID');
things.get(new ThingUID("mqtt:broker:broker")).getStatus().toString() == "ONLINE";

You have to create a ThingUID Object to get the Thing. You can’t just use a String. :frowning: But you don’t have to get a ThingStatus to make the comparison. You can use a String there.

1 Like

but …but …they keep saying we don’t get these startup timing issues in OH3 …

`var actions = getActions("mqtt","mqtt:broker:64e1eabf")`
failed: 'publishMQTT' is not a member of 'org.openhab.core.thing.binding.ThingActions'

I don’t suppose there is “instance of” style method to test if it is a member at runtime?

Frankly this is the first time I’ve seen any mention of a start timing issue. And I’m not convinced that is what is actually going on here. But with the information provided I’ve nothing else to go on.

Implemented start level service by kaikreuzer · Pull Request #1914 · openhab/openhab-core · GitHub lists all the startup levels. Notice that level “20 Model entities (items, things, links, persist config) have been loaded, both from db as well as files” happens before “40 Rules are loaded and parsed, both from db as well as dsl and script files” which happens before level “80 All things have been initialized”.

This does make some sense as it can take an extended about of time before Things become initialized and in some error cases Things may never become initialized (I’m not sure how that case is handled even now). Would we really want to delay the running of Rules for that long, potentially indefinitely? So this really isn’t a “rules started running before they should” problem as much as it is that the MQTT Thing took too long to initialize. And because Things can change states independently from startup, that’s a problem that can take place at any time, not just during startup. So, just like needing to check an Item’s state for NULL/UNDEF before blindly using its state, in some cases it may be required to test to see if the Thing is ONLINE before blindly calling its action. Or just live with the error in the log when the problem occurs.

You can check to see if actions is null before trying to call the publishMQTT. So really, we don’t even need to mess with checking the Thing’s status at all. We can just check to see if the call to getActions returns null or not. If it does, the Thing either doesn’t exist or it is not in a usable state.

1 Like

But doesn’t the error message indicate that the the actions is indeed not null but org.openhab.core.thing.binding.ThingActions. It sounds more that ThingActions (beeing a Java class?) doesn’t know this method yet.

So I tried to use your code provided to check for the thing status:

But I f I add an if statement with this the rule engine complains that The name 'Java' cannot be resolved to an item or type and when saving it also complains that ThingUID is not used anywhere. I guess the latter is just a nonsense warning and for ‘Java’ to be known I have tom import something but I cannot findout what import is needed.

You are right, it’s not null. But this is JavaScript right? So you can test to see if actions has the publishMQTT method.

if(actions.publishMQTT !== undefined) { actions.publishMQTT(...

You chose ECMAScript as the language for the condition? I’d expect that error if you chose Rules DSL as the language. If EXMAScript Java is built into the language, it can’t be undefined and there is nothing to import. In fact, that Java.type is how you import things from Java into the script.