diff --git a/lib/extension/groups.js b/lib/extension/groups.js index 8353f549a..ab50e2184 100644 --- a/lib/extension/groups.js +++ b/lib/extension/groups.js @@ -114,7 +114,16 @@ class Groups extends Extension { const groupIDsToPublish = new Set(); for (const member of resolvedEntity.group.members) { - await this.publishEntityState(member.getDevice().ieeeAddr, payload, reason); + const memberPayload = {...payload}; + const endpointName = this.zigbee.resolveEntity(member).endpointName; + if (endpointName) { + Object.keys(memberPayload).forEach((key) => { + memberPayload[`${key}_${endpointName}`] = memberPayload[key]; + delete memberPayload[key]; + }); + } + + await this.publishEntityState(member.getDevice().ieeeAddr, memberPayload, reason); for (const zigbeeGroup of zigbeeGroups) { if (zigbeeGroup.hasMember(member)) { if (!payload || payload.state !== 'OFF' || this.areAllMembersOff(zigbeeGroup)) { diff --git a/lib/util/utils.js b/lib/util/utils.js index 3e96f62b3..9b67f0cae 100644 --- a/lib/util/utils.js +++ b/lib/util/utils.js @@ -190,6 +190,16 @@ function* getExternalConvertersDefinitions(settings) { } } +function getKey(object, value, fallback, convertTo) { + for (const key in object) { + if (object[key]===value) { + return convertTo ? convertTo(key) : key; + } + } + + return fallback; +} + function toSnakeCase(value) { if (typeof value === 'object') { value = {...value}; @@ -257,4 +267,5 @@ module.exports = { validateFriendlyName, loadModuleFromFile, loadModuleFromText, + getKey, }; diff --git a/lib/zigbee.js b/lib/zigbee.js index a7d340f0d..b2eb1f4fd 100644 --- a/lib/zigbee.js +++ b/lib/zigbee.js @@ -183,7 +183,8 @@ class Zigbee extends events.EventEmitter { resolveEntity(key) { assert( typeof key === 'string' || typeof key === 'number' || - key.constructor.name === 'Device' || key.constructor.name === 'Group', + key.constructor.name === 'Device' || key.constructor.name === 'Group' || + key.constructor.name === 'Endpoint', `Wrong type '${typeof key}'`, ); @@ -201,6 +202,7 @@ class Zigbee extends events.EventEmitter { endpoint: coordinator.getEndpoint(1), settings: {friendlyName: 'Coordinator'}, name: 'Coordinator', + endpointName: null, }; } @@ -223,20 +225,21 @@ class Zigbee extends events.EventEmitter { } const definition = zigbeeHerdsmanConverters.findByDevice(device); - const endpoints = definition && definition.endpoint ? definition.endpoint(device) : null; + const endpointNames = definition && definition.endpoint ? definition.endpoint(device) : null; let endpoint; if (endpointKey) { if (endpointByNumber) { endpoint = device.getEndpoint(endpointKey); } else { assert(definition != null, `Endpoint name '${endpointKey}' is given but device is unsupported`); - assert(endpoints != null, `Endpoint name '${endpointKey}' is given but no endpoints defined`); - const endpointID = endpoints[endpointKey]; + assert(endpointNames != null, + `Endpoint name '${endpointKey}' is given but no endpoints defined`); + const endpointID = endpointNames[endpointKey]; assert(endpointID, `Endpoint name '${endpointKey}' is given but device has no such endpoint`); endpoint = device.getEndpoint(endpointID); } - } else if (endpoints && endpoints['default']) { - endpoint = device.getEndpoint(endpoints['default']); + } else if (endpointNames && endpointNames['default']) { + endpoint = device.getEndpoint(endpointNames['default']); } else { endpoint = device.endpoints[0]; } @@ -244,21 +247,29 @@ class Zigbee extends events.EventEmitter { return { type: 'device', device, endpoint, settings: {...deviceOptions, ...entity}, name: entity.friendlyName, definition, + endpointName: utils.getKey(endpointNames, endpoint.ID, null, ((v) => v === 'default' ? null : v)), }; } else { let group = this.getGroupByID(entity.ID); if (!group) group = this.createGroup(entity.ID); return {type: 'group', group, settings: {...deviceOptions, ...entity}, name: entity.friendlyName}; } - } else if (key.constructor.name === 'Device') { - const setting = settings.getEntity(key.ieeeAddr); + } else if (key.constructor.name === 'Device' || key.constructor.name === 'Endpoint') { + const device = key.constructor.name === 'Endpoint' ? key.getDevice() : key; + const setting = settings.getEntity(device.ieeeAddr); + const definition = zigbeeHerdsmanConverters.findByDevice(device); + const name = setting ? setting.friendlyName : + (device.type === 'Coordinator' ? 'Coordinator' : device.ieeeAddr); + const endpointNames = definition && definition.endpoint ? definition.endpoint(device) : null; + + let endpoint; + if (key.constructor.name === 'Endpoint') endpoint = key; + else if (endpointNames && endpointNames['default']) endpoint = device.getEndpoint(endpointNames['default']); + else endpoint = device.endpoints[0]; + return { - type: 'device', - device: key, - endpoint: key.endpoints[0], - settings: {...deviceOptions, ...(setting || {})}, - name: setting ? setting.friendlyName : (key.type === 'Coordinator' ? 'Coordinator' : key.ieeeAddr), - definition: zigbeeHerdsmanConverters.findByDevice(key), + type: 'device', definition, name, device, endpoint, settings: {...deviceOptions, ...(setting || {})}, + endpointName: utils.getKey(endpointNames, endpoint.ID, null, ((v) => v === 'default' ? null : v)), }; } else { // Group const setting = settings.getEntity(key.groupID); diff --git a/test/bridge.test.js b/test/bridge.test.js index 2fa3af4bd..201b4130a 100644 --- a/test/bridge.test.js +++ b/test/bridge.test.js @@ -43,7 +43,7 @@ describe('Bridge', () => { const directory = settings.get().advanced.log_directory; expect(MQTT.publish).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/info', - stringify({ "restart_required": false, "commit": version.commitHash, "config": { "advanced": { "adapter_concurrent": null, "adapter_delay": null, "availability_blacklist": [], "availability_blocklist": [], "availability_passlist": [], "availability_timeout": 0, "availability_whitelist": [], "cache_state": true, "cache_state_persistent": true, "cache_state_send_on_startup": true, "channel": 11, "elapsed": false, "ext_pan_id": [221, 221, 221, 221, 221, 221, 221, 221], "homeassistant_discovery_topic": "homeassistant", "homeassistant_legacy_triggers": true, "homeassistant_status_topic": "hass/status", "last_seen": "disable", "legacy_api": false, "log_directory": directory, "log_file": "log.txt", "log_level": "info", "log_output": ["console", "file"], "log_rotation": true, "log_syslog": {}, "pan_id": 6754, "report": false, "soft_reset_timeout": 0, "timestamp_format": "YYYY-MM-DD HH:mm:ss" }, "ban": [], "blocklist": [], "device_options": {}, "devices": { "0x000b57fffec6a5b2": { "friendly_name": "bulb", "retain": true }, "0x000b57fffec6a5b3": { "friendly_name": "bulb_color", "retain": false }, "0x000b57fffec6a5b4": { "friendly_name": "bulb_color_2", "retain": false }, "0x000b57fffec6a5b7": { "friendly_name": "bulb_2", "retain": false }, "0x0017880104a44559": { "friendly_name": "J1_cover" }, "0x0017880104e43559": { "friendly_name": "U202DST600ZB" }, "0x0017880104e44559": { "friendly_name": "3157100_thermostat" }, "0x0017880104e45517": { "friendly_name": "remote", "retain": true }, "0x0017880104e45518": { "friendly_name": "0x0017880104e45518" }, "0x0017880104e45520": { "friendly_name": "button", "retain": false }, "0x0017880104e45521": { "friendly_name": "button_double_key", "retain": false }, "0x0017880104e45522": { "friendly_name": "weather_sensor", "qos": 1, "retain": false }, "0x0017880104e45523": { "friendly_name": "occupancy_sensor", "retain": false }, "0x0017880104e45524": { "friendly_name": "power_plug", "retain": false }, "0x0017880104e45526": { "friendly_name": "GL-S-007ZS" }, "0x0017880104e45529": { "friendly_name": "unsupported2", "retain": false }, "0x0017880104e45530": { "friendly_name": "button_double_key_interviewing", "retain": false }, "0x0017880104e45540": { "friendly_name": "ikea_onoff" }, "0x0017880104e45541": { "friendly_name": "wall_switch", "retain": false }, "0x0017880104e45542": { "friendly_name": "wall_switch_double", "retain": false }, "0x0017880104e45543": { "friendly_name": "led_controller_1", "retain": false }, "0x0017880104e45544": { "friendly_name": "led_controller_2", "retain": false }, "0x0017880104e45545": { "friendly_name": "dimmer_wall_switch", "retain": false }, "0x0017880104e45547": { "friendly_name": "curtain", "retain": false }, "0x0017880104e45548": { "friendly_name": "fan", "retain": false }, "0x0017880104e45549": { "friendly_name": "siren", "retain": false }, "0x0017880104e45550": { "friendly_name": "thermostat", "retain": false }, "0x0017880104e45551": { "friendly_name": "smart vent", "retain": false }, "0x0017880104e45552": { "friendly_name": "j1", "retain": false }, "0x0017880104e45553": { "friendly_name": "bulb_enddevice", "retain": false }, "0x0017880104e45559": { "friendly_name": "cc2530_router", "retain": false }, "0x0017880104e45560": { "friendly_name": "livolo", "retain": false }, "0x0017882104a44559": { "friendly_name": "TS0601_thermostat" }, "0x0017882194e45543": { "friendly_name": "QS-Zigbee-D02-TRIAC-2C-LN" }, "0x90fd9ffffe4b64aa": { "friendly_name": "SP600_OLD" }, "0x90fd9ffffe4b64ab": { "friendly_name": "SP600_NEW" }, "0x90fd9ffffe4b64ac": { "friendly_name": "MKS-CM-W5" }, "0x90fd9ffffe4b64ae": { "friendly_name": "tradfri_remote", "retain": false }, "0x90fd9ffffe4b64af": { "friendly_name": "roller_shutter" }, "0x90fd9ffffe4b64ax": { "friendly_name": "ZNLDP12LM" } }, "experimental": { "output": "json" }, "external_converters": [], "groups": { "1": { "friendly_name": "group_1", "retain": false }, "11": { "devices": ["bulb_2"], "friendly_name": "group_with_tradfri", "retain": false }, "14": { "devices": ["power_plug"], "friendly_name": "switch_group", "retain": false }, "12": { "devices": ["TS0601_thermostat"], "friendly_name": "thermostat_group", "retain": false }, "15071": { "devices": ["bulb_color_2", "bulb_2"], "friendly_name": "group_tradfri_remote", "retain": false }, "2": { "friendly_name": "group_2", "retain": false } }, "homeassistant": false, "map_options": { "graphviz": { "colors": { "fill": { "coordinator": "#e04e5d", "enddevice": "#fff8ce", "router": "#4ea3e0" }, "font": { "coordinator": "#ffffff", "enddevice": "#000000", "router": "#ffffff" }, "line": { "active": "#009900", "inactive": "#994444" } } } }, "mqtt": { "base_topic": "zigbee2mqtt", "include_device_information": false, "server": "mqtt://localhost", "force_disable_retain": false, }, "ota":{"disable_automatic_update_check":false,"update_check_interval":1440}, "passlist": [], "permit_join": true, "serial": { "disable_led": false, "port": "/dev/dummy" }, "whitelist": [] }, "coordinator": { "meta": { "revision": 20190425, "version": 1 }, "type": "z-Stack" }, "log_level": "info", "network": { "channel": 15, "extended_pan_id": [0, 11, 22], "pan_id": 5674 }, "permit_join": false, "version": version.version, config_schema: settings.schema }), + stringify({ "restart_required": false, "commit": version.commitHash, "config": { "advanced": { "adapter_concurrent": null, "adapter_delay": null, "availability_blacklist": [], "availability_blocklist": [], "availability_passlist": [], "availability_timeout": 0, "availability_whitelist": [], "cache_state": true, "cache_state_persistent": true, "cache_state_send_on_startup": true, "channel": 11, "elapsed": false, "ext_pan_id": [221, 221, 221, 221, 221, 221, 221, 221], "homeassistant_discovery_topic": "homeassistant", "homeassistant_legacy_triggers": true, "homeassistant_status_topic": "hass/status", "last_seen": "disable", "legacy_api": false, "log_directory": directory, "log_file": "log.txt", "log_level": "info", "log_output": ["console", "file"], "log_rotation": true, "log_syslog": {}, "pan_id": 6754, "report": false, "soft_reset_timeout": 0, "timestamp_format": "YYYY-MM-DD HH:mm:ss" }, "ban": [], "blocklist": [], "device_options": {}, "devices": { "0x000b57fffec6a5b2": { "friendly_name": "bulb", "retain": true }, "0x000b57fffec6a5b3": { "friendly_name": "bulb_color", "retain": false }, "0x000b57fffec6a5b4": { "friendly_name": "bulb_color_2", "retain": false }, "0x000b57fffec6a5b7": { "friendly_name": "bulb_2", "retain": false }, "0x0017880104a44559": { "friendly_name": "J1_cover" }, "0x0017880104e43559": { "friendly_name": "U202DST600ZB" }, "0x0017880104e44559": { "friendly_name": "3157100_thermostat" }, "0x0017880104e45517": { "friendly_name": "remote", "retain": true }, "0x0017880104e45518": { "friendly_name": "0x0017880104e45518" }, "0x0017880104e45520": { "friendly_name": "button", "retain": false }, "0x0017880104e45521": { "friendly_name": "button_double_key", "retain": false }, "0x0017880104e45522": { "friendly_name": "weather_sensor", "qos": 1, "retain": false }, "0x0017880104e45523": { "friendly_name": "occupancy_sensor", "retain": false }, "0x0017880104e45524": { "friendly_name": "power_plug", "retain": false }, "0x0017880104e45526": { "friendly_name": "GL-S-007ZS" }, "0x0017880104e45529": { "friendly_name": "unsupported2", "retain": false }, "0x0017880104e45530": { "friendly_name": "button_double_key_interviewing", "retain": false }, "0x0017880104e45540": { "friendly_name": "ikea_onoff" }, "0x0017880104e45541": { "friendly_name": "wall_switch", "retain": false }, "0x0017880104e45542": { "friendly_name": "wall_switch_double", "retain": false }, "0x0017880104e45543": { "friendly_name": "led_controller_1", "retain": false }, "0x0017880104e45544": { "friendly_name": "led_controller_2", "retain": false }, "0x0017880104e45545": { "friendly_name": "dimmer_wall_switch", "retain": false }, "0x0017880104e45547": { "friendly_name": "curtain", "retain": false }, "0x0017880104e45548": { "friendly_name": "fan", "retain": false }, "0x0017880104e45549": { "friendly_name": "siren", "retain": false }, "0x0017880104e45550": { "friendly_name": "thermostat", "retain": false }, "0x0017880104e45551": { "friendly_name": "smart vent", "retain": false }, "0x0017880104e45552": { "friendly_name": "j1", "retain": false }, "0x0017880104e45553": { "friendly_name": "bulb_enddevice", "retain": false }, "0x0017880104e45559": { "friendly_name": "cc2530_router", "retain": false }, "0x0017880104e45560": { "friendly_name": "livolo", "retain": false }, "0x0017880104e45724":{"friendly_name":"GLEDOPTO_2ID"}, "0x0017882104a44559": { "friendly_name": "TS0601_thermostat" }, "0x0017882194e45543": { "friendly_name": "QS-Zigbee-D02-TRIAC-2C-LN" }, "0x90fd9ffffe4b64aa": { "friendly_name": "SP600_OLD" }, "0x90fd9ffffe4b64ab": { "friendly_name": "SP600_NEW" }, "0x90fd9ffffe4b64ac": { "friendly_name": "MKS-CM-W5" }, "0x90fd9ffffe4b64ae": { "friendly_name": "tradfri_remote", "retain": false }, "0x90fd9ffffe4b64af": { "friendly_name": "roller_shutter" }, "0x90fd9ffffe4b64ax": { "friendly_name": "ZNLDP12LM" } }, "experimental": { "output": "json" }, "external_converters": [], "groups": { "1": { "friendly_name": "group_1", "retain": false }, "11": { "devices": ["bulb_2"], "friendly_name": "group_with_tradfri", "retain": false }, "14": { "devices": ["power_plug"], "friendly_name": "switch_group", "retain": false }, "12": { "devices": ["TS0601_thermostat"], "friendly_name": "thermostat_group", "retain": false }, "15071": { "devices": ["bulb_color_2", "bulb_2"], "friendly_name": "group_tradfri_remote", "retain": false }, "2": { "friendly_name": "group_2", "retain": false }, "21":{"devices":["GLEDOPTO_2ID/cct"],"friendly_name":"gledopto_group"} }, "homeassistant": false, "map_options": { "graphviz": { "colors": { "fill": { "coordinator": "#e04e5d", "enddevice": "#fff8ce", "router": "#4ea3e0" }, "font": { "coordinator": "#ffffff", "enddevice": "#000000", "router": "#ffffff" }, "line": { "active": "#009900", "inactive": "#994444" } } } }, "mqtt": { "base_topic": "zigbee2mqtt", "include_device_information": false, "server": "mqtt://localhost", "force_disable_retain": false, }, "ota":{"disable_automatic_update_check":false,"update_check_interval":1440}, "passlist": [], "permit_join": true, "serial": { "disable_led": false, "port": "/dev/dummy" }, "whitelist": [] }, "coordinator": { "meta": { "revision": 20190425, "version": 1 }, "type": "z-Stack" }, "log_level": "info", "network": { "channel": 15, "extended_pan_id": [0, 11, 22], "pan_id": 5674 }, "permit_join": false, "version": version.version, config_schema: settings.schema }), { retain: true, qos: 0 }, expect.any(Function) ); @@ -88,7 +88,7 @@ describe('Bridge', () => { logger.setTransportsEnabled(true); expect(MQTT.publish).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/groups', - 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":"switch_group","id":14,"members":[{"endpoint":1,"ieee_address":"0x0017880104e45524"}]},{"friendly_name":"default_bind_group","id":901,"members":[]},{"friendly_name":"group_2","id":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":"switch_group","id":14,"members":[{"endpoint":1,"ieee_address":"0x0017880104e45524"}]},{"friendly_name":"gledopto_group","id":21,"members":[]},{"friendly_name":"default_bind_group","id":901,"members":[]},{"friendly_name":"group_2","id":2,"members":[]}]), { retain: true, qos: 0 }, expect.any(Function) ); diff --git a/test/group.test.js b/test/group.test.js index 89b20c98c..ccfca44bf 100644 --- a/test/group.test.js +++ b/test/group.test.js @@ -9,6 +9,7 @@ zigbeeHerdsman.returnDevices.push('0x000b57fffec6a5b2'); zigbeeHerdsman.returnDevices.push('0x0017880104e45542'); zigbeeHerdsman.returnDevices.push('0x000b57fffec6a5b4'); zigbeeHerdsman.returnDevices.push('0x000b57fffec6a5b7'); +zigbeeHerdsman.returnDevices.push('0x0017880104e45724'); const MQTT = require('./stub/mqtt'); const Controller = require('../lib/controller'); @@ -371,6 +372,21 @@ describe('Groups', () => { expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function)); }); + it('Should publish state of device with endpoint name', async () => { + const group = zigbeeHerdsman.groups.gledopto_group; + await controller.start(); + await flushPromises(); + + MQTT.publish.mockClear(); + await MQTT.events.message('zigbee2mqtt/gledopto_group/set', stringify({state: 'ON'})); + await flushPromises(); + expect(MQTT.publish).toHaveBeenCalledTimes(2); + expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/GLEDOPTO_2ID", stringify({"state_cct":"ON"}), {"retain": false, qos: 0}, expect.any(Function)); + expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/gledopto_group", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function)); + expect(group.command).toHaveBeenCalledTimes(1); + expect(group.command).toHaveBeenCalledWith("genOnOff", "on", {}, {}); + }); + it('Should publish state change of all members when a group changes its state, filtered', async () => { const device = zigbeeHerdsman.devices.bulb_color; const endpoint = device.getEndpoint(1); diff --git a/test/legacy/bridgeLegacy.test.js b/test/legacy/bridgeLegacy.test.js index 432ae9cfe..c33df6d85 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,"devices":[],"friendly_name":"group_1","optimistic":true,"retain":false},{"ID":2,"devices":[],"friendly_name":"group_2","optimistic":true,"retain":false},{"ID":11,"devices":["bulb_2"],"friendly_name":"group_with_tradfri","optimistic":true,"retain":false},{"ID":12,"devices":["TS0601_thermostat"],"friendly_name":"thermostat_group","optimistic":true,"retain":false},{"ID":14,"devices":["power_plug"],"friendly_name":"switch_group","optimistic":true,"retain":false},{"ID":15071,"devices":["bulb_color_2","bulb_2"],"friendly_name":"group_tradfri_remote","optimistic":true,"retain":false}],"type":"groups"}); + expect(payload).toStrictEqual({"message":[{"ID":1,"devices":[],"friendly_name":"group_1","optimistic":true,"retain":false},{"ID":2,"devices":[],"friendly_name":"group_2","optimistic":true,"retain":false},{"ID":11,"devices":["bulb_2"],"friendly_name":"group_with_tradfri","optimistic":true,"retain":false},{"ID":12,"devices":["TS0601_thermostat"],"friendly_name":"thermostat_group","optimistic":true,"retain":false},{"ID":14,"devices":["power_plug"],"friendly_name":"switch_group","optimistic":true,"retain":false},{"ID":21,"devices":["GLEDOPTO_2ID/cct"],"friendly_name":"gledopto_group","optimistic":true},{"ID":15071,"devices":["bulb_color_2","bulb_2"],"friendly_name":"group_tradfri_remote","optimistic":true,"retain":false}],"type":"groups"}); }); it('Should allow rename devices', async () => { diff --git a/test/publish.test.js b/test/publish.test.js index 4999c176a..81f324410 100644 --- a/test/publish.test.js +++ b/test/publish.test.js @@ -403,10 +403,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(7); + expect(Object.values(zigbeeHerdsman.groups).length).toBe(8); await MQTT.events.message('zigbee2mqtt/group_2/set', stringify({state: 'ON'})); await flushPromises(); - expect(Object.values(zigbeeHerdsman.groups).length).toBe(8); + expect(Object.values(zigbeeHerdsman.groups).length).toBe(9); 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 b1376dd91..4712afe98 100644 --- a/test/stub/data.js +++ b/test/stub/data.js @@ -165,6 +165,9 @@ function writeDefaultConfiguration() { '0x0017882194e45543': { friendly_name: 'QS-Zigbee-D02-TRIAC-2C-LN', }, + '0x0017880104e45724': { + friendly_name: 'GLEDOPTO_2ID', + } }, groups: { '1': { @@ -194,6 +197,10 @@ function writeDefaultConfiguration() { friendly_name: 'switch_group', retain: false, devices: ['power_plug'], + }, + '21': { + friendly_name: 'gledopto_group', + devices: ['GLEDOPTO_2ID/cct'], } }, external_converters: [], diff --git a/test/stub/zigbeeHerdsman.js b/test/stub/zigbeeHerdsman.js index adc8d7c32..9cb1c6e5d 100644 --- a/test/stub/zigbeeHerdsman.js +++ b/test/stub/zigbeeHerdsman.js @@ -29,7 +29,7 @@ const clusters = { } class Endpoint { - constructor(ID, inputClusters, outputClusters, deviceIeeeAddress, binds=[], clusterValues={}, configuredReportings=[]) { + constructor(ID, inputClusters, outputClusters, deviceIeeeAddress, binds=[], clusterValues={}, configuredReportings=[], profileID=null, deviceID=null) { this.deviceIeeeAddress = deviceIeeeAddress; this.clusterValues = clusterValues; this.ID = ID; @@ -45,6 +45,8 @@ class Endpoint { this.configureReporting = jest.fn(); this.meta = {}; this.binds = binds; + this.profileID = profileID; + this.deviceID = deviceID; this.configuredReportings = configuredReportings; this.getInputClusters = () => inputClusters.map((c) => { return {ID: c, name: getKeyByValue(clusters, c)}; @@ -127,6 +129,7 @@ const bulb_color_2 = new Device('Router', '0x000b57fffec6a5b4', 401292, 4107, [n 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', [], {lightingColorCtrl: {colorCapabilities: 17}})], 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 ZNCZ02LM = new Device('Router', '0x0017880104e45524', 6540,4151, [new Endpoint(1, [0], [], '0x0017880104e45524')], true, "Mains (single phase)", "lumi.plug"); +const GLEDOPTO_2ID = new Device('Router', '0x0017880104e45724', 6540,4151, [new Endpoint(11, [0,3,4,5,6,8,768], [], '0x0017880104e45724', [], {}, [], 49246, 528), new Endpoint(12, [0, 3, 4, 5, 6, 8, 768], [], '0x0017880104e45724', [], {}, [], 260, 258), new Endpoint(13, [4096], [4096], '0x0017880104e45724', [], {}, [], 49246, 57694), new Endpoint(15, [0, 3, 4, 5, 6, 8, 768], [], '0x0017880104e45724', [], {}, [], 49246, 256)], true, "Mains (single phase)", 'GL-C-007', false, 'GLEDOPTO'); const groups = { 'group_1': new Group(1, []), @@ -135,6 +138,7 @@ const groups = { 'group_with_tradfri': new Group(11, [bulb_2.endpoints[0]]), 'thermostat_group': new Group(12, [TS0601_thermostat.endpoints[0]]), 'group_with_switch': new Group(14, [ZNCZ02LM.endpoints[0]]), + 'gledopto_group': new Group(21, [GLEDOPTO_2ID.endpoints[3]]), 'default_bind_group': new Group(901, []), } @@ -159,6 +163,7 @@ const devices = { 'QBKG03LM':new Device('Router', '0x0017880104e45542', 6540,4151, [new Endpoint(1, [0], [], '0x0017880104e45542'), new Endpoint(2, [0, 6], [], '0x0017880104e45542'), new Endpoint(3, [0, 6], [], '0x0017880104e45542')], true, "Mains (single phase)", 'lumi.ctrl_neutral2'), 'GLEDOPTO1112': new Device('Router', '0x0017880104e45543', 6540, 4151, [new Endpoint(11, [0], [], '0x0017880104e45543'), new Endpoint(13, [0], [], '0x0017880104e45543')], true, "Mains (single phase)", 'GL-C-008'), 'GLEDOPTO111213': new Device('Router', '0x0017880104e45544', 6540,4151, [new Endpoint(11, [0], []), new Endpoint(13, [0], []), new Endpoint(12, [0], [])], true, "Mains (single phase)", 'GL-C-008'), + 'GLEDOPTO_2ID': GLEDOPTO_2ID, 'HGZB04D': new Device('Router', '0x0017880104e45545', 6540,4151, [new Endpoint(1, [0], [], '0x0017880104e45545')], true, "Mains (single phase)", 'FB56+ZSC05HG1.0'), 'ZNCLDJ11LM': new Device('Router', '0x0017880104e45547', 6540,4151, [new Endpoint(1, [0], []), new Endpoint(2, [0], [])], true, "Mains (single phase)", 'lumi.curtain'), 'HAMPTON99432': new Device('Router', '0x0017880104e45548', 6540,4151, [new Endpoint(1, [0], []), new Endpoint(2, [0], [])], true, "Mains (single phase)", 'HDC52EastwindFan'), @@ -182,7 +187,7 @@ const devices = { '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': TS0601_thermostat, - 'external_converter_device': new Device('EndDevice', '0x0017880104e45511', 1114, 'external', [], false, null, 'external_converter_device' ), + 'external_converter_device': new Device('EndDevice', '0x0017880104e45511', 1114, 'external', [new Endpoint(1, [], [], '0x0017880104e45511')], false, null, 'external_converter_device' ), 'QS_Zigbee_D02_TRIAC_2C_LN':new Device('Router', '0x0017882194e45543', 6549,4151, [new Endpoint(1, [0], [], '0x0017882194e45543'), new Endpoint(2, [0, 6], [], '0x0017882194e45543')], true, "Mains (single phase)", 'TS110F', false, '_TYZB01_v8gtiaed'), } diff --git a/test/utils.test.js b/test/utils.test.js index 8682dced7..1a687002d 100644 --- a/test/utils.test.js +++ b/test/utils.test.js @@ -46,7 +46,7 @@ describe('Utils', () => { var dependency = 'zigbee-herdsman'; expect(await utils.getDependencyVersion(dependency)).toStrictEqual({"version": versionHerdsman}); }) - + it('To local iso string', async () => { var date = new Date('August 19, 1975 23:15:30 UTC+00:00'); var getTimezoneOffset = Date.prototype.getTimezoneOffset; @@ -61,4 +61,10 @@ describe('Utils', () => { var date = new Date('August 19, 1975 23:15:30 UTC+00:00'); expect(() => utils.formatDate(date, 'invalid', 1)).toThrowError("Unsupported type 'invalid'") }) + + it('Get key', async () => { + expect(utils.getKey({'1': '1'}, '1', 2, null)).toBe('1'); + expect(utils.getKey({'1': '1'}, '2', 2, null)).toBe(2); + expect(utils.getKey({'1': '1'}, '1', null, () => '3')).toBe('3'); + }) });