diff --git a/lib/extension/publish.js b/lib/extension/publish.js index 070d345b81..f13446c24e 100644 --- a/lib/extension/publish.js +++ b/lib/extension/publish.js @@ -98,10 +98,15 @@ class EntityPublish extends Extension { converters = resolvedEntity.definition.toZigbee; options = resolvedEntity.settings; } else { - converters = groupConverters; target = resolvedEntity.group; options = resolvedEntity.settings; - definition = resolvedEntity.group.members.map((e) => zigbeeHerdsmanConverters.findByDevice(e.getDevice())); + definition = resolvedEntity.group.members + .map((e) => zigbeeHerdsmanConverters.findByDevice(e.getDevice())).filter((d) => d); + converters = new Set(groupConverters); + for (const d of definition) { + d.toZigbee.forEach(converters.add, converters); + } + converters = [...converters]; } // Convert the MQTT message to a Zigbee message. diff --git a/test/bridge.test.js b/test/bridge.test.js index 8e3754fa8b..f3cf107d3d 100644 --- a/test/bridge.test.js +++ b/test/bridge.test.js @@ -80,11 +80,11 @@ describe('Bridge', () => { expect(logger.error).toHaveBeenCalledTimes(0); }); - it('Should publish devices on startup', async () => { + it('Should publish groups on startup', async () => { logger.setTransportsEnabled(true); expect(MQTT.publish).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/groups', - stringify([{"id":1,"friendly_name":"group_1","members":[]},{"id":15071,"friendly_name":"group_tradfri_remote","members":[]},{"id":99,"friendly_name":99,"members":[]},{"id":11,"friendly_name":"group_with_tradfri","members":[]},{"id":2,"friendly_name":"group_2","members":[]}]), + stringify([{"friendly_name":"group_1","id":1,"members":[]},{"friendly_name":"group_tradfri_remote","id":15071,"members":[]},{"friendly_name":99,"id":99,"members":[]},{"friendly_name":"group_with_tradfri","id":11,"members":[]},{"friendly_name":"thermostat_group","id":12,"members":[]},{"friendly_name":"group_2","id":2,"members":[]}]), { retain: true, qos: 0 }, expect.any(Function) ); diff --git a/test/legacy/bridgeLegacy.test.js b/test/legacy/bridgeLegacy.test.js index 21010ee79d..f5e322663e 100644 --- a/test/legacy/bridgeLegacy.test.js +++ b/test/legacy/bridgeLegacy.test.js @@ -188,7 +188,7 @@ describe('Bridge legacy', () => { await flushPromises(); expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bridge/log'); const payload = JSON.parse(MQTT.publish.mock.calls[0][1]); - expect(payload).toStrictEqual({"message": [{"ID": 1, "friendly_name": "group_1", "retain": false, 'devices': [], optimistic: true}, {"ID": 2, "friendly_name": "group_2", "retain": false, "devices": [], optimistic: true}, {"ID": 11, "friendly_name": "group_with_tradfri", "retain": false, "devices": ['bulb_2'], optimistic: true}, {"ID": 15071, "friendly_name": "group_tradfri_remote", "retain": false, "devices": ['bulb_color_2', 'bulb_2'], optimistic: true}], "type": "groups"}); + expect(payload).toStrictEqual({"message": [{"ID": 1, "friendly_name": "group_1", "retain": false, 'devices': [], optimistic: true}, {"ID": 2, "friendly_name": "group_2", "retain": false, "devices": [], optimistic: true}, {"ID": 11, "friendly_name": "group_with_tradfri", "retain": false, "devices": ['bulb_2'], optimistic: true}, {"ID": 12, "friendly_name": "thermostat_group", "retain": false, "devices": ['TS0601_thermostat'], optimistic: true}, {"ID": 15071, "friendly_name": "group_tradfri_remote", "retain": false, "devices": ['bulb_color_2', 'bulb_2'], optimistic: true}], "type": "groups"}); }); it('Should allow rename devices', async () => { diff --git a/test/publish.test.js b/test/publish.test.js index 8e0bce5652..2145777933 100644 --- a/test/publish.test.js +++ b/test/publish.test.js @@ -322,6 +322,14 @@ describe('Publish', () => { expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({state: 'ON', brightness: 127}); }); + it('Should publish messages to groups when converter is not in the default list but device in it supports it', async () => { + const group = zigbeeHerdsman.groups.thermostat_group; + await MQTT.events.message('zigbee2mqtt/thermostat_group/set', stringify({child_lock: 'LOCK'})); + await flushPromises(); + expect(group.command).toHaveBeenCalledTimes(1); + expect(group.command).toHaveBeenCalledWith("manuSpecificTuyaDimmer", "setData", {data: [1,1], dp: 263, fn: 0, status: 0, transid: expect.any(Number)}, {disableDefaultResponse: true}); + }); + it('Should publish messages to groups with on and brightness', async () => { const group = zigbeeHerdsman.groups.group_1; await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON', brightness: 50})); @@ -368,10 +376,10 @@ describe('Publish', () => { it('Should create and publish to group which is in configuration.yaml but not in zigbee-herdsman', async () => { delete zigbeeHerdsman.groups.group_2; - expect(Object.values(zigbeeHerdsman.groups).length).toBe(4); + expect(Object.values(zigbeeHerdsman.groups).length).toBe(5); await MQTT.events.message('zigbee2mqtt/group_2/set', stringify({state: 'ON'})); await flushPromises(); - expect(Object.values(zigbeeHerdsman.groups).length).toBe(5); + expect(Object.values(zigbeeHerdsman.groups).length).toBe(6); expect(zigbeeHerdsman.groups.group_2.command).toHaveBeenCalledTimes(1); expect(zigbeeHerdsman.groups.group_2.command).toHaveBeenCalledWith("genOnOff", "on", {}, {}); }); diff --git a/test/stub/data.js b/test/stub/data.js index 7d300a39ed..a196be2ef1 100644 --- a/test/stub/data.js +++ b/test/stub/data.js @@ -182,6 +182,11 @@ function writeDefaultConfiguration() { retain: false, devices: ['bulb_2'] }, + '12': { + friendly_name: 'thermostat_group', + retain: false, + devices: ['TS0601_thermostat'], + } }, external_converters: [], }; diff --git a/test/stub/zigbeeHerdsman.js b/test/stub/zigbeeHerdsman.js index 4dda843bc9..c210eca0d6 100644 --- a/test/stub/zigbeeHerdsman.js +++ b/test/stub/zigbeeHerdsman.js @@ -106,6 +106,7 @@ const returnDevices = []; const bulb_color = new Device('Router', '0x000b57fffec6a5b3', 40399, 4107, [new Endpoint(1, [0,3,4,5,6,8,768,2821,4096], [5,25,32,4096], '0x000b57fffec6a5b3', [], {lightingColorCtrl: {colorCapabilities: 254}})], true, "Mains (single phase)", "LLC020"); const bulb_color_2 = new Device('Router', '0x000b57fffec6a5b4', 401292, 4107, [new Endpoint(1, [0,3,4,5,6,8,768,2821,4096], [5,25,32,4096], '0x000b57fffec6a5b4')], true, "Mains (single phase)", "LLC020"); const bulb_2 = new Device('Router', '0x000b57fffec6a5b7', 40369, 4476, [new Endpoint(1, [0,3,4,5,6,8,768,2821,4096], [5,25,32,4096], '0x000b57fffec6a5b7')], true, "Mains (single phase)", "TRADFRI bulb E27 WS opal 980lm"); +const TS0601_thermostat = new Device('EndDevice', '0x0017882104a44559', 6544,4151, [new Endpoint(1, [], [], '0x0017882104a44559')], true, "Mains (single phase)", 'kud7u2l'); const devices = { 'coordinator': new Device('Coordinator', '0x00124b00120144ae', 0, 0, [new Endpoint(1, [], [])], false), @@ -150,14 +151,15 @@ const devices = { 'U202DST600ZB': new Device('Router', '0x0017880104e43559', 6540,4151, [new Endpoint(10, [0, 6], [], '0x0017880104e43559'), new Endpoint(11, [0, 6], [], '0x0017880104e43559')], true, "Mains (single phase)", 'U202DST600ZB'), '3157100': new Device('Router', '0x0017880104e44559', 6542,4151, [new Endpoint(1, [], [], '0x0017880104e44559')], true, "Mains (single phase)", '3157100'), 'J1': new Device('Router', '0x0017880104a44559', 6543,4151, [new Endpoint(1, [], [], '0x0017880104a44559')], true, "Mains (single phase)", 'J1 (5502)'), - 'TS0601_thermostat': new Device('EndDevice', '0x0017882104a44559', 6544,4151, [new Endpoint(1, [], [], '0x0017882104a44559')], true, "Mains (single phase)", 'kud7u2l'), + 'TS0601_thermostat': TS0601_thermostat, } const groups = { 'group_1': new Group(1, []), 'group_tradfri_remote': new Group(15071, [bulb_color_2.endpoints[0], bulb_2.endpoints[0]]), 'group/with/slashes': new Group(99, []), - 'group_with_tradfri': new Group(11, [bulb_2.endpoints[0]]) + 'group_with_tradfri': new Group(11, [bulb_2.endpoints[0]]), + 'thermostat_group': new Group(12, [TS0601_thermostat.endpoints[0]]) } const mock = {