This commit is contained in:
Koen Kanters 2020-04-12 20:32:14 +02:00
parent eab9886402
commit 22126a3e5d
2 changed files with 88 additions and 68 deletions

View File

@ -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() {
/* 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
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,15 +138,20 @@ class Groups extends Extension {
return true;
}
async onMQTTMessage(topic, message) {
let type;
let group;
const topicMatch = topic.match(topicRegex);
parseMQTTMessage(topic, message) {
let type = null;
let resolvedEntityGroup = null;
let resolvedEntityDevice = null;
let hasEndpointName = null;
/* istanbul ignore else */
if (this.legacyApi) {
const topicMatch = topic.match(legacyTopicRegex);
if (topicMatch) {
group = this.zigbee.resolveEntity(topicMatch[1]);
resolvedEntityGroup = this.zigbee.resolveEntity(topicMatch[1]);
type = topicMatch[2];
if (!group || group.type !== 'group') {
if (!resolvedEntityGroup || resolvedEntityGroup.type !== 'group') {
logger.error(`Group '${topicMatch[1]}' does not exist`);
/* istanbul ignore else */
@ -154,16 +163,16 @@ class Groups extends Extension {
);
}
return;
return {};
}
} else if (topic.match(topicRegexRemoveAll)) {
} else if (topic.match(legacyTopicRegexRemoveAll)) {
type = 'remove_all';
} else {
return;
return {};
}
const entity = this.zigbee.resolveEntity(message);
if (!entity || !entity.type === 'device') {
resolvedEntityDevice = this.zigbee.resolveEntity(message);
if (!resolvedEntityDevice || !resolvedEntityDevice.type === 'device') {
logger.error(`Device '${message}' does not exist`);
/* istanbul ignore else */
@ -175,64 +184,75 @@ class Groups extends Extension {
);
}
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}),

View File

@ -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;