Skip to content

Instantly share code, notes, and snippets.

@Eetezadi
Last active May 18, 2025 12:03
Show Gist options
  • Select an option

  • Save Eetezadi/c539d15fa4648856c2732a71ef0a4e25 to your computer and use it in GitHub Desktop.

Select an option

Save Eetezadi/c539d15fa4648856c2732a71ef0a4e25 to your computer and use it in GitHub Desktop.
/// <reference path="../../shelly-script.d.ts" />
// <reference path="../../shelly-script.d.ts" />
// Shelly Script: Router Watchdog
// Monitors internet connectivity and resets router by cycling power when needed
let CONFIG = {
endpoints: [
"https://global.gcping.com/ping",
"https://www.switch.ch",
],
numberOfFails: 3, // failures before reset
httpTimeout: 10, // seconds before request timeout
toggleTime: 30, // seconds to keep relay off
pingTime: 180, // seconds between connectivity checks
maxBackoffTime: 21600, // max backoff (6 hours)
initialBackoffTime: 300, // initial backoff (5 minutes)
backoffMultiplier: 2, // backoff multiplier
};
let endpointIdx = 0;
let failCounter = 0;
let pingTimer = null;
let resetCounter = 0;
let currentBackoffTime = 0;
/**
* Reset router with exponential backoff for persistent failures
*/
function resetRouter() {
print("Too many fails, resetting...");
failCounter = 0;
resetCounter++;
Timer.clear(pingTimer);
// Apply backoff after first reset
if (resetCounter > 1) {
if (currentBackoffTime === 0) {
currentBackoffTime = CONFIG.initialBackoffTime;
} else {
// Increase backoff time exponentially with cap
currentBackoffTime = currentBackoffTime * CONFIG.backoffMultiplier;
if (currentBackoffTime > CONFIG.maxBackoffTime) {
currentBackoffTime = CONFIG.maxBackoffTime;
}
}
print("Backoff active: " + currentBackoffTime + " seconds before next check");
}
// Turn off switch and toggle back after delay
Shelly.call(
"Switch.Set",
{ id: 0, on: false, toggle_after: CONFIG.toggleTime },
function () {}
);
}
/**
* Start timer for connectivity checks
*/
function startPingTimer() {
pingTimer = Timer.set(CONFIG.pingTime * 1000, true, pingEndpoints);
}
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 === -114 || error_code === -104) {
print("Failed to fetch ", CONFIG.endpoints[endpointIdx]);
failCounter++;
print("Number of fails:", failCounter);
print("Checking next endpoint in " + CONFIG.pingTime + " seconds");
endpointIdx++;
endpointIdx = endpointIdx % CONFIG.endpoints.length;
} else {
print("We are online :)");
failCounter = 0;
// Reset backoff on successful connection
resetCounter = 0;
currentBackoffTime = 0;
}
if (failCounter >= CONFIG.numberOfFails) {
resetRouter();
return;
}
}
);
}
print("Starting watchdog timer...");
startPingTimer();
// Handle router restart completion
Shelly.addStatusHandler(function (status) {
// Only process switch events from timer for our switch
if (status.name !== "switch" ||
status.id !== 0 ||
typeof status.delta.source === "undefined" ||
status.delta.source !== "timer" ||
status.delta.output !== true) return;
// Apply backoff if needed or restart immediately
if (currentBackoffTime > 0) {
print("Applying backoff: " + currentBackoffTime + " seconds");
Timer.set(currentBackoffTime * 1000, false, function() {
print("Backoff complete, resuming checks");
startPingTimer();
});
} else {
startPingTimer();
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment