Skip to content

Backoff Wait time and Refactor for Router Watchdog #153

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 74 additions & 32 deletions router-watchdog.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,58 @@ let CONFIG = {
"https://global.gcping.com/ping",
"https://us-central1-5tkroniexa-uc.a.run.app/ping",
],
//number of failures that trigger the reset
numberOfFails: 3,
//time in seconds after which the http request is considered failed
httpTimeout: 10,
//time in seconds for the relay to be off
toggleTime: 30,
//time in seconds to retry a "ping"
pingTime: 60,
numberOfFails: 3, // failures before reset
httpTimeout: 10, // seconds before request timeout
toggleTime: 30, // seconds to keep relay off
pingTime: 60, // seconds between connectivity checks
maxBackoffTime: 14400, // max backoff (4 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(
Expand All @@ -48,43 +87,46 @@ function pingEndpoints() {
if (error_code === -114 || error_code === -104) {
print("Failed to fetch ", CONFIG.endpoints[endpointIdx]);
failCounter++;
print("Rotating through endpoints");
print("Number of fails:", failCounter);
print("Checking next endpoint...");
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) {
print("Too many fails, resetting...");
failCounter = 0;
Timer.clear(pingTimer);
//set the output with toggling back
Shelly.call(
"Switch.Set",
{ id: 0, on: false, toggle_after: CONFIG.toggleTime },
function () {}
);
resetRouter();
return;
}
}
);
}

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

// Handle router restart completion
Shelly.addStatusHandler(function (status) {
//is the component a switch
if(status.name !== "switch") return;
//is it the one with id 0
if(status.id !== 0) return;
//does it have a delta.source property
if(typeof status.delta.source === "undefined") return;
//is the source a timer
if(status.delta.source !== "timer") return;
//is it turned on
if(status.delta.output !== true) return;
//start the loop to ping the endpoints again
pingTimer = Timer.set(CONFIG.pingTime * 1000, true, pingEndpoints);
// 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();
}
});