Thing Status Reporting [3.2.0;3.4.9]


Note, in the first half of 2023 this rule template will be adjusted to only support JS Scripting.

A rule that runs periodically and calls a user defined rule with the list of Things that match a configured criteria. For example, one can run it once a minute or so and call another rule with the list of Things that are OFFLINE, or the ones that are not ONLINE.

A list of Things that match the criteria is passed to the called rule and is available with context.getAttribute("things");. This list can be processed any way desired.

The conditions of the rule called are taken into account so one can put limits on when or how often the the rule runs (e.g. don’t run at night).

By default it is configured with a cron trigger to run periodically based on the desired periodicity. One could customize the rule by changing the triggers to list each Things’s status change individually or use any other criteria desired to trigger the rule.

One useful trick will be to keep track of the old list of Things in the called rule so one can see which ones changed since the last call. (var oldthings = (oldthings === undefined) ? context.getAttribute("things") : oldthings;).

Feel free to customize this rule as much as desired to meet your needs. You could even replace the code that calls your rule and put that processing inline.

All but the rule properties are optional. If unchanged the rule template will cause the rule to run every 30 seconds and call your rule with all the Things that are OFFLINE.

Language: Nashorn JavaScript or JSScripting


  • in OH 4 Nashorn must be separately installed or change the script type to application/javascript;version=ECMAScript-2021


Version 0.3

  • adjusted for breaking change, will no longer work for versions of OH prior to December 15th

Version 0.2

  • Made compatible with Nashorn and JSScripting
    NOTE: Support for Nashorn will be dropped sometime after OH 3.2 is released.

Version 0.1

  • initial release



Thanks! This looks just like something I have been trying to achieve! I’m a complete idiot on javascript, but is there a way to filter the things to monitor? I don’t need to know if a light bulb goes offline, but I would very much like to know when a smoke detector goes out!

Not without modifying the rule to the point it will be completely different from what is posted here.

But you can create a rule triggered by the status changes on the Things you care about and then do something based on that.

Or in the rule you write that gets called by this rule you can add some code to loo through the Things passed to it and ignore the ones you don’t care about.

I hope you can stand me drifting slightly of topic, but how can I make a rule that takes another rules output as an argument? I have been using OH for a while now, but this is completely new for me!

Once you’ve installed this rule template from the marketplace.

  1. Create a rule to be called. Remember it’s ID.
  2. Create a rule and choose this Rule Template from the list of templates.
  3. In the “Rule to Call” property, select the rule you created in 1.

In the rule created in step 1, you can access the list of Things passed to it from the template created rule using the code from above:

var things = context.getAttribute("things")

It will look like that line should work just like that as written in Rules DSL or JavaScript.

It really couldn’t be simpler.

1 Like

Aha, I think I see it now! The rule created using the template is executing a script that in turn is executing my pre-made rule (as defined in the template properties when instantiating the template) that can execute a script that fetches the list of things from the context object!
As you say, couldn’t be simpler! Just needed to figure out the whole template-thing!

Just to make this clear.

You code Rule 1
You create Rule 2 from the Template, filling out the properties as desired.

The data flows:

Rule 2 → Rule 1

The data is a List of Thing objects named “things”.

Rule 2 collects the Things that match the criteria and passes them to Rule 1 for further processing. All that the rule template above does is create that list of Things (e.g. all Things that are not ONLINE). Nothing more.

You need to be on the latest milestone. Then go to Settings → Automation and add the template from the list.

1 Like

That’s the thing monitoring, I needed - Thanks!

I see this code in my blockly:

offlineThings = ctx['things'];

Which seems to work fine.
However, the things are cryptic:
Is there a way to translate this?

I know how to work with transformations like MAP, but where to look for the reference of this thing?
(your Threshold Alert rule returns the ItemLabel if I am not wrong)

Threshold Alert sends a List of the Items that are NULL or UNDEF, a comma separated string of the labels of the Items that are NULL or UNDEF, a List of the items that meet the criteria, and a comma separated string of the labels of the Items.

It’s all explained in the Marketplace posting.

The Thing Status rule template sends a List of the raw Thing Objects that match the criteria. What you are seeing and call cryptic is the name of the Class that implements the Thing and the @bc5a35e is the address in memory.

You will need to call methods on the Thing Object to get information out of it and I’m not sure Blockly supports that. Consider using Rules DSL (since I bet that’s what you know best) for this one Script action. It’s not a problem to have more than one language going in your set up.

The reference for this and all other openHAB classes are at Overview (openHAB Core 3.3.0-SNAPSHOT API) (or something like that, I’m having weird issues on my phone bringing up pages right now so can’t double check). Search for “thing” and you’ll find the doc for that class.

That’s not neccessary, Rich.
Even a single check from you is more than enough.

So, thanks for the explanation I will build in my DSL rule stuff for this topic then.
I have a lot of well working DSL rules, but try to move as much as possible over to mainUI rules - (preferrably Blockly). Maybe it’s better to move slowly on my migration path.

Just remember, you could still move this to MainUI. Just choose Rules DSL as the script language instead of Blockly.

Yes, thank you.
I did this for some rules (e.g. geofencing with Location items within a group).
Maybe I should look into JS direct for the future, but will start with my DSL code for now.
Again, thanks for your help, Rich.

But what about this:

which in code is this:

is this the same like

you are referring to in the threshold-alert thread?

or is there no way to get the context attribute from the Threshold-alert in Blockly?

You can get the things and you can probably even manipulate the List of Things. But I don’t think you can then call any of these methods on the Thing Objects which means you can’t even get the name or uid of the Things that are offline from Blockly.

I assume though that you can get to the variables passed to the script using Rules DSL. In Nashorn it’s called context and it has a comtext.getAttribute('key'). In JSScripting the passed in stuff just appears as a variable. So the things is just directly accessed (see below).

I don’t know whether or how it appears in Rules DSL. I would assume it’s one of these two ways.

Here is my script that gets called by the Thing Status Alert for reference. It’s in JSScripting.

configuration: {}
triggers: []
conditions: []
  - inputs: {}
    id: "1"
      type: application/javascript;version=ECMAScript-2021
      script: >-
        var logger = log('Thing Status Processor');

        logger.debug("There are " + things.length + " things that are not ONLINE");
    type: script.ScriptAction

Thank you!
I will look into this and see, how I can make use of your code.

@NCO sorry for resurecting this, but I was wondering if you had found a solution to display a “readable” list of things that were offline and if you would mind sharing it?
I know i resurect an old topic, but i think it complements nicely the Thing Status Reporting.

Sorry, I did not follow this path yet.
I had more urgent things to fix :wink:

update: Don’t use the following blockly example. There is a better one further down that uses the original marketplace rule from Rich.

Hi Rich (and @NCO, @thefathefa),
I changed/extended your rule a bit, especially since I wanted to see readable names (labels) of the offline things… but then I did a little bit more… Maybe you can use something for your next revision, or somebody else likes it. I thought the whole solution might be nice for the marketplace but I didn’t want to steal Rich’s code. Tell me if it’s better to start a new thread. The changes are:

  • Filter all things that are not ONLINE or UNINITIALIZED (to see things with initialisation problems too. You get the things with status INITIALIZING, OFFLINE, REMOVED, REMOVING, UNKNOWN for further processing.
  • Export of labels, IDs and status. (As far as I understand, the values of the context hashmap are passed as strings. Please tell me if there is a better way than passing as arrays)
  • evaluation rule with Ignore-list for things to be skipped, that sends telegram messages when things are offline (love it).
Cron-rule with changes at the end
if(typeof(require) === "function") Object.assign(this, require('@runtime'));
var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.rules_tools.Thing Status Change");

var ThingStatus = Java.type("org.openhab.core.thing.ThingStatus");
var FrameworkUtil = Java.type("org.osgi.framework.FrameworkUtil");
var ScriptHandler = Java.type("org.openhab.core.automation.module.script.rulesupport.shared.ScriptedHandler");
var _bundle = FrameworkUtil.getBundle(ScriptHandler.class);
var bundle_context = _bundle.getBundleContext();

// Get the Thing Regsitry
var classname = "org.openhab.core.thing.ThingRegistry";
var ThingRegistry_Ref = bundle_context.getServiceReference(classname);
var ThingRegistry = bundle_context.getService(ThingRegistry_Ref);

// Get the RuleManager
var classname = "org.openhab.core.automation.RuleManager"
var RuleManager_Ref = bundle_context.getServiceReference(classname);
var RuleManager = bundle_context.getService(RuleManager_Ref);

var things_Label = []
var things_Id = []
var things_Status = []

// Get Things that are not online {
                        return (t.getStatus() != ThingStatus.ONLINE) && (t.getStatus() != ThingStatus.UNINITIALIZED)
                      .forEach (function(t) {
                        things_Label.push (t.getLabel() );
                        things_Id.push (t.getUID().getId() );
                        things_Status.push (t.getStatus() );
logger.debug("There are " + things_Id.length + " things that are not online");

// Call the handler rule with the lists of IDs, Labels and status
if (things_Id.length > 0) {
  var data = new java.util.HashMap();
  data.put("id", things_Id);
  data.put("label", things_Label);
  data.put("status", things_Status);
  RuleManager.runNow("Alarm_offlineThing", true, data);
else {
  logger.debug("No items match the criteria");
evaluation rule (blockly). To use paste it in the code tab of an empty rule
configuration: {}
triggers: []
conditions: []
  - inputs: {}
    id: "1"
      blockSource: "<xml
        type=\"variables_set\" id=\"A`J${j$nZ_dOHC]+d`GI\" x=\"-42\"
        y=\"-352\"><field name=\"VAR\"
        id=\"]8Kt_8cXjoij*UWfL]}Z\">ignore</field><value name=\"VALUE\"><block
        type=\"text\" id=\"i:tQdMWT($M2/|(JSPl9\"><field
        Example_ID_2</field></block></value><next><block type=\"variables_set\"
        id=\"0vV{KknuF_oq_@1;-c5s\"><field name=\"VAR\"
        name=\"VALUE\"><block type=\"text\" id=\"hD-daeK:-q]Hoi{~lbW;\"><field
        name=\"TEXT\"></field></block></value><next><block type=\"oh_log\"
        id=\"V67tNp:rbdm+Y}L/x,4o\"><field name=\"severity\">debug</field><value
        name=\"message\"><shadow type=\"text\"
        name=\"TEXT\">abc</field></shadow><block type=\"oh_context_attribute\"
        id=\"ItP}])9xOd?Z/v6/F)Py\"><value name=\"key\"><shadow type=\"text\"
        type=\"variables_set\" id=\"GR{vZ946}Ug;S8z_N.Wz\"><field name=\"VAR\"
        name=\"VALUE\"><block type=\"lists_split\"
        id=\"*zsq,7yH1Fr0x9~3-Y7B\"><mutation mode=\"SPLIT\"></mutation><field
        name=\"MODE\">SPLIT</field><value name=\"INPUT\"><block
        type=\"text_join\" id=\"P1QH8RtkOeX/I:c?A+U5\"><mutation
        items=\"1\"></mutation><value name=\"ADD0\"><block
        type=\"oh_context_attribute\" id=\"LTWWl(QN-P|L`2DSuXC(\"><value
        name=\"key\"><shadow type=\"text\" id=\"tSoTPJD=s.K5TvwBe6?:\"><field
        alue><value name=\"DELIM\"><shadow type=\"text\"
        type=\"variables_set\" id=\"!n@S={3Jr8hQo%JI9Hub\"><field name=\"VAR\"
        id=\":mi-th}K]|8t\">list_id</field><value name=\"VALUE\"><block
        type=\"lists_split\" id=\"h5wDS.dtQKismlHuoE1_\"><mutation
        mode=\"SPLIT\"></mutation><field name=\"MODE\">SPLIT</field><value
        name=\"INPUT\"><block type=\"text_join\"
        id=\"uYIGn3P(N)O9%sHsp8j%\"><mutation items=\"1\"></mutation><value
        name=\"ADD0\"><block type=\"oh_context_attribute\"
        id=\"l#C@M/Ub^GGiWD=cVMe{\"><value name=\"key\"><shadow type=\"text\"
        e><value name=\"DELIM\"><shadow type=\"text\"
        type=\"variables_set\" id=\"fLS9V8Tnb[Tm@tS5d^k!\"><field name=\"VAR\"
        name=\"VALUE\"><block type=\"lists_split\"
        id=\"!h`%(OE44#P7d!yBEb_%\"><mutation mode=\"SPLIT\"></mutation><field
        name=\"MODE\">SPLIT</field><value name=\"INPUT\"><block
        type=\"text_join\" id=\"[%1U++k%0%ewT/EkZCB}\"><mutation
        items=\"1\"></mutation><value name=\"ADD0\"><block
        type=\"oh_context_attribute\" id=\"POH;[%EDtuu,]/#XI_`t\"><value
        name=\"key\"><shadow type=\"text\" id=\"ehDvr7dus+oWwpRZccc%\"><field
        value><value name=\"DELIM\"><shadow type=\"text\"
        type=\"controls_for\" id=\"a%PPQ=;_xNd:W6v}}f5[\"><field name=\"VAR\"
        id=\"E@l+)XgabtaQN$-j#0}]\">i</field><value name=\"FROM\"><shadow
        type=\"math_number\" id=\"asvPqe~e|*?MZKV+Ldyl\"><field
        name=\"NUM\">1</field></shadow></value><value name=\"TO\"><shadow
        type=\"math_number\" id=\"mGf:6L7d*iJfact;;=AZ\"><field
        name=\"NUM\">10</field></shadow><block type=\"lists_length\"
        id=\"2h31}(uN6Y.nCua7$zpL\"><value name=\"VALUE\"><block
        type=\"variables_get\" id=\"9BNtfh^bi`G9?.5+L#xL\"><field name=\"VAR\"
        lue><value name=\"BY\"><shadow type=\"math_number\"
        name=\"NUM\">1</field></shadow></value><statement name=\"DO\"><block
        type=\"oh_log\" id=\"6[A;2a`PzE:W2hE],=7b\"><field
        name=\"severity\">info</field><value name=\"message\"><shadow
        type=\"text\" id=\"TZIr.27H$*;W#JE]7ZZy\"><field
        name=\"TEXT\">abc</field></shadow><block type=\"lists_getIndex\"
        id=\"mkBmXfyc5+0$Nc}wPROP\"><mutation statement=\"false\"
        at=\"true\"></mutation><field name=\"MODE\">GET</field><field
        name=\"WHERE\">FROM_START</field><value name=\"VALUE\"><block
        type=\"variables_get\" id=\"pPf+EO7#plsGN.tE_Gmj\"><field name=\"VAR\"
        name=\"AT\"><block type=\"variables_get\"
        id=\"Q4AV~M?C`kpaBUc1bQX#\"><field name=\"VAR\"
        ext><block type=\"controls_if\" id=\"7.Kl=xcVc6TJI/EK80-G\"><value
        name=\"IF0\"><block type=\"logic_compare\" id=\"4XmvLDcX@(]BPi]YBkR#\"
        inline=\"false\"><field name=\"OP\">EQ</field><value name=\"A\"><block
        type=\"text_indexOf\" id=\"pug+5L}uk22)e,PME/M;\"><field
        name=\"END\">FIRST</field><value name=\"VALUE\"><block
        type=\"variables_get\" id=\"#oba*+x9eth:Hv:X!@c}\"><field name=\"VAR\"
        name=\"FIND\"><shadow type=\"text\" id=\"44R*w).I5vC5Jj(o$n^J\"><field
        name=\"TEXT\">abc</field></shadow><block type=\"lists_getIndex\"
        id=\"-n8Z/Og:$NP^[F,hL#Wc\"><mutation statement=\"false\"
        at=\"true\"></mutation><field name=\"MODE\">GET</field><field
        name=\"WHERE\">FROM_START</field><value name=\"VALUE\"><block
        type=\"variables_get\" id=\"2`Y6V-Kr@XebR_SS2c`+\"><field name=\"VAR\"
        name=\"AT\"><block type=\"variables_get\"
        id=\"pVu.PDdX[GNA*;f4.R4H\"><field name=\"VAR\"
        block></value><value name=\"B\"><block type=\"math_number\"
        name=\"DO0\"><block type=\"text_append\"
        id=\"-6+nO0pA`L|8efOG2{PO\"><field name=\"VAR\"
        name=\"TEXT\"><shadow type=\"text\" id=\"P5=(ge5fi5^{59q]jiCS\"><field
        name=\"TEXT\"></field></shadow><block type=\"text_join\"
        id=\"6FMD8J.XGVB#Hv8fe{hP\"><mutation items=\"8\"></mutation><value
        name=\"ADD0\"><block type=\"oh_text_crlf\"
        id=\"|T1A/qty_!,q/ENHq~sz\"></block></value><value name=\"ADD1\"><block
        type=\"text\" id=\"7c=`LG4uz455s*ilSHb2\"><field
        name=\"ADD2\"><block type=\"oh_text_crlf\"
        id=\"j3lKb_(%g$3_1a:+j`rI\"></block></value><value name=\"ADD3\"><block
        type=\"lists_getIndex\" id=\"s60t+$D!3#hd9}J;_!yS\"><mutation
        statement=\"false\" at=\"true\"></mutation><field
        name=\"MODE\">GET</field><field name=\"WHERE\">FROM_START</field><value
        name=\"VALUE\"><block type=\"variables_get\"
        id=\"5]cha6#4,r6l`Cm]y)Cp\"><field name=\"VAR\"
        name=\"AT\"><block type=\"variables_get\"
        id=\"l4-k0q8h+SPCJ0KNX6O,\"><field name=\"VAR\"
        alue name=\"ADD4\"><block type=\"text\"
        id=\")Ul2AieSM5@w5zt/3$wW\"><field name=\"TEXT\">
        (</field></block></value><value name=\"ADD5\"><block
        type=\"lists_getIndex\" id=\"*4brwi?ZNr8;@[dp|nyI\"><mutation
        statement=\"false\" at=\"true\"></mutation><field
        name=\"MODE\">GET</field><field name=\"WHERE\">FROM_START</field><value
        name=\"VALUE\"><block type=\"variables_get\"
        id=\"`H`txBv]S|c^,NE92XDI\"><field name=\"VAR\"
        name=\"AT\"><block type=\"variables_get\"
        id=\"XBkzkQk[DUL:#9zDa*Vl\"><field name=\"VAR\"
        alue name=\"ADD6\"><block type=\"text\"
        id=\"@PD=Nz#q_S]EOyzv@{Y}\"><field name=\"TEXT\">):
        </field></block></value><value name=\"ADD7\"><block
        type=\"lists_getIndex\" id=\"*emnJot)d@7*H-}]]omZ\"><mutation
        statement=\"false\" at=\"true\"></mutation><field
        name=\"MODE\">GET</field><field name=\"WHERE\">FROM_START</field><value
        name=\"VALUE\"><block type=\"variables_get\"
        id=\"z.#(n#(~Bj++-MrL/,D!\"><field name=\"VAR\"
        name=\"AT\"><block type=\"variables_get\"
        id=\"j3$=Ok..vpBRQm7c{q1l\"><field name=\"VAR\"
        ext><block type=\"controls_if\" id=\"4l=bGRXqL*m#4st3WbIV\"><value
        name=\"IF0\"><block type=\"variables_get\"
        id=\"}eT[MPm),_,qbA._-|nz\"><field name=\"VAR\"
        nt name=\"DO0\"><block type=\"telegram_with_chatID_message\"
        id=\"%?.)2$rZJ)1$`[CGZ9qP\" inline=\"false\"><value
        name=\"MESSAGE\"><shadow type=\"text\"
        id=\"%jIrNi@b+!)=WA-_P8,8\"><field name=\"TEXT\">What's
        up?</field></shadow><block type=\"text_join\"
        id=\"+:B^y;:|mtkaD2WvQ-/;\"><mutation items=\"2\"></mutation><value
        name=\"ADD0\"><block type=\"text\" id=\"Vq%.RspM!Sb]4j|oA!Yq\"><field
        name=\"TEXT\">*Things not online:*</field></block></value><value
        name=\"ADD1\"><block type=\"variables_get\"
        id=\";tdF%Ltj{[p+U`Yxi09~\"><field name=\"VAR\"
        </value><value name=\"BOT\"><shadow type=\"oh_thing\"
        alue><value name=\"CHAT_ID\"><shadow type=\"math_number\"
      type: application/javascript
      script: >
        var ignore, messageText, list_label, list_id, list_status, i;
        var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID);
        var things = Java.type('org.openhab.core.model.script.actions.Things');
        ignore = 'Example_ID_1, Example_ID_2';
        messageText = '';
        list_label = String(ctx['label']).split(',');
        list_id = String(ctx['id']).split(',');
        list_status = String(ctx['status']).split(',');

        var i_end = list_id.length;
        var i_inc = 1;
        if (1 > i_end) {
          i_inc = -i_inc;

        for (i = 1; i_inc >= 0 ? i <= i_end : i >= i_end; i += i_inc) {
[(i - 1)]));
          if (ignore.indexOf(list_id[(i - 1)]) + 1 == 0) {
            messageText += String(['\r\n','____________________________','\r\n',list_label[(i - 1)],' (',list_id[(i - 1)],'): ',list_status[(i - 1)]].join(''));

        if (messageText) {
          things.getActions('telegram', 'telegram:telegramBot:Telegram-Bot').sendTelegram(-123, '*Things not online:*' + String(messageText));
    type: script.ScriptAction

The blocky rule should look like this. You need the telegram binding and the telegram-with-chat-ids blockly library from the marketplace. Finally you have to change the bot and chat-id to your needs (see doc of telegram binding).

If everything works well the telegram message look like this:

Hope you like it. Comments/hints/improvements are welcome.

The original rule template supports this too. Choose ONLINE for the status and != for the comparison.

This is why I pass the full Thing Object to the called script. With the Thing Object you have access to all of the properties of the Thing.

I think it’s better to keep it to passing the Thing Object because you get not just these three properties but access to all the Thing info like the Channels, location, properties, the ID of the bridge, and the ability to change the Thing (e.g. change the properties).

This is deliberately left as an exercise to the user to implement in the called rule. The end user writes a script to filter the list and send alerts or taking what ever remedial actions they want to take. That part is custom. The rule template should not make assumptions about what the user wants to do with the list of Things that match.

The rule that’s called can define a condition to only run when certain Things appear in the list. They are passed as a Map so all you need to do is check to see if the Thing ID you care about is in the keys.

Given this, I’m not seeing what this does differently beyond remove some capability from the original. Because it’s not a rule template, it cannot be installed and instantiated. The Thing status it looks for is hard coded. And it passes less information to the called rule. But it does show that there might be something I can do to improve this template’s use for Blockly users, but that needs to be in addition to, not in place of passing the Thing Object.

The Blockly rule you’ve posted is a good example of the sort of thing that can be done in response to being called though and with only minor modifications it should be able to work with the original, though you might need to use the script block to access the properties of the Thing Objects.

Note, when posting Blockly, post both a screen shot and the contents of the “code” tab in code fences.

code goes here

This rule template will be completely rewritten for OH 4 by porting it to ECMAScript 2021, taking advantage of the JS Scripting helper library and my own helper library, and possibly changing the triggers so it can run based on events rather than a polling period.

Generally speaking, if your goal is to suggest improvements to the original it’s proper to post here. If your goal is to provide an alternative to the original, it’s probably best to start a new thread. However, pay mind to the marketplace rules. The moderators want to avoid a proliferation of lots of alternative implementations of the same thing, confusing end users and making it difficult to keep track of. So it would probably be posted to the tutorials and solutions category.

I like the example for a script to be called by the original script in your blockly and it appears your aim is to improve the original script so I think it’s appropriate here.


I tried but somehow could not access any properties in the called rule. That’s why I thought the object is not passed as a reference.

That’s basically what my called rule does, not in the condition but in the action.

I tried that, but .getLabel() returned nothing. But I’ll try again after the transfer to ecma 2021.

That’s what I did. You have the code collapsed under “…evaluation rule (blockly)…” above the screenshot (in code fences of course;).
Anyway, thanks for your reply and helpful hints.

In Blockly or a text based script action?

Post the code you tired so I can look into this further.