fix!: Improve permit join (#24257)

* fix: Improve permit join

* Update Home Assistant permit join switch

* Remove `permit_join` from `settings.schema.json`

* Update zigbee-herdsman version to pre-release.

* Fix pnpm overrides

* Update test/homeassistant.test.js

---------

Co-authored-by: Koen Kanters <koenkanters94@gmail.com>
This commit is contained in:
Nerivec
2024-12-01 20:07:06 +01:00
committed by Koen Kanters
co-authored by Koen Kanters
parent b6186623f0
commit 2124d3450c
15 changed files with 96 additions and 204 deletions
-13
View File
@@ -167,19 +167,6 @@ export class Controller {
logger.info(`Currently ${deviceCount} devices are joined.`);
// Enable zigbee join
try {
if (settings.get().permit_join) {
logger.warning('`permit_join` set to `true` in configuration.yaml.');
logger.warning('Allowing new devices to join.');
logger.warning('Set `permit_join` to `false` once you joined all devices.');
}
await this.zigbee.permitJoin(settings.get().permit_join);
} catch (error) {
logger.error(`Failed to set permit join to ${settings.get().permit_join} (${(error as Error).message})`);
}
// MQTT
try {
await this.mqtt.connect();
+10 -27
View File
@@ -225,10 +225,6 @@ export default class Bridge extends Extension {
if (restartRequired) this.restartRequired = true;
// Apply some settings on-the-fly.
if (newSettings.permit_join != undefined) {
await this.zigbee.permitJoin(settings.get().permit_join);
}
if (newSettings.homeassistant != undefined) {
await this.enableDisableExtension(!!settings.get().homeassistant, 'HomeAssistant');
}
@@ -323,17 +319,15 @@ export default class Bridge extends Extension {
}
@bind async permitJoin(message: KeyValue | string): Promise<MQTTResponse> {
if (typeof message === 'object' && message.value === undefined) {
throw new Error('Invalid payload');
}
let value: boolean | string;
let time: number | undefined;
let device: Device | undefined;
if (typeof message === 'object') {
value = message.value;
time = message.time;
if (message.time === undefined) {
throw new Error('Invalid payload');
}
time = Number.parseInt(message.time, 10);
if (message.device) {
const resolved = this.zigbee.resolveEntity(message.device);
@@ -345,25 +339,15 @@ export default class Bridge extends Extension {
}
}
} else {
value = message;
time = Number.parseInt(message, 10);
}
if (typeof value === 'string') {
value = value.toLowerCase() === 'true';
}
await this.zigbee.permitJoin(time, device);
await this.zigbee.permitJoin(value, device, time);
const response: {time: number; device?: string} = {time};
const response: {value: boolean; device?: string; time?: number} = {value};
if (typeof message === 'object') {
if (device) {
response.device = message.device;
}
if (time != undefined) {
response.time = message.time;
}
if (device) {
response.device = device.name;
}
return utils.getResponse(message, response);
@@ -679,7 +663,6 @@ export default class Bridge extends Extension {
},
network: utils.toSnakeCaseObject(await this.zigbee.getNetworkParameters()),
log_level: logger.getLevel(),
permit_join: this.zigbee.getPermitJoin(),
permit_join_timeout: this.zigbee.getPermitJoinTimeout(),
restart_required: this.restartRequired,
config,
+3 -2
View File
@@ -2203,8 +2203,9 @@ export default class HomeAssistant extends Extension {
value_template: '{{ value_json.permit_join | lower }}',
command_topic: `${baseTopic}/request/permit_join`,
state_on: 'true',
payload_on: '{"value": true, "time": 254}',
payload_off: 'false',
state_off: 'false',
payload_on: '{"time": 254}',
payload_off: '{"time": 0}',
},
},
);
-1
View File
@@ -120,7 +120,6 @@ declare global {
legacy_triggers: boolean;
experimental_event_entities: boolean;
};
permit_join: boolean;
availability?: {
active: {timeout: number};
passive: {timeout: number};
-6
View File
@@ -51,12 +51,6 @@
}
]
},
"permit_join": {
"type": "boolean",
"default": false,
"title": "Permit join",
"description": "Allow new devices to join (re-applied at restart)"
},
"availability": {
"oneOf": [
{
-1
View File
@@ -43,7 +43,6 @@ const ajvRestartRequiredGroupOptions = new Ajv({allErrors: true})
.addKeyword({keyword: 'requiresRestart', validate: (s: unknown) => !s})
.compile(schemaJson.definitions.group);
const defaults: RecursivePartial<Settings> = {
permit_join: false,
external_converters: [],
mqtt: {
base_topic: 'zigbee2mqtt',
+4 -12
View File
@@ -225,26 +225,18 @@ export default class Zigbee {
logger.info('Stopped zigbee-herdsman');
}
getPermitJoin(): boolean {
return this.herdsman.getPermitJoin();
}
getPermitJoinTimeout(): number | undefined {
getPermitJoinTimeout(): number {
return this.herdsman.getPermitJoinTimeout();
}
async permitJoin(permit: boolean, device?: Device, time?: number): Promise<void> {
if (permit) {
async permitJoin(time: number, device?: Device): Promise<void> {
if (time > 0) {
logger.info(`Zigbee: allowing new devices to join${device ? ` via ${device.name}` : ''}.`);
} else {
logger.info('Zigbee: disabling joining new devices.');
}
if (device && permit) {
await this.herdsman.permitJoin(permit, device.zh, time);
} else {
await this.herdsman.permitJoin(permit, undefined, time);
}
await this.herdsman.permitJoin(time, device?.zh);
}
@bind private resolveDevice(ieeeAddr: string): Device | undefined {
+5 -5
View File
@@ -61,9 +61,9 @@
"winston-syslog": "^2.7.1",
"winston-transport": "^4.9.0",
"ws": "^8.18.0",
"zigbee-herdsman": "2.1.9",
"zigbee-herdsman-converters": "20.58.0",
"zigbee2mqtt-frontend": "0.7.6"
"zigbee-herdsman": "3.0.0-pre.0",
"zigbee-herdsman-converters": "20.28.0",
"zigbee2mqtt-frontend": "0.7.4"
},
"devDependencies": {
"@babel/core": "^7.26.0",
@@ -93,8 +93,8 @@
"typescript": "^5.7.2",
"typescript-eslint": "^8.15.0"
},
"overrides": {
"zigbee-herdsman-converters": {
"pnpm": {
"overrides": {
"zigbee-herdsman": "$zigbee-herdsman"
}
},
+20 -16
View File
@@ -4,6 +4,9 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
overrides:
zigbee-herdsman: 3.0.0-pre.0
importers:
.:
@@ -81,11 +84,11 @@ importers:
specifier: ^8.18.0
version: 8.18.0
zigbee-herdsman:
specifier: 2.1.3
version: 2.1.3
specifier: 3.0.0-pre.0
version: 3.0.0-pre.0
zigbee-herdsman-converters:
specifier: 20.25.0
version: 20.25.0
specifier: 20.28.0
version: 20.28.0
zigbee2mqtt-frontend:
specifier: 0.7.4
version: 0.7.4
@@ -95,13 +98,13 @@ importers:
version: 2.8.0
devDependencies:
'@babel/core':
specifier: ^7.25.7
specifier: ^7.25.8
version: 7.25.8
'@babel/plugin-proposal-decorators':
specifier: ^7.25.7
version: 7.25.7(@babel/core@7.25.8)
'@babel/preset-env':
specifier: ^7.25.7
specifier: ^7.25.8
version: 7.25.8(@babel/core@7.25.8)
'@babel/preset-typescript':
specifier: ^7.25.7
@@ -131,7 +134,7 @@ importers:
specifier: ^4.0.9
version: 4.0.9
'@types/node':
specifier: ^22.7.4
specifier: ^22.7.5
version: 22.7.5
'@types/object-assign-deep':
specifier: ^0.4.3
@@ -164,10 +167,10 @@ importers:
specifier: ^0.2.3
version: 0.2.3
typescript:
specifier: ^5.6.2
specifier: ^5.6.3
version: 5.6.3
typescript-eslint:
specifier: ^8.8.0
specifier: ^8.8.1
version: 8.8.1(eslint@9.12.0)(typescript@5.6.3)
packages:
@@ -2800,11 +2803,11 @@ packages:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
engines: {node: '>=10'}
zigbee-herdsman-converters@20.25.0:
resolution: {integrity: sha512-iqCQmcdwrUGz00IhvtB36l3YN9sCWPhbx8D+lf9owyjpE5To7u2pOn4GA4g6QWQCU7BndCif8fXed1FWt9/nrg==}
zigbee-herdsman-converters@20.28.0:
resolution: {integrity: sha512-AMCuG4mlIR21JgShIyFnZsCimfITH2HpjQLS0vaAqIKmbYrG7sBF0MU/iznYc/8JqNq+Jav3BdV+BrE1M3GpEg==}
zigbee-herdsman@2.1.3:
resolution: {integrity: sha512-1LiSb3L2ZFzNOuGJHMWBFPRgPs1WVMS4CagtvYxEVUdifhVivp1vMNrCxBsbwNhDiNBh/Blk0pTR0szJttLzrA==}
zigbee-herdsman@3.0.0-pre.0:
resolution: {integrity: sha512-3mCSmdwu5eJb0DEJrTDSQPYEQAz1mOGAbqvXyEOjo6UOllo++GyzpmawTxWBH4FLWrbuKc9SefiVqQdC/4uZbQ==}
zigbee2mqtt-frontend@0.7.4:
resolution: {integrity: sha512-skWNYxThSa6Ywn7aRB0ZvRKWifpqbku4+vUM5BbXiNaXYxCCbU0b3pN258Ahxt3NsLtYk2zBdYoQcXuBZxmJxw==}
@@ -5939,7 +5942,7 @@ snapshots:
yocto-queue@0.1.0: {}
zigbee-herdsman-converters@20.25.0:
zigbee-herdsman-converters@20.28.0:
dependencies:
axios: 1.7.7
buffer-crc32: 1.0.0
@@ -5947,12 +5950,13 @@ snapshots:
iconv-lite: 0.6.3
semver: 7.6.3
tar-stream: 3.1.7
zigbee-herdsman: 2.1.3
uri-js: 4.4.1
zigbee-herdsman: 3.0.0-pre.0
transitivePeerDependencies:
- debug
- supports-color
zigbee-herdsman@2.1.3:
zigbee-herdsman@3.0.0-pre.0:
dependencies:
'@serialport/bindings-cpp': 12.0.1
'@serialport/parser-delimiter': 12.0.0
+46 -93
View File
@@ -64,6 +64,7 @@ describe('Bridge', () => {
logger.warning.mockClear();
logger.setTransportsEnabled(false);
MQTT.publish.mockClear();
zigbeeHerdsman.permitJoin.mockClear();
const device = zigbeeHerdsman.devices.bulb;
device.interview.mockClear();
device.removeFromDatabase.mockClear();
@@ -203,14 +204,13 @@ describe('Bridge', () => {
mqtt: {base_topic: 'zigbee2mqtt', force_disable_retain: false, include_device_information: false, server: 'mqtt://localhost'},
ota: {disable_automatic_update_check: false, update_check_interval: 1440},
passlist: [],
permit_join: true,
serial: {disable_led: false, port: '/dev/dummy'},
},
config_schema: settings.schema,
coordinator: {ieee_address: '0x00124b00120144ae', meta: {revision: 20190425, version: 1}, type: 'z-Stack'},
log_level: 'info',
network: {channel: 15, extended_pan_id: [0, 11, 22], pan_id: 5674},
permit_join: false,
permit_join_timeout: 0,
restart_required: false,
version: version.version,
zigbee_herdsman: zhVersion,
@@ -2579,70 +2579,52 @@ describe('Bridge', () => {
);
});
it('Should allow permit join', async () => {
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', 'true');
it('Should allow permit join on all', async () => {
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({time: 1}));
await flushPromises();
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(true, undefined, undefined);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(1, undefined);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
stringify({data: {value: true}, status: 'ok'}),
{retain: false, qos: 0},
expect.any(Function),
);
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({value: false}));
await flushPromises();
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(false, undefined, undefined);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
stringify({data: {value: false}, status: 'ok'}),
{retain: false, qos: 0},
expect.any(Function),
);
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({value: 'False'}));
await flushPromises();
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(false, undefined, undefined);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
stringify({data: {value: false}, status: 'ok'}),
{retain: false, qos: 0},
expect.any(Function),
);
// Invalid payload
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({value_bla: false}));
await flushPromises();
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
stringify({data: {}, status: 'error', error: 'Invalid payload'}),
stringify({data: {time: 1}, status: 'ok'}),
{retain: false, qos: 0},
expect.any(Function),
);
});
it('Should allow permit join for certain time', async () => {
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({value: false, time: 10}));
it('Should disallow permit join on all', async () => {
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({time: 0}));
await flushPromises();
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(false, undefined, 10);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(0, undefined);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
stringify({data: {value: false, time: 10}, status: 'ok'}),
stringify({data: {time: 0}, status: 'ok'}),
{retain: false, qos: 0},
expect.any(Function),
);
});
it('Should allow permit join with number string (automatically on all)', async () => {
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', '1');
await flushPromises();
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(1, undefined);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
stringify({data: {time: 1}, status: 'ok'}),
{retain: false, qos: 0},
expect.any(Function),
);
});
it('Should not allow permit join with invalid payload', async () => {
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({time_bla: false}));
await flushPromises();
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
stringify({data: {}, status: 'error', error: 'Invalid payload'}),
{retain: false, qos: 0},
expect.any(Function),
);
@@ -2665,23 +2647,22 @@ describe('Bridge', () => {
it('Should allow permit join via device', async () => {
const device = zigbeeHerdsman.devices.bulb;
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({value: true, device: 'bulb'}));
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({time: 123, device: 'bulb'}));
await flushPromises();
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(true, device, undefined);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(123, device);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
stringify({data: {value: true, device: 'bulb'}, status: 'ok'}),
stringify({data: {time: 123, device: 'bulb'}, status: 'ok'}),
{retain: false, qos: 0},
expect.any(Function),
);
});
// Device does not exist
zigbeeHerdsman.permitJoin.mockClear();
it('Should not allow permit join via non-existing device', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({value: true, device: 'bulb_not_existing_woeeee'}));
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({time: 123, device: 'bulb_not_existing_woeeee'}));
await flushPromises();
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith(
@@ -2694,11 +2675,11 @@ describe('Bridge', () => {
it('Should put transaction in response when request is done with transaction', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({value: false, transaction: 22}));
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({time: 0, transaction: 22}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
stringify({data: {value: false}, status: 'ok', transaction: 22}),
stringify({data: {time: 0}, status: 'ok', transaction: 22}),
{retain: false, qos: 0},
expect.any(Function),
);
@@ -2709,7 +2690,7 @@ describe('Bridge', () => {
throw new Error('Failed to connect to adapter');
});
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({value: false}));
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({time: 0}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
@@ -3555,7 +3536,6 @@ describe('Bridge', () => {
const device = zigbeeHerdsman.devices.bulb;
const endpoint = device.getEndpoint(1);
endpoint.configureReporting.mockClear();
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message(
'zigbee2mqtt/bridge/request/device/configure_reporting',
@@ -3600,7 +3580,6 @@ describe('Bridge', () => {
const device = zigbeeHerdsman.devices.bulb;
const endpoint = device.getEndpoint(1);
endpoint.configureReporting.mockClear();
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message(
'zigbee2mqtt/bridge/request/device/configure_reporting',
@@ -3627,7 +3606,6 @@ describe('Bridge', () => {
const device = zigbeeHerdsman.devices.bulb;
const endpoint = device.getEndpoint(1);
endpoint.configureReporting.mockClear();
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message(
'zigbee2mqtt/bridge/request/device/configure_reporting',
@@ -3654,7 +3632,6 @@ describe('Bridge', () => {
const device = zigbeeHerdsman.devices.bulb;
const endpoint = device.getEndpoint(1);
endpoint.configureReporting.mockClear();
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message(
'zigbee2mqtt/bridge/request/device/configure_reporting',
@@ -3704,7 +3681,6 @@ describe('Bridge', () => {
});
it('Should allow to restart', async () => {
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/restart', '');
await flushPromises();
@@ -3718,24 +3694,6 @@ describe('Bridge', () => {
);
});
it('Change options', async () => {
zigbeeHerdsman.permitJoin.mockClear();
settings.apply({permit_join: false});
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/options', stringify({options: {permit_join: true}}));
await flushPromises();
expect(settings.get().permit_join).toBe(true);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(true, undefined, undefined);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/options',
stringify({data: {restart_required: false}, status: 'ok'}),
{retain: false, qos: 0},
expect.any(Function),
);
});
it('Change options and apply - homeassistant', async () => {
expect(controller.extensions.find((e) => e.constructor.name === 'HomeAssistant')).toBeUndefined();
MQTT.publish.mockClear();
@@ -3807,7 +3765,6 @@ describe('Bridge', () => {
});
it('Change options restart required', async () => {
zigbeeHerdsman.permitJoin.mockClear();
settings.apply({serial: {port: '123'}});
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/options', stringify({options: {serial: {port: '/dev/newport'}}}));
@@ -3822,7 +3779,6 @@ describe('Bridge', () => {
});
it('Change options array', async () => {
zigbeeHerdsman.permitJoin.mockClear();
expect(settings.get().advanced.ext_pan_id).toStrictEqual([221, 221, 221, 221, 221, 221, 221, 221]);
MQTT.publish.mockClear();
MQTT.events.message(
@@ -3840,7 +3796,6 @@ describe('Bridge', () => {
});
it('Change options with null', async () => {
zigbeeHerdsman.permitJoin.mockClear();
expect(settings.get().serial).toStrictEqual({disable_led: false, port: '/dev/dummy'});
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/options', stringify({options: {serial: {disable_led: false, port: null}}}));
@@ -3855,7 +3810,6 @@ describe('Bridge', () => {
});
it('Change options invalid payload', async () => {
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/options', 'I am invalid');
await flushPromises();
@@ -3868,13 +3822,12 @@ describe('Bridge', () => {
});
it('Change options not valid against schema', async () => {
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/options', stringify({options: {permit_join: 'true'}}));
MQTT.events.message('zigbee2mqtt/bridge/request/options', stringify({options: {external_converters: 'true'}}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/options',
stringify({data: {}, error: 'permit_join must be boolean', status: 'error'}),
stringify({data: {}, error: 'external_converters must be array', status: 'error'}),
{retain: false, qos: 0},
expect.any(Function),
);
-19
View File
@@ -10,7 +10,6 @@ const stringify = require('json-stable-stringify-without-jsonify');
const flushPromises = require('./lib/flushPromises');
const tmp = require('tmp');
const mocksClear = [
zigbeeHerdsman.permitJoin,
MQTT.end,
zigbeeHerdsman.stop,
logger.debug,
@@ -84,8 +83,6 @@ describe('Controller', () => {
});
expect(zigbeeHerdsman.start).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.setTransmitPower).toHaveBeenCalledTimes(0);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(true, undefined, undefined);
expect(logger.info).toHaveBeenCalledWith(`Currently ${Object.values(zigbeeHerdsman.devices).length - 1} devices are joined.`);
expect(logger.info).toHaveBeenCalledWith(
'bulb (0x000b57fffec6a5b2): LED1545G12 - IKEA TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm (Router)',
@@ -105,15 +102,6 @@ describe('Controller', () => {
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255}), {retain: true, qos: 0}, expect.any(Function));
});
it('Start controller when permit join fails', async () => {
zigbeeHerdsman.permitJoin.mockImplementationOnce(() => {
throw new Error('failed!');
});
await controller.start();
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(MQTT.connect).toHaveBeenCalledTimes(1);
});
it('Start controller with specific MQTT settings', async () => {
const ca = tmp.fileSync().name;
fs.writeFileSync(ca, 'ca');
@@ -283,13 +271,6 @@ describe('Controller', () => {
expect(mockExit).toHaveBeenCalledWith(1, false);
});
it('Start controller with permit join true', async () => {
settings.set(['permit_join'], false);
await controller.start();
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(false, undefined, undefined);
});
it('Start controller and stop with restart', async () => {
await controller.start();
await controller.stop(true);
+3 -2
View File
@@ -2813,8 +2813,9 @@ describe('HomeAssistant extension', () => {
value_template: '{{ value_json.permit_join | lower }}',
command_topic: 'zigbee2mqtt/bridge/request/permit_join',
state_on: 'true',
payload_on: '{"value": true, "time": 254}',
payload_off: 'false',
state_off: 'false',
payload_on: '{"time": 254}',
payload_off: '{"time": 0}',
origin: origin,
device: devicePayload,
availability: [{topic: 'zigbee2mqtt/bridge/state', value_template: '{{ value_json.state }}'}],
+4 -4
View File
@@ -13,7 +13,7 @@ const yaml = require('js-yaml');
const objectAssignDeep = require(`object-assign-deep`);
const minimalConfig = {
permit_join: true,
external_converters: [],
homeassistant: true,
mqtt: {base_topic: 'zigbee2mqtt', server: 'localhost'},
};
@@ -55,12 +55,12 @@ describe('Settings', () => {
});
it('Should return settings', () => {
write(configurationFile, {permit_join: true});
write(configurationFile, {external_converters: ['abcd.js']});
const s = settings.get();
const expected = objectAssignDeep.noMutate({}, settings.testing.defaults);
expected.devices = {};
expected.groups = {};
expected.permit_join = true;
expected.external_converters = ['abcd.js'];
expect(s).toStrictEqual(expected);
});
@@ -964,7 +964,7 @@ describe('Settings', () => {
},
});
settings.reRead();
settings.apply({permit_join: false});
settings.apply({external_converters: []});
expect(settings.get().device_options.homeassistant).toStrictEqual({temperature: null});
expect(settings.get().devices['0x1234567812345678'].homeassistant).toStrictEqual({humidity: null});
});
-1
View File
@@ -10,7 +10,6 @@ const stateFile = path.join(mockDir, 'state.json');
function writeDefaultConfiguration() {
const config = {
homeassistant: false,
permit_join: true,
mqtt: {
base_topic: 'zigbee2mqtt',
server: 'mqtt://localhost',
+1 -2
View File
@@ -895,8 +895,7 @@ const mock = {
getGroupByID: jest.fn().mockImplementation((groupID) => {
return Object.values(groups).find((d) => d.groupID === groupID);
}),
getPermitJoin: jest.fn().mockReturnValue(false),
getPermitJoinTimeout: jest.fn().mockReturnValue(undefined),
getPermitJoinTimeout: jest.fn().mockReturnValue(0),
reset: jest.fn(),
createGroup: jest.fn().mockImplementation((groupID) => {
const group = new Group(groupID, []);