2018-04-18 09:25:40 -07:00
|
|
|
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) => {
|
2018-04-18 11:53:22 -07:00
|
|
|
logger.info('zigbee-shepherd stopped')
|
2018-04-18 09:25:40 -07:00
|
|
|
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:`);
|
2018-04-18 10:09:59 -07:00
|
|
|
devices.forEach((device) => logger.info(this.getDeviceLogMessage(device)));
|
2018-04-18 09:25:40 -07:00
|
|
|
|
|
|
|
// 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]) {
|
2018-04-18 11:53:22 -07:00
|
|
|
friendlyName = settings.get().devices[device.ieeeAddr].friendly_name
|
2018-04-18 09:25:40 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
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`);
|
|
|
|
}
|
|
|
|
|
2018-04-18 11:53:22 -07:00
|
|
|
logger.info(`Zigbee publish to '${deviceID}', ${cId} - ${cmd} - ${JSON.stringify(zclData)}`);
|
2018-04-18 09:25:40 -07:00
|
|
|
device.functional(cId, cmd, zclData, callback);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = Zigbee;
|