diff --git a/data/configuration.example.yaml b/data/configuration.example.yaml index 0bc67286..f0b2ae69 100644 --- a/data/configuration.example.yaml +++ b/data/configuration.example.yaml @@ -25,3 +25,5 @@ advanced: network_key: GENERATE # Let Zigbee2MQTT generate a pan_id on first start pan_id: GENERATE + # Let Zigbee2MQTT generate a ext_pan_id on first start + ext_pan_id: GENERATE diff --git a/lib/types/types.d.ts b/lib/types/types.d.ts index d5ada21c..1396ee63 100644 --- a/lib/types/types.d.ts +++ b/lib/types/types.d.ts @@ -257,7 +257,7 @@ declare global { log_level: 'debug' | 'info' | 'error' | 'warn', log_syslog: KeyValue, pan_id: number | 'GENERATE', - ext_pan_id: number[], + ext_pan_id: number[] | 'GENERATE', channel: number, adapter_concurrent: number | null, adapter_delay: number | null, diff --git a/lib/util/settings.schema.json b/lib/util/settings.schema.json index fe74ef0d..d7d58fb2 100644 --- a/lib/util/settings.schema.json +++ b/lib/util/settings.schema.json @@ -582,10 +582,18 @@ "description": "ZigBee pan ID, changing requires repairing all devices!" }, "ext_pan_id": { - "type": "array", - "items": { - "type": "number" - }, + "oneOf": [{ + "type": "string", + "title": "Extended pan ID (string)" + }, + { + "type": "array", + "items": { + "type": "number" + }, + "title": "Extended pan ID (array)" + } + ], "title": "Ext Pan ID", "requiresRestart": true, "description": "Zigbee extended pan ID, changing requires repairing all devices!" diff --git a/lib/util/settings.ts b/lib/util/settings.ts index 55b4898e..162bca80 100644 --- a/lib/util/settings.ts +++ b/lib/util/settings.ts @@ -285,6 +285,11 @@ export function validate(): string[] { errors.push(`advanced.pan_id: should be number or 'GENERATE' (is '${_settings.advanced.pan_id}')`); } + if (_settings.advanced && _settings.advanced.ext_pan_id && typeof _settings.advanced.ext_pan_id === 'string' && + _settings.advanced.ext_pan_id !== 'GENERATE') { + errors.push(`advanced.ext_pan_id: should be array or 'GENERATE' (is '${_settings.advanced.ext_pan_id}')`); + } + // Verify that all friendly names are unique const names: string[] = []; const check = (e: DeviceOptions | GroupOptions): void => { diff --git a/lib/zigbee.ts b/lib/zigbee.ts index 6ecc257c..d73102de 100644 --- a/lib/zigbee.ts +++ b/lib/zigbee.ts @@ -28,7 +28,8 @@ export default class Zigbee { network: { panID: settings.get().advanced.pan_id === 'GENERATE' ? this.generatePanID() : settings.get().advanced.pan_id as number, - extendedPanID: settings.get().advanced.ext_pan_id, + extendedPanID: settings.get().advanced.ext_pan_id === 'GENERATE' ? + this.generateExtPanID() : settings.get().advanced.ext_pan_id as number[], channelList: [settings.get().advanced.channel], networkKey: settings.get().advanced.network_key === 'GENERATE' ? this.generateNetworkKey() : settings.get().advanced.network_key as number[], @@ -170,6 +171,12 @@ export default class Zigbee { return key; } + private generateExtPanID(): number[] { + const key = Array.from({length: 8}, () => randomInt(256)); + settings.set(['advanced', 'ext_pan_id'], key); + return key; + } + private generatePanID(): number { const panID = randomInt(1, 0xFFFF - 1); settings.set(['advanced', 'pan_id'], panID); diff --git a/test/controller.test.js b/test/controller.test.js index ffc2574c..4f98b65a 100644 --- a/test/controller.test.js +++ b/test/controller.test.js @@ -103,14 +103,17 @@ describe('Controller', () => { expect(MQTT.connect).toHaveBeenCalledWith("mqtt://localhost", expected); }); - it('Should generate network_key and pan_id when set to GENERATE', async () => { + it('Should generate network_key, pan_id and ext_pan_id when set to GENERATE', async () => { settings.set(['advanced', 'network_key'], 'GENERATE'); settings.set(['advanced', 'pan_id'], 'GENERATE'); + settings.set(['advanced', 'ext_pan_id'], 'GENERATE'); await controller.start(); await flushPromises(); expect(zigbeeHerdsman.constructor.mock.calls[0][0].network.networkKey.length).toStrictEqual(16); + expect(zigbeeHerdsman.constructor.mock.calls[0][0].network.extendedPanID.length).toStrictEqual(8); expect(zigbeeHerdsman.constructor.mock.calls[0][0].network.panID).toStrictEqual(expect.any(Number)); expect(data.read().advanced.network_key.length).toStrictEqual(16); + expect(data.read().advanced.ext_pan_id.length).toStrictEqual(8); expect(data.read().advanced.pan_id).toStrictEqual(expect.any(Number)); }); diff --git a/test/settings.test.js b/test/settings.test.js index 4165d193..8171799a 100644 --- a/test/settings.test.js +++ b/test/settings.test.js @@ -682,6 +682,18 @@ describe('Settings', () => { }).toThrow(new Error("Device '0x123' already exists")); }); + it('Should not allow any string values for ext_pan_id', () => { + write(configurationFile, { + ...minimalConfig, + advanced: {ext_pan_id: 'NOT_GENERATE'}, + }); + + settings.reRead(); + + const error = `advanced.ext_pan_id: should be array or 'GENERATE' (is 'NOT_GENERATE')`; + expect(settings.validate()).toEqual(expect.arrayContaining([error])); + }); + it('Should not allow any string values for network_key', () => { write(configurationFile, { ...minimalConfig,