Refactor and rename. #8

This commit is contained in:
Koen Kanters
2018-04-18 18:25:40 +02:00
committed by Koenkk
parent bc52e11693
commit d06522d488
14 changed files with 430 additions and 300 deletions
+4 -4
View File
@@ -1,11 +1,11 @@
# xiaomi-zb2mqtt
# zigbee2mqtt
![Some Xiaomi sensors](xiaomi.png)
Allows you to use your Xiaomi Zigbee sensors and switches **without** Xiaomi's gateway.
Allows you to use your Zigbee devices **without** the vendors (Xiaomi/TRADFRI/Hue) bridge/gateway.
It bridges the events sent from the sensors and switches to MQTT. You can integrate the cheap and nice Zigbee sensors and switches with whatever smart home infrastructure you are using.
It bridges the events sent from the sensors and switches to MQTT. In this way you can integrate your Zigbee devices with whatever smart home infrastructure you are using.
The [wiki](https://github.com/Koenkk/xiaomi-zb2mqtt/wiki) provides you all the information needed to get up and running!
The [wiki](https://github.com/Koenkk/zigbee2mqtt/wiki) provides you all the information needed to get up and running!
### Contributors
* [AndrewLinden](https://github.com/AndrewLinden)
+2 -2
View File
@@ -1,4 +1,4 @@
#!/bin/bash
docker build -t koenkk/xiaomi-zb2mqtt:arm32v7 -f Dockerfile ../../
docker push koenkk/xiaomi-zb2mqtt:arm32v7
docker build -t koenkk/zigbee2mqtt:arm32v7 -f Dockerfile ../../
docker push koenkk/zigbee2mqtt:arm32v7
+5 -268
View File
@@ -1,273 +1,10 @@
const util = require("util");
const ZShepherd = require('zigbee-shepherd');
const mqtt = require('mqtt')
const fs = require('fs');
const parsers = require('./parsers');
const commands = require('./commands');
const deviceMapping = require('./devices');
const config = require('yaml-config');
const configFile = `${__dirname}/data/configuration.yaml`;
const winston = require('winston');
let settings = config.readConfig(configFile, 'user');
const stateCache = {};
const Controller = require('./lib/controller');
const logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({
timestamp: () => new Date().toLocaleString(),
formatter: function(options) {
return options.timestamp() + ' ' +
winston.config.colorize(options.level, options.level.toUpperCase()) + ' ' +
(options.message ? options.message : '') +
(options.meta && Object.keys(options.meta).length ? '\n\t'+ JSON.stringify(options.meta) : '' );
}
})
]
});
const controller = new Controller();
controller.start();
// Create empty device array if not set yet.
if (!settings.devices) {
settings.devices = {};
writeConfig();
}
// Setup client
logger.info(`Connecting to MQTT server at ${settings.mqtt.server}`)
const options = {};
if (settings.mqtt.user && settings.mqtt.password) {
options.username = settings.mqtt.user;
options.password = settings.mqtt.password;
}
const client = mqtt.connect(settings.mqtt.server, options)
const shepherd = new ZShepherd(
settings.serial.port,
{
net: {panId: 0x1a62},
dbPath: `${__dirname}/data/database.db`
}
);
// Register callbacks
client.on('connect', handleConnect);
client.on('message', handleMqttMessage);
shepherd.on('ready', handleReady);
shepherd.on('ind', handleMessage);
process.on('SIGINT', handleQuit);
// Check every interval if connected to MQTT server.
setInterval(() => {
if (client.reconnecting) {
logger.error('Not connected to MQTT server!');
}
}, 10 * 1000); // seconds * 1000.
// Start server
logger.info(`Starting zigbee-shepherd with device ${settings.serial.port}`)
shepherd.start((err) => {
if (err) {
logger.error('Error while starting zigbee-shepherd');
logger.error(err);
} else {
logger.info('zigbee-shepherd started');
}
});
function handleReady() {
logger.info('zigbee-shepherd ready');
const devices = shepherd.list().filter((device) => device.type !== 'Coordinator');
logger.info(`Currently ${devices.length} devices are joined:`);
devices.forEach((device) => logger.info(getDeviceLogMessage(device)));
// Set all Xiaomi devices to be online, so shepherd won't try
// to query info from devices (which would fail because they go tosleep).
devices.forEach((device) => {
shepherd.find(device.ieeeAddr, 1).getDevice().update({
status: 'online',
joinTime: Math.floor(Date.now()/1000)
});
});
// Allow or disallow new devices to join the network.
if (settings.allowJoin) {
logger.warn('allowJoin set to true in configuration.yaml.')
logger.warn('Allowing new devices to join.');
logger.warn('Remove this parameter once you joined all devices.');
}
shepherd.permitJoin(settings.allowJoin ? 255 : 0, (err) => {
if (err) {
logger.info(err);
}
});
}
function handleConnect() {
mqttPublish(`${settings.mqtt.base_topic}/bridge/state`, 'online', true);
client.subscribe(`${settings.mqtt.base_topic}/+/set`)
}
function handleMessage(msg) {
if (!msg.endpoints) {
return;
}
const device = msg.endpoints[0].device;
// New device!
if (!settings.devices[device.ieeeAddr]) {
logger.info(`New device with address ${device.ieeeAddr} connected!`);
settings.devices[device.ieeeAddr] = {
friendly_name: device.ieeeAddr,
retain: false,
};
writeConfig();
}
// We can't handle devices without modelId.
if (!device.modelId) {
return;
}
// Map modelID to Xiaomi model.
const modelID = msg.endpoints[0].device.modelId;
const mappedModel = deviceMapping[modelID];
if (!mappedModel) {
logger.error(`Device with modelID '${modelID}' is not supported.`);
logger.error('Please create an issue on https://github.com/Koenkk/xiaomi-zb2mqtt/issues to add support for your device');
return;
}
// Find a parser for this modelID and cid.
const cid = msg.data.cid;
const _parsers = parsers.filter((p) => p.devices.includes(mappedModel.model) && p.cid === cid && p.type === msg.type);
if (!_parsers.length) {
logger.error(`No parser available for '${mappedModel.model}' with cid '${cid}' and type '${msg.type}'`);
logger.error('Please create an issue on https://github.com/Koenkk/xiaomi-zb2mqtt/issues with this message.');
return;
}
// Parse generic information from message.
const friendlyName = settings.devices[device.ieeeAddr].friendly_name;
const retain = settings.devices[device.ieeeAddr].retain;
const topic = `${settings.mqtt.base_topic}/${friendlyName}`;
const publish = (payload) => {
if (stateCache[device.ieeeAddr]) {
payload = {...stateCache[device.ieeeAddr], ...payload};
}
mqttPublish(topic, JSON.stringify(payload), retain);
}
// Get payload for the message.
// - If a payload is returned publish it to the MQTT broker
// - If NO payload is returned do nothing. This is for non-standard behaviour
// for e.g. click switches where we need to count number of clicks and detect long presses.
_parsers.forEach((parser) => {
const payload = parser.parse(msg, publish);
if (payload) {
stateCache[device.ieeeAddr] = {...stateCache[device.ieeeAddr], ...payload};
if (!parser.disablePublish) {
publish(payload);
}
}
});
}
function handleQuit() {
mqttPublish(`${settings.mqtt.base_topic}/bridge/state`, 'offline', true);
shepherd.stop((err) => {
if (err) {
logger.error('Error while stopping zigbee-shepherd');
} else {
logger.error('zigbee-shepherd stopped')
}
process.exit();
});
}
function mqttPublish(topic, payload, retain) {
if (client.reconnecting) {
logger.error(`Not connected to MQTT server!`);
logger.error(`Cannot send message: topic: '${topic}', payload: '${payload}`);
return;
}
logger.info(`MQTT publish, topic: '${topic}', payload: '${payload}'`);
client.publish(topic, payload, {retain: retain});
}
function writeConfig() {
config.updateConfig(settings, configFile, 'user');
settings = config.readConfig(configFile, 'user');
}
function getDeviceLogMessage(device) {
let friendlyName = 'unknown';
let friendlyDevice = {model: 'unkown', description: 'unknown'};
if (deviceMapping[device.modelId]) {
friendlyDevice = deviceMapping[device.modelId];
}
if (settings.devices[device.ieeeAddr]) {
friendlyName = settings.devices[device.ieeeAddr].friendly_name
}
return `${friendlyName} (${device.ieeeAddr}): ${friendlyDevice.model} - ${friendlyDevice.description}`;
}
function handleMqttMessage(topic, message) {
const friendlyName = topic.split('/')[1];
// Find device id of this friendlyName
const deviceID = Object.keys(settings.devices).find((id) => settings.devices[id].friendly_name === friendlyName);
if (!deviceID) {
logger.error(`Cannot handle '${topic}' because ID of '${friendlyName}' cannot be found`);
}
// Find device in zigbee-shepherd
const device = shepherd.find(deviceID, 1);
if (!device) {
logger.error(`Cannot handle '${topic}' because '${deviceID}' is not known by zigbee-shepherd`);
}
logger.info(`Executing '${topic}' '${message.toString()}' `)
const json = JSON.parse(message);
// Iterate over all keys in the json.
Object.keys(json).forEach((key) => {
// Find parser for this key
const parser = commands[key];
if (!parser) {
logger.error(`No parser available for '${key}' (${json[key]})`);
return;
}
const parsed = parser(json[key]);
const callback = (err, rsp) => {
// Devices do not report when they go off.
// Ensure state (on/off) is always in sync.
if (!err && key === 'state') {
mqttPublish(
`${settings.mqtt.base_topic}/${friendlyName}`,
JSON.stringify({state: json[key]}),
true
);
}
};
device.functional(parsed.cId, parsed.cmd, parsed.zclData, callback);
});
}
controller.stop(() => process.exit());
}
+141
View File
@@ -0,0 +1,141 @@
const MQTT = require('./mqtt');
const Zigbee = require('./zigbee');
const logger = require('./util/logger');
const deviceMapping = require('./devices');
const zigbee2mqtt = require('./converters/zigbee2mqtt');
const mqtt2zigbee = require('./converters/mqtt2zigbee');
class Controller {
constructor() {
this.zigbee = new Zigbee();
this.mqtt = new MQTT();
this.stateCache = {};
this.handleZigbeeMessage = this.handleZigbeeMessage.bind(this);
this.handleMQTTMessage = this.handleMQTTMessage.bind(this);
}
start() {
this.zigbee.start(this.handleZigbeeMessage, (error) => {
if (error) {
logger.error('Failed to start');
} else {
this.mqtt.connect(this.handleMQTTMessage);
}
});
}
stop(callback) {
this.mqtt.disconnect();
this.zigbee.stop(callback);
}
handleZigbeeMessage(message) {
if (!message.endpoints) {
// We dont handle messages without endpoints.
return;
}
const device = message.endpoints[0].device;
// Check if this is a new device.
if (!settings.get().devices[device.ieeeAddr]) {
logger.info(`New device with address ${device.ieeeAddr} connected!`);
settings.get().devices[device.ieeeAddr] = {
friendly_name: device.ieeeAddr,
retain: false,
};
settings.write();
}
// We can't handle devices without modelId.
if (!device.modelId) {
return;
}
// Map Zigbee modelID to vendor modelID.
const modelID = msg.endpoints[0].device.modelId;
const mappedModel = deviceMapping[modelID];
if (!mappedModel) {
logger.error(`Device with modelID '${modelID}' is not supported.`);
logger.error('Please create an issue on https://github.com/Koenkk/zigbee2mqtt/issues to add support for your device');
return;
}
// Find a conveter for this message.
const cid = msg.data.cid;
const converters = zigbee2mqtt.filter((c) => c.devices.includes(mappedModel.model) && c.cid === cid && c.type === msg.type);
if (!converters.length) {
logger.error(`No converter available for '${mappedModel.model}' with cid '${cid}' and type '${msg.type}'`);
logger.error('Please create an issue on https://github.com/Koenkk/zigbee2mqtt/issues with this message.');
return;
}
// Convert this Zigbee message to a MQTT message.
const friendlyName = settings.get().devices[device.ieeeAddr].friendly_name;
const retain = settings.get().devices[device.ieeeAddr].retain;
const topic = `${settings.get().mqtt.base_topic}/${friendlyName}`;
const publish = (payload) => {
if (this.stateCache[device.ieeeAddr]) {
payload = {...this.stateCache[device.ieeeAddr], ...payload};
}
this.mqtt.publish(topic, JSON.stringify(payload), retain);
}
// Get payload for the message.
// - If a payload is returned publish it to the MQTT broker
// - If NO payload is returned do nothing. This is for non-standard behaviour
// for e.g. click switches where we need to count number of clicks and detect long presses.
converters.forEach((converter) => {
const payload = converter.convert(msg, publish);
if (payload) {
this.stateCache[device.ieeeAddr] = {...this.stateCache[device.ieeeAddr], ...payload};
if (!converter.disablePublish) {
publish(payload);
}
}
});
}
handleMQTTMessage(topic, message) {
const friendlyName = topic.split('/')[1];
// Map friendlyName to deviceID.
const deviceID = Object.keys(settings.get().devices).find((id) => settings.get().devices[id].friendly_name === friendlyName);
if (!deviceID) {
logger.error(`Cannot handle '${topic}' because deviceID of '${friendlyName}' cannot be found`);
}
// Convert the MQTT message to a Zigbee message.
const json = JSON.parse(message);
Object.keys(json).forEach((key) => {
// Find converter for this key.
const converter = mqtt2zigbee[key];
if (!converter) {
logger.error(`No converter available for '${key}' (${json[key]})`);
return;
}
const message = converter(json[key]);
const callback = (error, response) => {
// Devices do not report when they go off, this ensures state (on/off) is always in sync.
if (!error && key === 'state') {
this.mqtt.publish(friendlyName, JSON.stringify({state: json[key]}), true);
}
};
this.zigbee.publish(deviceID, message.cId, message.cmd, message.zclData, callback);
});
}
}
module.exports = Controller;
@@ -1,4 +1,4 @@
const commands = {
const converters = {
"state": (value) => {
return {
cId: 'genOnOff',
@@ -28,4 +28,4 @@ const commands = {
},
}
module.exports = commands;
module.exports = converters;
+11 -11
View File
@@ -29,7 +29,7 @@ const parsers = [
cid: 'genBasic',
type: 'attReport',
disablePublish: true,
parse: (msg, publish) => {
convert: (msg, publish) => {
let voltage = null;
if (msg.data.data['65281']) {
@@ -47,7 +47,7 @@ const parsers = [
devices: ['WXKG01LM'],
cid: 'genOnOff',
type: 'attReport',
parse: (msg, publish) => {
convert: (msg, publish) => {
const deviceID = msg.endpoints[0].device.ieeeAddr;
const state = msg.data.data['onOff'];
@@ -74,19 +74,19 @@ const parsers = [
devices: ['WSDCGQ01LM'],
cid: 'msTemperatureMeasurement',
type: 'attReport',
parse: (msg) => {return {temperature: parseFloat(msg.data.data['measuredValue']) / 100.0}}
convert: (msg) => {return {temperature: parseFloat(msg.data.data['measuredValue']) / 100.0}}
},
{
devices: ['WSDCGQ01LM'],
cid: 'msRelativeHumidity',
type: 'attReport',
parse: (msg) => {return {humidity: parseFloat(msg.data.data['measuredValue']) / 100.0}}
convert: (msg) => {return {humidity: parseFloat(msg.data.data['measuredValue']) / 100.0}}
},
{
devices: ['RTCGQ01LM'],
cid: 'msOccupancySensing',
type: 'attReport',
parse: (msg, publish) => {
convert: (msg, publish) => {
// The occupancy sensor only sends a message when motion detected.
// Therefore we need to publish the no_motion detected by ourselves.
// no_motion is triggered after 3 minutes of no motion.
@@ -102,7 +102,7 @@ const parsers = [
store[deviceID] = setTimeout(() => {
publish({occupancy: 'no_motion'})
store[deviceID] = null;
}, noMotionTimeout * 60 * 1000);
}, noMotionTimeout * 60 * 1000);
return {occupancy: 'motion'};
}
},
@@ -110,19 +110,19 @@ const parsers = [
devices: ['MCCGQ01LM'],
cid: 'genOnOff',
type: 'attReport',
parse: (msg) => {return {state: msg.data.data['onOff'] ? 'open' : 'closed'}}
convert: (msg) => {return {state: msg.data.data['onOff'] ? 'open' : 'closed'}}
},
{
devices: ['LED1545G12'],
cid: 'genLevelCtrl',
type: 'devChange',
parse: (msg) => {return {brightness: msg.data.data['currentLevel']}},
convert: (msg) => {return {brightness: msg.data.data['currentLevel']}},
},
{
devices: ['LED1545G12'],
cid: 'lightingColorCtrl',
type: 'devChange',
parse: (msg) => {return {color_temp: msg.data.data['colorTemperature']}},
convert: (msg) => {return {color_temp: msg.data.data['colorTemperature']}},
},
// Ignore the following messages:
@@ -130,13 +130,13 @@ const parsers = [
devices: ['LED1545G12'],
cid: 'genOnOff',
type: 'devChange',
parse: (msg) => null
convert: (msg) => null
},
{
devices: ['WXKG01LM'],
cid: 'genOnOff',
type: 'devChange',
parse: (msg) => null
convert: (msg) => null
},
];
View File
+76
View File
@@ -0,0 +1,76 @@
const mqtt = require('mqtt');
const logger = require('./util/logger');
const settings = require('./util/settings');
class MQTT {
constructor() {
this.handleConnect = this.handleConnect.bind(this);
this.handleMessage = this.handleMessage.bind(this);
}
connect(onMessage) {
const mqttSettings = settings.get().mqtt;
logger.info(`Connecting to MQTT server at ${mqttSettings.server}`);
const options = {};
if (mqttSettings.user && mqttSettings.password) {
options.username = mqttSettings.user;
options.password = mqttSettings.password;
}
this.client = mqtt.connect(mqttSettings.server, options);
// Register callbacks.
this.client.on('connect', this.handleConnect);
this.client.on('message', this.handleMessage);
// Set timer at interval to check if connected to MQTT server.
const interval = 10 * 1000; // seconds * 1000.
this.connectionTimer = setInterval(() => {
if (this.client.reconnecting) {
logger.error('Not connected to MQTT server!');
}
}, interval);
this.onMessage = onMessage;
}
disconnect() {
clearTimeout(this.connectionTimer);
this.connectionTimer = null;
this.publish('bridge/state', 'offline', true);
logger.info('Disconnecting from MQTT server');
this.client.end();
}
handleConnect() {
logger.info('Connected to MQTT server');
this.publish('bridge/state', 'online', true);
this.client.subscribe(`${settings.get().mqtt.base_topic}/+/set`);
}
handleMessage(topic, message) {
if (this.onMessage) {
this.onMessage(topic, message);
}
}
publish(topic, payload, retain) {
topic = `${settings.get().mqtt.base_topic}/${topic}`;
if (!this.client || this.client.reconnecting) {
logger.error(`Not connected to MQTT server!`);
logger.error(`Cannot send message: topic: '${topic}', payload: '${payload}`);
return;
}
logger.info(`MQTT publish, topic: '${topic}', payload: '${payload}'`);
this.client.publish(topic, payload, {retain: retain});
}
}
module.exports = MQTT;
+17
View File
@@ -0,0 +1,17 @@
const winston = require('winston');
const logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({
timestamp: () => new Date().toLocaleString(),
formatter: function(options) {
return options.timestamp() + ' ' +
winston.config.colorize(options.level, options.level.toUpperCase()) + ' ' +
(options.message ? options.message : '') +
(options.meta && Object.keys(options.meta).length ? '\n\t'+ JSON.stringify(options.meta) : '' );
}
})
]
});
module.exports = logger;
+23
View File
@@ -0,0 +1,23 @@
const yamlConfig = require('yaml-config');
const file = `${__dirname}/../../data/configuration.yaml`;
let settings = read();
// Create empty device array if not set yet.
if (!settings.devices) {
settings.devices = {};
write();
}
function write() {
yamlConfig.updateConfig(settings, file, 'user');
settings = read();
}
function read() {
return yamlConfig.readConfig(file, 'user');
}
module.exports = {
get: () => settings,
}
+118
View File
@@ -0,0 +1,118 @@
const ZShepherd = require('zigbee-shepherd');
const logger = require('./util/logger');
const settings = require('./util/settings');
const deviceMapping = require('./devices');
const shepherdSettings = {
net: {panId: 0x1a62},
dbPath: `${__dirname}/../data/database.db`
};
class Zigbee {
constructor() {
this.handleReady = this.handleReady.bind(this);
this.handleMessage = this.handleMessage.bind(this);
}
start(onMessage, callback) {
logger.info(`Starting zigbee-shepherd`);
this.shepherd = new ZShepherd(settings.get().serial.port, shepherdSettings);
this.shepherd.start((error) => {
if (error) {
logger.error('Error while starting zigbee-shepherd!');
} else {
logger.info('zigbee-shepherd started');
}
callback(error);
});
// Register callbacks.
this.shepherd.on('ready', this.handleReady);
this.shepherd.on('ind', this.handleMessage);
this.onMessage = onMessage;
}
stop(callback) {
this.shepherd.stop((error) => {
if (error) {
logger.error('Error while stopping zigbee-shepherd');
} else {
logger.error('zigbee-shepherd stopped')
}
callback(error);
});
}
handleReady() {
logger.info('zigbee-shepherd ready');
const devices = this.shepherd.list().filter((device) => device.type !== 'Coordinator');
logger.info(`Currently ${devices.length} devices are joined:`);
devices.forEach((device) => logger.info(getDeviceLogMessage(device)));
// Set all Xiaomi devices (manufId === 4151) to be online, so shepherd won't try
// to query info from devices (which would fail because they go tosleep).
devices.forEach((device) => {
if (device.manufId === 4151) {
this.shepherd.find(device.ieeeAddr, 1).getDevice().update({
status: 'online',
joinTime: Math.floor(Date.now()/1000)
});
}
});
// Allow or disallow new devices to join the network.
if (settings.get().allowJoin) {
logger.warn('allowJoin set to true in configuration.yaml.')
logger.warn('Allowing new devices to join.');
logger.warn('Remove this parameter once you joined all devices.');
}
this.shepherd.permitJoin(settings.get().allowJoin ? 255 : 0, (error) => {
if (error) {
logger.info(error);
}
});
}
handleMessage(message) {
if (this.onMessage) {
this.onMessage(message);
}
}
getDeviceLogMessage(device) {
let friendlyName = 'unknown';
let friendlyDevice = {model: 'unkown', description: 'unknown'};
if (deviceMapping[device.modelId]) {
friendlyDevice = deviceMapping[device.modelId];
}
if (settings.get().devices[device.ieeeAddr]) {
friendlyName = settings.devices[device.ieeeAddr].friendly_name
}
return `${friendlyName} (${device.ieeeAddr}): ${friendlyDevice.model} - ${friendlyDevice.description}`;
}
publish(deviceID, cId, cmd, zclData, callback) {
// Find device in zigbee-shepherd
const device = this.shepherd.find(deviceID, 1);
if (!device) {
logger.error(`Zigbee cannot publish message to device because '${deviceID}' is not known by zigbee-shepherd`);
}
logger.info(`Zigbee publish to '${deviceID}', ${cId} - ${cmd} - ${zclData}`);
device.functional(cId, cmd, zclData, callback);
}
}
module.exports = Zigbee;
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "xiaomi-zb2mqtt",
"version": "1.0.0",
"name": "zigbee2mqtt",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+8 -5
View File
@@ -1,21 +1,24 @@
{
"name": "xiaomi-zb2mqtt",
"version": "1.0.0",
"description": "Xiaomi Zigbee to MQTT bridge using zigbee-shepherd",
"name": "zigbee2mqtt",
"version": "0.1.0",
"description": "Zigbee to MQTT bridge using zigbee-shepherd",
"main": "index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/Koenkk/xiaomi-zb2mqtt.git"
"url": "git+https://github.com/Koenkk/zigbee2mqtt.git"
},
"keywords": [
"xiaomi",
"tradfri",
"hue",
"bridge",
"zigbee",
"mqtt",
"cc2531"
],
"scripts": {
"start": "node index.js",
"docs": "node doc.js"
"docgen": "node support/docgen.js"
},
"author": "Koen Kanters",
"license": "GPL-3.0",
+21 -6
View File
@@ -8,85 +8,100 @@ const plannedToSupport = [
model: 'WXKG11LM',
description: 'Aqara wireless switch',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'WSDCGQ11LM',
description: 'Aqara temperature & humidity sensor',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'MCCGQ11LM',
description: 'Aqara door & window contact sensor',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'RTCGQ11LM',
description: 'Aqara human body movement and illuminance sensor',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'SJCGQ11LM',
description: 'Aqara water leak sensor',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'MFKZQ01LM',
description: 'Mi magic cube controller',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'WXKG03LM',
description: 'Aqara single key wireless wall switch',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'WXKG02LM',
description: 'Aqara double key wireless wall switch',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'QBKG11LM',
description: 'Aqara single key wired wall switch',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'QBKG03LM',
description: 'Aqara double key wired wall switch',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'ZNCZ02LM',
description: 'Mi power plug ZigBee',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'QBCZ11LM',
description: 'Aqara wall socket',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'JTYJ-GD-01LM/BW',
description: 'MiJia Honeywell smoke detector',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'KTBL01LM',
description: 'Aqara air conditioning companion',
supports: '-',
vendor: 'Xiaomi',
},
{
model: 'KTBL02LM',
description: 'Aqara air conditioning companion 2',
supports: '-',
vendor: 'Xiaomi',
},
];
const parsers = require('./parsers');
const deviceMapping = require('./devices');
const zigbee2mqtt = require('../lib/converters/zigbee2mqtt');
const deviceMapping = require('../lib/devices');
// Sanity check if all supported devices are in deviceMapping
const supportedDevices = new Set();
parsers.forEach((p) => supportedDevices.add(...p.devices));
zigbee2mqtt.forEach((p) => supportedDevices.add(...p.devices));
// Check if in deviceMapping.
supportedDevices.forEach((s) => {
@@ -99,15 +114,15 @@ const logDevices = (devices) => {
console.log('| Model | Description | Picture |')
console.log('| ------------- | ------------- | -------------------------- |')
devices.forEach((device) => {
console.log(`| ${device.model} | ${device.description} (${device.supports}) | ![${device.model}](images/devices/${device.model.replace('/', '-')}.jpg) |`);
console.log(`| ${device.model} | ${device.vendor} ${device.description} (${device.supports}) | ![${device.model}](images/devices/${device.model.replace('/', '-')}.jpg) |`);
});
}
console.log('GENERATED')
console.log('=============================')
console.log('*NOTE: Automatically generated by `npm run docs`*')
console.log('*NOTE: Automatically generated by `npm run docgen`*')
console.log('')
console.log('In case your device is **NOT** listed here, please create an issue at: https://github.com/Koenkk/xiaomi-zb2mqtt/issues');
console.log('In case your device is **NOT** listed here, please create an issue at: https://github.com/Koenkk/zigbee2mqtt/issues');
console.log('')
logDevices(Object.values(deviceMapping));
console.log('');