fix: Look up the endpoint name of a group member to check its state (#33410)

This commit is contained in:
Cristian authored and GitHub committed 2026-10-18 07:20:57 +02:00
1 parent e00a46a2fb
commit c588eca20e
2 files changed
+37 -8

No files matched your search

+6 -8
View File
@@ -165,14 +165,12 @@ export default class Groups extends Extension {
if (this.state.exists(device)) {
const state = this.state.get(device);
const endpointNames = device.isDevice() && device.getEndpointNames();
const stateKey =
endpointNames &&
endpointNames.length >= member.ID &&
device.definition?.meta?.multiEndpoint &&
!device.definition.meta.multiEndpointSkip?.includes("state")
? `state_${endpointNames[member.ID - 1]}`
: "state";
// the endpoint ID is not a position in getEndpointNames(), look the name up instead
const endpointName =
device.isDevice() && device.definition?.meta?.multiEndpoint && !device.definition.meta.multiEndpointSkip?.includes("state")
? device.endpointName(member)
: undefined;
const stateKey = endpointName ? `state_${endpointName}` : "state";
if (state[stateKey] === "ON" || state[stateKey] === "OPEN") {
return false;
+31
View File
@@ -20,6 +20,7 @@ returnDevices.push(
devices.bulb_2.ieeeAddr,
devices.GLEDOPTO_2ID.ieeeAddr,
devices.InovelliVZM31SN.ieeeAddr,
devices.zigfred_plus.ieeeAddr,
);
describe("Extension: Groups", () => {
@@ -362,6 +363,36 @@ describe("Extension: Groups", () => {
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({state: "OFF"}), {retain: false, qos: 0});
});
it.each([
{name: "still on", l2: "ON", groupOff: false},
{name: "off", l2: "OFF", groupOff: true},
])("Should check the state of the member endpoint by its name when its ID is not its position ($name)", async ({l2, groupOff}) => {
// ZFP-1A-CH maps l2 to endpoint 7, so its state is state_l2, not state_<getEndpointNames()[7 - 1]>
const device_1 = devices.bulb_color;
const endpoint_1 = device_1.getEndpoint(1)!;
const endpoint_2 = devices.zigfred_plus.getEndpoint(7)!;
const group = groups.group_1;
group.members.push(endpoint_1);
group.members.push(endpoint_2);
settings.set(["groups"], {
1: {friendly_name: "group_1", retain: false},
});
await mockMQTTEvents.message("zigbee2mqtt/group_1/set", stringify({state: "ON"}));
await flushPromises();
// @ts-expect-error private
controller.state.set(controller.zigbee.resolveEntity(devices.zigfred_plus.ieeeAddr)!, {state_l2: l2});
mockMQTTPublishAsync.mockClear();
await mockMQTTEvents.message("zigbee2mqtt/bulb_color/set", stringify({state: "OFF"}));
await flushPromises();
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", stringify({state: "OFF"}), {retain: false, qos: 0});
if (groupOff) {
expect(mockMQTTPublishAsync).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({state: "OFF"}), {retain: false, qos: 0});
} else {
expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({state: "OFF"}), {retain: false, qos: 0});
}
});
it("Should not publish state change off if any lights within are still on when changed via shared group", async () => {
const device_1 = devices.bulb_color;
const device_2 = devices.bulb;