diff --git a/lib/extension/groups.js b/lib/extension/groups.js index 4c241a43..b5621e7a 100644 --- a/lib/extension/groups.js +++ b/lib/extension/groups.js @@ -4,23 +4,27 @@ const Extension = require('./extension'); const utils = require('../util/utils'); const postfixes = utils.getEndpointNames(); -const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/(.+)/(remove|add|remove_all)$`); -const topicRegexRemoveAll = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/remove_all$`); +const legacyTopicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/(.+)/(remove|add|remove_all)$`); +const legacyTopicRegexRemoveAll = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/remove_all$`); class Groups extends Extension { constructor(zigbee, mqtt, state, publishEntityState, eventBus) { super(zigbee, mqtt, state, publishEntityState, eventBus); this.onStateChange = this.onStateChange.bind(this); + this.legacyApi = settings.get().advanced.legacy_api; } onMQTTConnected() { - this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/group/remove_all`); + /* istanbul ignore else */ + if (this.legacyApi) { + this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/group/remove_all`); - for (let step = 1; step < 20; step++) { - const topic = `${settings.get().mqtt.base_topic}/bridge/group/${'+/'.repeat(step)}`; - this.mqtt.subscribe(`${topic}remove`); - this.mqtt.subscribe(`${topic}add`); - this.mqtt.subscribe(`${topic}remove_all`); // DEPRECATED + for (let step = 1; step < 20; step++) { + const topic = `${settings.get().mqtt.base_topic}/bridge/group/${'+/'.repeat(step)}`; + this.mqtt.subscribe(`${topic}remove`); + this.mqtt.subscribe(`${topic}add`); + this.mqtt.subscribe(`${topic}remove_all`); + } } } @@ -87,33 +91,33 @@ class Groups extends Extension { }); if (Object.keys(payload).length) { - const entity = this.zigbee.resolveEntity(data.ID); + const resolvedEntity = this.zigbee.resolveEntity(data.ID); const zigbeeGroups = this.zigbee.getGroups().filter((zigbeeGroup) => { const settingsGroup = settings.getGroup(zigbeeGroup.groupID); return settingsGroup && settingsGroup.optimistic; }); - if (entity.type === 'device') { + if (resolvedEntity.type === 'device') { for (const zigbeeGroup of zigbeeGroups) { - if (zigbeeGroup.hasMember(entity.endpoint)) { - if (!payload || payload.state !== 'OFF' || this.allMembersOff(zigbeeGroup)) { + if (zigbeeGroup.hasMember(resolvedEntity.endpoint)) { + if (!payload || payload.state !== 'OFF' || this.areAllMembersOff(zigbeeGroup)) { await this.publishEntityState(zigbeeGroup.groupID, payload, reason); } } } } else { const groupIDsToPublish = new Set(); - for (const member of entity.group.members) { + for (const member of resolvedEntity.group.members) { await this.publishEntityState(member.getDevice().ieeeAddr, payload, reason); for (const zigbeeGroup of zigbeeGroups) { if (zigbeeGroup.hasMember(member)) { - if (!payload || payload.state !== 'OFF' || this.allMembersOff(zigbeeGroup)) { + if (!payload || payload.state !== 'OFF' || this.areAllMembersOff(zigbeeGroup)) { groupIDsToPublish.add(zigbeeGroup.groupID); } } } } - groupIDsToPublish.delete(entity.group.groupID); + groupIDsToPublish.delete(resolvedEntity.group.groupID); for (const groupID of groupIDsToPublish) { await this.publishEntityState(groupID, payload, reason); } @@ -121,7 +125,7 @@ class Groups extends Extension { } } - allMembersOff(zigbeeGroup) { + areAllMembersOff(zigbeeGroup) { for (const member of zigbeeGroup.members) { const device = member.getDevice(); if (this.state.exists(device.ieeeAddr)) { @@ -134,105 +138,121 @@ class Groups extends Extension { return true; } - async onMQTTMessage(topic, message) { - let type; - let group; - const topicMatch = topic.match(topicRegex); - if (topicMatch) { - group = this.zigbee.resolveEntity(topicMatch[1]); - type = topicMatch[2]; + parseMQTTMessage(topic, message) { + let type = null; + let resolvedEntityGroup = null; + let resolvedEntityDevice = null; + let hasEndpointName = null; - if (!group || group.type !== 'group') { - logger.error(`Group '${topicMatch[1]}' does not exist`); + /* istanbul ignore else */ + if (this.legacyApi) { + const topicMatch = topic.match(legacyTopicRegex); + if (topicMatch) { + resolvedEntityGroup = this.zigbee.resolveEntity(topicMatch[1]); + type = topicMatch[2]; + + if (!resolvedEntityGroup || resolvedEntityGroup.type !== 'group') { + logger.error(`Group '${topicMatch[1]}' does not exist`); + + /* istanbul ignore else */ + if (settings.get().advanced.legacy_api) { + const payload = {friendly_name: message, group: topicMatch[1], error: 'group doesn\'t exists'}; + this.mqtt.publish( + 'bridge/log', + JSON.stringify({type: `device_group_${type}_failed`, message: payload}), + ); + } + + return {}; + } + } else if (topic.match(legacyTopicRegexRemoveAll)) { + type = 'remove_all'; + } else { + return {}; + } + + resolvedEntityDevice = this.zigbee.resolveEntity(message); + if (!resolvedEntityDevice || !resolvedEntityDevice.type === 'device') { + logger.error(`Device '${message}' does not exist`); /* istanbul ignore else */ if (settings.get().advanced.legacy_api) { - const payload = {friendly_name: message, group: topicMatch[1], error: 'group doesn\'t exists'}; + const payload = {friendly_name: message, group: topicMatch[1], error: 'entity doesn\'t exists'}; this.mqtt.publish( 'bridge/log', JSON.stringify({type: `device_group_${type}_failed`, message: payload}), ); } - return; - } - } else if (topic.match(topicRegexRemoveAll)) { - type = 'remove_all'; - } else { - return; - } - - const entity = this.zigbee.resolveEntity(message); - if (!entity || !entity.type === 'device') { - logger.error(`Device '${message}' does not exist`); - - /* istanbul ignore else */ - if (settings.get().advanced.legacy_api) { - const payload = {friendly_name: message, group: topicMatch[1], error: 'entity doesn\'t exists'}; - this.mqtt.publish( - 'bridge/log', - JSON.stringify({type: `device_group_${type}_failed`, message: payload}), - ); + return {}; } - return; + hasEndpointName = postfixes.find((p) => message.endsWith(`/${p}`)); } + return {resolvedEntityGroup, resolvedEntityDevice, type, hasEndpointName}; + } + + async onMQTTMessage(topic, message) { + const {resolvedEntityGroup, resolvedEntityDevice, type, hasEndpointName} = + this.parseMQTTMessage(topic, message); + if (!type) return; + const keys = [ - `${entity.device.ieeeAddr}/${entity.endpoint.ID}`, - `${entity.name}/${entity.endpoint.ID}`, + `${resolvedEntityDevice.device.ieeeAddr}/${resolvedEntityDevice.endpoint.ID}`, + `${resolvedEntityDevice.name}/${resolvedEntityDevice.endpoint.ID}`, ]; - const definition = entity.definition; - const endpoints = definition && definition.endpoint ? definition.endpoint(entity.device) : null; - const endpointName = endpoints ? Object.entries(endpoints).find((e) => e[1] === entity.endpoint.ID)[0] : null; + const definition = resolvedEntityDevice.definition; + const endpoints = definition && definition.endpoint ? definition.endpoint(resolvedEntityDevice.device) : null; + const endpointName = endpoints ? + Object.entries(endpoints).find((e) => e[1] === resolvedEntityDevice.endpoint.ID)[0] : null; if (endpointName) { - keys.push(`${entity.device.ieeeAddr}/${endpointName}`); - keys.push(`${entity.name}/${endpointName}`); + keys.push(`${resolvedEntityDevice.device.ieeeAddr}/${endpointName}`); + keys.push(`${resolvedEntityDevice.name}/${endpointName}`); } - const hasEndpointName = postfixes.find((p) => message.endsWith(`/${p}`)); if (!hasEndpointName) { - keys.push(entity.name); - keys.push(entity.device.ieeeAddr); + keys.push(resolvedEntityDevice.name); + keys.push(resolvedEntityDevice.device.ieeeAddr); } if (type === 'add') { - logger.info(`Adding '${entity.name}' to '${group.name}'`); - await entity.endpoint.addToGroup(group.group); - settings.addDeviceToGroup(group.settings.ID, keys); + logger.info(`Adding '${resolvedEntityDevice.name}' to '${resolvedEntityGroup.name}'`); + await resolvedEntityDevice.endpoint.addToGroup(resolvedEntityGroup.group); + settings.addDeviceToGroup(resolvedEntityGroup.settings.ID, keys); /* istanbul ignore else */ if (settings.get().advanced.legacy_api) { - const payload = {friendly_name: entity.name, group: group.name}; + const payload = {friendly_name: resolvedEntityDevice.name, group: resolvedEntityGroup.name}; this.mqtt.publish( 'bridge/log', JSON.stringify({type: `device_group_add`, message: payload}), ); } } else if (type === 'remove') { - logger.info(`Removing '${entity.name}' from '${group.name}'`); - await entity.endpoint.removeFromGroup(group.group); - settings.removeDeviceFromGroup(group.settings.ID, keys); + logger.info(`Removing '${resolvedEntityDevice.name}' from '${resolvedEntityGroup.name}'`); + await resolvedEntityDevice.endpoint.removeFromGroup(resolvedEntityGroup.group); + settings.removeDeviceFromGroup(resolvedEntityGroup.settings.ID, keys); /* istanbul ignore else */ if (settings.get().advanced.legacy_api) { - const payload = {friendly_name: entity.name, group: group.name}; + const payload = {friendly_name: resolvedEntityDevice.name, group: resolvedEntityGroup.name}; this.mqtt.publish( 'bridge/log', JSON.stringify({type: `device_group_remove`, message: payload}), ); } } else { // remove_all - logger.info(`Removing '${entity.name}' from all groups`); - await entity.endpoint.removeFromAllGroups(); + logger.info(`Removing '${resolvedEntityDevice.name}' from all groups`); + await resolvedEntityDevice.endpoint.removeFromAllGroups(); for (const settingsGroup of settings.getGroups()) { settings.removeDeviceFromGroup(settingsGroup.ID, keys); /* istanbul ignore else */ if (settings.get().advanced.legacy_api) { - const payload = {friendly_name: entity.name}; + const payload = {friendly_name: resolvedEntityDevice.name}; this.mqtt.publish( 'bridge/log', JSON.stringify({type: `device_group_remove_all`, message: payload}), diff --git a/lib/extension/otaUpdate.js b/lib/extension/otaUpdate.js index 4508cf8d..ae3ccd9e 100644 --- a/lib/extension/otaUpdate.js +++ b/lib/extension/otaUpdate.js @@ -77,8 +77,8 @@ class OTAUpdate extends Extension { } async onMQTTMessage(topic, message) { - /* istanbul ignore else */ let resolvedEntity = null; + /* istanbul ignore else */ if (this.legacyApi) { if (!topic.match(legacyTopicRegex)) { return null;