diff --git a/lib/eventBus.ts b/lib/eventBus.ts index ebbc45dea..a390d2be2 100644 --- a/lib/eventBus.ts +++ b/lib/eventBus.ts @@ -27,10 +27,10 @@ export default class EventBus { this.on('permitJoinChanged', callback, key); } - public emitDeviceRenamed(data: eventdata.DeviceRenamed): void { + public emitEntityRenamed(data: eventdata.EntityRenamed): void { this.emitter.emit('deviceRenamed', data); } - public onDeviceRenamed(key: ListenerKey, callback: (data: eventdata.DeviceRenamed) => void): void { + public onEntityRenamed(key: ListenerKey, callback: (data: eventdata.EntityRenamed) => void): void { this.on('deviceRenamed', callback, key); } diff --git a/lib/extension/availability.ts b/lib/extension/availability.ts index bd24d58c9..5519c59fe 100644 --- a/lib/extension/availability.ts +++ b/lib/extension/availability.ts @@ -95,7 +95,8 @@ export default class Availability extends Extension { } override async start(): Promise { - this.eventBus.onDeviceRenamed(this, (data) => this.publishAvailability(data.device, false, true)); + this.eventBus.onEntityRenamed(this, (data) => + data.entity.isDevice() && this.publishAvailability(data.entity, false, true)); this.eventBus.onDeviceRemoved(this, (data) => clearTimeout(this.timers[data.ieeeAddr])); this.eventBus.onDeviceLeave(this, (data) => clearTimeout(this.timers[data.ieeeAddr])); this.eventBus.onDeviceAnnounce(this, (data) => this.retrieveState(data.device)); diff --git a/lib/extension/bridge.ts b/lib/extension/bridge.ts index 13a0bf4b7..d0189f3d3 100644 --- a/lib/extension/bridge.ts +++ b/lib/extension/bridge.ts @@ -64,7 +64,7 @@ export default class Bridge extends Extension { this.zigbee2mqttVersion = await utils.getZigbee2MQTTVersion(); this.coordinatorVersion = await this.zigbee.getCoordinatorVersion(); - this.eventBus.onDeviceRenamed(this, () => this.publishInfo()); + this.eventBus.onEntityRenamed(this, () => this.publishInfo()); this.eventBus.onGroupMembersChanged(this, () => this.publishGroups()); this.eventBus.onDevicesChanged(this, () => this.publishDevices() && this.publishInfo()); this.eventBus.onPermitJoinChanged(this, () => !this.zigbee.isStopping() && this.publishInfo()); @@ -455,9 +455,10 @@ export default class Bridge extends Extension { // Clear retained messages this.mqtt.publish(oldFriendlyName, '', {retain: true}); + this.eventBus.emitEntityRenamed({entity: entity, homeAssisantRename, from: oldFriendlyName, to}); + if (entity instanceof Device) { this.publishDevices(); - this.eventBus.emitDeviceRenamed({device: entity, homeAssisantRename, from: oldFriendlyName, to}); } else { this.publishGroups(); this.publishInfo(); diff --git a/lib/extension/homeassistant.ts b/lib/extension/homeassistant.ts index f5363b901..bef32f6d8 100644 --- a/lib/extension/homeassistant.ts +++ b/lib/extension/homeassistant.ts @@ -88,7 +88,7 @@ export default class HomeAssistant extends Extension { this.eventBus.onDeviceRemoved(this, this.onDeviceRemoved); this.eventBus.onMQTTMessage(this, this.onMQTTMessage); - this.eventBus.onDeviceRenamed(this, this.onDeviceRenamed); + this.eventBus.onEntityRenamed(this, this.onEntityRenamed); this.eventBus.onPublishEntityState(this, this.onPublishEntityState); this.eventBus.onGroupMembersChanged(this, this.onGroupMembersChanged); this.eventBus.onDeviceAnnounce(this, this.onZigbeeEvent); @@ -858,25 +858,25 @@ export default class HomeAssistant extends Extension { } } - @bind onDeviceRenamed(data: eventdata.DeviceRenamed): void { - logger.debug(`Refreshing Home Assistant discovery topic for '${data.device.ieeeAddr}'`); + @bind onEntityRenamed(data: eventdata.EntityRenamed): void { + logger.debug(`Refreshing Home Assistant discovery topic for '${data.entity.name}'`); // Clear before rename so Home Assistant uses new friendly_name // https://github.com/Koenkk/zigbee2mqtt/issues/4096#issuecomment-674044916 if (data.homeAssisantRename) { - for (const config of this.getConfigs(data.device)) { - const topic = this.getDiscoveryTopic(config, data.device); + for (const config of this.getConfigs(data.entity)) { + const topic = this.getDiscoveryTopic(config, data.entity); this.mqtt.publish(topic, null, {retain: true, qos: 0}, this.discoveryTopic, false, false); } } - this.discover(data.device, true); + this.discover(data.entity, true); - if (this.discoveredTriggers[data.device.ieeeAddr]) { - for (const config of this.discoveredTriggers[data.device.ieeeAddr]) { + if (data.entity.isDevice() && this.discoveredTriggers[data.entity.ieeeAddr]) { + for (const config of this.discoveredTriggers[data.entity.ieeeAddr]) { const key = config.substring(0, config.indexOf('_')); const value = config.substring(config.indexOf('_') + 1); - this.publishDeviceTriggerDiscover(data.device, key, value, true); + this.publishDeviceTriggerDiscover(data.entity, key, value, true); } } } diff --git a/lib/extension/legacy/bridgeLegacy.ts b/lib/extension/legacy/bridgeLegacy.ts index cf8ccf443..0cf76124a 100644 --- a/lib/extension/legacy/bridgeLegacy.ts +++ b/lib/extension/legacy/bridgeLegacy.ts @@ -217,7 +217,7 @@ export default class BridgeLegacy extends Extension { logger.info(`Successfully renamed - ${from} to ${to} `); const entity = this.zigbee.resolveEntity(to); if (entity.isDevice()) { - this.eventBus.emitDeviceRenamed({homeAssisantRename: false, from, to, device: entity}); + this.eventBus.emitEntityRenamed({homeAssisantRename: false, from, to, entity}); } this.mqtt.publish( diff --git a/lib/types/types.d.ts b/lib/types/types.d.ts index 044186ee4..315e840be 100644 --- a/lib/types/types.d.ts +++ b/lib/types/types.d.ts @@ -129,7 +129,7 @@ declare global { } namespace eventdata { - type DeviceRenamed = { device: Device, homeAssisantRename: boolean, from: string, to: string }; + type EntityRenamed = { entity: Device | Group, homeAssisantRename: boolean, from: string, to: string }; type DeviceRemoved = { ieeeAddr: string, name: string }; type MQTTMessage = { topic: string, message: string }; type MQTTMessagePublished = { topic: string, payload: string, options: {retain: boolean, qos: number} }; diff --git a/test/homeassistant.test.js b/test/homeassistant.test.js index 0ac820c91..5bb941a44 100644 --- a/test/homeassistant.test.js +++ b/test/homeassistant.test.js @@ -1141,6 +1141,50 @@ describe('HomeAssistant extension', () => { ); }); + it('Should refresh discovery when group is renamed', async () => { + MQTT.publish.mockClear(); + MQTT.events.message('zigbee2mqtt/bridge/request/group/rename', stringify({"from": "ha_discovery_group", "to": "ha_discovery_group_new","homeassistant_rename":true})); + await flushPromises(); + + const payload = { + "availability":[{"topic":"zigbee2mqtt/bridge/state"}], + "brightness":true, + "brightness_scale":254, + "color_mode":true, + "command_topic":"zigbee2mqtt/ha_discovery_group_new/set", + "device":{ + "identifiers":["zigbee2mqtt_1221051039810110150109113116116_9"], + "name":"ha_discovery_group_new", + "sw_version": version, + }, + "json_attributes_topic":"zigbee2mqtt/ha_discovery_group_new", + "max_mireds": 454, + "min_mireds": 250, + "name":"ha_discovery_group_new", + "schema":"json", + "state_topic":"zigbee2mqtt/ha_discovery_group_new", + "supported_color_modes":[ + "xy", + "color_temp" + ], + "unique_id":"9_light_zigbee2mqtt" + }; + + expect(MQTT.publish).toHaveBeenCalledWith( + 'homeassistant/light/1221051039810110150109113116116_9/light/config', + stringify(payload), + { retain: true, qos: 0 }, + expect.any(Function), + ); + + expect(MQTT.publish).toHaveBeenCalledWith( + 'homeassistant/light/1221051039810110150109113116116_9/light/config', + null, + { retain: true, qos: 0 }, + expect.any(Function), + ); + }); + it('Shouldnt refresh discovery when device is renamed and homeassistant_rename is false', async () => { MQTT.publish.mockClear(); MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({"from": "weather_sensor", "to": "weather_sensor_renamed","homeassistant_rename":false}));