diff --git a/lib/extension/groups.js b/lib/extension/groups.js index aac5274d4..a7cab58b4 100644 --- a/lib/extension/groups.js +++ b/lib/extension/groups.js @@ -6,6 +6,7 @@ const fs = require('fs'); const diff = require('deep-diff'); const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/.+/(remove|add|remove_all)$`); +const topicRegexRemoveAll = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/(remove|add|remove_all)$`); class Groups { constructor(zigbee, mqtt, state, publishEntityState) { @@ -31,6 +32,7 @@ class Groups { this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/group/+/remove`); this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/group/+/add`); this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/group/+/remove_all`); + this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/group/remove_all`); } apply(from, to) { @@ -117,7 +119,7 @@ class Groups { } parseTopic(topic) { - if (!topic.match(topicRegex)) { + if (!topic.match(topicRegex) && !topic.match(topicRegexRemoveAll)) { return null; } @@ -130,7 +132,7 @@ class Groups { // Remove type from topic topic = topic.replace(`/${type}`, ''); - return {friendly_name: topic, type}; + return {friendly_name: type === 'remove_all' ? null : topic, type}; } updateDeviceGroup(ID, cmd, groupID) { @@ -190,8 +192,8 @@ class Groups { } else if (cmd === 'remove') { settings.removeDeviceFromGroup(groupID, ieeeAddr); } else if (cmd === 'removeAll') { - Object.keys(settings.get().groups).forEach((groupID) => { - settings.removeDeviceFromGroup(groupID, ieeeAddr); + Object.keys(settings.get().groups).forEach((groupID_) => { + settings.removeDeviceFromGroup(groupID_, ieeeAddr); }); } } @@ -211,14 +213,19 @@ class Groups { } // Find ID of this group. - const groupID = settings.getGroupIDByFriendlyName(topic.friendly_name); - if (!groupID) { - logger.error(`Group with friendly_name '${topic.friendly_name}' doesn't exist`); - return; + let groupID = null; + if (topic.type !== 'remove_all') { + groupID = settings.getGroupIDByFriendlyName(topic.friendly_name); + if (!groupID) { + logger.error(`Group with friendly_name '${topic.friendly_name}' doesn't exist`); + return; + } + + groupID = groupID.toString(); } // Send command to the device. - this.updateDeviceGroup(message.toString(), topic.type, groupID.toString()); + this.updateDeviceGroup(message.toString(), topic.type, groupID); return true; } diff --git a/test/group.test.js b/test/group.test.js index 26adb5cde..dd0851db0 100644 --- a/test/group.test.js +++ b/test/group.test.js @@ -257,4 +257,32 @@ describe('Groups', () => { {groupid: '1'}, null, 2, expect.any(Function) ); }); + + it('Remove all group via MQTT', async () => { + zigbee.publish.mockClear(); + zigbee.getDevice = () => ({modelId: 'lumi.ctrl_neutral2'}); + zigbee.getEndpoint = (entityID, ep) => ({epId: ep}); + //jest.spyOn(settings, 'getGroupIDByFriendlyName').mockReturnValue(1); + jest.spyOn(settings, 'getIeeeAddrByFriendlyName').mockReturnValue('0x12345689'); + groupExtension.onMQTTMessage('zigbee2mqtt/bridge/group/remove_all', 'my_switch'); + expect(zigbee.publish).toHaveBeenCalledTimes(1); + expect(zigbee.publish).toHaveBeenCalledWith( + '0x12345689', 'device', 'genGroups', 'removeAll', 'functional', + {}, null, null, expect.any(Function) + ); + }); + + it('Remove all group via MQTT deprecated', async () => { + zigbee.publish.mockClear(); + zigbee.getDevice = () => ({modelId: 'lumi.ctrl_neutral2'}); + zigbee.getEndpoint = (entityID, ep) => ({epId: ep}); + jest.spyOn(settings, 'getGroupIDByFriendlyName').mockReturnValue(1); + jest.spyOn(settings, 'getIeeeAddrByFriendlyName').mockReturnValue('0x12345689'); + groupExtension.onMQTTMessage('zigbee2mqtt/bridge/group/my_group/remove_all', 'my_switch'); + expect(zigbee.publish).toHaveBeenCalledTimes(1); + expect(zigbee.publish).toHaveBeenCalledWith( + '0x12345689', 'device', 'genGroups', 'removeAll', 'functional', + {}, null, null, expect.any(Function) + ); + }); });