diff --git a/lib/extension/groups.ts b/lib/extension/groups.ts index 7d8ba29fcf..5af557ee43 100644 --- a/lib/extension/groups.ts +++ b/lib/extension/groups.ts @@ -100,7 +100,7 @@ export default class Groups extends Extension { if ( group.zh.hasMember(endpoint) && !equals(this.lastOptimisticState[group.ID], payload) && - this.shouldPublishPayloadForGroup(group, payload, endpointName) + this.shouldPublishPayloadForGroup(group, payload) ) { this.lastOptimisticState[group.ID] = payload; @@ -142,7 +142,7 @@ export default class Groups extends Extension { await this.publishEntityState(device, memberPayload, reason); for (const zigbeeGroup of groups) { - if (zigbeeGroup.zh.hasMember(member) && this.shouldPublishPayloadForGroup(zigbeeGroup, memberPayload, endpointName)) { + if (zigbeeGroup.zh.hasMember(member) && this.shouldPublishPayloadForGroup(zigbeeGroup, payload)) { groupsToPublish.add(zigbeeGroup); } } @@ -157,12 +157,11 @@ export default class Groups extends Extension { } } - private shouldPublishPayloadForGroup(group: Group, payload: KeyValue, endpointName: string | undefined): boolean { - const stateKey = endpointName ? `state_${endpointName}` : 'state'; + private shouldPublishPayloadForGroup(group: Group, payload: KeyValue): boolean { return ( group.options.off_state === 'last_member_state' || !payload || - (payload[stateKey] !== 'OFF' && payload[stateKey] !== 'CLOSE') || + (payload.state !== 'OFF' && payload.state !== 'CLOSE') || this.areAllMembersOffOrClosed(group) ); } diff --git a/test/extensions/groups.test.ts b/test/extensions/groups.test.ts index c5eb3347ac..7db66bcd05 100644 --- a/test/extensions/groups.test.ts +++ b/test/extensions/groups.test.ts @@ -266,11 +266,11 @@ describe('Extension: Groups', () => { expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); }); - it('Should not publish state change off if any lights within are still on when changed via device with non default-ep', async () => { + it('Should not publish state change off if any lights within with non default-ep are still on when changed via device', async () => { const device_1 = devices.bulb_color; const device_2 = devices.QBKG03LM; const endpoint_1 = device_1.getEndpoint(1)!; - const endpoint_2 = device_2.getEndpoint(3)!; + const endpoint_2 = device_2.getEndpoint(2)!; const group = groups.group_1; group.members.push(endpoint_1); group.members.push(endpoint_2); @@ -285,6 +285,31 @@ describe('Extension: Groups', () => { expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); }); + it('Should not publish state change off if any lights within are still on when changed via device with non default-ep', async () => { + const device_1 = devices.bulb_color; + const device_2 = devices.QBKG03LM; + const endpoint_1 = device_1.getEndpoint(1)!; + const endpoint_2 = device_2.getEndpoint(2)!; + const endpoint_3 = device_2.getEndpoint(3)!; + endpoint_3.removeFromGroup(groups.ha_discovery_group); + const group = groups.group_1; + group.members.push(endpoint_1); + group.members.push(endpoint_2); + group.members.push(endpoint_3); + + await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'})); + await flushPromises(); + mockMQTT.publishAsync.mockClear(); + + await mockMQTTEvents.message('zigbee2mqtt/wall_switch_double/set', stringify({state_left: 'OFF'})); + await flushPromises(); + expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1); + expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/wall_switch_double', stringify({state_left: 'OFF', state_right: 'ON'}), { + retain: false, + qos: 0, + }); + }); + it('Should publish state change off if all lights within turn off with non default-ep', async () => { const device_1 = devices.bulb_color; const device_2 = devices.QBKG03LM;