mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2024-11-17 10:58:31 -07:00
122 lines
5.0 KiB
JavaScript
122 lines
5.0 KiB
JavaScript
const settings = require('../util/settings');
|
|
const logger = require('../util/logger');
|
|
const assert = require('assert');
|
|
const legacyTopicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/(bind|unbind)/.+$`);
|
|
const Extension = require('./extension');
|
|
|
|
const clusters = ['genScenes', 'genOnOff', 'genLevelCtrl', 'lightingColorCtrl', 'closuresWindowCovering'];
|
|
|
|
// See zigbee-herdsman-converters devices.js
|
|
const defaultBindGroup = {type: 'group_number', ID: 901};
|
|
|
|
class Bind extends Extension {
|
|
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
|
|
super(zigbee, mqtt, state, publishEntityState, eventBus);
|
|
this.legacyApi = settings.get().advanced.legacy_api;
|
|
}
|
|
|
|
onMQTTConnected() {
|
|
/* istanbul ignore else */
|
|
if (this.legacyApi) {
|
|
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/bind/#`);
|
|
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/unbind/#`);
|
|
}
|
|
}
|
|
|
|
parseMQTTMessage(topic, message) {
|
|
let type = null;
|
|
let sourceKey = null;
|
|
let targetKey = null;
|
|
|
|
if (this.legacyApi && topic.match(legacyTopicRegex)) {
|
|
topic = topic.replace(`${settings.get().mqtt.base_topic}/bridge/`, '');
|
|
type = topic.split('/')[0];
|
|
sourceKey = topic.replace(`${type}/`, '');
|
|
targetKey = message;
|
|
}
|
|
|
|
return {type, sourceKey, targetKey};
|
|
}
|
|
|
|
async onMQTTMessage(topic, message) {
|
|
const {type, sourceKey, targetKey} = this.parseMQTTMessage(topic, message);
|
|
if (!type) return null;
|
|
|
|
// Find source; can only be a device and target
|
|
const source = this.zigbee.resolveEntity(sourceKey);
|
|
assert(source != null && source.type === 'device', 'Source undefined or not a device');
|
|
const target = targetKey === 'default_bind_group' ? defaultBindGroup : this.zigbee.resolveEntity(targetKey);
|
|
assert(target != null, 'Target is unknown');
|
|
|
|
const sourceName = source.settings.friendlyName;
|
|
const targetName = targetKey === 'default_bind_group' ? targetKey : target.settings.friendlyName;
|
|
let attemptedToBindSomething = false;
|
|
|
|
// Find which clusters are supported by both the source and target.
|
|
// Groups are assumed to support all clusters.
|
|
for (const cluster of clusters) {
|
|
const targetValid = target.type === 'group' || target.type === 'group_number' ||
|
|
target.device.type === 'Coordinator' || target.endpoint.supportsInputCluster(cluster);
|
|
|
|
if (source.endpoint.supportsOutputCluster(cluster) && targetValid) {
|
|
logger.debug(`${type}ing cluster '${cluster}' from '${sourceName}' to '${targetName}'`);
|
|
attemptedToBindSomething = true;
|
|
try {
|
|
let bindTarget = null;
|
|
if (target.type === 'group') bindTarget = target.group;
|
|
else if (target.type === 'group_number') bindTarget = target.ID;
|
|
else bindTarget = target.endpoint;
|
|
|
|
if (type === 'bind') {
|
|
await source.endpoint.bind(cluster, bindTarget);
|
|
} else {
|
|
await source.endpoint.unbind(cluster, bindTarget);
|
|
}
|
|
|
|
logger.info(
|
|
`Successfully ${type === 'bind' ? 'bound' : 'unbound'} cluster '${cluster}' from ` +
|
|
`'${sourceName}' to '${targetName}'`,
|
|
);
|
|
|
|
/* istanbul ignore else */
|
|
if (settings.get().advanced.legacy_api) {
|
|
this.mqtt.publish(
|
|
'bridge/log',
|
|
JSON.stringify({type: `device_${type}`,
|
|
message: {from: sourceName, to: targetName, cluster}}),
|
|
);
|
|
}
|
|
} catch (error) {
|
|
logger.error(
|
|
`Failed to ${type} cluster '${cluster}' from '${sourceName}' to ` +
|
|
`'${targetName}' (${error})`,
|
|
);
|
|
|
|
/* istanbul ignore else */
|
|
if (settings.get().advanced.legacy_api) {
|
|
this.mqtt.publish(
|
|
'bridge/log',
|
|
JSON.stringify({type: `device_${type}_failed`,
|
|
message: {from: sourceName, to: targetName, cluster}}),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!attemptedToBindSomething) {
|
|
logger.error(`Nothing to ${type} from '${sourceName}' to '${targetName}'`);
|
|
|
|
/* istanbul ignore else */
|
|
if (settings.get().advanced.legacy_api) {
|
|
this.mqtt.publish(
|
|
'bridge/log',
|
|
JSON.stringify({type: `device_${type}_failed`, message: {from: sourceName, to: targetName}}),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = Bind;
|