Last active
May 18, 2025 12:03
-
-
Save Eetezadi/c539d15fa4648856c2732a71ef0a4e25 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /// <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