zigbee2mqtt
Version:
Zigbee to MQTT bridge using Zigbee-herdsman
328 lines • 33.5 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_assert_1 = __importDefault(require("node:assert"));
const bind_decorator_1 = __importDefault(require("bind-decorator"));
const debounce_1 = __importDefault(require("debounce"));
const logger_1 = __importDefault(require("../util/logger"));
const settings = __importStar(require("../util/settings"));
const utils_1 = __importDefault(require("../util/utils"));
const extension_1 = __importDefault(require("./extension"));
const RETRIEVE_ON_RECONNECT = [
{ keys: ["state"] },
{ keys: ["brightness"], condition: (state) => state.state === "ON" },
{ keys: ["color", "color_temp"], condition: (state) => state.state === "ON" },
];
class Availability extends extension_1.default {
/** Mapped by IEEE address */
timers = new Map();
/** Mapped by IEEE address or Group ID */
lastPublishedAvailabilities = new Map();
/** Mapped by IEEE address */
pingBackoffs = new Map();
/** IEEE addresses, waiting for last seen changes to take them out of "availability sleep" */
backoffPausedDevices = new Set();
/** Mapped by IEEE address */
retrieveStateDebouncers = new Map();
pingQueue = [];
pingQueueExecuting = false;
stopped = false;
getTimeout(device) {
if (typeof device.options.availability === "object" && device.options.availability?.timeout != null) {
return utils_1.default.minutes(device.options.availability.timeout);
}
return utils_1.default.minutes(this.isActiveDevice(device) ? settings.get().availability.active.timeout : settings.get().availability.passive.timeout);
}
getMaxJitter(device) {
if (typeof device.options.availability === "object" && device.options.availability?.max_jitter != null) {
return device.options.availability.max_jitter;
}
return settings.get().availability.active.max_jitter;
}
getBackoff(device) {
if (typeof device.options.availability === "object" && device.options.availability?.backoff != null) {
return device.options.availability.backoff;
}
return settings.get().availability.active.backoff;
}
getPauseOnBackoffGt(device) {
if (typeof device.options.availability === "object" && device.options.availability?.pause_on_backoff_gt != null) {
return device.options.availability.pause_on_backoff_gt;
}
return settings.get().availability.active.pause_on_backoff_gt;
}
isActiveDevice(device) {
return ((device.zh.type === "Router" && device.zh.powerSource !== "Battery") ||
(device.zh.powerSource !== undefined && device.zh.powerSource !== "Unknown" && device.zh.powerSource !== "Battery"));
}
isAvailable(entity) {
if (entity.isDevice()) {
const lastSeen = entity.zh.lastSeen ?? /* v8 ignore next */ 0;
return Date.now() - lastSeen < this.getTimeout(entity) + this.getMaxJitter(entity);
}
for (const memberDevice of entity.membersDevices()) {
if (this.lastPublishedAvailabilities.get(memberDevice.ieeeAddr) === true) {
return true;
}
}
return false;
}
resetTimer(device, resetBackoff = false) {
clearTimeout(this.timers.get(device.ieeeAddr));
this.removeFromPingQueue(device);
// If the timer triggers, the device is not available anymore otherwise resetTimer already has been called
if (this.isActiveDevice(device)) {
const backoffEnabled = this.getBackoff(device);
const jitter = Math.random() * this.getMaxJitter(device);
let backoff = 1;
if (resetBackoff) {
// always cleanup even if backoff disabled (ensures proper state if changed at runtime)
this.backoffPausedDevices.delete(device.ieeeAddr);
this.pingBackoffs.delete(device.ieeeAddr);
}
else if (backoffEnabled) {
backoff = this.pingBackoffs.get(device.ieeeAddr) ?? 1;
}
// never paused if was reset (just deleted) or backoff disabled, might as well skip the Set lookup
if (!backoffEnabled || resetBackoff || !this.backoffPausedDevices.has(device.ieeeAddr)) {
// 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_1.default.seconds(1) + jitter) * backoff));
}
}
else {
this.timers.set(device.ieeeAddr, setTimeout(this.publishAvailability.bind(this, device, true), this.getTimeout(device) + utils_1.default.seconds(1)));
}
}
clearTimer(ieeeAddress) {
clearTimeout(this.timers.get(ieeeAddress));
this.timers.delete(ieeeAddress);
}
addToPingQueue(device) {
this.pingQueue.push(device);
this.pingQueueExecuteNext().catch(utils_1.default.noop);
}
removeFromPingQueue(device) {
const index = this.pingQueue.findIndex((d) => d.ieeeAddr === device.ieeeAddr);
if (index !== -1) {
this.pingQueue.splice(index, 1);
}
}
async pingQueueExecuteNext() {
if (this.pingQueue.length === 0 || this.pingQueueExecuting) {
return;
}
this.pingQueueExecuting = true;
const device = this.pingQueue[0];
let pingSuccess = false;
const available = this.lastPublishedAvailabilities.get(device.ieeeAddr) || this.isAvailable(device);
const attempts = available ? 2 : 1;
for (let i = 1; i <= attempts; i++) {
try {
// Enable recovery if device is marked as available and first ping fails.
await device.zh.ping(!available || i !== 2);
pingSuccess = true;
logger_1.default.debug(`Successfully pinged '${device.name}' (attempt ${i}/${attempts})`);
break;
}
catch (error) {
logger_1.default.warning(`Failed to ping '${device.name}' (attempt ${i}/${attempts}, ${error.message})`);
// Try again in 3 seconds.
if (i !== attempts) {
await utils_1.default.sleep(3);
}
}
}
if (this.stopped) {
// Exit here to avoid triggering any follow-up activity (e.g., re-queuing another ping attempt).
return;
}
if (!pingSuccess && this.getBackoff(device)) {
const currentBackoff = this.pingBackoffs.get(device.ieeeAddr) ?? 1;
// setting is "greater than" but since we already did the ping, we use ">=" for comparison below (pause next)
const pauseOnBackoff = this.getPauseOnBackoffGt(device);
if (pauseOnBackoff > 0 && currentBackoff >= pauseOnBackoff) {
this.backoffPausedDevices.add(device.ieeeAddr);
}
else {
// results in backoffs: *1.5, *3, *6, *12... (with default timeout: 10, 15, 30, 60, 120)
this.pingBackoffs.set(device.ieeeAddr, currentBackoff * (available ? 1.5 : 2));
}
}
await this.publishAvailability(device, !pingSuccess);
this.resetTimer(device, pingSuccess);
this.removeFromPingQueue(device);
// Sleep 2 seconds before executing next ping
await utils_1.default.sleep(2);
this.pingQueueExecuting = false;
await this.pingQueueExecuteNext();
}
async start() {
if (this.stopped) {
throw new Error("This extension cannot be restarted.");
}
this.eventBus.onEntityRenamed(this, async (data) => {
if (utils_1.default.isAvailabilityEnabledForEntity(data.entity, settings.get())) {
await this.mqtt.publish(`${data.from}/availability`, "", { clientOptions: { retain: true, qos: 1 } });
await this.publishAvailability(data.entity, false, true);
}
});
this.eventBus.onEntityRemoved(this, (data) => data.type === "device" && this.clearTimer(data.id));
this.eventBus.onDeviceLeave(this, (data) => this.clearTimer(data.ieeeAddr));
this.eventBus.onDeviceAnnounce(this, (data) => this.retrieveState(data.device));
this.eventBus.onLastSeenChanged(this, this.onLastSeenChanged);
this.eventBus.onGroupMembersChanged(this, (data) => this.publishAvailability(data.group, false));
// Publish initial availability
await this.publishAvailabilityForAllEntities();
// Start availability for the devices
for (const device of this.zigbee.devicesIterator(utils_1.default.deviceNotCoordinator)) {
if (utils_1.default.isAvailabilityEnabledForEntity(device, settings.get())) {
this.resetTimer(device);
// If an active device is unavailable on start, add it to the pingqueue immediately.
if (this.isActiveDevice(device) && !this.isAvailable(device)) {
this.addToPingQueue(device);
}
}
}
}
async publishAvailabilityForAllEntities() {
for (const entity of this.zigbee.devicesAndGroupsIterator(utils_1.default.deviceNotCoordinator)) {
if (utils_1.default.isAvailabilityEnabledForEntity(entity, settings.get())) {
await this.publishAvailability(entity, true, false, true);
}
}
}
async publishAvailability(entity, logLastSeen, forcePublish = false, skipGroups = false) {
if (logLastSeen && entity.isDevice()) {
const ago = Date.now() - (entity.zh.lastSeen ?? /* v8 ignore next */ 0);
if (this.isActiveDevice(entity)) {
logger_1.default.debug(`Active device '${entity.name}' was last seen '${(ago / utils_1.default.minutes(1)).toFixed(2)}' minutes ago.`);
}
else {
logger_1.default.debug(`Passive device '${entity.name}' was last seen '${(ago / utils_1.default.hours(1)).toFixed(2)}' hours ago.`);
}
}
const available = this.isAvailable(entity);
if (!forcePublish && this.lastPublishedAvailabilities.get(entity.ID) === available) {
return;
}
if (entity.isDevice() && available && this.lastPublishedAvailabilities.get(entity.ieeeAddr) === false) {
logger_1.default.debug(`Device '${entity.name}' reconnected`);
this.retrieveState(entity);
}
const topic = `${entity.name}/availability`;
const payload = { state: available ? "online" : "offline" };
this.lastPublishedAvailabilities.set(entity.ID, available);
await this.mqtt.publish(topic, JSON.stringify(payload), { clientOptions: { retain: true, qos: 1 } });
if (!skipGroups && entity.isDevice()) {
for (const group of this.zigbee.groupsIterator()) {
if (group.hasMember(entity) && utils_1.default.isAvailabilityEnabledForEntity(group, settings.get())) {
await this.publishAvailability(group, false, forcePublish);
}
}
}
}
async onLastSeenChanged(data) {
if (utils_1.default.isAvailabilityEnabledForEntity(data.device, settings.get())) {
// Remove from ping queue, not necessary anymore since we know the device is online.
this.removeFromPingQueue(data.device);
this.resetTimer(data.device, true);
await this.publishAvailability(data.device, false);
}
}
async stop() {
this.stopped = true;
this.pingQueue = [];
for (const [, t] of this.timers) {
clearTimeout(t);
}
await super.stop();
}
retrieveState(device) {
/**
* Retrieve state of a device in a debounced manner, this function is called on a 'deviceAnnounce' which a
* device can send multiple times after each other.
*/
if (device.definition && device.interviewed && !this.retrieveStateDebouncers.get(device.ieeeAddr)) {
this.retrieveStateDebouncers.set(device.ieeeAddr, (0, debounce_1.default)(async () => {
logger_1.default.debug(`Retrieving state of '${device.name}' after reconnect`);
// Color and color temperature converters do both, only needs to be called once.
for (const item of RETRIEVE_ON_RECONNECT) {
if (item.condition && this.state.get(device) && !item.condition(this.state.get(device))) {
continue;
}
// biome-ignore lint/style/noNonNullAssertion: doesn't change once valid
const converter = device.definition.toZigbee.find((c) => !c.key || c.key.find((k) => item.keys.includes(k)));
const options = device.options;
const state = this.state.get(device);
const meta = {
message: this.state.get(device),
// biome-ignore lint/style/noNonNullAssertion: doesn't change once valid
mapped: device.definition,
endpoint_name: undefined,
options,
state,
device: device.zh,
/* v8 ignore next */
publish: (payload) => this.publishEntityState(device, payload),
};
try {
const endpoint = device.endpoint();
(0, node_assert_1.default)(endpoint);
await converter?.convertGet?.(endpoint, item.keys[0], meta);
}
catch (error) {
logger_1.default.error(`Failed to read state of '${device.name}' after reconnect (${error.message})`);
}
await utils_1.default.sleep(500);
}
}, utils_1.default.seconds(2)));
}
this.retrieveStateDebouncers.get(device.ieeeAddr)?.();
}
}
exports.default = Availability;
__decorate([
bind_decorator_1.default
], Availability.prototype, "onLastSeenChanged", null);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXZhaWxhYmlsaXR5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vbGliL2V4dGVuc2lvbi9hdmFpbGFiaWxpdHkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFJQSw4REFBaUM7QUFFakMsb0VBQWtDO0FBQ2xDLHdEQUFnQztBQUVoQyw0REFBb0M7QUFDcEMsMkRBQTZDO0FBQzdDLDBEQUFrQztBQUNsQyw0REFBb0M7QUFFcEMsTUFBTSxxQkFBcUIsR0FBMEU7SUFDakcsRUFBQyxJQUFJLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBQztJQUNqQixFQUFDLElBQUksRUFBRSxDQUFDLFlBQVksQ0FBQyxFQUFFLFNBQVMsRUFBRSxDQUFDLEtBQWUsRUFBVyxFQUFFLENBQUMsS0FBSyxDQUFDLEtBQUssS0FBSyxJQUFJLEVBQUM7SUFDckYsRUFBQyxJQUFJLEVBQUUsQ0FBQyxPQUFPLEVBQUUsWUFBWSxDQUFDLEVBQUUsU0FBUyxFQUFFLENBQUMsS0FBZSxFQUFXLEVBQUUsQ0FBQyxLQUFLLENBQUMsS0FBSyxLQUFLLElBQUksRUFBQztDQUNqRyxDQUFDO0FBRUYsTUFBcUIsWUFBYSxTQUFRLG1CQUFTO0lBQy9DLDZCQUE2QjtJQUNaLE1BQU0sR0FBRyxJQUFJLEdBQUcsRUFBMEIsQ0FBQztJQUM1RCx5Q0FBeUM7SUFDeEIsMkJBQTJCLEdBQUcsSUFBSSxHQUFHLEVBQTRCLENBQUM7SUFDbkYsNkJBQTZCO0lBQ1osWUFBWSxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO0lBQzFELDZGQUE2RjtJQUM1RSxvQkFBb0IsR0FBRyxJQUFJLEdBQUcsRUFBVSxDQUFDO0lBQzFELDZCQUE2QjtJQUNaLHVCQUF1QixHQUFHLElBQUksR0FBRyxFQUFzQixDQUFDO0lBQ2pFLFNBQVMsR0FBYSxFQUFFLENBQUM7SUFDekIsa0JBQWtCLEdBQUcsS0FBSyxDQUFDO0lBQzNCLE9B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