Thing Status API - can't get sample code to work

Hi all,

there is a Thing Status API sample code in the docs.
I try to get this code to work. My aim is to get a list of all things and show this list in my log-file.

I have changed the code the way I think it should be right, but I get the following error:

2019-04-22 10:15:22.022 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model '18_ThingsCheck.rules'
2019-04-22 10:16:12.737 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule 'rTestThingStatusAPI': The name 'thingRegistry' cannot be resolved to an item or type; line 86, column 20, length 13

This error refers to line 86 which is
MyThings = thingRegistry.getAll()

At the moment my rule looks like this

rule "rTestThingStatusAPI"   // test the code sample from https://www.openhab.org/docs/concepts/things.html#thing-status-api
 when 
	Time cron "0 */1 * ? * *" // Only for testing! Every minute.             
 then 
    // first prepare the log entry
        val String          MyLogFile = "MySeparateLogFile"   // if 'MySeparateLogFile' does not exist, logging goes into openhab.log (to create a separate logfile see https://www.openhab.org/docs/administration/logging.html#logging-into-separate-file)
        val DateTime        MyLogDate = now  
        val StringBuilder   MyLogMessage = new StringBuilder
        MyLogMessage.append( "\n" + "\n" + " ----------   " + MyLogDate.toString("HH:mm") + " rTestThingStatusAPI v001   ----------" + "\n" + "\n" )

    // second create values we can work with 
        MyThings = thingRegistry.getAll()

    // for each thing check status
        for (MyThings : things) {
            ThingStatusInfo statusInfo = thing.getStatusInfo()
            switch (statusInfo.getStatus()) {
                case "ONLINE":
                    {
                    MyLogMessage.append( "Thing is online" + "\n" )
                    break
                    }
                case "OFFLINE":
                    {
                    MyLogMessage.append( "Thing is offline" + "\n" )
                    break
                    }
                default:
                    {
                    MyLogMessage.append( "Thing is in state " + statusInfo.getStatus() + "\n" )
                    break
                    }
            }
            MyLogMessage.append( "Thing status detail: " + statusInfo.getStatusDetail() + "\n" )
            MyLogMessage.append( "Thing status description: " + statusInfo.getDescription() + "\n" )
        }

    // finally, write the log-entry into the log-file 
        MyLogMessage.append( "\n" + " ----------   End Of Rule   ----------" + "\n" + "\n" )
        logInfo( MyLogFile, MyLogMessage.toString )

end // EndOfRule rTestThingStatusAPI

Can someone help me solving this problem? Do I need some imports?

What version of OH are you running?

Sorry, Raspberry3, openhabian, openhab 2.4 stable release.

You haven’t declared MyThings as a variable.

val MyThings = thing...

I have had this before but have changed it because using val I get additional info variable MyThings is not used.

val MyThings = thingRegistry.getAll()

leads to this messages

2019-04-23 02:50:14.052 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model '18_ThingsCheck.rules', using it anyway:
The value of the local variable MyThings is not used
2019-04-23 02:50:14.304 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model '18_ThingsCheck.rules'
2019-04-23 02:51:11.108 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule 'rTestThingStatusAPI': The name 'thingRegistry' cannot be resolved to an item or type; line 91, column 24, length 13
2019-04-23 02:52:00.040 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule 'rTestThingStatusAPI': The name 'thingRegistry' cannot be resolved to an item or type; line 91, column 24, length 13

I also tried this

val MyThings = thingsRegistry.getAll()

with this messages

2019-04-23 02:54:20.355 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model '18_ThingsCheck.rules', using it anyway:
The value of the local variable MyThings is not used
2019-04-23 02:54:20.616 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model '18_ThingsCheck.rules'
2019-04-23 02:55:11.515 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule 'rTestThingStatusAPI': The name 'thingsRegistry' cannot be resolved to an item or type; line 91, column 24, length 14
2019-04-23 02:56:00.039 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule 'rTestThingStatusAPI': The name 'thingsRegistry' cannot be resolved to an item or type; line 91, column 24, length 14

Ok, but that’s progress. You’ve gone from it complaining that something that does exist not existing, to it complaining that a variable that you’ve created isn’t used.
This points to a syntax error.

I’m pretty sure that the example in the docs had an error in that it is missing the val or var. But it is also including the type that gets returned in the example and your coffee is not.

val Collection<Thing> MyThings = ...

I also see that your for loop is wrong which explains the “not being used” warning. If you look at the example in the docs it uses:

for (Thing thing : things) {

To be correct with your code it would be

for (Thing thing : MyThings)

my code snipped now is

        val MyThings = thingRegistry.getAll();   
        // MyThings = ScriptServiceUtil.getInstance.thingRegistry.getAll() // Error during the execution of rule 'rTestThingStatusAPI': An error occurred during the script execution: Couldn't invoke 'assignValueTo' for feature JvmVoid:  (eProxyURI: 18_ThingsCheck.rules#|::0.2.0.2.0.4::0::/1)

    // for each thing check status
        for (Thing thing : MyThings) {
            ThingStatusInfo statusInfo = thing.getStatusInfo();
            switch (statusInfo.getStatus()) {
                case "ONLINE":
                    {
                    MyLogMessage.append( "Thing is online" + "\n" )
                    break;
                    }
                case "OFFLINE":
                    {
                    MyLogMessage.append( "Thing is offline" + "\n" )
                    break;
                    }
                default:
                    {
                    MyLogMessage.append( "Thing is in state " + statusInfo.getStatus() + "\n" )
                    break;
                    }
            }
            MyLogMessage.append( "Thing status detail: " + statusInfo.getStatusDetail() + "\n" )
            MyLogMessage.append( "Thing status description: " + statusInfo.getDescription() + "\n" )
        }

log-messages with warning, but rule is not executed

2019-04-23 05:26:19.974 [WARN ] [el.core.internal.ModelRepositoryImpl] - Configuration model '18_ThingsCheck.rules' has errors, therefore ignoring it: [95,14]: mismatched input 'Thing' expecting ';'
[95,26]: mismatched input ':' expecting 'end'

I’m not shure about the ;
Do I need them behind the MyLogMessage.append commands?

I strongly suggest using VSCode with the openHAB extension which should help with solving syntax errors like these. I’m on my phone and have no experience working with Things in Rules.

Always consider the actual line of code the error refers to. It appears to be referring to the for.

VSCode and openHAB extension tell me “No Problems”.

I searched for examples to learn the syntax of Collection and for ... case , but didn’t find anything that was useful to me. D o you know useful links?

That’s odd because it is the same thing that generates the error in openhab.log as what syntax checks the code.

Wait, that example code isn’t Rules code, it’s binding code. You can’t write code like this as a Rule. The switch statement syntax is different for Rules. You don’t use breaks in Rules for one.

I don’t know if code like this is even possible in Rules DSL. But it certainly wont work as written here.

I suppose that means that this is not Xtend.
Just out of curiosity. If this isn’t Xtend, do you know what language it is?

Even though we did not find a solution, I am still very grateful for your help.
Thank you very much!

Java.

Would you mind posting your rule once you have it working as I would love to use it to alert me when one of my things goes offline.

Of course. But at the moment it don’t look like i will get it running.

As I needed to dump all things status to log due to debugging purposes, found your code inspirative. However was not easy to make it working. But, finally, here is working code:

import org.eclipse.smarthome.model.script.ScriptServiceUtil

rule "dump all things after system started (or rules reloaded)"
when
    System started
then
    logInfo("SYSTEM started:", "system started (or rules reloaded), dumping things status")
    // first prepare the log entry
    val String          MyLogFile = "MySeparateLogFile"   // if 'MySeparateLogFile' does not exist, logging goes into openhab.log (to create a separate logfile see https://www.openhab.org/docs/administration/logging.html#logging-into-separate-file)
    val DateTime        MyLogDate = now
    val StringBuilder   MyLogMessage = new StringBuilder
    MyLogMessage.append( "\n" + "\n" + " ----------   " + MyLogDate.toString("HH:mm") + " DumpAllThings v.RKA.20200503   ----------" + "\n" + "\n" )

    val things = ScriptServiceUtil.getInstance.thingRegistry.getAll()
    things.forEach[ thing |
        MyLogMessage.append( "Thing Bridge UID: " + thing.getBridgeUID() + "\n" )
        MyLogMessage.append( "Thing UID: " + thing.getUID() + "\n" )
        MyLogMessage.append( "Thing Label: " + thing.getLabel() + "\n" )

        val statusInfo = thing.getStatusInfo()
        if (statusInfo.getStatus().toString() == 'ONLINE') {
            MyLogMessage.append( "Thing status: online" + "\n" )
        }
        else if (statusInfo.getStatus().toString() == 'OFFLINE') {
            MyLogMessage.append( "Thing status: OFFLINE" + "\n" )
        }
        else {
            MyLogMessage.append( "Thing state: " + statusInfo.getStatus() + "\n" )
        }
        MyLogMessage.append( "Thing status detail: " + statusInfo.getStatusDetail() + "\n" )
        MyLogMessage.append( "Thing status description: " + statusInfo.getDescription() + "\n" )
    ]

    // finally, write the log-entry into the log-file 
    MyLogMessage.append( "\n" + " ----------   End Of Rule   ----------" + "\n" + "\n" )
    logInfo( MyLogFile, MyLogMessage.toString )

end
2 Likes

All Thing status changes get logged out to events.log by default. What is missing from that, that you need to log?

2020-05-03 04:44:14.985 [hingStatusInfoChangedEvent] - 'mqtt:broker:spare' changed from OFFLINE (COMMUNICATION_ERROR): io.netty.channel.AbstractChannel$AnnotatedNoRouteToHostException: No route to host: spare.koshak.net/10.10.1.167:1883 to OFFLINE
2020-05-03 04:44:18.182 [hingStatusInfoChangedEvent] - 'mqtt:broker:spare' changed from OFFLINE to OFFLINE (COMMUNICATION_ERROR): io.netty.channel.AbstractChannel$AnnotatedNoRouteToHostException: No route to host: spare.koshak.net/10.10.1.167:1883
2020-05-03 05:00:20.331 [hingStatusInfoChangedEvent] - 'chromecast:chromecast:ba707f086019e93c9e9bcc4dee240c26' changed from OFFLINE to ONLINE
2020-05-03 16:23:53.332 [hingStatusInfoChangedEvent] - 'mqtt:broker:spare' changed from OFFLINE (COMMUNICATION_ERROR): io.netty.channel.AbstractChannel$AnnotatedNoRouteToHostException: No route to host: spare.koshak.net/10.10.1.167:1883 to UNINITIALIZED
2020-05-03 16:23:53.336 [hingStatusInfoChangedEvent] - 'mqtt:broker:spare' changed from UNINITIALIZED to OFFLINE

The above are just a few random Thing status updates.

I dont know if its this feature your trying to get to work. But I´m using this rather simple code to notify me if a thing goes on or offline. The only bad issue is, it needs to be done to every thing. You cant group things, as far as I know. Having tons of things, is a time killer doing this rule.

rule "Zwave Node3 status"
when
  Thing "zwave:device:zstick:node3" changed 
then

var thingStatusInfo = getThingStatusInfo("zwave:device:zstick:node3")

if ((thingStatusInfo !== null) && (thingStatusInfo.getStatus().toString() == "ONLINE")) {
    logInfo("ThingStatus", "ZwaveNode3 is online.")
  ZWaveNode3_status.postUpdate(ON)
} else {
    logError("ThingStatus", "ZwaveNode3 is offline or doesn't exist.")
  ZWaveNode3_status.postUpdate(OFF)

}
end
1 Like

You cannot make a Group of Things to listen out for in a rule, but you can have a (NGRE) rule listen to all Thing status changes. That rule can then filter out certain classes of Things and/or events.

A practical example exploiting Yannick’s method to listen to Modbus poller type things only. Because I wasn’t very comfortable with writing NGRE, it actually transmits events to a DSL rule for further action.

Another approach that would also work for Rules DSL would be to use the Log Reader binding to watch events.log for the hingStatusInfoChangedEvent lines to trigger a Rule to send the alert.

Hmm wasn´t aware of that binding… Looks rather usefull for my needs. Thanks!