diff --git a/lib/eventBus.ts b/lib/eventBus.ts index aef2c95b..5a360d21 100644 --- a/lib/eventBus.ts +++ b/lib/eventBus.ts @@ -41,10 +41,10 @@ export default class EventBus { this.on('deviceRenamed', callback, key); } - public emitDeviceRemoved(data: eventdata.DeviceRemoved): void { + public emitEntityRemoved(data: eventdata.EntityRemoved): void { this.emitter.emit('deviceRemoved', data); } - public onDeviceRemoved(key: ListenerKey, callback: (data: eventdata.DeviceRemoved) => void): void { + public onEntityRemoved(key: ListenerKey, callback: (data: eventdata.EntityRemoved) => void): void { this.on('deviceRemoved', callback, key); } diff --git a/lib/extension/availability.ts b/lib/extension/availability.ts index 0789a38f..b2d60983 100644 --- a/lib/extension/availability.ts +++ b/lib/extension/availability.ts @@ -128,7 +128,7 @@ export default class Availability extends Extension { } }); - this.eventBus.onDeviceRemoved(this, (data) => clearTimeout(this.timers[data.ieeeAddr])); + this.eventBus.onEntityRemoved(this, (data) => data.type == 'device' && clearTimeout(this.timers[data.id])); this.eventBus.onDeviceLeave(this, (data) => clearTimeout(this.timers[data.ieeeAddr])); this.eventBus.onDeviceAnnounce(this, (data) => this.retrieveState(data.device)); this.eventBus.onLastSeenChanged(this, this.onLastSeenChanged); diff --git a/lib/extension/bridge.ts b/lib/extension/bridge.ts index 87f8cdff..40266ac3 100644 --- a/lib/extension/bridge.ts +++ b/lib/extension/bridge.ts @@ -618,7 +618,6 @@ export default class Bridge extends Extension { try { logger.info(`Removing ${entityType} '${entity.name}'${blockForceLog}`); - const ieeeAddr = entity.isDevice() && entity.ieeeAddr; const name = entity.name; if (entity instanceof Device) { @@ -641,7 +640,9 @@ export default class Bridge extends Extension { // Fire event if (entity instanceof Device) { - this.eventBus.emitDeviceRemoved({ieeeAddr, name}); + this.eventBus.emitEntityRemoved({id: entityID, name, type: 'device'}); + } else { + this.eventBus.emitEntityRemoved({id: entityID, name, type: 'group'}); } // Remove from configuration.yaml diff --git a/lib/extension/homeassistant.ts b/lib/extension/homeassistant.ts index 40d19fec..e69f7a39 100644 --- a/lib/extension/homeassistant.ts +++ b/lib/extension/homeassistant.ts @@ -201,7 +201,7 @@ export default class HomeAssistant extends Extension { this.discoveryOrigin = {name: 'Zigbee2MQTT', sw: this.zigbee2MQTTVersion, url: 'https://www.zigbee2mqtt.io'}; this.bridge = this.getBridgeEntity(await this.zigbee.getCoordinatorVersion()); this.bridgeIdentifier = this.getDevicePayload(this.bridge).identifiers[0]; - this.eventBus.onDeviceRemoved(this, this.onDeviceRemoved); + this.eventBus.onEntityRemoved(this, this.onEntityRemoved); this.eventBus.onMQTTMessage(this, this.onMQTTMessage); this.eventBus.onEntityRenamed(this, this.onEntityRenamed); this.eventBus.onPublishEntityState(this, this.onPublishEntityState); @@ -244,8 +244,8 @@ export default class HomeAssistant extends Extension { this.eventBus.emitPublishAvailability(); } - private getDiscovered(entity: Device | Group | Bridge | string): Discovered { - const ID = typeof entity === 'string' ? entity : entity.ID; + private getDiscovered(entity: Device | Group | Bridge | string | number): Discovered { + const ID = typeof entity === 'string' || typeof entity === 'number' ? entity : entity.ID; if (!(ID in this.discovered)) { this.discovered[ID] = {messages: {}, triggers: new Set(), mockProperties: new Set(), discovered: false}; } @@ -1202,15 +1202,15 @@ export default class HomeAssistant extends Extension { return discoveryEntries; } - @bind async onDeviceRemoved(data: eventdata.DeviceRemoved): Promise { + @bind async onEntityRemoved(data: eventdata.EntityRemoved): Promise { logger.debug(`Clearing Home Assistant discovery for '${data.name}'`); - const discovered = this.getDiscovered(data.ieeeAddr); + const discovered = this.getDiscovered(data.id); for (const topic of Object.keys(discovered.messages)) { await this.mqtt.publish(topic, null, {retain: true, qos: 1}, this.discoveryTopic, false, false); } - delete this.discovered[data.ieeeAddr]; + delete this.discovered[data.id]; } @bind async onGroupMembersChanged(data: eventdata.GroupMembersChanged): Promise { diff --git a/lib/extension/legacy/bridgeLegacy.ts b/lib/extension/legacy/bridgeLegacy.ts index e8240b4c..93ddcd00 100644 --- a/lib/extension/legacy/bridgeLegacy.ts +++ b/lib/extension/legacy/bridgeLegacy.ts @@ -295,7 +295,7 @@ export default class BridgeLegacy extends Extension { const cleanup = async (): Promise => { // Fire event - this.eventBus.emitDeviceRemoved({ieeeAddr, name}); + this.eventBus.emitEntityRemoved({id: ieeeAddr, name: name, type: 'device'}); // Remove from configuration.yaml settings.removeDevice(entity.ieeeAddr); diff --git a/lib/types/types.d.ts b/lib/types/types.d.ts index ff23add5..49fc6260 100644 --- a/lib/types/types.d.ts +++ b/lib/types/types.d.ts @@ -73,7 +73,7 @@ declare global { namespace eventdata { type EntityRenamed = {entity: Device | Group; homeAssisantRename: boolean; from: string; to: string}; - type DeviceRemoved = {ieeeAddr: string; name: string}; + type EntityRemoved = {id: number | string; name: string; type: 'device' | 'group'}; type MQTTMessage = {topic: string; message: string}; type MQTTMessagePublished = {topic: string; payload: string; options: {retain: boolean; qos: number}}; type StateChange = { diff --git a/test/homeassistant.test.js b/test/homeassistant.test.js index 26bbacad..493ebe47 100644 --- a/test/homeassistant.test.js +++ b/test/homeassistant.test.js @@ -1443,6 +1443,19 @@ describe('HomeAssistant extension', () => { ); }); + it('Should clear discovery when group is removed', async () => { + MQTT.publish.mockClear(); + MQTT.events.message('zigbee2mqtt/bridge/request/group/remove', stringify({id: 'ha_discovery_group'})); + await flushPromises(); + + expect(MQTT.publish).toHaveBeenCalledWith( + 'homeassistant/light/1221051039810110150109113116116_9/light/config', + null, + {retain: true, qos: 1}, + expect.any(Function), + ); + }); + it('Should refresh discovery when device is renamed', async () => { await MQTT.events.message( 'homeassistant/device_automation/0x0017880104e45522/action_double/config',