diff --git a/lib/controller.js b/lib/controller.js index e55c504228..ff034c9d02 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -3,7 +3,6 @@ const Zigbee = require('./zigbee'); const logger = require('./util/logger'); const settings = require('./util/settings'); const deviceMapping = require('./devices'); -const zigbee2mqtt = require('./converters/zigbee2mqtt'); const mqtt2zigbee = require('./converters/mqtt2zigbee'); const homeassistant = require('./homeassistant'); const debug = require('debug')('zigbee2mqtt:controller'); @@ -217,9 +216,7 @@ class Controller { // Find a conveter for this message. const cid = message.data.cid; - const converters = zigbee2mqtt.filter((c) => - c.devices.includes(mappedModel.model) && c.cid === cid && c.type === message.type - ); + const converters = mappedModel.fromZigbee.filter((c) => c.cid === cid && c.type === message.type); if (!converters.length) { logger.warn( diff --git a/lib/converters/zigbee2mqtt.js b/lib/converters/fromZigbee.js similarity index 82% rename from lib/converters/zigbee2mqtt.js rename to lib/converters/fromZigbee.js index b58baa47b0..34610a45c0 100644 --- a/lib/converters/zigbee2mqtt.js +++ b/lib/converters/fromZigbee.js @@ -43,12 +43,8 @@ const precisionRound = (number, precision) => { // Global variable store that can be used by devices. const store = {}; -const parsers = [ - { - devices: [ - 'WXKG01LM', 'RTCGQ01LM', 'WSDCGQ01LM', 'MCCGQ01LM', 'WXKG11LM', 'MCCGQ11LM', 'RTCGQ11LM', 'WSDCGQ11LM', - 'SJCGQ11LM', 'MFKZQ01LM', 'JTYJ-GD-01LM/BW', 'WXKG02LM', 'WXKG03LM', - ], +const parsers = { + xiaomi_battery_3v: { cid: 'genBasic', type: 'attReport', convert: (msg) => { @@ -65,8 +61,7 @@ const parsers = [ } }, }, - { - devices: ['WXKG01LM'], + WXKG01LM_click: { cid: 'genOnOff', type: 'attReport', convert: (msg, publish) => { @@ -92,16 +87,14 @@ const parsers = [ } }, }, - { - devices: ['WSDCGQ01LM', 'WSDCGQ11LM'], + xiaomi_temperature: { cid: 'msTemperatureMeasurement', type: 'attReport', convert: (msg) => { return {temperature: parseFloat(msg.data.data['measuredValue']) / 100.0}; }, }, - { - devices: ['MFKZQ01LM'], + MFKZQ01LM_action_multistate: { cid: 'genMultistateInput', type: 'attReport', convert: (msg) => { @@ -139,8 +132,7 @@ const parsers = [ return action ? {'action': action} : null; }, }, - { - devices: ['MFKZQ01LM'], + MFKZQ01LM_action_analog: { cid: 'genAnalogInput', type: 'attReport', convert: (msg) => { @@ -152,16 +144,14 @@ const parsers = [ return {action: value < 0 ? 'rotate_left' : 'rotate_right'}; }, }, - { - devices: ['WSDCGQ01LM', 'WSDCGQ11LM'], + xiaomi_humidity: { cid: 'msRelativeHumidity', type: 'attReport', convert: (msg) => { return {humidity: parseFloat(msg.data.data['measuredValue']) / 100.0}; }, }, - { - devices: ['RTCGQ01LM', 'RTCGQ11LM'], + xiaomi_occupancy: { cid: 'msOccupancySensing', type: 'attReport', convert: (msg, publish, options) => { @@ -183,27 +173,21 @@ const parsers = [ return {occupancy: true}; }, }, - { - devices: ['MCCGQ01LM', 'MCCGQ11LM'], + xiaomi_contact: { cid: 'genOnOff', type: 'attReport', convert: (msg) => { return {contact: msg.data.data['onOff'] === 0}; }, }, - { - devices: [ - 'LED1545G12', '7146060PH', 'LED1537R6', 'LED1623G12', 'LED1650R5', 'LED1536G5', 'F7C033', - 'LED1622G12', - ], + light_brightness: { cid: 'genLevelCtrl', type: 'devChange', convert: (msg) => { return {brightness: msg.data.data['currentLevel']}; }, }, - { - devices: ['LED1545G12', '7146060PH', 'LED1537R6', 'LED1536G5'], + light_color_colortemp: { cid: 'lightingColorCtrl', type: 'devChange', convert: (msg) => { @@ -223,8 +207,7 @@ const parsers = [ return result; }, }, - { - devices: ['WXKG11LM'], + WXKG11LM_click: { cid: 'genOnOff', type: 'attReport', convert: (msg) => { @@ -242,72 +225,63 @@ const parsers = [ } }, }, - { - devices: ['RTCGQ11LM'], + xiaomi_illuminance: { cid: 'msIlluminanceMeasurement', type: 'attReport', convert: (msg) => { return {illuminance: msg.data.data['measuredValue']}; }, }, - { - devices: ['WSDCGQ11LM'], + xiaomi_pressure: { cid: 'msPressureMeasurement', type: 'attReport', convert: (msg) => { return {pressure: msg.data.data['measuredValue']}; }, }, - { - devices: ['WXKG02LM'], + WXKG02LM_click: { cid: 'genOnOff', type: 'attReport', convert: (msg) => { return {click: WXKG02LM[msg.endpoints[0].epId]}; }, }, - { - devices: ['WXKG03LM'], + WXKG03LM_click: { cid: 'genOnOff', type: 'attReport', convert: () => { return {click: 'single'}; }, }, - { - devices: ['SJCGQ11LM'], + SJCGQ11LM_water_leak_basic: { cid: 'genBasic', type: 'attReport', convert: (msg) => { return {water_leak: msg.data.data['65281']['100'] === 1}; }, }, - { - devices: ['SJCGQ11LM'], + SJCGQ11LM_water_leak_iaszone: { cid: 'ssIasZone', type: 'statusChange', convert: (msg) => { return {water_leak: msg.data.zoneStatus === 1}; }, }, - { - devices: ['ZNCZ02LM', 'QBCZ11LM'], + xiaomi_state: { cid: 'genOnOff', type: 'attReport', convert: (msg) => { return {state: msg.data.data['onOff'] === 1 ? 'ON' : 'OFF'}; }, }, - { - devices: ['ZNCZ02LM', 'QBCZ11LM'], + xiaomi_power: { cid: 'genAnalogInput', type: 'attReport', convert: (msg) => { return {power: precisionRound(msg.data.data['presentValue'], 2)}; }, }, - { - devices: ['ZNCZ02LM'], + ZNCZ02LM_state: { cid: 'genBasic', type: 'attReport', convert: (msg) => { @@ -323,8 +297,7 @@ const parsers = [ } }, }, - { - devices: ['QBKG04LM'], + QBKG04LM_state: { cid: 'genOnOff', type: 'attReport', convert: (msg) => { @@ -333,8 +306,7 @@ const parsers = [ } }, }, - { - devices: ['QBKG03LM'], + QBKG03LM_state: { cid: 'genOnOff', type: 'attReport', convert: (msg) => { @@ -346,32 +318,28 @@ const parsers = [ } }, }, - { - devices: ['JTYJ-GD-01LM/BW'], + JTYJGD01LMBW_smoke: { cid: 'ssIasZone', type: 'statusChange', convert: (msg) => { return {smoke: msg.data.zoneStatus === 1}; }, }, - { - devices: ['PLUG EDP RE:DY'], + EDP_power: { cid: 'seMetering', type: 'attReport', convert: (msg) => { return {power: precisionRound(msg.data.data['instantaneousDemand'], 2)}; }, }, - { - devices: ['CC2530.ROUTER'], + CC2530ROUTER_state: { cid: 'genOnOff', type: 'attReport', convert: (msg) => { return {state: msg.data.data['onOff'] === 1}; }, }, - { - devices: ['CC2530.ROUTER'], + CC2530ROUTER_meta: { cid: 'genBinaryValue', type: 'attReport', convert: (msg) => { @@ -385,79 +353,56 @@ const parsers = [ }, // Ignore parsers (these message dont need parsing). - { - devices: [ - 'WXKG11LM', 'MCCGQ11LM', 'MCCGQ01LM', 'WXKG01LM', 'LED1545G12', '7146060PH', 'LED1537R6', 'ZNCZ02LM', - 'QBCZ11LM', 'QBKG04LM', 'QBKG03LM', 'LED1623G12', 'LED1650R5', 'LED1536G5', 'F7C033', 'LED1622G12', - 'PLUG EDP RE:DY', - ], + ignore_onoff_change: { cid: 'genOnOff', type: 'devChange', convert: () => null, }, - { - devices: [ - 'WXKG11LM', 'MCCGQ11LM', 'RTCGQ11LM', 'WSDCGQ11LM', 'SJCGQ11LM', 'MCCGQ01LM', 'RTCGQ01LM', 'WXKG01LM', - 'WSDCGQ01LM', 'JTYJ-GD-01LM/BW', 'ZNCZ02LM', - ], + ignore_basic_change: { cid: 'genBasic', type: 'devChange', convert: () => null, }, - { - devices: ['RTCGQ11LM'], + ignore_illuminance_change: { cid: 'msIlluminanceMeasurement', type: 'devChange', convert: () => null, }, - { - devices: ['RTCGQ11LM'], + ignore_occupancy_change: { cid: 'msOccupancySensing', type: 'devChange', convert: () => null, }, - { - devices: ['WSDCGQ11LM'], + ignore_temperature_change: { cid: 'msTemperatureMeasurement', type: 'devChange', convert: () => null, }, - { - devices: ['WSDCGQ11LM'], + ignore_humidity_change: { cid: 'msRelativeHumidity', type: 'devChange', convert: () => null, }, - { - devices: ['WSDCGQ11LM'], + ignore_pressure_change: { cid: 'msPressureMeasurement', type: 'devChange', convert: () => null, }, - { - devices: ['ZNCZ02LM', 'QBCZ11LM'], + ignore_analog_change: { cid: 'genAnalogInput', type: 'devChange', convert: () => null, }, - { - devices: ['MFKZQ01LM'], + ignore_multistate_change: { cid: 'genMultistateInput', type: 'devChange', convert: () => null, }, - { - devices: ['MFKZQ01LM'], - cid: 'genAnalogInput', - type: 'devChange', - convert: () => null, - }, - { - devices: ['PLUG EDP RE:DY'], + ignore_metering_change: { cid: 'seMetering', type: 'devChange', convert: () => null, }, -]; +}; module.exports = parsers; diff --git a/lib/devices.js b/lib/devices.js index 2b75f199c3..e330c80e4a 100644 --- a/lib/devices.js +++ b/lib/devices.js @@ -1,8 +1,11 @@ +const fz = require('./converters/fromZigbee'); + const LED1623G12 = { model: 'LED1623G12', vendor: 'IKEA', description: 'TRADFRI LED bulb E27 1000 lumen, dimmable, opal white', supports: 'on/off, brightness', + fromZigbee: [fz.light_brightness, fz.ignore_onoff_change], }; const LED1536G5 = { @@ -10,6 +13,7 @@ const LED1536G5 = { vendor: 'IKEA', description: 'TRADFRI LED bulb E12/E14 400 lumen, dimmable, white spectrum, opal white', supports: 'on/off, brightness, color temperature', + fromZigbee: [fz.light_brightness, fz.light_color_colortemp, fz.ignore_onoff_change], }; const devices = { @@ -19,30 +23,35 @@ const devices = { vendor: 'Xiaomi', description: 'MiJia wireless switch', supports: 'single, double, triple, quadruple, many and long click', + fromZigbee: [fz.xiaomi_battery_3v, fz.WXKG01LM_click, fz.ignore_onoff_change, fz.ignore_basic_change], }, 'lumi.sensor_switch.aq2': { model: 'WXKG11LM', vendor: 'Xiaomi', description: 'Aqara wireless switch', supports: 'single, double, triple, quadruple click', + fromZigbee: [fz.xiaomi_battery_3v, fz.WXKG11LM_click, fz.ignore_onoff_change, fz.ignore_basic_change], }, 'lumi.sensor_86sw1\u0000lu': { model: 'WXKG03LM', vendor: 'Xiaomi', description: 'Aqara single key wireless wall switch', supports: 'single click', + fromZigbee: [fz.xiaomi_battery_3v, fz.WXKG03LM_click], }, 'lumi.sensor_86sw2\u0000Un': { model: 'WXKG02LM', vendor: 'Xiaomi', description: 'Aqara double key wireless wall switch', supports: 'left, right and both click', + fromZigbee: [fz.xiaomi_battery_3v, fz.WXKG02LM_click], }, 'lumi.ctrl_neutral1': { model: 'QBKG04LM', vendor: 'Xiaomi', description: 'Aqara single key wired wall switch', supports: 'on/off', + fromZigbee: [fz.QBKG04LM_state, fz.ignore_onoff_change], ep: {'': 2}, }, 'lumi.ctrl_neutral2': { @@ -50,6 +59,7 @@ const devices = { vendor: 'Xiaomi', description: 'Aqara double key wired wall switch', supports: 'l1 and l2 on/off', + fromZigbee: [fz.QBKG03LM_state, fz.ignore_onoff_change], ep: {'l1': 2, 'l2': 3}, }, 'lumi.sens': { @@ -57,66 +67,92 @@ const devices = { vendor: 'Xiaomi', description: 'MiJia temperature & humidity sensor ', supports: 'temperature and humidity', + fromZigbee: [fz.xiaomi_battery_3v, fz.xiaomi_temperature, fz.xiaomi_humidity, fz.ignore_basic_change], }, 'lumi.weather': { model: 'WSDCGQ11LM', vendor: 'Xiaomi', description: 'Aqara temperature, humidity and pressure sensor', supports: 'temperature, humidity and pressure', + fromZigbee: [ + fz.xiaomi_battery_3v, fz.xiaomi_temperature, fz.xiaomi_humidity, fz.xiaomi_pressure, fz.ignore_basic_change, + fz.ignore_temperature_change, fz.ignore_humidity_change, fz.ignore_pressure_change, + ], }, 'lumi.sensor_motion': { model: 'RTCGQ01LM', vendor: 'Xiaomi', description: 'MiJia human body movement sensor', supports: 'occupancy', + fromZigbee: [fz.xiaomi_battery_3v, fz.xiaomi_occupancy, fz.ignore_basic_change], }, 'lumi.sensor_motion.aq2': { model: 'RTCGQ11LM', vendor: 'Xiaomi', description: 'Aqara human body movement and illuminance sensor', supports: 'occupancy and illuminance', + fromZigbee: [ + fz.xiaomi_battery_3v, fz.xiaomi_occupancy, fz.xiaomi_illuminance, fz.ignore_basic_change, + fz.ignore_illuminance_change, fz.ignore_occupancy_change, + ], }, 'lumi.sensor_magnet': { model: 'MCCGQ01LM', vendor: 'Xiaomi', description: 'MiJia door & window contact sensor', supports: 'contact', + fromZigbee: [fz.xiaomi_battery_3v, fz.xiaomi_contact, fz.ignore_onoff_change, fz.ignore_basic_change], }, 'lumi.sensor_magnet.aq2': { model: 'MCCGQ11LM', vendor: 'Xiaomi', description: 'Aqara door & window contact sensor', supports: 'contact', + fromZigbee: [fz.xiaomi_battery_3v, fz.xiaomi_contact, fz.ignore_onoff_change, fz.ignore_basic_change], }, 'lumi.sensor_wleak.aq1': { model: 'SJCGQ11LM', vendor: 'Xiaomi', description: 'Aqara water leak sensor', supports: 'water leak true/false', + fromZigbee: [ + fz.xiaomi_battery_3v, fz.SJCGQ11LM_water_leak_basic, fz.SJCGQ11LM_water_leak_iaszone, + fz.ignore_basic_change, + ], }, 'lumi.sensor_cube': { model: 'MFKZQ01LM', vendor: 'Xiaomi', description: 'Mi smart home cube', supports: 'shake, wakeup, fall, tap, slide, flip180, flip90, rotate_left and rotate_right', + fromZigbee: [ + fz.xiaomi_battery_3v, fz.MFKZQ01LM_action_multistate, fz.MFKZQ01LM_action_analog, + fz.ignore_analog_change, fz.ignore_multistate_change, + ], }, 'lumi.plug': { model: 'ZNCZ02LM', description: 'Mi power plug ZigBee', supports: 'on/off, power measurement', vendor: 'Xiaomi', + fromZigbee: [ + fz.xiaomi_state, fz.xiaomi_power, fz.ZNCZ02LM_state, fz.ignore_onoff_change, + fz.ignore_basic_change, fz.ignore_analog_change, + ], }, 'lumi.ctrl_86plug': { model: 'QBCZ11LM', description: 'Aqara socket Zigbee', supports: 'on/off, power measurement', vendor: 'Xiaomi', + fromZigbee: [fz.xiaomi_state, fz.xiaomi_power, fz.ignore_onoff_change, fz.ignore_analog_change], }, 'lumi.sensor_smoke': { model: 'JTYJ-GD-01LM/BW', description: 'MiJia Honeywell smoke detector', supports: 'smoke', vendor: 'Xiaomi', + fromZigbee: [fz.xiaomi_battery_3v, fz.JTYJGD01LMBW_smoke, fz.ignore_basic_change], }, // IKEA @@ -125,6 +161,7 @@ const devices = { vendor: 'IKEA', description: 'TRADFRI LED bulb E27 980 lumen, dimmable, white spectrum, opal white', supports: 'on/off, brightness, color temperature', + fromZigbee: [fz.light_brightness, fz.light_color_colortemp, fz.ignore_onoff_change], }, // LED1623G12 uses 2 model IDs, see https://github.com/Koenkk/zigbee2mqtt/issues/21 'TRADFRI bulb E27 opal 1000lm': LED1623G12, @@ -134,12 +171,14 @@ const devices = { vendor: 'IKEA', description: 'TRADFRI LED bulb GU10 400 lumen, dimmable, white spectrum', supports: 'on/off, brightness, color temperature', + fromZigbee: [fz.light_brightness, fz.light_color_colortemp, fz.ignore_onoff_change], }, 'TRADFRI bulb GU10 W 400lm': { model: 'LED1650R5', vendor: 'IKEA', description: 'TRADFRI LED bulb GU10 400 lumen, dimmable', supports: 'on/off, brightness', + fromZigbee: [fz.light_brightness, fz.ignore_onoff_change], }, // LED1536G5 has an E12 and E14 version. 'TRADFRI bulb E14 WS opal 400lm': LED1536G5, @@ -149,6 +188,7 @@ const devices = { vendor: 'IKEA', description: 'TRADFRI LED bulb E26 1000 lumen, dimmable, opal white', supports: 'on/off, brightness', + fromZigbee: [fz.light_brightness, fz.ignore_onoff_change], }, // Philips @@ -157,6 +197,7 @@ const devices = { vendor: 'Philips', description: 'Hue Go', supports: 'on/off, brightness, color temperature, color xy', + fromZigbee: [fz.light_brightness, fz.light_color_colortemp, fz.ignore_onoff_change], }, // Belkin @@ -165,6 +206,7 @@ const devices = { vendor: 'Belkin', description: 'WeMo smart LED bulb', supports: 'on/off, brightness', + fromZigbee: [fz.light_brightness, fz.ignore_onoff_change], }, // EDP @@ -173,6 +215,7 @@ const devices = { vendor: 'EDP', description: 're:dy plug', supports: 'on/off, power measurement', + fromZigbee: [fz.ignore_onoff_change, fz.EDP_power, fz.ignore_metering_change], report: [{ 'cid': 'seMetering', 'attr': 'instantaneousDemand', @@ -189,6 +232,7 @@ const devices = { vendor: 'Texas Instruments', description: '[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)', supports: 'state, description, type, rssi', + fromZigbee: [fz.CC2530ROUTER_state, fz.CC2530ROUTER_meta], }, }; diff --git a/support/docgen.js b/support/docgen.js index 8dadec0fcd..8b40a4e7be 100644 --- a/support/docgen.js +++ b/support/docgen.js @@ -3,23 +3,11 @@ * Run by executing: npm run docs */ -const zigbee2mqtt = require('../lib/converters/zigbee2mqtt'); const deviceMapping = require('../lib/devices'); const fs = require('fs'); const YAML = require('json2yaml'); const homeassistant = require('../lib/homeassistant'); -// Sanity check if all supported devices are in deviceMapping -const supportedDevices = new Set(); -zigbee2mqtt.forEach((p) => supportedDevices.add(...p.devices)); - -// Check if in deviceMapping. -supportedDevices.forEach((s) => { - if (!Object.values(deviceMapping).find((d) => d.model === s)) { - console.log(`ERROR: ${s} not in deviceMapping`); - } -}); - const outputdir = process.argv[2]; if (!outputdir) {