From d484405cf099f384aa6888c2b8665b763433674a Mon Sep 17 00:00:00 2001 From: Laurent Chardin Date: Tue, 21 May 2024 19:54:18 +0200 Subject: [PATCH] fix: Expose custom clusters to MQTT (#22583) * (feat) Expose Custom Clusters in MQTT - Introducing bridge/definitions - Updating test case for custom clusters - Updating new path after ZCL revamp with 0.47.0 This change is needed for nurikk/zigbee2mqtt-frontend#2001 * Fixing test case * Update lib/model/device.ts Co-authored-by: Koen Kanters * Update test/bridge.test.js Co-authored-by: Koen Kanters * Removing the publishDefinition call from some events. updating tests --------- Co-authored-by: Koen Kanters --- lib/extension/bridge.ts | 35 +++++++++++- lib/model/device.ts | 4 ++ test/bridge.test.js | 104 ++++++++++++++++++++++++++++++++++-- test/controller.test.js | 2 +- test/stub/zigbeeHerdsman.js | 23 ++++++-- 5 files changed, 156 insertions(+), 12 deletions(-) diff --git a/lib/extension/bridge.ts b/lib/extension/bridge.ts index d256a69cc..db6b4be10 100644 --- a/lib/extension/bridge.ts +++ b/lib/extension/bridge.ts @@ -13,6 +13,8 @@ import data from '../util/data'; import JSZip from 'jszip'; import fs from 'fs'; import * as zhc from 'zigbee-herdsman-converters'; +import {CustomClusters, ClusterDefinition, ClusterName} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype'; +import {Clusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/cluster'; import winston from 'winston'; const requestRegex = new RegExp(`${settings.get().mqtt.base_topic}/bridge/request/(.*)`); @@ -104,7 +106,9 @@ export default class Bridge extends Extension { this.eventBus.onEntityRenamed(this, () => this.publishInfo()); this.eventBus.onGroupMembersChanged(this, () => this.publishGroups()); - this.eventBus.onDevicesChanged(this, () => this.publishDevices() && this.publishInfo()); + this.eventBus.onDevicesChanged(this, () => this.publishDevices() && + this.publishInfo() && + this.publishDefinitions()); this.eventBus.onPermitJoinChanged(this, () => !this.zigbee.isStopping() && this.publishInfo()); this.eventBus.onScenesChanged(this, () => { this.publishDevices(); @@ -121,6 +125,7 @@ export default class Bridge extends Extension { }); this.eventBus.onDeviceLeave(this, (data) => { this.publishDevices(); + this.publishDefinitions(); publishEvent('device_leave', {ieee_address: data.ieeeAddr, friendly_name: data.name}); }); this.eventBus.onDeviceNetworkAddressChanged(this, () => this.publishDevices()); @@ -142,6 +147,7 @@ export default class Bridge extends Extension { await this.publishInfo(); await this.publishDevices(); await this.publishGroups(); + await this.publishDefinitions(); this.eventBus.onMQTTMessage(this, this.onMQTTMessage); } @@ -614,6 +620,8 @@ export default class Bridge extends Extension { if (entity instanceof Device) { this.publishGroups(); this.publishDevices(); + // Refresh Cluster definition + this.publishDefinitions(); return utils.getResponse(message, {id: ID, block, force}, null); } else { this.publishGroups(); @@ -742,6 +750,27 @@ export default class Bridge extends Extension { 'bridge/groups', stringify(groups), {retain: true, qos: 0}, settings.get().mqtt.base_topic, true); } + async publishDefinitions(): Promise { + interface ClusterDefinitionPayload { + clusters: Readonly>>, + custom_clusters: {[key: string] : CustomClusters} + } + + const data: ClusterDefinitionPayload = { + clusters: Clusters, + custom_clusters: {}, + }; + + for (const device of this.zigbee.devices()) { + if (Object.keys(device.customClusters).length !== 0) { + data.custom_clusters[device.ieeeAddr] = device.customClusters; + } + } + + await this.mqtt.publish('bridge/definitions', stringify(data), + {retain: true, qos: 0}, settings.get().mqtt.base_topic, true); + } + getDefinitionPayload(device: Device): DefinitionPayload { if (!device.definition) return null; // @ts-expect-error icon is valid for external definitions @@ -752,7 +781,7 @@ export default class Bridge extends Extension { icon = icon.replace('${model}', utils.sanitizeImageParameter(device.definition.model)); } - return { + const payload: DefinitionPayload = { model: device.definition.model, vendor: device.definition.vendor, description: device.definition.description, @@ -761,5 +790,7 @@ export default class Bridge extends Extension { options: device.definition.options, icon, }; + + return payload; } } diff --git a/lib/model/device.ts b/lib/model/device.ts index 750414b5a..bba162757 100644 --- a/lib/model/device.ts +++ b/lib/model/device.ts @@ -1,6 +1,7 @@ /* eslint-disable brace-style */ import * as settings from '../util/settings'; import * as zhc from 'zigbee-herdsman-converters'; +import {CustomClusters} from 'zigbee-herdsman/dist/zspec/zcl/definition/tstype'; export default class Device { public zh: zh.Device; @@ -16,6 +17,9 @@ export default class Device { get isSupported(): boolean { return this.zh.type === 'Coordinator' || (this.definition && !this.definition.generated); } + get customClusters(): CustomClusters { + return this.zh.customClusters; + } constructor(device: zh.Device) { this.zh = device; diff --git a/test/bridge.test.js b/test/bridge.test.js index 3eff931dc..c66d7fefd 100644 --- a/test/bridge.test.js +++ b/test/bridge.test.js @@ -22,7 +22,7 @@ jest.mock("jszip", () => }) ); -const {coordinator, bulb, unsupported, WXKG11LM, remote, ZNCZ02LM, bulb_color_2, WSDCGQ11LM, zigfred_plus} = zigbeeHerdsman.devices; +const {coordinator, bulb, unsupported, WXKG11LM, remote, ZNCZ02LM, bulb_color_2, WSDCGQ11LM, zigfred_plus, bulb_custom_cluster} = zigbeeHerdsman.devices; zigbeeHerdsman.returnDevices.push(coordinator.ieeeAddr); zigbeeHerdsman.returnDevices.push(bulb.ieeeAddr); zigbeeHerdsman.returnDevices.push(unsupported.ieeeAddr); @@ -32,6 +32,7 @@ zigbeeHerdsman.returnDevices.push(ZNCZ02LM.ieeeAddr); zigbeeHerdsman.returnDevices.push(bulb_color_2.ieeeAddr); zigbeeHerdsman.returnDevices.push(WSDCGQ11LM.ieeeAddr); zigbeeHerdsman.returnDevices.push(zigfred_plus.ieeeAddr); +zigbeeHerdsman.returnDevices.push(bulb_custom_cluster.ieeeAddr); describe('Bridge', () => { let controller; @@ -96,7 +97,17 @@ describe('Bridge', () => { // console.log(MQTT.publish.mock.calls.find((c) => c[0] === 'zigbee2mqtt/bridge/devices')[1]) expect(MQTT.publish).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/devices', - stringify([{"date_code":null,"definition":null,"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":[],"output":[]},"configured_reportings":[],"scenes":[]}},"friendly_name":"Coordinator","ieee_address":"0x00124b00120144ae","interview_completed":false,"interviewing":false,"model_id":null,"network_address":0,"power_source":null,"software_build_id":null,"supported":true,"type":"Coordinator"},{"date_code":null,"definition":{"description":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm","exposes":[{"features":[{"access":7,"description":"On/off state of this light","label":"State","name":"state","property":"state","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","label":"Brightness","name":"brightness","property":"brightness","type":"numeric","value_max":254,"value_min":0},{"access":7,"description":"Color temperature of this light","label":"Color temp","name":"color_temp","presets":[{"description":"Coolest temperature supported","name":"coolest","value":250},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":454}],"property":"color_temp","type":"numeric","unit":"mired","value_max":454,"value_min":250},{"access":7,"description":"Color temperature after cold power on of this light","label":"Color temp startup","name":"color_temp_startup","presets":[{"description":"Coolest temperature supported","name":"coolest","value":250},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":454},{"description":"Restore previous color_temp on cold power on","name":"previous","value":65535}],"property":"color_temp_startup","type":"numeric","unit":"mired","value_max":454,"value_min":250}],"type":"light"},{"access":2,"description":"Triggers an effect on the light (e.g. make light blink for a few seconds)","label":"Effect","name":"effect","property":"effect","type":"enum","values":["blink","breathe","okay","channel_change","finish_effect","stop_effect"]},{"access":7,"category":"config","description":"Controls the behavior when the device is powered on after power loss. If you get an `UNSUPPORTED_ATTRIBUTE` error, the device does not support it.","label":"Power-on behavior","name":"power_on_behavior","property":"power_on_behavior","type":"enum","values":["off","on","toggle","previous"]},{"access":7,"category":"config","description":"Advanced color behavior","features":[{"access":2,"description":"Controls whether color and color temperature can be set while light is off","label":"Execute if off","name":"execute_if_off","property":"execute_if_off","type":"binary","value_off":false,"value_on":true}],"label":"Color options","name":"color_options","property":"color_options","type":"composite"},{"access":2,"category":"config","description":"Initiate device identification","label":"Identify","name":"identify","property":"identify","type":"enum","values":["identify"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"LED1545G12","options":[{"access":2,"description":"Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).","label":"Transition","name":"transition","property":"transition","type":"numeric","value_min":0},{"access":2,"description":"When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).","label":"Color sync","name":"color_sync","property":"color_sync","type":"binary","value_off":false,"value_on":true},{"access":2,"description":"Sets the duration of the identification procedure in seconds (i.e., how long the device would flash).The value ranges from 1 to 30 seconds (default: 3).","label":"Identify timeout","name":"identify_timeout","property":"identify_timeout","type":"numeric","value_max":30,"value_min":1},{"access":2,"description":"State actions will also be published as 'action' when true (default false).","label":"State action","name":"state_action","property":"state_action","type":"binary","value_off":false,"value_on":true}],"supports_ota":true,"vendor":"IKEA"},"description":"this is my bulb","disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl","lightingColorCtrl"],"output":["genScenes","genOta"]},"configured_reportings":[{"attribute":"onOff","cluster":"genOnOff","maximum_report_interval":10,"minimum_report_interval":1,"reportable_change":20}],"scenes":[]}},"friendly_name":"bulb","ieee_address":"0x000b57fffec6a5b2","interview_completed":true,"interviewing":false,"model_id":"TRADFRI bulb E27 WS opal 980lm","network_address":40369,"power_source":"Mains (single phase)","software_build_id":null,"supported":true,"type":"Router"},{"date_code":"2019.09","definition":{"description":"Hue Go","exposes":[{"features":[{"access":7,"description":"On/off state of this light","label":"State","name":"state","property":"state","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","label":"Brightness","name":"brightness","property":"brightness","type":"numeric","value_max":254,"value_min":0},{"access":7,"description":"Color temperature of this light","label":"Color temp","name":"color_temp","presets":[{"description":"Coolest temperature supported","name":"coolest","value":150},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":500}],"property":"color_temp","type":"numeric","unit":"mired","value_max":500,"value_min":150},{"access":7,"description":"Color temperature after cold power on of this light","label":"Color temp startup","name":"color_temp_startup","presets":[{"description":"Coolest temperature supported","name":"coolest","value":150},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":500},{"description":"Restore previous color_temp on cold power on","name":"previous","value":65535}],"property":"color_temp_startup","type":"numeric","unit":"mired","value_max":500,"value_min":150},{"access":7,"description":"Color of this light in the CIE 1931 color space (x/y)","features":[{"access":7,"label":"X","name":"x","property":"x","type":"numeric"},{"access":7,"label":"Y","name":"y","property":"y","type":"numeric"}],"label":"Color (X/Y)","name":"color_xy","property":"color","type":"composite"},{"access":7,"description":"Color of this light expressed as hue/saturation","features":[{"access":7,"label":"Hue","name":"hue","property":"hue","type":"numeric"},{"access":7,"label":"Saturation","name":"saturation","property":"saturation","type":"numeric"}],"label":"Color (HS)","name":"color_hs","property":"color","type":"composite"}],"type":"light"},{"access":7,"category":"config","description":"Controls the behavior when the device is powered on after power loss. If you get an `UNSUPPORTED_ATTRIBUTE` error, the device does not support it.","label":"Power-on behavior","name":"power_on_behavior","property":"power_on_behavior","type":"enum","values":["off","on","toggle","previous"]},{"access":2,"label":"Effect","name":"effect","property":"effect","type":"enum","values":["blink","breathe","okay","channel_change","candle","fireplace","colorloop","finish_effect","stop_effect","stop_hue_effect"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"7146060PH","options":[{"access":2,"description":"Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).","label":"Transition","name":"transition","property":"transition","type":"numeric","value_min":0},{"access":2,"description":"When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).","label":"Color sync","name":"color_sync","property":"color_sync","type":"binary","value_off":false,"value_on":true},{"access":2,"description":"State actions will also be published as 'action' when true (default false).","label":"State action","name":"state_action","property":"state_action","type":"binary","value_off":false,"value_on":true}],"supports_ota":true,"vendor":"Philips"},"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl","lightingColorCtrl"],"output":["genScenes","genOta"]},"configured_reportings":[],"scenes":[{"id":1,"name":"Chill scene"}]}},"friendly_name":"bulb_color_2","ieee_address":"0x000b57fffec6a5b4","interview_completed":true,"interviewing":false,"manufacturer":"Philips","model_id":"LLC020","network_address":401292,"power_source":"Mains (single phase)","software_build_id":"5.127.1.26581","supported":true,"type":"Router"},{"date_code":null,"definition":{"description":"Hue dimmer switch","exposes":[{"access":1,"category":"diagnostic","description":"Remaining battery in %, can take up to 24 hours before reported","label":"Battery","name":"battery","property":"battery","type":"numeric","unit":"%","value_max":100,"value_min":0},{"access":1,"category":"diagnostic","description":"Triggered action duration in seconds","label":"Action duration","name":"action_duration","property":"action_duration","type":"numeric","unit":"s"},{"access":1,"category":"diagnostic","description":"Triggered action (e.g. a button click)","label":"Action","name":"action","property":"action","type":"enum","values":["on_press","on_press_release","on_hold","on_hold_release","up_press","up_press_release","up_hold","up_hold_release","down_press","down_press_release","down_hold","down_hold_release","off_press","off_press_release","off_hold","off_hold_release"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"324131092621","options":[{"access":2,"description":"Set to false to disable the legacy integration (highly recommended), will change structure of the published payload (default true).","label":"Legacy","name":"legacy","property":"legacy","type":"binary","value_off":false,"value_on":true},{"access":2,"description":"Simulate a brightness value. If this device provides a brightness_move_up or brightness_move_down action it is possible to specify the update interval and delta. The action_brightness_delta indicates the delta for each interval. Only works when legacy is false.","features":[{"access":2,"description":"Delta per interval, 20 by default","label":"Delta","name":"delta","property":"delta","type":"numeric","value_min":0},{"access":2,"description":"Interval duration","label":"Interval","name":"interval","property":"interval","type":"numeric","unit":"ms","value_min":0}],"label":"Simulated brightness","name":"simulated_brightness","property":"simulated_brightness","type":"composite"}],"supports_ota":true,"vendor":"Philips"},"disabled":false,"endpoints":{"1":{"bindings":[{"cluster":"genLevelCtrl","target":{"endpoint":1,"ieee_address":"0x000b57fffec6a5b3","type":"endpoint"}},{"cluster":"genOnOff","target":{"endpoint":1,"ieee_address":"0x000b57fffec6a5b3","type":"endpoint"}},{"cluster":"lightingColorCtrl","target":{"endpoint":1,"ieee_address":"0x000b57fffec6a5b3","type":"endpoint"}},{"cluster":"genOnOff","target":{"id":1,"type":"group"}},{"cluster":"genLevelCtrl","target":{"id":1,"type":"group"}}],"clusters":{"input":["genBasic"],"output":["genBasic","genOnOff","genLevelCtrl","genScenes"]},"configured_reportings":[],"scenes":[]},"2":{"bindings":[],"clusters":{"input":["genBasic"],"output":["genOta","genOnOff"]},"configured_reportings":[],"scenes":[]}},"friendly_name":"remote","ieee_address":"0x0017880104e45517","interview_completed":true,"interviewing":false,"model_id":"RWL021","network_address":6535,"power_source":"Battery","software_build_id":null,"supported":true,"type":"EndDevice"},{"date_code":null,"definition":{"description":"Automatically generated definition","exposes":[{"access":1,"category":"diagnostic","description":"Triggered action (e.g. a button click)","label":"Action","name":"action","property":"action","type":"enum","values":["on","off","toggle","brightness_move_to_level","brightness_move_up","brightness_move_down","brightness_step_up","brightness_step_down","brightness_stop"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"notSupportedModelID","options":[{"access":2,"description":"Simulate a brightness value. If this device provides a brightness_move_up or brightness_move_down action it is possible to specify the update interval and delta. The action_brightness_delta indicates the delta for each interval. ","features":[{"access":2,"description":"Delta per interval, 20 by default","label":"Delta","name":"delta","property":"delta","type":"numeric","value_min":0},{"access":2,"description":"Interval duration","label":"Interval","name":"interval","property":"interval","type":"numeric","unit":"ms","value_min":0}],"label":"Simulated brightness","name":"simulated_brightness","property":"simulated_brightness","type":"composite"}],"supports_ota":false,"vendor":"notSupportedMfg"},"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic"],"output":["genBasic","genOnOff","genLevelCtrl","genScenes"]},"configured_reportings":[],"scenes":[]}},"friendly_name":"0x0017880104e45518","ieee_address":"0x0017880104e45518","interview_completed":true,"interviewing":false,"manufacturer":"notSupportedMfg","model_id":"notSupportedModelID","network_address":6536,"power_source":"Battery","software_build_id":null,"supported":false,"type":"EndDevice"},{"date_code":null,"definition":{"description":"Wireless mini switch","exposes":[{"access":1,"category":"diagnostic","description":"Remaining battery in %, can take up to 24 hours before reported","label":"Battery","name":"battery","property":"battery","type":"numeric","unit":"%","value_max":100,"value_min":0},{"access":1,"category":"diagnostic","description":"Voltage of the battery in millivolts","label":"Voltage","name":"voltage","property":"voltage","type":"numeric","unit":"mV"},{"access":1,"category":"diagnostic","description":"Temperature of the device","label":"Device temperature","name":"device_temperature","property":"device_temperature","type":"numeric","unit":"°C"},{"access":1,"category":"diagnostic","description":"Number of power outages (since last pairing)","label":"Power outage count","name":"power_outage_count","property":"power_outage_count","type":"numeric"},{"access":1,"category":"diagnostic","description":"Triggered action (e.g. a button click)","label":"Action","name":"action","property":"action","type":"enum","values":["single","double","triple","quadruple","hold","release"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"WXKG11LM","options":[{"access":2,"description":"Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.","label":"Device temperature calibration","name":"device_temperature_calibration","property":"device_temperature_calibration","type":"numeric"},{"access":2,"description":"Set to false to disable the legacy integration (highly recommended), will change structure of the published payload (default true).","label":"Legacy","name":"legacy","property":"legacy","type":"binary","value_off":false,"value_on":true}],"supports_ota":false,"vendor":"Aqara"},"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic"],"output":["genBasic","genOnOff","genLevelCtrl","genScenes"]},"configured_reportings":[{"attribute":1337,"cluster":"genOnOff","maximum_report_interval":10,"minimum_report_interval":1,"reportable_change":20}],"scenes":[]}},"friendly_name":"button","ieee_address":"0x0017880104e45520","interview_completed":true,"interviewing":false,"model_id":"lumi.sensor_switch.aq2","network_address":6537,"power_source":"Battery","software_build_id":null,"supported":true,"type":"EndDevice"},{"date_code":null,"definition":{"description":"Temperature and humidity sensor","exposes":[{"access":1,"category":"diagnostic","description":"Remaining battery in %, can take up to 24 hours before reported","label":"Battery","name":"battery","property":"battery","type":"numeric","unit":"%","value_max":100,"value_min":0},{"access":1,"description":"Measured temperature value","label":"Temperature","name":"temperature","property":"temperature","type":"numeric","unit":"°C"},{"access":1,"description":"Measured relative humidity","label":"Humidity","name":"humidity","property":"humidity","type":"numeric","unit":"%"},{"access":1,"description":"The measured atmospheric pressure","label":"Pressure","name":"pressure","property":"pressure","type":"numeric","unit":"hPa"},{"access":1,"category":"diagnostic","description":"Voltage of the battery in millivolts","label":"Voltage","name":"voltage","property":"voltage","type":"numeric","unit":"mV"},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"WSDCGQ11LM","options":[{"access":2,"description":"Calibrates the temperature value (absolute offset), takes into effect on next report of device.","label":"Temperature calibration","name":"temperature_calibration","property":"temperature_calibration","type":"numeric"},{"access":2,"description":"Number of digits after decimal point for temperature, takes into effect on next report of device. This option can only decrease the precision, not increase it.","label":"Temperature precision","name":"temperature_precision","property":"temperature_precision","type":"numeric","value_max":3,"value_min":0},{"access":2,"description":"Calibrates the humidity value (absolute offset), takes into effect on next report of device.","label":"Humidity calibration","name":"humidity_calibration","property":"humidity_calibration","type":"numeric"},{"access":2,"description":"Number of digits after decimal point for humidity, takes into effect on next report of device. This option can only decrease the precision, not increase it.","label":"Humidity precision","name":"humidity_precision","property":"humidity_precision","type":"numeric","value_max":3,"value_min":0},{"access":2,"description":"Calibrates the pressure value (absolute offset), takes into effect on next report of device.","label":"Pressure calibration","name":"pressure_calibration","property":"pressure_calibration","type":"numeric"},{"access":2,"description":"Number of digits after decimal point for pressure, takes into effect on next report of device. This option can only decrease the precision, not increase it.","label":"Pressure precision","name":"pressure_precision","property":"pressure_precision","type":"numeric","value_max":3,"value_min":0}],"supports_ota":false,"vendor":"Aqara"},"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic"],"output":[]},"configured_reportings":[],"scenes":[]}},"friendly_name":"weather_sensor","ieee_address":"0x0017880104e45522","interview_completed":true,"interviewing":false,"model_id":"lumi.weather","network_address":6539,"power_source":"Battery","software_build_id":null,"supported":true,"type":"EndDevice"},{"date_code":null,"definition":{"description":"Mi smart plug","exposes":[{"features":[{"access":7,"description":"On/off state of the switch","label":"State","name":"state","property":"state","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"}],"type":"switch"},{"access":5,"category":"diagnostic","description":"Instantaneous measured power","label":"Power","name":"power","property":"power","type":"numeric","unit":"W"},{"access":1,"description":"Sum of consumed energy","label":"Energy","name":"energy","property":"energy","type":"numeric","unit":"kWh"},{"access":1,"category":"diagnostic","description":"Temperature of the device","label":"Device temperature","name":"device_temperature","property":"device_temperature","type":"numeric","unit":"°C"},{"access":7,"category":"config","description":"Enable/disable the power outage memory, this recovers the on/off mode after power failure","label":"Power outage memory","name":"power_outage_memory","property":"power_outage_memory","type":"binary","value_off":false,"value_on":true},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"ZNCZ02LM","options":[{"access":2,"description":"Calibrates the power value (percentual offset), takes into effect on next report of device.","label":"Power calibration","name":"power_calibration","property":"power_calibration","type":"numeric"},{"access":2,"description":"Number of digits after decimal point for power, takes into effect on next report of device. This option can only decrease the precision, not increase it.","label":"Power precision","name":"power_precision","property":"power_precision","type":"numeric","value_max":3,"value_min":0},{"access":2,"description":"Calibrates the energy value (percentual offset), takes into effect on next report of device.","label":"Energy calibration","name":"energy_calibration","property":"energy_calibration","type":"numeric"},{"access":2,"description":"Number of digits after decimal point for energy, takes into effect on next report of device. This option can only decrease the precision, not increase it.","label":"Energy precision","name":"energy_precision","property":"energy_precision","type":"numeric","value_max":3,"value_min":0},{"access":2,"description":"Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.","label":"Device temperature calibration","name":"device_temperature_calibration","property":"device_temperature_calibration","type":"numeric"},{"access":2,"description":"State actions will also be published as 'action' when true (default false).","label":"State action","name":"state_action","property":"state_action","type":"binary","value_off":false,"value_on":true}],"supports_ota":true,"vendor":"Xiaomi"},"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic","genOnOff"],"output":[]},"configured_reportings":[],"scenes":[]}},"friendly_name":"power_plug","ieee_address":"0x0017880104e45524","interview_completed":true,"interviewing":false,"model_id":"lumi.plug","network_address":6540,"power_source":"Mains (single phase)","software_build_id":null,"supported":true,"type":"Router"},{"date_code":null,"definition":{"description":"zigfred plus smart in-wall switch","exposes":[{"access":1,"category":"diagnostic","description":"Triggered action (e.g. a button click)","label":"Action","name":"action","property":"action","type":"enum","values":["button_1_single","button_1_double","button_1_hold","button_1_release","button_2_single","button_2_double","button_2_hold","button_2_release","button_3_single","button_3_double","button_3_hold","button_3_release","button_4_single","button_4_double","button_4_hold","button_4_release"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0},{"endpoint":"l1","features":[{"access":7,"description":"On/off state of this light","endpoint":"l1","label":"State","name":"state","property":"state_l1","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","endpoint":"l1","label":"Brightness","name":"brightness","property":"brightness_l1","type":"numeric","value_max":254,"value_min":0},{"access":7,"description":"Color of this light in the CIE 1931 color space (x/y)","endpoint":"l1","features":[{"access":7,"label":"X","name":"x","property":"x","type":"numeric"},{"access":7,"label":"Y","name":"y","property":"y","type":"numeric"}],"label":"Color (X/Y)","name":"color_xy","property":"color_l1","type":"composite"}],"type":"light"},{"endpoint":"l2","features":[{"access":7,"description":"On/off state of this light","endpoint":"l2","label":"State","name":"state","property":"state_l2","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","endpoint":"l2","label":"Brightness","name":"brightness","property":"brightness_l2","type":"numeric","value_max":254,"value_min":0}],"type":"light"},{"endpoint":"l3","features":[{"access":7,"description":"On/off state of this light","endpoint":"l3","label":"State","name":"state","property":"state_l3","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","endpoint":"l3","label":"Brightness","name":"brightness","property":"brightness_l3","type":"numeric","value_max":254,"value_min":0}],"type":"light"},{"endpoint":"l4","features":[{"access":7,"description":"On/off state of this light","endpoint":"l4","label":"State","name":"state","property":"state_l4","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","endpoint":"l4","label":"Brightness","name":"brightness","property":"brightness_l4","type":"numeric","value_max":254,"value_min":0}],"type":"light"},{"endpoint":"l5","features":[{"access":7,"description":"On/off state of this light","endpoint":"l5","label":"State","name":"state","property":"state_l5","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","endpoint":"l5","label":"Brightness","name":"brightness","property":"brightness_l5","type":"numeric","value_max":254,"value_min":0}],"type":"light"},{"endpoint":"l6","features":[{"access":7,"endpoint":"l6","label":"State","name":"state","property":"state_l6","type":"enum","values":["OPEN","CLOSE","STOP"]},{"access":7,"description":"Position of this cover","endpoint":"l6","label":"Position","name":"position","property":"position_l6","type":"numeric","unit":"%","value_max":100,"value_min":0},{"access":7,"description":"Tilt of this cover","endpoint":"l6","label":"Tilt","name":"tilt","property":"tilt_l6","type":"numeric","unit":"%","value_max":100,"value_min":0}],"type":"cover"},{"endpoint":"l7","features":[{"access":7,"endpoint":"l7","label":"State","name":"state","property":"state_l7","type":"enum","values":["OPEN","CLOSE","STOP"]},{"access":7,"description":"Position of this cover","endpoint":"l7","label":"Position","name":"position","property":"position_l7","type":"numeric","unit":"%","value_max":100,"value_min":0},{"access":7,"description":"Tilt of this cover","endpoint":"l7","label":"Tilt","name":"tilt","property":"tilt_l7","type":"numeric","unit":"%","value_max":100,"value_min":0}],"type":"cover"}],"model":"ZFP-1A-CH","options":[{"access":2,"description":"Front Surface LED enabled","label":"Front surface enabled","name":"front_surface_enabled","property":"front_surface_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 1 enabled","label":"Dimmer 1 enabled","name":"dimmer_1_enabled","property":"dimmer_1_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 1 dimmable","label":"Dimmer 1 dimming enabled","name":"dimmer_1_dimming_enabled","property":"dimmer_1_dimming_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 2 enabled","label":"Dimmer 2 enabled","name":"dimmer_2_enabled","property":"dimmer_2_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 2 dimmable","label":"Dimmer 2 dimming enabled","name":"dimmer_2_dimming_enabled","property":"dimmer_2_dimming_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 3 enabled","label":"Dimmer 3 enabled","name":"dimmer_3_enabled","property":"dimmer_3_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 3 dimmable","label":"Dimmer 3 dimming enabled","name":"dimmer_3_dimming_enabled","property":"dimmer_3_dimming_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 4 enabled","label":"Dimmer 4 enabled","name":"dimmer_4_enabled","property":"dimmer_4_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 4 dimmable","label":"Dimmer 4 dimming enabled","name":"dimmer_4_dimming_enabled","property":"dimmer_4_dimming_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Cover 1 enabled","label":"Cover 1 enabled","name":"cover_1_enabled","property":"cover_1_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Cover 1 tiltable","label":"Cover 1 tilt enabled","name":"cover_1_tilt_enabled","property":"cover_1_tilt_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Cover 2 enabled","label":"Cover 2 enabled","name":"cover_2_enabled","property":"cover_2_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Cover 2 tiltable","label":"Cover 2 tilt enabled","name":"cover_2_tilt_enabled","property":"cover_2_tilt_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).","label":"Color sync","name":"color_sync","property":"color_sync","type":"binary","value_off":false,"value_on":true},{"access":2,"description":"Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).","label":"Transition","name":"transition","property":"transition","type":"numeric","value_min":0},{"access":2,"description":"State actions will also be published as 'action' when true (default false).","label":"State action","name":"state_action","property":"state_action","type":"binary","value_off":false,"value_on":true},{"access":2,"description":"Inverts the cover position, false: open=100,close=0, true: open=0,close=100 (default false).","label":"Invert cover","name":"invert_cover","property":"invert_cover","type":"binary","value_off":false,"value_on":true}],"supports_ota":false,"vendor":"Siglis"},"disabled":false,"endpoints":{"10":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl"],"output":[]},"configured_reportings":[],"scenes":[]},"11":{"bindings":[],"clusters":{"input":["genBasic","genScenes","closuresWindowCovering"],"output":[]},"configured_reportings":[],"scenes":[]},"12":{"bindings":[],"clusters":{"input":["genBasic","genScenes","closuresWindowCovering"],"output":[]},"configured_reportings":[],"scenes":[]},"5":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl","lightingColorCtrl"],"output":[]},"configured_reportings":[],"scenes":[]},"7":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl"],"output":[]},"configured_reportings":[],"scenes":[]},"8":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl"],"output":[]},"configured_reportings":[],"scenes":[]},"9":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl"],"output":[]},"configured_reportings":[],"scenes":[]}},"friendly_name":"zigfred_plus","ieee_address":"0xf4ce368a38be56a1","interview_completed":true,"interviewing":false,"manufacturer":"Siglis","model_id":"zigfred plus","network_address":6589,"power_source":"Mains (single phase)","software_build_id":null,"supported":true,"type":"Router"}]), + stringify([{"date_code":null,"definition":null,"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":[],"output":[]},"configured_reportings":[],"scenes":[]}},"friendly_name":"Coordinator","ieee_address":"0x00124b00120144ae","interview_completed":false,"interviewing":false,"model_id":null,"network_address":0,"power_source":null,"software_build_id":null,"supported":true,"type":"Coordinator"},{"date_code":null,"definition":{"description":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm","exposes":[{"features":[{"access":7,"description":"On/off state of this light","label":"State","name":"state","property":"state","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","label":"Brightness","name":"brightness","property":"brightness","type":"numeric","value_max":254,"value_min":0},{"access":7,"description":"Color temperature of this light","label":"Color temp","name":"color_temp","presets":[{"description":"Coolest temperature supported","name":"coolest","value":250},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":454}],"property":"color_temp","type":"numeric","unit":"mired","value_max":454,"value_min":250},{"access":7,"description":"Color temperature after cold power on of this light","label":"Color temp startup","name":"color_temp_startup","presets":[{"description":"Coolest temperature supported","name":"coolest","value":250},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":454},{"description":"Restore previous color_temp on cold power on","name":"previous","value":65535}],"property":"color_temp_startup","type":"numeric","unit":"mired","value_max":454,"value_min":250}],"type":"light"},{"access":2,"description":"Triggers an effect on the light (e.g. make light blink for a few seconds)","label":"Effect","name":"effect","property":"effect","type":"enum","values":["blink","breathe","okay","channel_change","finish_effect","stop_effect"]},{"access":7,"category":"config","description":"Controls the behavior when the device is powered on after power loss. If you get an `UNSUPPORTED_ATTRIBUTE` error, the device does not support it.","label":"Power-on behavior","name":"power_on_behavior","property":"power_on_behavior","type":"enum","values":["off","on","toggle","previous"]},{"access":7,"category":"config","description":"Advanced color behavior","features":[{"access":2,"description":"Controls whether color and color temperature can be set while light is off","label":"Execute if off","name":"execute_if_off","property":"execute_if_off","type":"binary","value_off":false,"value_on":true}],"label":"Color options","name":"color_options","property":"color_options","type":"composite"},{"access":2,"category":"config","description":"Initiate device identification","label":"Identify","name":"identify","property":"identify","type":"enum","values":["identify"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"LED1545G12","options":[{"access":2,"description":"Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).","label":"Transition","name":"transition","property":"transition","type":"numeric","value_min":0},{"access":2,"description":"When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).","label":"Color sync","name":"color_sync","property":"color_sync","type":"binary","value_off":false,"value_on":true},{"access":2,"description":"Sets the duration of the identification procedure in seconds (i.e., how long the device would flash).The value ranges from 1 to 30 seconds (default: 3).","label":"Identify timeout","name":"identify_timeout","property":"identify_timeout","type":"numeric","value_max":30,"value_min":1},{"access":2,"description":"State actions will also be published as 'action' when true (default false).","label":"State action","name":"state_action","property":"state_action","type":"binary","value_off":false,"value_on":true}],"supports_ota":true,"vendor":"IKEA"},"description":"this is my bulb","disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl","lightingColorCtrl"],"output":["genScenes","genOta"]},"configured_reportings":[{"attribute":"onOff","cluster":"genOnOff","maximum_report_interval":10,"minimum_report_interval":1,"reportable_change":20}],"scenes":[]}},"friendly_name":"bulb","ieee_address":"0x000b57fffec6a5b2","interview_completed":true,"interviewing":false,"model_id":"TRADFRI bulb E27 WS opal 980lm","network_address":40369,"power_source":"Mains (single phase)","software_build_id":null,"supported":true,"type":"Router"},{"date_code":"2019.09","definition":{"description":"Hue Go","exposes":[{"features":[{"access":7,"description":"On/off state of this light","label":"State","name":"state","property":"state","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","label":"Brightness","name":"brightness","property":"brightness","type":"numeric","value_max":254,"value_min":0},{"access":7,"description":"Color temperature of this light","label":"Color temp","name":"color_temp","presets":[{"description":"Coolest temperature supported","name":"coolest","value":150},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":500}],"property":"color_temp","type":"numeric","unit":"mired","value_max":500,"value_min":150},{"access":7,"description":"Color temperature after cold power on of this light","label":"Color temp startup","name":"color_temp_startup","presets":[{"description":"Coolest temperature supported","name":"coolest","value":150},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":500},{"description":"Restore previous color_temp on cold power on","name":"previous","value":65535}],"property":"color_temp_startup","type":"numeric","unit":"mired","value_max":500,"value_min":150},{"access":7,"description":"Color of this light in the CIE 1931 color space (x/y)","features":[{"access":7,"label":"X","name":"x","property":"x","type":"numeric"},{"access":7,"label":"Y","name":"y","property":"y","type":"numeric"}],"label":"Color (X/Y)","name":"color_xy","property":"color","type":"composite"},{"access":7,"description":"Color of this light expressed as hue/saturation","features":[{"access":7,"label":"Hue","name":"hue","property":"hue","type":"numeric"},{"access":7,"label":"Saturation","name":"saturation","property":"saturation","type":"numeric"}],"label":"Color (HS)","name":"color_hs","property":"color","type":"composite"}],"type":"light"},{"access":7,"category":"config","description":"Controls the behavior when the device is powered on after power loss. If you get an `UNSUPPORTED_ATTRIBUTE` error, the device does not support it.","label":"Power-on behavior","name":"power_on_behavior","property":"power_on_behavior","type":"enum","values":["off","on","toggle","previous"]},{"access":2,"label":"Effect","name":"effect","property":"effect","type":"enum","values":["blink","breathe","okay","channel_change","candle","fireplace","colorloop","finish_effect","stop_effect","stop_hue_effect"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"7146060PH","options":[{"access":2,"description":"Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).","label":"Transition","name":"transition","property":"transition","type":"numeric","value_min":0},{"access":2,"description":"When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).","label":"Color sync","name":"color_sync","property":"color_sync","type":"binary","value_off":false,"value_on":true},{"access":2,"description":"State actions will also be published as 'action' when true (default false).","label":"State action","name":"state_action","property":"state_action","type":"binary","value_off":false,"value_on":true}],"supports_ota":true,"vendor":"Philips"},"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl","lightingColorCtrl"],"output":["genScenes","genOta"]},"configured_reportings":[],"scenes":[{"id":1,"name":"Chill scene"}]}},"friendly_name":"bulb_color_2","ieee_address":"0x000b57fffec6a5b4","interview_completed":true,"interviewing":false,"manufacturer":"Philips","model_id":"LLC020","network_address":401292,"power_source":"Mains (single phase)","software_build_id":"5.127.1.26581","supported":true,"type":"Router"},{"date_code":null,"definition":{"description":"Hue dimmer switch","exposes":[{"access":1,"category":"diagnostic","description":"Remaining battery in %, can take up to 24 hours before reported","label":"Battery","name":"battery","property":"battery","type":"numeric","unit":"%","value_max":100,"value_min":0},{"access":1,"category":"diagnostic","description":"Triggered action duration in seconds","label":"Action duration","name":"action_duration","property":"action_duration","type":"numeric","unit":"s"},{"access":1,"category":"diagnostic","description":"Triggered action (e.g. a button click)","label":"Action","name":"action","property":"action","type":"enum","values":["on_press","on_press_release","on_hold","on_hold_release","up_press","up_press_release","up_hold","up_hold_release","down_press","down_press_release","down_hold","down_hold_release","off_press","off_press_release","off_hold","off_hold_release"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"324131092621","options":[{"access":2,"description":"Set to false to disable the legacy integration (highly recommended), will change structure of the published payload (default true).","label":"Legacy","name":"legacy","property":"legacy","type":"binary","value_off":false,"value_on":true},{"access":2,"description":"Simulate a brightness value. If this device provides a brightness_move_up or brightness_move_down action it is possible to specify the update interval and delta. The action_brightness_delta indicates the delta for each interval. Only works when legacy is false.","features":[{"access":2,"description":"Delta per interval, 20 by default","label":"Delta","name":"delta","property":"delta","type":"numeric","value_min":0},{"access":2,"description":"Interval duration","label":"Interval","name":"interval","property":"interval","type":"numeric","unit":"ms","value_min":0}],"label":"Simulated brightness","name":"simulated_brightness","property":"simulated_brightness","type":"composite"}],"supports_ota":true,"vendor":"Philips"},"disabled":false,"endpoints":{"1":{"bindings":[{"cluster":"genLevelCtrl","target":{"endpoint":1,"ieee_address":"0x000b57fffec6a5b3","type":"endpoint"}},{"cluster":"genOnOff","target":{"endpoint":1,"ieee_address":"0x000b57fffec6a5b3","type":"endpoint"}},{"cluster":"lightingColorCtrl","target":{"endpoint":1,"ieee_address":"0x000b57fffec6a5b3","type":"endpoint"}},{"cluster":"genOnOff","target":{"id":1,"type":"group"}},{"cluster":"genLevelCtrl","target":{"id":1,"type":"group"}}],"clusters":{"input":["genBasic"],"output":["genBasic","genOnOff","genLevelCtrl","genScenes"]},"configured_reportings":[],"scenes":[]},"2":{"bindings":[],"clusters":{"input":["genBasic"],"output":["genOta","genOnOff"]},"configured_reportings":[],"scenes":[]}},"friendly_name":"remote","ieee_address":"0x0017880104e45517","interview_completed":true,"interviewing":false,"model_id":"RWL021","network_address":6535,"power_source":"Battery","software_build_id":null,"supported":true,"type":"EndDevice"},{"date_code":null,"definition":{"description":"Automatically generated definition","exposes":[{"access":1,"category":"diagnostic","description":"Triggered action (e.g. a button click)","label":"Action","name":"action","property":"action","type":"enum","values":["on","off","toggle","brightness_move_to_level","brightness_move_up","brightness_move_down","brightness_step_up","brightness_step_down","brightness_stop"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"notSupportedModelID","options":[{"access":2,"description":"Simulate a brightness value. If this device provides a brightness_move_up or brightness_move_down action it is possible to specify the update interval and delta. The action_brightness_delta indicates the delta for each interval. ","features":[{"access":2,"description":"Delta per interval, 20 by default","label":"Delta","name":"delta","property":"delta","type":"numeric","value_min":0},{"access":2,"description":"Interval duration","label":"Interval","name":"interval","property":"interval","type":"numeric","unit":"ms","value_min":0}],"label":"Simulated brightness","name":"simulated_brightness","property":"simulated_brightness","type":"composite"}],"supports_ota":false,"vendor":"notSupportedMfg"},"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic"],"output":["genBasic","genOnOff","genLevelCtrl","genScenes"]},"configured_reportings":[],"scenes":[]}},"friendly_name":"0x0017880104e45518","ieee_address":"0x0017880104e45518","interview_completed":true,"interviewing":false,"manufacturer":"notSupportedMfg","model_id":"notSupportedModelID","network_address":6536,"power_source":"Battery","software_build_id":null,"supported":false,"type":"EndDevice"},{"date_code":null,"definition":{"description":"Wireless mini switch","exposes":[{"access":1,"category":"diagnostic","description":"Remaining battery in %, can take up to 24 hours before reported","label":"Battery","name":"battery","property":"battery","type":"numeric","unit":"%","value_max":100,"value_min":0},{"access":1,"category":"diagnostic","description":"Voltage of the battery in millivolts","label":"Voltage","name":"voltage","property":"voltage","type":"numeric","unit":"mV"},{"access":1,"category":"diagnostic","description":"Temperature of the device","label":"Device temperature","name":"device_temperature","property":"device_temperature","type":"numeric","unit":"°C"},{"access":1,"category":"diagnostic","description":"Number of power outages (since last pairing)","label":"Power outage count","name":"power_outage_count","property":"power_outage_count","type":"numeric"},{"access":1,"category":"diagnostic","description":"Triggered action (e.g. a button click)","label":"Action","name":"action","property":"action","type":"enum","values":["single","double","triple","quadruple","hold","release"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"WXKG11LM","options":[{"access":2,"description":"Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.","label":"Device temperature calibration","name":"device_temperature_calibration","property":"device_temperature_calibration","type":"numeric"},{"access":2,"description":"Set to false to disable the legacy integration (highly recommended), will change structure of the published payload (default true).","label":"Legacy","name":"legacy","property":"legacy","type":"binary","value_off":false,"value_on":true}],"supports_ota":false,"vendor":"Aqara"},"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic"],"output":["genBasic","genOnOff","genLevelCtrl","genScenes"]},"configured_reportings":[{"attribute":1337,"cluster":"genOnOff","maximum_report_interval":10,"minimum_report_interval":1,"reportable_change":20}],"scenes":[]}},"friendly_name":"button","ieee_address":"0x0017880104e45520","interview_completed":true,"interviewing":false,"model_id":"lumi.sensor_switch.aq2","network_address":6537,"power_source":"Battery","software_build_id":null,"supported":true,"type":"EndDevice"},{"date_code":null,"definition":{"description":"Temperature and humidity sensor","exposes":[{"access":1,"category":"diagnostic","description":"Remaining battery in %, can take up to 24 hours before reported","label":"Battery","name":"battery","property":"battery","type":"numeric","unit":"%","value_max":100,"value_min":0},{"access":1,"description":"Measured temperature value","label":"Temperature","name":"temperature","property":"temperature","type":"numeric","unit":"°C"},{"access":1,"description":"Measured relative humidity","label":"Humidity","name":"humidity","property":"humidity","type":"numeric","unit":"%"},{"access":1,"description":"The measured atmospheric pressure","label":"Pressure","name":"pressure","property":"pressure","type":"numeric","unit":"hPa"},{"access":1,"category":"diagnostic","description":"Voltage of the battery in millivolts","label":"Voltage","name":"voltage","property":"voltage","type":"numeric","unit":"mV"},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"WSDCGQ11LM","options":[{"access":2,"description":"Calibrates the temperature value (absolute offset), takes into effect on next report of device.","label":"Temperature calibration","name":"temperature_calibration","property":"temperature_calibration","type":"numeric"},{"access":2,"description":"Number of digits after decimal point for temperature, takes into effect on next report of device. This option can only decrease the precision, not increase it.","label":"Temperature precision","name":"temperature_precision","property":"temperature_precision","type":"numeric","value_max":3,"value_min":0},{"access":2,"description":"Calibrates the humidity value (absolute offset), takes into effect on next report of device.","label":"Humidity calibration","name":"humidity_calibration","property":"humidity_calibration","type":"numeric"},{"access":2,"description":"Number of digits after decimal point for humidity, takes into effect on next report of device. This option can only decrease the precision, not increase it.","label":"Humidity precision","name":"humidity_precision","property":"humidity_precision","type":"numeric","value_max":3,"value_min":0},{"access":2,"description":"Calibrates the pressure value (absolute offset), takes into effect on next report of device.","label":"Pressure calibration","name":"pressure_calibration","property":"pressure_calibration","type":"numeric"},{"access":2,"description":"Number of digits after decimal point for pressure, takes into effect on next report of device. This option can only decrease the precision, not increase it.","label":"Pressure precision","name":"pressure_precision","property":"pressure_precision","type":"numeric","value_max":3,"value_min":0}],"supports_ota":false,"vendor":"Aqara"},"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic"],"output":[]},"configured_reportings":[],"scenes":[]}},"friendly_name":"weather_sensor","ieee_address":"0x0017880104e45522","interview_completed":true,"interviewing":false,"model_id":"lumi.weather","network_address":6539,"power_source":"Battery","software_build_id":null,"supported":true,"type":"EndDevice"},{"date_code":null,"definition":{"description":"Mi smart plug","exposes":[{"features":[{"access":7,"description":"On/off state of the switch","label":"State","name":"state","property":"state","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"}],"type":"switch"},{"access":5,"category":"diagnostic","description":"Instantaneous measured power","label":"Power","name":"power","property":"power","type":"numeric","unit":"W"},{"access":1,"description":"Sum of consumed energy","label":"Energy","name":"energy","property":"energy","type":"numeric","unit":"kWh"},{"access":1,"category":"diagnostic","description":"Temperature of the device","label":"Device temperature","name":"device_temperature","property":"device_temperature","type":"numeric","unit":"°C"},{"access":7,"category":"config","description":"Enable/disable the power outage memory, this recovers the on/off mode after power failure","label":"Power outage memory","name":"power_outage_memory","property":"power_outage_memory","type":"binary","value_off":false,"value_on":true},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"ZNCZ02LM","options":[{"access":2,"description":"Calibrates the power value (percentual offset), takes into effect on next report of device.","label":"Power calibration","name":"power_calibration","property":"power_calibration","type":"numeric"},{"access":2,"description":"Number of digits after decimal point for power, takes into effect on next report of device. This option can only decrease the precision, not increase it.","label":"Power precision","name":"power_precision","property":"power_precision","type":"numeric","value_max":3,"value_min":0},{"access":2,"description":"Calibrates the energy value (percentual offset), takes into effect on next report of device.","label":"Energy calibration","name":"energy_calibration","property":"energy_calibration","type":"numeric"},{"access":2,"description":"Number of digits after decimal point for energy, takes into effect on next report of device. This option can only decrease the precision, not increase it.","label":"Energy precision","name":"energy_precision","property":"energy_precision","type":"numeric","value_max":3,"value_min":0},{"access":2,"description":"Calibrates the device_temperature value (absolute offset), takes into effect on next report of device.","label":"Device temperature calibration","name":"device_temperature_calibration","property":"device_temperature_calibration","type":"numeric"},{"access":2,"description":"State actions will also be published as 'action' when true (default false).","label":"State action","name":"state_action","property":"state_action","type":"binary","value_off":false,"value_on":true}],"supports_ota":true,"vendor":"Xiaomi"},"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic","genOnOff"],"output":[]},"configured_reportings":[],"scenes":[]}},"friendly_name":"power_plug","ieee_address":"0x0017880104e45524","interview_completed":true,"interviewing":false,"model_id":"lumi.plug","network_address":6540,"power_source":"Mains (single phase)","software_build_id":null,"supported":true,"type":"Router"},{"date_code":null,"definition":{"description":"zigfred plus smart in-wall switch","exposes":[{"access":1,"category":"diagnostic","description":"Triggered action (e.g. a button click)","label":"Action","name":"action","property":"action","type":"enum","values":["button_1_single","button_1_double","button_1_hold","button_1_release","button_2_single","button_2_double","button_2_hold","button_2_release","button_3_single","button_3_double","button_3_hold","button_3_release","button_4_single","button_4_double","button_4_hold","button_4_release"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0},{"endpoint":"l1","features":[{"access":7,"description":"On/off state of this light","endpoint":"l1","label":"State","name":"state","property":"state_l1","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","endpoint":"l1","label":"Brightness","name":"brightness","property":"brightness_l1","type":"numeric","value_max":254,"value_min":0},{"access":7,"description":"Color of this light in the CIE 1931 color space (x/y)","endpoint":"l1","features":[{"access":7,"label":"X","name":"x","property":"x","type":"numeric"},{"access":7,"label":"Y","name":"y","property":"y","type":"numeric"}],"label":"Color (X/Y)","name":"color_xy","property":"color_l1","type":"composite"}],"type":"light"},{"endpoint":"l2","features":[{"access":7,"description":"On/off state of this light","endpoint":"l2","label":"State","name":"state","property":"state_l2","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","endpoint":"l2","label":"Brightness","name":"brightness","property":"brightness_l2","type":"numeric","value_max":254,"value_min":0}],"type":"light"},{"endpoint":"l3","features":[{"access":7,"description":"On/off state of this light","endpoint":"l3","label":"State","name":"state","property":"state_l3","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","endpoint":"l3","label":"Brightness","name":"brightness","property":"brightness_l3","type":"numeric","value_max":254,"value_min":0}],"type":"light"},{"endpoint":"l4","features":[{"access":7,"description":"On/off state of this light","endpoint":"l4","label":"State","name":"state","property":"state_l4","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","endpoint":"l4","label":"Brightness","name":"brightness","property":"brightness_l4","type":"numeric","value_max":254,"value_min":0}],"type":"light"},{"endpoint":"l5","features":[{"access":7,"description":"On/off state of this light","endpoint":"l5","label":"State","name":"state","property":"state_l5","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","endpoint":"l5","label":"Brightness","name":"brightness","property":"brightness_l5","type":"numeric","value_max":254,"value_min":0}],"type":"light"},{"endpoint":"l6","features":[{"access":7,"endpoint":"l6","label":"State","name":"state","property":"state_l6","type":"enum","values":["OPEN","CLOSE","STOP"]},{"access":7,"description":"Position of this cover","endpoint":"l6","label":"Position","name":"position","property":"position_l6","type":"numeric","unit":"%","value_max":100,"value_min":0},{"access":7,"description":"Tilt of this cover","endpoint":"l6","label":"Tilt","name":"tilt","property":"tilt_l6","type":"numeric","unit":"%","value_max":100,"value_min":0}],"type":"cover"},{"endpoint":"l7","features":[{"access":7,"endpoint":"l7","label":"State","name":"state","property":"state_l7","type":"enum","values":["OPEN","CLOSE","STOP"]},{"access":7,"description":"Position of this cover","endpoint":"l7","label":"Position","name":"position","property":"position_l7","type":"numeric","unit":"%","value_max":100,"value_min":0},{"access":7,"description":"Tilt of this cover","endpoint":"l7","label":"Tilt","name":"tilt","property":"tilt_l7","type":"numeric","unit":"%","value_max":100,"value_min":0}],"type":"cover"}],"model":"ZFP-1A-CH","options":[{"access":2,"description":"Front Surface LED enabled","label":"Front surface enabled","name":"front_surface_enabled","property":"front_surface_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 1 enabled","label":"Dimmer 1 enabled","name":"dimmer_1_enabled","property":"dimmer_1_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 1 dimmable","label":"Dimmer 1 dimming enabled","name":"dimmer_1_dimming_enabled","property":"dimmer_1_dimming_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 2 enabled","label":"Dimmer 2 enabled","name":"dimmer_2_enabled","property":"dimmer_2_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 2 dimmable","label":"Dimmer 2 dimming enabled","name":"dimmer_2_dimming_enabled","property":"dimmer_2_dimming_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 3 enabled","label":"Dimmer 3 enabled","name":"dimmer_3_enabled","property":"dimmer_3_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 3 dimmable","label":"Dimmer 3 dimming enabled","name":"dimmer_3_dimming_enabled","property":"dimmer_3_dimming_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 4 enabled","label":"Dimmer 4 enabled","name":"dimmer_4_enabled","property":"dimmer_4_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Dimmer 4 dimmable","label":"Dimmer 4 dimming enabled","name":"dimmer_4_dimming_enabled","property":"dimmer_4_dimming_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Cover 1 enabled","label":"Cover 1 enabled","name":"cover_1_enabled","property":"cover_1_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Cover 1 tiltable","label":"Cover 1 tilt enabled","name":"cover_1_tilt_enabled","property":"cover_1_tilt_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Cover 2 enabled","label":"Cover 2 enabled","name":"cover_2_enabled","property":"cover_2_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"Cover 2 tiltable","label":"Cover 2 tilt enabled","name":"cover_2_tilt_enabled","property":"cover_2_tilt_enabled","type":"enum","values":["auto","true","false"]},{"access":2,"description":"When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).","label":"Color sync","name":"color_sync","property":"color_sync","type":"binary","value_off":false,"value_on":true},{"access":2,"description":"Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).","label":"Transition","name":"transition","property":"transition","type":"numeric","value_min":0},{"access":2,"description":"State actions will also be published as 'action' when true (default false).","label":"State action","name":"state_action","property":"state_action","type":"binary","value_off":false,"value_on":true},{"access":2,"description":"Inverts the cover position, false: open=100,close=0, true: open=0,close=100 (default false).","label":"Invert cover","name":"invert_cover","property":"invert_cover","type":"binary","value_off":false,"value_on":true}],"supports_ota":false,"vendor":"Siglis"},"disabled":false,"endpoints":{"10":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl"],"output":[]},"configured_reportings":[],"scenes":[]},"11":{"bindings":[],"clusters":{"input":["genBasic","genScenes","closuresWindowCovering"],"output":[]},"configured_reportings":[],"scenes":[]},"12":{"bindings":[],"clusters":{"input":["genBasic","genScenes","closuresWindowCovering"],"output":[]},"configured_reportings":[],"scenes":[]},"5":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl","lightingColorCtrl"],"output":[]},"configured_reportings":[],"scenes":[]},"7":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl"],"output":[]},"configured_reportings":[],"scenes":[]},"8":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl"],"output":[]},"configured_reportings":[],"scenes":[]},"9":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl"],"output":[]},"configured_reportings":[],"scenes":[]}},"friendly_name":"zigfred_plus","ieee_address":"0xf4ce368a38be56a1","interview_completed":true,"interviewing":false,"manufacturer":"Siglis","model_id":"zigfred plus","network_address":6589,"power_source":"Mains (single phase)","software_build_id":null,"supported":true,"type":"Router"},{"date_code":null,"definition":{"description":"TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm","exposes":[{"features":[{"access":7,"description":"On/off state of this light","label":"State","name":"state","property":"state","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","label":"Brightness","name":"brightness","property":"brightness","type":"numeric","value_max":254,"value_min":0},{"access":7,"description":"Color temperature of this light","label":"Color temp","name":"color_temp","presets":[{"description":"Coolest temperature supported","name":"coolest","value":250},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":454}],"property":"color_temp","type":"numeric","unit":"mired","value_max":454,"value_min":250},{"access":7,"description":"Color temperature after cold power on of this light","label":"Color temp startup","name":"color_temp_startup","presets":[{"description":"Coolest temperature supported","name":"coolest","value":250},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":454},{"description":"Restore previous color_temp on cold power on","name":"previous","value":65535}],"property":"color_temp_startup","type":"numeric","unit":"mired","value_max":454,"value_min":250}],"type":"light"},{"access":2,"description":"Triggers an effect on the light (e.g. make light blink for a few seconds)","label":"Effect","name":"effect","property":"effect","type":"enum","values":["blink","breathe","okay","channel_change","finish_effect","stop_effect"]},{"access":7,"category":"config","description":"Controls the behavior when the device is powered on after power loss. If you get an `UNSUPPORTED_ATTRIBUTE` error, the device does not support it.","label":"Power-on behavior","name":"power_on_behavior","property":"power_on_behavior","type":"enum","values":["off","on","toggle","previous"]},{"access":7,"category":"config","description":"Advanced color behavior","features":[{"access":2,"description":"Controls whether color and color temperature can be set while light is off","label":"Execute if off","name":"execute_if_off","property":"execute_if_off","type":"binary","value_off":false,"value_on":true}],"label":"Color options","name":"color_options","property":"color_options","type":"composite"},{"access":2,"category":"config","description":"Initiate device identification","label":"Identify","name":"identify","property":"identify","type":"enum","values":["identify"]},{"access":1,"category":"diagnostic","description":"Link quality (signal strength)","label":"Linkquality","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"LED1545G12","options":[{"access":2,"description":"Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).","label":"Transition","name":"transition","property":"transition","type":"numeric","value_min":0},{"access":2,"description":"When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).","label":"Color sync","name":"color_sync","property":"color_sync","type":"binary","value_off":false,"value_on":true},{"access":2,"description":"Sets the duration of the identification procedure in seconds (i.e., how long the device would flash).The value ranges from 1 to 30 seconds (default: 3).","label":"Identify timeout","name":"identify_timeout","property":"identify_timeout","type":"numeric","value_max":30,"value_min":1},{"access":2,"description":"State actions will also be published as 'action' when true (default false).","label":"State action","name":"state_action","property":"state_action","type":"binary","value_off":false,"value_on":true}],"supports_ota":true,"vendor":"IKEA"},"disabled":false,"endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl","lightingColorCtrl"],"output":["genScenes","genOta"]},"configured_reportings":[],"scenes":[]}},"friendly_name":"0x000b57fffec6a5c2","ieee_address":"0x000b57fffec6a5c2","interview_completed":true,"interviewing":false,"manufacturer":null,"model_id":"TRADFRI bulb E27 WS opal 980lm","network_address":40369,"power_source":"Mains (single phase)","software_build_id":null,"supported":true,"type":"Router"}]), + { retain: true, qos: 0 }, + expect.any(Function) + ); + }); + + it('Should publish definitions on startup', async () => { + await resetExtension(); + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/definitions', + expect.stringContaining(stringify(zigbeeHerdsman.custom_clusters)), { retain: true, qos: 0 }, expect.any(Function) ); @@ -183,7 +194,7 @@ describe('Bridge', () => { ); }); - it('Should publish event when device joined', async () => { + it('Should publish devices when device joined', async () => { MQTT.publish.mockClear(); await zigbeeHerdsman.events.deviceNetworkAddressChanged({device: zigbeeHerdsman.devices.bulb}); await flushPromises(); @@ -245,7 +256,7 @@ describe('Bridge', () => { await zigbeeHerdsman.events.deviceInterview({device: zigbeeHerdsman.devices.bulb, status: 'successful'}); await zigbeeHerdsman.events.deviceInterview({device: zigbeeHerdsman.devices.unsupported, status: 'successful'}); await flushPromises(); - expect(MQTT.publish).toHaveBeenCalledTimes(6); + expect(MQTT.publish).toHaveBeenCalledTimes(7); // console.log(MQTT.publish.mock.calls.filter((c) => c[0] === 'zigbee2mqtt/bridge/event')) expect(MQTT.publish).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', @@ -265,13 +276,19 @@ describe('Bridge', () => { { retain: true, qos: 0 }, expect.any(Function) ); + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/definitions', + expect.any(String), + { retain: true, qos: 0 }, + expect.any(Function) + ); }); it('Should publish event and devices when device leaves', async () => { MQTT.publish.mockClear(); await zigbeeHerdsman.events.deviceLeave({ieeeAddr: zigbeeHerdsman.devices.bulb.ieeeAddr}); await flushPromises(); - expect(MQTT.publish).toHaveBeenCalledTimes(2); + expect(MQTT.publish).toHaveBeenCalledTimes(3); expect(MQTT.publish).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({"type":"device_leave","data":{"ieee_address":"0x000b57fffec6a5b2","friendly_name":"bulb"}}), @@ -284,6 +301,12 @@ describe('Bridge', () => { { retain: true, qos: 0 }, expect.any(Function) ); + expect(MQTT.publish).toHaveBeenCalledWith( // Defintitions should be updated on device event + 'zigbee2mqtt/bridge/definitions', + expect.any(String), + { retain: true, qos: 0 }, + expect.any(Function) + ); }); it('Should allow permit join', async () => { @@ -1440,4 +1463,75 @@ describe('Bridge', () => { expect(payload['icon']).not.toBeUndefined() expect(payload.icon).toBe('_------_-._Z-NC-Z02-LM_'); }); + + it('Should publish bridge info, devices and definitions when a device with custom_clusters joined', async () => { + MQTT.publish.mockClear(); + await zigbeeHerdsman.events.deviceJoined({device: zigbeeHerdsman.devices.bulb_custom_cluster}); + await flushPromises(); + + // console.log(MQTT.publish.mock.calls); + expect(MQTT.publish).toHaveBeenCalledTimes(5); + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/info', + expect.any(String), + { retain: true, qos: 0 }, + expect.any(Function) + ); + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/devices', + expect.any(String), + { retain: true, qos: 0 }, + expect.any(Function) + ); + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/definitions', + expect.stringContaining(stringify(zigbeeHerdsman.custom_clusters)), + { retain: true, qos: 0 }, + expect.any(Function) + ); + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/event', + stringify({"data":{"friendly_name":"0x000b57fffec6a5c2","ieee_address":"0x000b57fffec6a5c2"},"type":"device_joined"}), + { retain: false, qos: 0 }, + expect.any(Function) + ); + }); + + it('Should publish bridge info, devices and definitions when a device with custom_clusters is reconfigured', async () => { + // Adding a device first + await zigbeeHerdsman.events.deviceJoined({device: zigbeeHerdsman.devices.bulb_custom_cluster}); + await flushPromises(); + MQTT.publish.mockClear(); + + // After cleaning, reconfigure it + MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', zigbeeHerdsman.devices.bulb_custom_cluster.ieeeAddr); + await flushPromises(); + + // console.log(MQTT.publish.mock.calls); + expect(MQTT.publish).toHaveBeenCalledTimes(4); + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/info', + expect.any(String), + { retain: true, qos: 0 }, + expect.any(Function) + ); + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/devices', + expect.any(String), + { retain: true, qos: 0 }, + expect.any(Function) + ); + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/definitions', + expect.stringContaining(stringify(zigbeeHerdsman.custom_clusters)), + { retain: true, qos: 0 }, + expect.any(Function) + ); + expect(MQTT.publish).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/response/device/configure', + expect.any(String), + { retain: false, qos: 0 }, + expect.any(Function) + ); + }); }); diff --git a/test/controller.test.js b/test/controller.test.js index e47c0a510..e1dfd6d5d 100644 --- a/test/controller.test.js +++ b/test/controller.test.js @@ -667,7 +667,7 @@ describe('Controller', () => { MQTT.events['connect'](); await flushPromises(); jest.runOnlyPendingTimers(); - expect(MQTT.publish).toHaveBeenCalledTimes(13); + expect(MQTT.publish).toHaveBeenCalledTimes(14); expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), { retain: true, qos: 0 }, expect.any(Function)); }); diff --git a/test/stub/zigbeeHerdsman.js b/test/stub/zigbeeHerdsman.js index b664f6863..5ccab350a 100644 --- a/test/stub/zigbeeHerdsman.js +++ b/test/stub/zigbeeHerdsman.js @@ -34,6 +34,20 @@ const clusters = { 'msCO2': 1037 } +const custom_clusters = { + custom_1: { + ID: 64672, + manufacturerCode: 4617, + attributes: { + attribute_0: {ID: 0, type: 49}, + }, + commands: { + command_0: { ID: 0, response: 0, parameters: [{name: 'reset', type: 40}], }, + }, + commandsResponse: {}, + }, +} + class Endpoint { constructor(ID, inputClusters, outputClusters, deviceIeeeAddress, binds=[], clusterValues={}, configuredReportings=[], profileID=null, deviceID=null, meta={}) { this.deviceIeeeAddress = deviceIeeeAddress; @@ -102,7 +116,7 @@ class Endpoint { } class Device { - constructor(type, ieeeAddr, networkAddress, manufacturerID, endpoints, interviewCompleted, powerSource = null, modelID = null, interviewing=false, manufacturerName, dateCode= null, softwareBuildID=null) { + constructor(type, ieeeAddr, networkAddress, manufacturerID, endpoints, interviewCompleted, powerSource = null, modelID = null, interviewing=false, manufacturerName, dateCode= null, softwareBuildID=null, customClusters = {}) { this.type = type; this.ieeeAddr = ieeeAddr; this.dateCode = dateCode; @@ -118,7 +132,7 @@ class Device { this.ping = jest.fn(); this.removeFromNetwork = jest.fn(); this.removeFromDatabase = jest.fn(); - this.customClusters = {}; + this.customClusters = customClusters; this.addCustomCluster = jest.fn(); this.save = jest.fn(); this.manufacturerName = manufacturerName; @@ -211,7 +225,8 @@ const devices = { 'bj_scene_switch': new Device('EndDevice', '0xd85def11a1002caa', 50117, 4398, [new Endpoint(10, [0,4096], [3,4,5,6,8,25,768,4096], '0xd85def11a1002caa', [{target: bulb_color_2.endpoints[0], cluster: {ID: 8, name: 'genLevelCtrl'}}, {target: bulb_color_2.endpoints[0], cluster: {ID: 6, name: 'genOnOff'}}, {target: bulb_color_2.endpoints[0], cluster: {ID: 768, name: 'lightingColorCtrl'}},]), new Endpoint(11, [0,4096], [3,4,5,6,8,25,768,4096], '0xd85def11a1002caa')], true, 'Battery', 'RB01', false, 'Busch-Jaeger', '20161222', '1.2.0'), 'GW003-AS-IN-TE-FC': new Device('Router', '0x0017548104a44669', 6545,4699, [new Endpoint(1, [3], [0,3,513,514], '0x0017548104a44669')], true, "Mains (single phase)", 'Adapter Zigbee FUJITSU'), 'BMCT-SLZ': new Device('Router', '0x18fc26000000cafe', 6546,4617, [new Endpoint(1, [0,3,4,5,258,1794,2820,2821,64672], [10,25], '0x18fc26000000cafe')], true, "Mains (single phase)", 'RBSH-MMS-ZB-EU'), - + 'BMCT_SLZ': new Device('Router', '0x0026decafe000473', 6546,4617, [new Endpoint(1, [0,3,4,5,258,1794,2820,2821,64672], [10,25], '0x0026decafe000473')], true, "Mains (single phase)", 'RBSH-MMS-ZB-EU', false, null, null, null, custom_clusters), + 'bulb_custom_cluster': new Device('Router', '0x000b57fffec6a5c2', 40369, 4476, [new Endpoint(1, [0,3,4,5,6,8,768,2821,4096], [5,25,32,4096], '0x000b57fffec6a5c2')], true, "Mains (single phase)", "TRADFRI bulb E27 WS opal 980lm", false, null, null, null, custom_clusters), } const mock = { @@ -268,5 +283,5 @@ jest.mock('zigbee-herdsman', () => ({ })); module.exports = { - events, ...mock, constructor: mockConstructor, devices, groups, returnDevices + events, ...mock, constructor: mockConstructor, devices, groups, returnDevices, custom_clusters };