mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-08-26 04:29:58 +00:00
Fix various crashes when removing a device. https://github.com/Koenkk/zigbee2mqtt/issues/2004
This commit is contained in:
+21
-18
@@ -169,63 +169,66 @@ class Controller {
|
||||
|
||||
async onZigbeeEvent(type, data) {
|
||||
const entity = this.zigbee.resolveEntity(data.device || data.ieeeAddr);
|
||||
if (!entity.settings && data.device) {
|
||||
if (data.device && !entity.settings) {
|
||||
// Only deviceLeave doesn't have a device (not interesting to add to settings)
|
||||
entity.settings = settings.addDevice(data.device.ieeeAddr);
|
||||
}
|
||||
|
||||
const friendlyName = entity.settings.friendlyName;
|
||||
const name = entity && entity.settings ? entity.settings.friendlyName : null;
|
||||
|
||||
if (type === 'message') {
|
||||
logger.debug(
|
||||
`Received Zigbee message from '${entity.settings.friendlyName}' of type '${data.type}' ` +
|
||||
`Received Zigbee message from '${name}' of type '${data.type}' ` +
|
||||
`with data '${JSON.stringify(data.data)}' from endpoint ${data.endpoint.ID}` +
|
||||
(data.hasOwnProperty('groupID') ? ` with groupID ${data.groupID}` : ``)
|
||||
);
|
||||
} else if (type === 'deviceJoined') {
|
||||
logger.info(`Device '${friendlyName}' joined`);
|
||||
this.mqtt.log('device_connected', {friendly_name: friendlyName});
|
||||
logger.info(`Device '${name}' joined`);
|
||||
this.mqtt.log('device_connected', {friendly_name: name});
|
||||
} else if (type === 'deviceInterview') {
|
||||
if (data.status === 'successful') {
|
||||
logger.info(`Successfully interviewed '${friendlyName}', device has succesfully been paired`);
|
||||
logger.info(`Successfully interviewed '${name}', device has succesfully been paired`);
|
||||
|
||||
if (entity.mapped) {
|
||||
const {vendor, description, model} = entity.mapped;
|
||||
logger.info(
|
||||
`Device '${friendlyName}' is supported, identified as: ${vendor} ${description} (${model})`
|
||||
`Device '${name}' is supported, identified as: ${vendor} ${description} (${model})`
|
||||
);
|
||||
|
||||
const log = {friendly_name: friendlyName, model, vendor, description, supported: true};
|
||||
const log = {friendly_name: name, model, vendor, description, supported: true};
|
||||
this.mqtt.log('pairing', 'interview_successful', log);
|
||||
} else {
|
||||
logger.warn(
|
||||
`Device '${friendlyName}' with Zigbee model '${data.device.modelID}' is NOT supported, ` +
|
||||
`Device '${name}' with Zigbee model '${data.device.modelID}' is NOT supported, ` +
|
||||
`please follow https://www.zigbee2mqtt.io/how_tos/how_to_support_new_devices.html`
|
||||
);
|
||||
this.mqtt.log('pairing', 'interview_successful', {friendly_name: friendlyName, supported: false});
|
||||
this.mqtt.log('pairing', 'interview_successful', {friendly_name: name, supported: false});
|
||||
}
|
||||
} else if (data.status === 'failed') {
|
||||
logger.error(`Failed to interview '${friendlyName}', device has not succesfully been paired`);
|
||||
this.mqtt.log('pairing', 'interview_failed', {friendly_name: friendlyName});
|
||||
logger.error(`Failed to interview '${name}', device has not succesfully been paired`);
|
||||
this.mqtt.log('pairing', 'interview_failed', {friendly_name: name});
|
||||
} else {
|
||||
/* istanbul ignore else */
|
||||
if (data.status === 'started') {
|
||||
logger.info(`Starting interview of '${friendlyName}'`);
|
||||
this.mqtt.log('pairing', 'interview_started', {friendly_name: friendlyName});
|
||||
logger.info(`Starting interview of '${name}'`);
|
||||
this.mqtt.log('pairing', 'interview_started', {friendly_name: name});
|
||||
}
|
||||
}
|
||||
} else if (type === 'deviceAnnounce') {
|
||||
logger.debug(`Device '${friendlyName}' announced itself`);
|
||||
logger.debug(`Device '${name}' announced itself`);
|
||||
} else {
|
||||
/* istanbul ignore else */
|
||||
if (type === 'deviceLeave') {
|
||||
logger.warn(`Device '${friendlyName}' left the network`);
|
||||
this.mqtt.log('device_removed', 'left_network', {friendly_name: friendlyName});
|
||||
logger.warn(`Device '${name || data.ieeeAddr}' left the network`);
|
||||
this.mqtt.log('device_removed', 'left_network', {friendly_name: name || data.ieeeAddr});
|
||||
}
|
||||
}
|
||||
|
||||
// Call extensions
|
||||
this.callExtensionMethod('onZigbeeEvent', [type, data, entity.mapped, entity.settings]);
|
||||
this.callExtensionMethod(
|
||||
'onZigbeeEvent',
|
||||
[type, data, entity ? entity.mapped : null, entity ? entity.settings : null]
|
||||
);
|
||||
}
|
||||
|
||||
onMQTTMessage(payload) {
|
||||
|
||||
@@ -223,7 +223,7 @@ class BridgeConfig extends BaseExtension {
|
||||
}
|
||||
|
||||
async removeOrBan(ban, message) {
|
||||
const entity = this.zigbee.resolveEntity(message);
|
||||
const entity = this.zigbee.resolveEntity(message.trim());
|
||||
|
||||
const cleanup = () => {
|
||||
// Remove from configuration.yaml
|
||||
|
||||
@@ -154,6 +154,10 @@ class Zigbee extends events.EventEmitter {
|
||||
return null;
|
||||
} else if (entity.type === 'device') {
|
||||
const device = this.getDeviceByIeeeAddr(entity.ID);
|
||||
if (!device) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const mapped = zigbeeHerdsmanConverters.findByZigbeeModel(device.modelID);
|
||||
const endpoints = mapped && mapped.endpoint ? mapped.endpoint(device) : null;
|
||||
let isDefaultEndpoint = true;
|
||||
|
||||
+17
-2
@@ -20,6 +20,7 @@ describe('Controller', () => {
|
||||
let controller;
|
||||
|
||||
beforeEach(() => {
|
||||
zigbeeHerdsman.returnDevices.splice(0);
|
||||
controller = new Controller();
|
||||
mocksClear.forEach((m) => m.mockClear());
|
||||
data.writeDefaultConfiguration();
|
||||
@@ -303,13 +304,27 @@ describe('Controller', () => {
|
||||
expect(logger.debug).toHaveBeenCalledWith(`Device 'bulb' announced itself`);
|
||||
});
|
||||
|
||||
it('On zigbee event device leave', async () => {
|
||||
it('On zigbee event device leave (removed from database and settings)', async () => {
|
||||
await controller.start();
|
||||
zigbeeHerdsman.returnDevices.push('0x00124b00120144ae');
|
||||
settings.set(['devices'], {})
|
||||
MQTT.publish.mockClear();
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
const payload = {ieeeAddr: device.ieeeAddr};
|
||||
await zigbeeHerdsman.events.deviceLeave(payload);
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', '{"type":"device_removed","message":"left_network","meta":{"friendly_name":"bulb"}}', { retain: false, qos: 0}, expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', '{"type":"device_removed","message":"left_network","meta":{"friendly_name":"0x000b57fffec6a5b2"}}', { retain: false, qos: 0}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('On zigbee event device leave (removed from database and NOT settings)', async () => {
|
||||
await controller.start();
|
||||
zigbeeHerdsman.returnDevices.push('0x00124b00120144ae');
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
MQTT.publish.mockClear();
|
||||
const payload = {ieeeAddr: device.ieeeAddr};
|
||||
await zigbeeHerdsman.events.deviceLeave(payload);
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', '{"type":"device_removed","message":"left_network","meta":{"friendly_name":"0x000b57fffec6a5b2"}}', { retain: false, qos: 0}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Publish entity state attribute output', async () => {
|
||||
|
||||
Reference in New Issue
Block a user