zigbee2mqtt/lib/extension/deviceBind.js

128 lines
4.0 KiB
JavaScript
Raw Normal View History

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 {
constructor(zigbee, mqtt, state, publishEntityState) {
this.zigbee = zigbee;
this.mqtt = mqtt;
this.state = state;
this.publishEntityState = publishEntityState;
// 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}/`, '');
return {ID: topic, type};
}
onMQTTMessage(topic, message) {
topic = this.parseTopic(topic);
if (!topic) {
return false;
}
// Find source; can only be a device.
const sourceEntity = settings.resolveEntity(topic.ID);
2019-02-01 11:04:49 -07:00
const source = this.zigbee.getEndpoint(sourceEntity.ID);
if (!source) {
logger.error(`Failed to find device '${sourceEntity.ID}'`);
return false;
}
// Find target; can be a device or group.
const targetEntity = settings.resolveEntity(message.toString());
let target = null;
if (targetEntity.type === 'device') {
2019-02-01 11:04:49 -07:00
target = this.zigbee.getEndpoint(targetEntity.ID);
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}`,
{from: sourceEntity.ID, to: targetEntity.ID, cluster}
2019-02-13 11:49:06 -07:00
);
}
queueCallback();
});
});
});
2019-02-26 12:21:35 -07:00
return true;
}
}
2019-02-13 12:55:14 -07:00
module.exports = DeviceBind;