diff --git a/lib/controller.js b/lib/controller.js index 8623c330c4..be9d6acef3 100644 --- a/lib/controller.js +++ b/lib/controller.js @@ -16,6 +16,7 @@ const ExtensionDeviceConfigure = require('./extension/deviceConfigure'); const ExtensionDeviceReceive = require('./extension/deviceReceive'); const ExtensionMarkOnlineXiaomi = require('./extension/markOnlineXiaomi'); const ExtensionBridgeConfig = require('./extension/bridgeConfig'); +const ExtensionGroups = require('./extension/groups'); class Controller { constructor() { @@ -38,6 +39,7 @@ class Controller { new ExtensionRouterPollXiaomi(this.zigbee, this.mqtt, this.state, this.publishDeviceState), new ExtensionMarkOnlineXiaomi(this.zigbee, this.mqtt, this.state, this.publishDeviceState), new ExtensionBridgeConfig(this.zigbee, this.mqtt, this.state, this.publishDeviceState), + new ExtensionGroups(this.zigbee, this.mqtt, this.state, this.publishDeviceState), ]; if (settings.get().homeassistant) { diff --git a/lib/extension/devicePublish.js b/lib/extension/devicePublish.js index 4131f0cb6f..9b02ed0720 100644 --- a/lib/extension/devicePublish.js +++ b/lib/extension/devicePublish.js @@ -4,7 +4,7 @@ const zigbeeShepherdConverters = require('zigbee-shepherd-converters'); const Queue = require('queue'); const logger = require('../util/logger'); -const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/.+/(set|get)$`); +const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/((?!group).+)/(set|get)$`); const postfixes = ['left', 'right', 'center', 'bottom_left', 'bottom_right', 'top_left', 'top_right']; const maxDepth = 20; diff --git a/lib/extension/groups.js b/lib/extension/groups.js new file mode 100644 index 0000000000..dd440f6db3 --- /dev/null +++ b/lib/extension/groups.js @@ -0,0 +1,86 @@ +const settings = require('../util/settings'); +const logger = require('../util/logger'); + +const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/group/.+/set$`); + +class Groups { + constructor(zigbee, mqtt, state, publishDeviceState) { + this.zigbee = zigbee; + this.mqtt = mqtt; + this.state = state; + this.publishDeviceState = publishDeviceState; + } + + onMQTTConnected() { + this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/group/+/set`); + } + + onZigbeeStarted() { + const groups = settings.get().groups; + + Object.keys(groups).forEach((name) => { + const group = groups[name]; + + const callback = (device, error, rsp) => { + if (!error) { + logger.info(`Added '${device}' to group '${name}' (${group.ID})`); + } else { + logger.error(`Failed to add '${device}' to group '${name}' (${group.ID})`); + } + }; + + group.devices.forEach((device) => { + const ieeeAddr = settings.getIeeeAddrByFriendlyName(device) || device; + this.zigbee.publish( + ieeeAddr, + 'genGroups', + 'add', + 'functional', + {groupid: group.ID, groupname: name}, + null, + null, + (error, rsp) => callback(device, error, rsp), + ); + }); + }); + } + + parseTopic(topic) { + if (!topic.match(topicRegex)) { + return null; + } + + // Remove base from topic + topic = topic.replace(`${settings.get().mqtt.base_topic}/group/`, ''); + + // Parse type from topic + const type = topic.substr(topic.lastIndexOf('/') + 1, topic.length); + + // Remove type from topic + topic = topic.replace(`/${type}`, ''); + + const name = topic; + + return {type: type, name: name}; + } + + onMQTTMessage(topic, message) { + topic = this.parseTopic(topic); + + if (!topic) { + return false; + } + + if (!settings.get().groups.hasOwnProperty(topic.name)) { + logger.error(`Group '${topic.name}' doesn't exist`); + return false; + } + + const groupID = settings.get().groups[topic.name].ID; + logger.info(groupID, message.toString()); + + return true; + } +} + +module.exports = Groups; diff --git a/lib/util/settings.js b/lib/util/settings.js index 29181eacc7..9e3706459a 100644 --- a/lib/util/settings.js +++ b/lib/util/settings.js @@ -10,6 +10,7 @@ const defaults = { mqtt: { include_device_information: false, }, + groups: {}, advanced: { log_directory: path.join(data.getPath(), 'log', '%TIMESTAMP%'), log_level: process.env.DEBUG ? 'debug' : 'info', diff --git a/lib/zigbee.js b/lib/zigbee.js index 72c3e058ca..23a16b837b 100644 --- a/lib/zigbee.js +++ b/lib/zigbee.js @@ -18,6 +18,11 @@ const shepherdSettings = { }, }; +const defaultCfg = { + manufSpec: 0, + disDefaultRsp: 0, +}; + logger.debug(`Using zigbee-shepherd with settings: '${JSON.stringify(shepherdSettings)}'`); class Zigbee { @@ -187,7 +192,7 @@ class Zigbee { return this.shepherd.find(device.ieeeAddr, 1); } - publish(ieeAddr, cid, cmd, cmdType, zclData, cfg, ep, callback) { + publish(ieeAddr, cid, cmd, cmdType, zclData, cfg=defaultCfg, ep, callback) { const device = this.findDevice(ieeAddr, ep); if (!device) { logger.error(`Zigbee cannot publish message to device because '${ieeAddr}' not known by zigbee-shepherd`);