#!/usr/bin/env node /* Patch zigbee2mqtt bridge.js to tolerate BLZ adapter quirks. The Pine64 `latest-dev` image of zigbee2mqtt crashes on start when used with the BLZ (BL702) dongle because the Bridge extension calls `firstCoordinatorEndpoint().deviceIeeeAddress` and the BLZ coordinator never registers its endpoint — `firstCoordinatorEndpoint()` returns undefined. The crash happens inside `Bridge.start()` BEFORE the `onMQTTMessage` listener is registered, so permit_join (and every other bridge MQTT request) silently no-ops. This script applies two surgical patches: 1. Optional-chaining on the `deviceIeeeAddress` access so the missing endpoint doesn't throw. 2. Move `onMQTTMessage` registration to BEFORE the publishInfo/devices/groups/ definitions calls AND wrap each publish in try/catch — so Bridge.start() completes even if a single publish throws, and permit_join still works. Run inside the zigbee2mqtt container after image pull: docker exec zigbee2mqtt node /app/blz-bridge-patch.js Idempotent: re-running is safe (sed -i replaces whole matched block). */ const fs = require("fs"); const BRIDGE_PATH = process.argv[2] || "/app/dist/extension/bridge.js"; function patch(content) { let changed = false; // Patch 1: optional chain on firstCoordinatorEndpoint().deviceIeeeAddress const oldAccess = "ieee_address: this.zigbee.firstCoordinatorEndpoint().deviceIeeeAddress,"; const newAccess = "ieee_address: this.zigbee.firstCoordinatorEndpoint()?.deviceIeeeAddress,"; if (content.includes(oldAccess) && !content.includes(newAccess)) { content = content.replace(oldAccess, newAccess); changed = true; console.log("[patch 1] added optional chaining on deviceIeeeAddress"); } else if (content.includes(newAccess)) { console.log("[patch 1] already applied"); } else { console.warn("[patch 1] target line not found, skipping"); } // Patch 2: move onMQTTMessage registration to BEFORE publishInfo/devices/groups/definitions const oldStartTail = ` await this.publishInfo(); await this.publishDevices(); await this.publishGroups(); await this.publishDefinitions(); this.eventBus.onMQTTMessage(this, this.onMQTTMessage);`; const newStartTail = ` this.eventBus.onMQTTMessage(this, this.onMQTTMessage); // BLZ adapter workaround: each publish wrapped so Bridge.start completes // even if a single publish fails (e.g. BLZ coordinator endpoint missing). for (const [name, fn] of [["publishInfo", this.publishInfo], ["publishDevices", this.publishDevices], ["publishGroups", this.publishGroups], ["publishDefinitions", this.publishDefinitions]]) { try { await fn.call(this); } catch (err) { logger_1.default.error("Bridge " + name + " failed: " + err.message); } }`; if (content.includes(oldStartTail)) { content = content.replace(oldStartTail, newStartTail); changed = true; console.log("[patch 2] moved onMQTTMessage before publish calls + added try/catch"); } else if (content.includes(newStartTail)) { console.log("[patch 2] already applied"); } else { console.warn("[patch 2] target block not found, skipping"); } return { content, changed }; } const original = fs.readFileSync(BRIDGE_PATH, "utf8"); const { content, changed } = patch(original); if (!changed) { console.log("no changes needed"); process.exit(0); } // Backup original once (only if .bak does not exist) const bak = BRIDGE_PATH + ".bak"; if (!fs.existsSync(bak)) { fs.writeFileSync(bak, original); console.log("backup written to " + bak); } fs.writeFileSync(BRIDGE_PATH, content); console.log("patched " + BRIDGE_PATH);