From 206f02b200a5aa0034bf6f243cfd3e80fb242fd6 Mon Sep 17 00:00:00 2001 From: Koen Kanters Date: Sat, 19 Sep 2020 11:07:54 +0200 Subject: [PATCH] Updates for scenes. https://github.com/Koenkk/zigbee-herdsman-converters/pull/1531 --- lib/extension/publish.js | 21 ++++++++++++--- lib/state.js | 1 - test/publish.test.js | 52 +++++++++++++++++++++++++++++++++++++ test/stub/zigbeeHerdsman.js | 2 ++ 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/lib/extension/publish.js b/lib/extension/publish.js index 130f0ffe..1596d68f 100644 --- a/lib/extension/publish.js +++ b/lib/extension/publish.js @@ -83,6 +83,7 @@ class EntityPublish extends Extension { let options = {}; let device = null; let definition = null; + let membersState = null; assert(resolvedEntity.type === 'device' || resolvedEntity.type === 'group'); if (resolvedEntity.type === 'device') { @@ -107,6 +108,11 @@ class EntityPublish extends Extension { d.toZigbee.forEach(converters.add, converters); } converters = [...converters]; + membersState = {}; + for (const member of resolvedEntity.group.members) { + const ieeeAddr = member.getDevice().ieeeAddr; + membersState[ieeeAddr] = this.state.get(ieeeAddr); + } } // Convert the MQTT message to a Zigbee message. @@ -139,12 +145,12 @@ class EntityPublish extends Extension { * the color temperature. This would lead to 2 zigbee publishes, where the first one * (state) is probably unecessary. */ - const deviceState = this.state.get(resolvedEntity.settings.ID) || {}; + const entityState = this.state.get(resolvedEntity.settings.ID) || {}; if (settings.get().homeassistant) { const hasColorTemp = json.hasOwnProperty('color_temp'); const hasColor = json.hasOwnProperty('color'); const hasBrightness = json.hasOwnProperty('brightness'); - const isOn = deviceState && deviceState.state === 'ON' ? true : false; + const isOn = entityState && entityState.state === 'ON' ? true : false; if (isOn && (hasColorTemp || hasColor) && !hasBrightness) { delete json.state; logger.debug('Skipping state because of Home Assistant'); @@ -208,7 +214,8 @@ class EntityPublish extends Extension { message: json, logger, device, - state: deviceState, + state: entityState, + membersState, mapped: definition, }; @@ -231,6 +238,14 @@ class EntityPublish extends Extension { this.publishEntityState(resolvedEntity.settings.ID, msg); } + // Enable after merge: https://github.com/Koenkk/zigbee-herdsman-converters/pull/1531 + /* istanbul ignore next */ + if (result && result.membersState) { + for (const [ieeeAddr, state] of Object.entries(result.membersState)) { + this.publishEntityState(ieeeAddr, state); + } + } + // It's possible for devices to get out of sync when writing an attribute that's not reportable. // So here we re-read the value after a specified timeout, this timeout could for example be the // transition time of a color change or for forcing a state read for devices that don't diff --git a/lib/state.js b/lib/state.js index 301c2217..402abffa 100644 --- a/lib/state.js +++ b/lib/state.js @@ -61,7 +61,6 @@ class State { try { fs.writeFileSync(this.file, json, 'utf8'); } catch (e) { - console.log(e.message); logger.error(`Failed to write state to '${this.file}' (${e.message})`); } } else { diff --git a/test/publish.test.js b/test/publish.test.js index 1eeaf3a0..90089f97 100644 --- a/test/publish.test.js +++ b/test/publish.test.js @@ -1250,4 +1250,56 @@ describe('Publish', () => { expect(MQTT.publish.mock.calls[2]).toEqual([ 'zigbee2mqtt/bulb_color', stringify({"state":"OFF","brightness":150}), { qos: 0, retain: false }, expect.any(Function)]); expect(MQTT.publish.mock.calls[3]).toEqual([ 'zigbee2mqtt/bulb_color', stringify({"state":"ON","brightness":150}), { qos: 0, retain: false }, expect.any(Function)]); }); + + // Enable after merge: https://github.com/Koenkk/zigbee-herdsman-converters/pull/1531 + // it('Scenes', async () => { + // const bulb_color_2 = zigbeeHerdsman.devices.bulb_color_2.getEndpoint(1); + // const bulb_2 = zigbeeHerdsman.devices.bulb_2.getEndpoint(1); + // const group = zigbeeHerdsman.groups.group_tradfri_remote; + // await MQTT.events.message('zigbee2mqtt/bulb_color_2/set', stringify({"state": "ON", "brightness": 50, "color_temp": 290})); + // await MQTT.events.message('zigbee2mqtt/bulb_2/set', stringify({"state": "ON", "brightness": 100})); + // await flushPromises(); + + // await MQTT.events.message('zigbee2mqtt/group_tradfri_remote/set', stringify({"scene_store": 1})); + // await flushPromises(); + // expect(group.command).toHaveBeenCalledTimes(1); + // expect(group.command).toHaveBeenCalledWith('genScenes', 'store', { groupid: 15071, sceneid: 1 }, {}); + + // await MQTT.events.message('zigbee2mqtt/bulb_color_2/set', stringify({"state": "ON", "brightness": 250, "color_temp": 20})); + // await MQTT.events.message('zigbee2mqtt/bulb_2/set', stringify({"state": "ON", "brightness": 110})); + // await flushPromises(); + + // MQTT.publish.mockClear(); + // group.command.mockClear(); + // await MQTT.events.message('zigbee2mqtt/group_tradfri_remote/set', stringify({"scene_recall": 1})); + // await flushPromises(); + // expect(group.command).toHaveBeenCalledTimes(1); + // expect(group.command).toHaveBeenCalledWith('genScenes', 'recall', { groupid: 15071, sceneid: 1 }, {}); + // expect(MQTT.publish).toHaveBeenCalledTimes(5); + // expect(MQTT.publish).toHaveBeenNthCalledWith(1, + // 'zigbee2mqtt/group_tradfri_remote', + // stringify({"brightness":50,"color":{"x":0.408707336668894,"y":0.39239142575868},"color_temp":290,"state":"ON"}), + // {retain: false, qos: 0}, expect.any(Function) + // ); + // expect(MQTT.publish).toHaveBeenNthCalledWith(2, + // 'zigbee2mqtt/bulb_color_2', + // stringify({"brightness":50,"color":{"x":0.408707336668894,"y":0.39239142575868},"color_temp":290,"state":"ON"}), + // {retain: false, qos: 0}, expect.any(Function) + // ); + // expect(MQTT.publish).toHaveBeenNthCalledWith(3, + // 'zigbee2mqtt/group_tradfri_remote', + // stringify({"brightness":100,"color":{"x":0.408707336668894,"y":0.39239142575868},"color_temp":290,"state":"ON"}), + // {retain: false, qos: 0}, expect.any(Function) + // ); + // expect(MQTT.publish).toHaveBeenNthCalledWith(4, + // 'zigbee2mqtt/bulb_2', + // stringify({"brightness":100,"color":{"x":0.408707336668894,"y":0.39239142575868},"color_temp":290,"state":"ON"}), + // {retain: false, qos: 0}, expect.any(Function) + // ); + // expect(MQTT.publish).toHaveBeenNthCalledWith(5, + // 'zigbee2mqtt/group_with_tradfri', + // stringify({"brightness":100,"color":{"x":0.408707336668894,"y":0.39239142575868},"color_temp":290,"state":"ON"}), + // {retain: false, qos: 0}, expect.any(Function) + // ); + // }); }); diff --git a/test/stub/zigbeeHerdsman.js b/test/stub/zigbeeHerdsman.js index 13bb75fd..afeb1c22 100644 --- a/test/stub/zigbeeHerdsman.js +++ b/test/stub/zigbeeHerdsman.js @@ -41,7 +41,9 @@ class Endpoint { this.write = jest.fn(); this.bind = jest.fn(); this.unbind = jest.fn(); + this.save = jest.fn(); this.configureReporting = jest.fn(); + this.meta = {}; this.binds = binds; this.getInputClusters = () => inputClusters.map((c) => { return {ID: c, name: getKeyByValue(clusters, c)};