Shelly pro 2 internal watchdog script

Logs from shelly script engine says

#when openHAB server is offline
shelly_http_client.:606 0x3ffe3648: Finished; bytes 0, code 0, redir 0/3, auth 0, status DEADLINE_EXCEEDED: Timed out

06:54:02

Success reset failcounter 0

06:54:02

shelly_ejs_rpc.cpp:41 Shelly.call http.get {"url":"http://ip.adress:port","timeout":10}


06:54:51

shelly_http_client.:306 0x3ffe3648: HTTP GET http://ip.adress:port

06:54:51
#when openHAB server is online
shelly_http_client.:606 0x3ffe3648: Finished; bytes 1477, code 200, redir 0/3, auth 0, status OK

So i found a work around changing “error_code === -114” to “error_code === -104” simply meaning request timed out, is that a proper solution or is it a bad solution.

My complete script for successfully verify if openHAB server is reachable


// This script tries to execute HTTP GET requests within a set time, against a set of endpoints
// After certain number of failures the script resets the shelly




let CONFIG = {
  endpoints: [
    "http://ip.adress:port",
      ],
  //number of failures that trigger the reset
  numberOfFails: 5,
  //time in seconds after which the http request is considered failed
  httpTimeout: 10,
  //time in seconds to retry a "ping"
  pingTime: 60,
};

let endpointIdx = 0;
let failCounter = 0;
let pingTimer = null;

function pingEndpoints() {
  Shelly.call(
    "http.get",
    { url: CONFIG.endpoints[endpointIdx], timeout: CONFIG.httpTimeout },
    function (response, error_code, error_message) {
      //http timeout, magic number, not yet documented
      if (error_code === -104) {
        print("Failed to fetch ", CONFIG.endpoints[endpointIdx]);
        failCounter++;
        print("Rotating through endpoints");
        endpointIdx++;
        endpointIdx = endpointIdx % CONFIG.endpoints.length;
        print("Error openHAB server not reachable number of errors:", failCounter);        
        } else {
        failCounter = 0;
        print("Success openHAB server online setting failconter to:", failCounter);
      }

      if (failCounter >= CONFIG.numberOfFails) {
        print("Too many fails, resetting...");
        failCounter = 0;
        Timer.clear(pingTimer);
        Shelly.call("Shelly.Reboot");
      }
    }
  );
}

print("Start watchdog timer");
pingTimer = Timer.set(CONFIG.pingTime * 1000, true, pingEndpoints);

All this seems to work but how do one handle to check status on input for a relay in detached mode and set output to the same state or set relay mode to attached and sync input and output when openHAB server is not reachable?

I did try to implement the snippet in web interface for that but I was not sure how to make it run smooth.

Here’s the solution I came up with in case soemone need to make their own and need isnpiration. Most certainly one could make this much better but it works. I also made a HTTP binding thing and a switch channel to verify that the watchdog is running.

 let input_state = "";
 let output_state ="";
 let input_obj = "";
 let output_obj ="";
function timerCode() {
  
  // Set your input to Switch and in Detached mode for this code to function correctly.
 input_state = Shelly.getComponentStatus("input:0");
 output_state = Shelly.getComponentStatus("switch:0");
 input_obj = (JSON.parse(input_state.state));
 output_obj = (JSON.parse(output_state.output));
 if (input_obj != output_obj && failCounter >= 4){
   print("openHAB server not reachable and Input & Output not in sync input:", input_obj,", Output:", output_obj);
   print("Toggle switc to get input and output in sync");
   Shelly.call("Switch.toggle", {'id': 0});
 }
 if (input_obj != output_obj && failCounter === 0){
   print("openHAB server reachable and Input & Output not in sync input:", input_obj,", Output:", output_obj);
 }
 if (input_obj === output_obj && failCounter === 0){
   print("openHAB server reachable Input & Output is in sync input:", input_obj,", Output:", output_obj);
 }
 //print(obj);
 //print(obj.id);
};

Timer.set(
  /* number of miliseconds */ 5000,
  /* repeat? */ true,
  /* callback */ timerCode
);



// This script tries to execute HTTP GET requests within a set time, against a set of endpoints
// After certain number of failures the script resets the shelly




let CONFIG = {
  endpoints: [
    "http://openHAB.server.ipadress:port",
      ],
  //number of failures that trigger the reset
  numberOfFails: 5,
  //time in seconds after which the http request is considered failed
  httpTimeout: 10,
  //time in seconds to retry a "ping"
  pingTime: 15,
};

let endpointIdx = 0;
let failCounter = 0;
let pingTimer = null;

function pingEndpoints() {
  Shelly.call(
    "http.get",
    { url: CONFIG.endpoints[endpointIdx], timeout: CONFIG.httpTimeout },
    function (response, error_code, error_message) {
      //http timeout, magic number, not yet documented
      if (error_code === -104) {
        print("Failed to fetch ", CONFIG.endpoints[endpointIdx]);
        failCounter++;
        print("Rotating through endpoints");
        endpointIdx++;
        endpointIdx = endpointIdx % CONFIG.endpoints.length;
        print("Error openHAB server not reachable number of errors:", failCounter, output_obj);
                
        } else {
        failCounter = 0;
        print("Success openHAB server online setting failcounter to:", failCounter, output_obj);
      }

      if (failCounter >= CONFIG.numberOfFails) {
        print("Too many fails, resetting...");
        failCounter = 0;
        Timer.clear(pingTimer);
        Shelly.call("Shelly.Reboot");
      }
    }
  );
}

print("Start watchdog timer");
pingTimer = Timer.set(CONFIG.pingTime * 1000, true, pingEndpoints);



great. I think I’m going to adapt this so that night mode (detached) is set to momentary if there is no openhab-server.

Could you replace the code-fences with the correct (straight) ones so that code is readable. Just use the menu if your keyboard uses the wrong ones

Sorry my bad, now the code fence is updated. As I said I also made a switch item from http bindning to verify the state of the script so i can be noticed if sceipt goes down even if the device is online. I haven’t foun a way to change mode from detached yto attached so i just mad up the toggle code which schould work similar.

That’s easy: To switch to momentary, send a http-get to the ip of your shelly:

http://192.168.1.100/settings/relay/0?btn_type=momentary

To switch to detached:

http://192.168.1.100/settings/relay/0?btn_type=detached

Could that be done also via internal script upon detectionnof lost connection with openHAB server?

I think hence the shelly binding has difficultys to reconnect to a device thats offline for a while maybe better solution is to announcr heartbrats via mqtt ot so. This weekend I unplugged my network for a while and reconnected but shelly binding never reconnected even thoug ny scriot could reach openHAB server.

That was the idea. I think it should work but haven’t tried yet.

Following Rich’s advice to design “escalators not elevators” I liked the idea that a shelly relay resets itself to non-smart-home if it cannot connect to the openhab server.

I changed the above script to switch the mode to “momentary” if openhab-server is unavailable and removed everything else.
(This is for Gen2 like Shelly Plus etc; for Gen1 see the command above).

edit: changed script so that the Shelly switches back to “detached” if openhab-server is online again

let CONFIG = {
  endpoints: [ "http://openhab_server:8080", ],
  numberOfFails: 5, httpTimeout: 10, pingTime: 120,
};
let endpointIdx = 0;
let failCounter = 0;
let pingTimer = null;

function pingEndpoints() {
  Shelly.call("http.get", { url: CONFIG.endpoints[endpointIdx], timeout: CONFIG.httpTimeout },
    function (response, error_code, error_message) {
      if (error_code === -104) {
        print("Failed to fetch ", CONFIG.endpoints[endpointIdx]);
        failCounter++;
        print("Rotating through endpoints");
        endpointIdx++;
        endpointIdx = endpointIdx % CONFIG.endpoints.length;
        print("Error openHAB server not reachable number of errors:", failCounter);
                
        } else {        
        print("Success openHAB server online setting failcounter to:", failCounter);
        failCounter = 0;
      }

      if (failCounter >= CONFIG.numberOfFails) {
        print("Too many fails, resetting...");
        failCounter = 0;
        //Timer.clear(pingTimer);
        Shelly.call( "http.get", { url: "http://127.0.0.1/rpc/Switch.SetConfig?id=0&config={%22in_mode%22:%22momentary%22}", timeout: CONFIG.httpTimeout })
      }
    }
  );
}

print("Start watchdog timer");
pingTimer = Timer.set(CONFIG.pingTime * 1000, true, pingEndpoints);

Not really tested yet, so please report in case of errors or problems.

Hello

Is this a valid state, I see this in the instructions, however the script doesn’t work properly for me

oh, my mistake. I meant “detached”. It’s corrected, please try.

It seems to work really fine, but I rhink it seems like the pinging stops once failcounter exceeds numberOfFails. I would like it to continue pinging just becasue. I have the watchdog in shelly device that sets mode to follow and another watchdog in openHAB that should reset it when connection is reastablished and that concept it because sometimes if unit becomes offline whiel updating switches or so in openHAB and it doesn’t heal it self when unit is back online, so shelly device ends up having connection with openHAB but not the other way around.

I removed the clearing of the timer. Now it should run forever.

Now it runs like you said forever, I think I just want to use a in_mode status as condition to setting new_inmode to follow only if old in_mode is follow. For now I just set the if(failcounter part as this, just to make use of testing and troubleshooting it’s seems easier if in_mode isn’t sent if not requierd.

if (failCounter === CONFIG.numberOfFails) {

But when i test code below I just get undefined, but when I use same code in webbrowser via network i get a json with in_mode defined.


print(Shelly.call( "http.get", { url: "http://127.0.0.1/rpc/switch.getconfig?id=0",timeout:10}))

I think getting the in_mode is not available via rpc. But you can use getComponentConfig e.g.

print ("in_mode: " + Shelly.getComponentConfig("switch:0").in_mode);

I figuerd it out, this script seems to work, I have note yet simulated a real network failure, but if I set a bad ip adress it does what it’s supposed to. My openHAB server is setting mode to detached when connection is re established just to make sure connection is ok. For some reason my devices can be stuck as unreachable for openHAB binding if device is offline for more than xtime, lets say that I cut power to a switch or to the outdoor baths that this unit controls openHAB sometimes fail to get this things back online.

I also implemented a successCounter just in case, thanks for all help and I hope this can help someone else.

let CONFIG = {
  endpoints: [ "http://openHABserver:8080", ],
  numberOfFails: 4, httpTimeout: 10, pingTime: 15,
};
let endpointIdx = 0;
let failCounter = 0;
let successCounter = 0;
let pingTimer = null;
let op_mode = "";
let old_op_mode ="";

function pingEndpoints() {
  Shelly.call("http.get", { url: CONFIG.endpoints[endpointIdx], timeout: CONFIG.httpTimeout },
     
      function (response, error_code, error_message) {
      old_op_mode = op_mode;
      op_mode = Shelly.getComponentConfig("switch:0").in_mode;
      if (error_code === -104) {
        print("Failed to fetch ", CONFIG.endpoints[endpointIdx]);
        failCounter++;
        successCounter=0;
        print("Rotating through endpoints");
        endpointIdx++;
        endpointIdx = endpointIdx % CONFIG.endpoints.length;
        print("Error openHAB server not reachable number of errors:", failCounter);
        print ("operating_mode: " + op_mode);
                
        } else {
        failCounter = 0;
        successCounter++;        
        print("Success openHAB server reachable setting failcounter to:", failCounter);
        print("Gamla operating mode: " +old_op_mode+ " Ny operating mode: " +op_mode);
        print("successCounter: "+successCounter)
        }
      
      if (failCounter >= CONFIG.numberOfFails && op_mode ==="detached"){
        //if (failCounter >= CONFIG.numberOfFails) {
        print("Too many fails, change mode to follow!");
        successCounter = 0;
        Shelly.call( "http.get", { url: "http://127.0.0.1/rpc/Switch.SetConfig?id=0&config={%22in_mode%22:%22follow%22}", timeout: CONFIG.httpTimeout })
      }
      
    }
  );
}

print("Start watchdog timer");
pingTimer = Timer.set(CONFIG.pingTime * 1000, true, pingEndpoints);

That’s not going to work like you wanted: e.g. you never set back to “follow”. Frankly with all the code commented out that doesn’t look like a solution to recommend

I removed the out commented code in solution. In my implementation standard in_mode is detached. When connection to openHAB server is lost for 4 retrys in my case one minute, watchdog change in_mode to follow. In my case the watchdog shouldn’t set mode to detached again that should come from the rule in openHAB which it does. If in_mode is follow and connections exist from openHAB to shelly device it should set it in detached mode again.

I haven’t tested a real outage yet but I simulated with bad ip adress setting in the script and upon failure it does what I needed it to do and if openHAB has connection it set in_mode to detached again.

1 Like

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.