mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-08-28 05:24:29 +00:00
fix: Clamp availability ping delay to the maximum supported timeout (#32937)
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,12 @@ import * as settings from "../util/settings";
|
||||
import utils from "../util/utils";
|
||||
import Extension from "./extension";
|
||||
|
||||
/**
|
||||
* Upper bound for a `setTimeout` delay. Node.js stores the delay as a 32-bit signed integer; anything above this
|
||||
* is coerced to `1`, which would turn an ever-growing backoff into a tight loop instead of an ever-longer wait.
|
||||
*/
|
||||
const MAX_TIMEOUT = 2147483647;
|
||||
|
||||
const RETRIEVE_ON_RECONNECT: readonly {keys: string[]; condition?: (state: KeyValue) => boolean}[] = [
|
||||
{keys: ["state"]},
|
||||
{keys: ["brightness"], condition: (state: KeyValue): boolean => state.state === "ON"},
|
||||
@@ -108,7 +114,10 @@ export default class Availability extends Extension {
|
||||
// If device did not check in, ping it, if that fails it will be marked as offline
|
||||
this.timers.set(
|
||||
device.ieeeAddr,
|
||||
setTimeout(this.addToPingQueue.bind(this, device), (this.getTimeout(device) + utils.seconds(1) + jitter) * backoff),
|
||||
setTimeout(
|
||||
this.addToPingQueue.bind(this, device),
|
||||
Math.min((this.getTimeout(device) + utils.seconds(1) + jitter) * backoff, MAX_TIMEOUT),
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -633,6 +633,19 @@ describe("Extension: Availability", () => {
|
||||
expect(devices.QBKG03LM.ping).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
it("clamps the ping delay to the maximum supported timeout", async () => {
|
||||
// `setTimeout` takes a 32-bit signed integer and coerces anything above it to `1`. A delay can exceed
|
||||
// that either directly, through a long `timeout`, or gradually, once `backoff` has multiplied a normal
|
||||
// one over successive failures. Unclamped, that turns an ever-longer wait into a tight ping loop.
|
||||
settings.set(["devices", devices.bulb_color.ieeeAddr, "availability"], {timeout: 40000, max_jitter: 0}); // ~27.8 days
|
||||
await resetExtension();
|
||||
|
||||
// unclamped, the delay collapses to 1ms, so pings would already be looping by now
|
||||
await setTimeAndAdvanceTimers(utils.seconds(1));
|
||||
|
||||
expect(devices.bulb_color.ping).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("allows to disable backoff", async () => {
|
||||
settings.set(["availability", "active", "max_jitter"], 0); // easier testing
|
||||
settings.set(["availability", "active", "backoff"], false);
|
||||
|
||||
Reference in New Issue
Block a user