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).
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().
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
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!
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.
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.
Now I want to use that with a watchdog timer.
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…
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 ![]()
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
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
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).
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.
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!