Disable / enable a thing from DSL or?

I tried to disable a thing or a compleet binding but didn’t succeed. A option to call with executeCommandLine to the karaf console seems impossible because I run OH in a docker. Any ideas?

Docker does not prevent you from using executeCommandLine. The only warning is that stuff that might otherwise be avaialble to the command line like python doesn’t exist inside the container so it’s often not useful. But most standard POSIX stuff is still there.

Unfortunately ssh isn’t in the container but you don’t need ssh to get to the console.

val results = executeCommandLine(Duration.ofSeconds(1), "runtime/bin/client", "-p", "<your password>", "openhab:things disable <uid of thing>")

Note: this will work for any OH installation but the path to the client script will be different.

Though this might be a good time to explore some of the other options for rules. In JS it’s as simple as:

things.getThing("<uid of thing>").setEnabled(false);

jRuby has as simple of a one liner.

Besides being simpler and more straight forward, you don’t need to hard code in a secret like a bearer token (for the REST API) or the karaf console password (for executeCommandLine).

1 Like

This approach works for me in Docker/DSL:

rule "Test"
when Item MyItem changed to OFF
then
     val headers = newHashMap("Authorization" -> "Bearer oh.DisableThingToken.VqSRXrIc....9QOjA",
                              "WWW-Authenticate" -> "Basic")
	 var rc = ""
     // "false" = disable Thing, "true" = enable Thing
	 rc = sendHttpPutRequest("http://<openHAB IP>:8080/rest/things/<MyThingId>/enable","text/plain",'false',headers,5000)
     if (rc.length() > 1000) {
	    logInfo("Test",transform("JSONPATH", "label" + " disabled"))
	 } else {
	    logInfo("Test","Error disableing MyThing, error message is " + transform("JSONPATH", "error.message", rc))
	 }
end

Thanks all. I implemented the JS way from Rich and works perfect. The timer is maybe not efficient but works.

var myTimer = actions.ScriptExecution.createTimer(time.ZonedDateTime.now().plusSeconds(5), function() {

I don’t see the full code but I can make some suggestions on improvments or at least options available to the timer.

If this is a managed rule you should store the timer in the cache. But this looks like it might be a “fire-and-forget” timer. If that’s the case, using a JS setTimeout might be a more concise way to schedule the timer.

And there’s a untility function called time.toZDT() in JS which lets you convert almost anything to a ZonedDateTime. It’s almost always more concise to use that over getting now() and then add-ing stuff to it.

So a few different versions of that line that creates the timer:

// Fire and forget setTimeout
setTimeout(() => {
    // you don't show your code so what ever is inside your timer
}, 5000);

// Fire and forget openHAB Timer, ISO8601 duration string
actions.ScriptExecution.createTimer(time.toZDT("PT5S"), () => {
    // code goes here
});

// Save the timeout to the cache, note it returns just a number, you need to use clearTimeout(<ID>) to cancel, there is no reschedule
cache.private.put("myTimer", setTimeout(() => {
    // code goes here
}, 5000));

// Save the timer to the cache so it can be cancelled or rescheduled, milliseconds
cache.private.put("myTimer", actions.ScriptExecution.createTimer(time.toZDT(5000), () => {
    // code goes here
}));

() => is the equivalent of function().

1 Like

This is the rule

rules.JSRule({
    name: "Herstart Thing",
    description: "Schakel de Thing uit en na 5 seconden weer in",
    triggers: [ triggers.TimeOfDayTrigger("03:03:00") ], // snachts om 3 over 3
    execute: function(event) {
        console.log("Onecta", "Thing wordt uitgeschakeld...");
        var thing = things.getThing("<uid of thing>");
        if (thing) {
            thing.setEnabled(false);
            var myTimer = actions.ScriptExecution.createTimer(time.ZonedDateTime.now().plusSeconds(5), function() {
              console.log("Thing wordt weer ingeschakeld...");
              thing.setEnabled(true);
            });
        } else {
            console.log("Thing niet gevonden!");
        }
    }
});

OK the most concise way to do it would probably the fire and forget timeout.

rules.JSRule({
    name: "Herstart Thing",
    description: "Schakel de Thing uit en na 5 seconden weer in",
    triggers: [ triggers.TimeOfDayTrigger("03:03:00") ], // snachts om 3 over 3
    execute: function(event) {
        console.log("Onecta", "Thing wordt uitgeschakeld...");
        var thing = things.getThing("<uid of thing>");
        if (thing) {
            thing.setEnabled(false);
            setTimeout( () => {
              console.log("Thing wordt weer ingeschakeld...");
              thing.setEnabled(true);
            }, 5000);
        } else {
            console.log("Thing niet gevonden!");
        }
    }
});

Note, you need to replace "<uid of thing>" with the actual UID of the THing you want to disable and enable.

JRuby (without any logging)

every(:day, at: "3:03") do
  things["uid"]&.then do |thing|
    thing.disable
    after(5.seconds) { thing.enable }
  end
end

With logging:

every(:day, at: "3:03") do
  thing = things["uid"]
  unless thing
    logger.info("Thing niet gevonden!")
    next
  end

  logger.info("Thing wordt uitgeschakeld...")
  thing.disable
  after(5.seconds) do
    logger.info("Thing wordt weer ingeschakeld..."
    thing.enable
  end
end

PS Manual logging seems unnecessary. You could just enable set the openhab.event.ThingStatusInfoChangedEvent log prefix to INFO

If you’d like to set the rule’s name and description:

every(:day, at: "3:30", name: "Herstart Thing", description: "Schakel de Thing uit en na 5 seconden weer in") do

You can break it up into multiple lines if you prefer

every(:day, at: "3:30", 
      name: "Herstart Thing", 
      description: "Schakel de Thing uit en na 5 seconden weer in") do
# The rest is the same

Or you can use the traditional non-terse syntax

rule "Herstart Thing" do
  description "Schakel de Thing uit en na 5 seconden weer in"
  every :day, at: "3:30"
  run do
    things["uid"]&.then do |thing|
      thing.disable
      after(5.seconds) { thing.enable }
    end
  end
end
1 Like

Thanks but I just started with JavaScripting and need to learn a lot.

I did of course. This is a much nicer solution. Thanks

There’s fewer examples for JRuby around, so this is mainly for the LLM bots and other people who might happen to stumble on this post in the future.

Hi @jimtng,

thanks for providing the simple JRuby example on how to disable and enable a thing. I got a rule in place that works as expected when triggered manually. Now I want to use that with a watchdog timer. I’m struggling to create that timer and cancel and reschedule it in case an item is still active.

My script looks like this:

        thing = things["shelly:shellyplusplug:5c63b11906"]
        thing.disable
        logger.info("timer: Disable PCPlug Thing")
        after(10.seconds) do
          thing.enable
        end
        logger.info("timer: reEnable PCPlug Thing")

The trigger is on “Item received an update”. So as long as there is an update coming, the timer shall be canceled and rescheduled. When there is no update coming, the timer shall expire and do the actions.

Thanks!

You were very close.
This is the crucial change to do

after(10.seconds, id: thing) do

I added id: thing. What that does is when an update is received before the previous timer expired (so another update in less than 10 seconds), the existing timer matching the same id, in this case the thing object is the ID, then the existing timer gets rescheduled.

No boilerplate code required to manually maintain your timers or checking it / manually rescheduling it like how you would need to do in other languages.

Another thing you want to change is to move the last log line into the timer, so the final code would look like:

        thing = things["shelly:shellyplusplug:5c63b11906"]

        thing.disable
        logger.info("timer: Disable PCPlug Thing")

        after(10.seconds, id: thing) do
          thing.enable
          logger.info("timer: reEnable PCPlug Thing")
        end

Lastly, if the “Item received an update” is actually linked to the item, you don’t need to look up the thing manually. You could simply get the item’s thing like this:

thing = event.item.thing

So full rule again with this change:

        thing = event.item.thing

        thing.disable
        logger.info("timer: Disable PCPlug Thing")

        after(10.seconds, id: thing) do
          thing.enable
          logger.info("timer: reEnable PCPlug Thing")
        end

Another tip, if you have labelled your Thing nicely, you could use thing.label e.g.:

        thing = event.item.thing

        thing.disable
        logger.info("timer: Disable #{thing.label}")

        after(10.seconds, id: thing) do
          thing.enable
          logger.info("timer: reEnable #{thing.label}")
        end

With these changes, the rule is now “generic”, so you can add more triggers to it and it would automatically apply to the corresponding Thing that belongs to the triggering item.

EDIT: If your trigger is not based on the item, then you would still need to do the thing lookup manually as before.

Can you elaborate what you mean by a watchdog timer?

Are you referring to a cron job?

Hi jimtng,

thanks a lot for your help and elaborations. That “id: thing” confused me but you explained it very well.

What I mean with a watchdog timer is a constantly running timer that gets regularly reset unless it does not and then some actions need to be taken. In my case I have an Item that gets frequently updated but sometimes get stuck. To get it back working I need to disable/enable a Thing. So this is my full rule now:

configuration: {}
triggers:
  - id: "2"
    configuration:
      itemName: WS90_WindDirection
    type: core.ItemStateUpdateTrigger
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/x-ruby
      script: |
        thing = things["shelly:shellyplusplug:5c63b11906"]
        after(3.minutes, id: thing) do
          thing.disable
          logger.info("timer: Disable PCPlug Thing")
          after(10.seconds) do
            thing.enable
            logger.info("timer: reEnable PCPlug Thing")
          end
        end
    type: script.ScriptAction

So far so good, need to wait for the Item to get stuck again…

Better yet, why did it often get stuck? We shouldn’t have to be doing this (disable/reenable things).

Certainly this is an ugly workaround for that behavior. Good news is it’s working - got stuck and recovered by the rule :smiling_face_with_sunglasses:

This is about Shelly BLE support. Sometimes the BLE device (WS90) stops communication. By disable/enable the Shelly device which acts as the BLE gateway (Shelly Plus Plug) it recovers. Have not been able to debug/root cause this issue - adding @markus7017 for awareness.

hello everyone,

I have a similar problem and I need a suggestion :slight_smile: Sometimes, because of different issues (like Netatmo not able to reach their servers, or other problems) some things go offline, and I need to manually put the thing in “disable” then “enable”, and everything’s working again correctly. I need to understand if there is a way to make it automatic (a sort of check of the status of my things, and if a thing is not online it triggers a script to disable and enable that thing).

My OH is openhabian 5.1.1, and usually I use files to create things.

thanks for any suggestion

Andrea

Certainly, just not using Rules DSL. Trigger the rule with what ever event occurs to let you know that the Things needs to be restarted. In Blocky there’s a Things block. In JS there’s things.getThing("id:of:thing").setEnabled(false). I didn’t know the other languages.

I recommend using a timer to re enable the Thing so as not to do it to fast and perhaps mess something up. One second should be sufficient.

1 Like

Hi Andrea,

the rule I posted does exactly that - it runs a timer that gets reset when the BLE Item WS90_WindDirection updates but when it get stuck, the timer expires and disables/re-enables the Gateway Thing shelly:shellyplusplug:5c63b11906. You can copy that code into a GUI Rule while choosing JRUBY for the script language.

Good luck!

1 Like