diff --git a/lib/extension/groups.js b/lib/extension/groups.js index f17d2e01..27c424f0 100644 --- a/lib/extension/groups.js +++ b/lib/extension/groups.js @@ -82,18 +82,30 @@ class Groups extends BaseExtension { if (Object.keys(payload).length) { const entity = this.zigbee.resolveEntity(data.ID); + const zigbeeGroups = this.zigbee.getGroups().filter((zigbeeGroup) => { + const settingsGroup = settings.getGroup(zigbeeGroup.groupID); + return settingsGroup && settingsGroup.optimistic; + }); if (entity.type === 'device') { - const zigbeeGroups = this.zigbee.getGroups(); for (const zigbeeGroup of zigbeeGroups) { - const settingsGroup = settings.getGroup(zigbeeGroup.groupID); - if (settingsGroup && settingsGroup.optimistic && zigbeeGroup.hasMember(entity.endpoint)) { + if (zigbeeGroup.hasMember(entity.endpoint)) { await this.publishEntityState(zigbeeGroup.groupID, payload, reason); } } } else { + const groupIDsToPublish = new Set(); for (const member of entity.group.getMembers()) { await this.publishEntityState(member.deviceIeeeAddress, payload, reason); + for (const zigbeeGroup of zigbeeGroups) { + if (zigbeeGroup.hasMember(member)) { + groupIDsToPublish.add(zigbeeGroup.groupID); + } + } + } + groupIDsToPublish.delete(entity.group.groupID); + for (const groupID of groupIDsToPublish) { + await this.publishEntityState(groupID, payload, reason); } } } diff --git a/test/group.test.js b/test/group.test.js index 18acdadb..d1fcff64 100644 --- a/test/group.test.js +++ b/test/group.test.js @@ -293,4 +293,26 @@ describe('Groups', () => { expect(MQTT.publish).toHaveBeenCalledTimes(1); expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", '{"state":"ON","linkquality":10}', {"retain": false, qos: 0}, expect.any(Function)); }); + + it('Should publish state change of another group with shared device when a group changes its state', async () => { + const device = zigbeeHerdsman.devices.bulb_color; + const endpoint = device.getEndpoint(1); + const group = zigbeeHerdsman.groups.group_1; + group.members.push(endpoint); + settings.set(['groups'], { + '1': {friendly_name: 'group_1', retain: false, devices: [device.ieeeAddr]}, + '2': {friendly_name: 'group_2', retain: false, devices: [device.ieeeAddr]}, + '3': {friendly_name: 'group_3', retain: false, devices: []} + }); + await controller.start(); + await flushPromises(); + + MQTT.publish.mockClear(); + await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'ON'})); + await flushPromises(); + expect(MQTT.publish).toHaveBeenCalledTimes(3); + expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", '{"state":"ON"}', {"retain": false, qos: 0}, expect.any(Function)); + expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", '{"state":"ON"}', {"retain": false, qos: 0}, expect.any(Function)); + expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_2", '{"state":"ON"}', {"retain": false, qos: 0}, expect.any(Function)); + }); });