2018-12-30 14:42:55 -07:00
|
|
|
const settings = require('../util/settings');
|
|
|
|
const logger = require('../util/logger');
|
|
|
|
const Queue = require('queue');
|
|
|
|
|
|
|
|
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/(bind|unbind)/.+$`);
|
|
|
|
|
|
|
|
const allowedClusters = [
|
|
|
|
5, // genScenes
|
|
|
|
6, // genOnOff
|
|
|
|
8, // genLevelCtrl
|
|
|
|
768, // lightingColorCtrl
|
|
|
|
];
|
|
|
|
|
2019-02-13 12:55:14 -07:00
|
|
|
class DeviceBind {
|
2019-02-04 10:36:49 -07:00
|
|
|
constructor(zigbee, mqtt, state, publishEntityState) {
|
2018-12-30 14:42:55 -07:00
|
|
|
this.zigbee = zigbee;
|
|
|
|
this.mqtt = mqtt;
|
|
|
|
this.state = state;
|
2019-02-04 10:36:49 -07:00
|
|
|
this.publishEntityState = publishEntityState;
|
2018-12-30 14:42:55 -07:00
|
|
|
|
|
|
|
// Setup queue
|
|
|
|
this.queue = new Queue();
|
|
|
|
this.queue.concurrency = 1;
|
|
|
|
this.queue.autostart = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
onMQTTConnected() {
|
|
|
|
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/bind/+`);
|
|
|
|
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/unbind/+`);
|
|
|
|
}
|
|
|
|
|
|
|
|
parseTopic(topic) {
|
|
|
|
if (!topic.match(topicRegex)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove base from topic
|
|
|
|
topic = topic.replace(`${settings.get().mqtt.base_topic}/bridge/`, '');
|
|
|
|
|
|
|
|
// Parse type from topic
|
|
|
|
const type = topic.split('/')[0];
|
|
|
|
|
|
|
|
// Remove type from topic
|
|
|
|
topic = topic.replace(`${type}/`, '');
|
|
|
|
|
2019-03-08 09:39:47 -07:00
|
|
|
return {ID: topic, type};
|
2018-12-30 14:42:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
onMQTTMessage(topic, message) {
|
|
|
|
topic = this.parseTopic(topic);
|
|
|
|
|
|
|
|
if (!topic) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find source; can only be a device.
|
2019-03-08 08:10:28 -07:00
|
|
|
const sourceEntity = settings.resolveEntity(topic.ID);
|
2019-02-01 11:04:49 -07:00
|
|
|
const source = this.zigbee.getEndpoint(sourceEntity.ID);
|
2018-12-30 14:42:55 -07:00
|
|
|
|
|
|
|
if (!source) {
|
|
|
|
logger.error(`Failed to find device '${sourceEntity.ID}'`);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find target; can be a device or group.
|
2019-03-08 08:10:28 -07:00
|
|
|
const targetEntity = settings.resolveEntity(message.toString());
|
2018-12-30 14:42:55 -07:00
|
|
|
let target = null;
|
|
|
|
|
|
|
|
if (targetEntity.type === 'device') {
|
2019-02-01 11:04:49 -07:00
|
|
|
target = this.zigbee.getEndpoint(targetEntity.ID);
|
2018-12-30 14:42:55 -07:00
|
|
|
|
|
|
|
if (!target) {
|
|
|
|
logger.error(`Failed to find target device '${targetEntity.ID}'`);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (targetEntity.type === 'group') {
|
|
|
|
target = targetEntity.ID;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find which clusters are supported by both the source and target.
|
|
|
|
// Groups are assumed to support all clusters (as we don't know which devices are in)
|
|
|
|
let supported = [];
|
|
|
|
if (targetEntity.type === 'device') {
|
|
|
|
supported = target.getSimpleDesc().inClusterList.filter((cluster) => {
|
|
|
|
return allowedClusters.includes(cluster);
|
|
|
|
});
|
|
|
|
} else if (targetEntity.type === 'group') {
|
|
|
|
supported = allowedClusters;
|
|
|
|
}
|
|
|
|
|
|
|
|
const clusters = source.getSimpleDesc().outClusterList.filter((cluster) => {
|
|
|
|
return supported.includes(cluster);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Bind
|
|
|
|
clusters.forEach((cluster) => {
|
|
|
|
this.queue.push((queueCallback) => {
|
|
|
|
logger.debug(`${topic.type}ing cluster '${cluster}' from ${sourceEntity.ID}' to '${targetEntity.ID}'`);
|
|
|
|
|
|
|
|
source[topic.type](cluster, target, (error) => {
|
|
|
|
if (error) {
|
|
|
|
logger.error(
|
|
|
|
`Failed to ${topic.type} cluster '${cluster}' from ${sourceEntity.ID}' to ` +
|
|
|
|
`'${targetEntity.ID}' (${error})`
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
logger.info(
|
|
|
|
`Successfully ${topic.type === 'bind' ? 'bound' : 'unbound'} cluster '${cluster}' from ` +
|
|
|
|
`${sourceEntity.ID}' to '${targetEntity.ID}'`
|
|
|
|
);
|
2019-02-13 11:49:06 -07:00
|
|
|
|
|
|
|
this.mqtt.log(
|
|
|
|
`device_${topic.type}`,
|
2019-03-08 09:39:47 -07:00
|
|
|
{from: sourceEntity.ID, to: targetEntity.ID, cluster}
|
2019-02-13 11:49:06 -07:00
|
|
|
);
|
2018-12-30 14:42:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
queueCallback();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2019-02-26 12:21:35 -07:00
|
|
|
|
|
|
|
return true;
|
2018-12-30 14:42:55 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-13 12:55:14 -07:00
|
|
|
module.exports = DeviceBind;
|