2018-12-21 16:07:53 -07:00
|
|
|
const settings = require('../util/settings');
|
|
|
|
const logger = require('../util/logger');
|
2019-09-17 09:32:16 -07:00
|
|
|
const BaseExtension = require('./baseExtension');
|
2018-12-21 16:07:53 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
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$`);
|
2018-12-21 16:07:53 -07:00
|
|
|
|
2019-09-17 09:32:16 -07:00
|
|
|
class Groups extends BaseExtension {
|
2019-02-04 10:36:49 -07:00
|
|
|
constructor(zigbee, mqtt, state, publishEntityState) {
|
2019-09-17 09:32:16 -07:00
|
|
|
super(zigbee, mqtt, state, publishEntityState);
|
|
|
|
this.onStateChange = this.onStateChange.bind(this);
|
2018-12-21 16:07:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
onMQTTConnected() {
|
2018-12-27 10:43:34 -07:00
|
|
|
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/group/+/remove`);
|
|
|
|
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/group/+/add`);
|
|
|
|
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/group/+/remove_all`);
|
2019-06-17 12:17:29 -07:00
|
|
|
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/group/remove_all`);
|
2018-12-21 16:07:53 -07:00
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
async onZigbeeStarted() {
|
|
|
|
await this.syncGroupsWithSettings();
|
2019-09-17 09:33:27 -07:00
|
|
|
this.state.on('stateChange', this.onStateChange);
|
2019-04-29 11:38:40 -07:00
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
async syncGroupsWithSettings() {
|
|
|
|
const settingsGroups = settings.getGroups();
|
2019-09-23 13:21:27 -07:00
|
|
|
const zigbeeGroups = this.zigbee.getGroups();
|
2019-09-09 10:48:09 -07:00
|
|
|
for (const settingGroup of settingsGroups) {
|
|
|
|
const groupID = settingGroup.ID;
|
2019-09-23 13:21:27 -07:00
|
|
|
const zigbeeGroup = zigbeeGroups.find((g) => g.groupID === groupID) || this.zigbee.createGroup(groupID);
|
|
|
|
const settingsEntity = settingGroup.devices.map((d) => {
|
|
|
|
const entity = this.zigbee.resolveEntity(d);
|
2019-09-09 10:48:09 -07:00
|
|
|
if (!entity) logger.error(`Cannot find '${d}' of group '${settingGroup.friendlyName}'`);
|
|
|
|
return entity;
|
2019-09-23 13:21:27 -07:00
|
|
|
}).filter((e) => e != null);
|
2019-09-09 10:48:09 -07:00
|
|
|
|
|
|
|
// In settings but not in zigbee
|
|
|
|
for (const entity of settingsEntity) {
|
|
|
|
if (!zigbeeGroup.hasMember(entity.endpoint)) {
|
|
|
|
logger.info(`Adding '${entity.name}' to group '${settingGroup.friendlyName}'`);
|
|
|
|
await entity.endpoint.addToGroup(zigbeeGroup);
|
|
|
|
}
|
2019-05-29 11:11:40 -07:00
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
// In zigbee but not in settings
|
|
|
|
for (const endpoint of zigbeeGroup.getMembers()) {
|
|
|
|
if (!settingsEntity.find((e) => e.endpoint === endpoint)) {
|
|
|
|
const deviceSettings = settings.getDevice(endpoint.deviceIeeeAddress);
|
|
|
|
logger.info(`Removing '${deviceSettings.friendlyName}' from group '${settingGroup.friendlyName}'`);
|
|
|
|
await endpoint.removeFromGroup(zigbeeGroup);
|
|
|
|
}
|
|
|
|
}
|
2019-05-29 11:11:40 -07:00
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
// eslint-disable-next-line
|
|
|
|
for (const zigbeeGroup of zigbeeGroups) {
|
|
|
|
if (!settingsGroups.find((g) => g.ID === zigbeeGroup.groupID)) {
|
|
|
|
for (const endpoint of zigbeeGroup.getMembers()) {
|
|
|
|
const deviceSettings = settings.getDevice(endpoint.deviceIeeeAddress);
|
|
|
|
logger.info(`Removing '${deviceSettings.friendlyName}' from group ${zigbeeGroup.groupID}`);
|
|
|
|
await endpoint.removeFromGroup(zigbeeGroup);
|
|
|
|
}
|
2019-04-29 11:38:40 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-17 09:33:27 -07:00
|
|
|
async onStateChange(data) {
|
|
|
|
const properties = ['state', 'brightness', 'color_temp', 'color'];
|
|
|
|
const payload = {};
|
|
|
|
|
|
|
|
properties.forEach((prop) => {
|
|
|
|
if (data.to.hasOwnProperty(prop) && (!data.from || data.from[prop] != data.to[prop])) {
|
|
|
|
payload[prop] = data.to[prop];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (Object.keys(payload).length) {
|
2019-09-23 13:21:27 -07:00
|
|
|
const entity = this.zigbee.resolveEntity(data.ID);
|
2019-09-17 09:33:27 -07:00
|
|
|
if (entity.type !== 'device') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-09-23 13:21:27 -07:00
|
|
|
const zigbeeGroups = this.zigbee.getGroups();
|
2019-09-17 09:33:27 -07:00
|
|
|
for (const zigbeeGroup of zigbeeGroups) {
|
2019-09-17 10:01:57 -07:00
|
|
|
const settingsGroup = settings.getGroup(zigbeeGroup.groupID);
|
2019-09-25 03:51:17 -07:00
|
|
|
if (settingsGroup && zigbeeGroup.hasMember(entity.endpoint)) {
|
|
|
|
if (settingsGroup.optimistic === 'group' || settingsGroup.optimistic === 'group_devices') {
|
|
|
|
if (!this.state.is(zigbeeGroup.groupID, payload)) {
|
|
|
|
await this.publishEntityState(zigbeeGroup.groupID, payload);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (settingsGroup.optimistic === 'group_devices') {
|
|
|
|
for (const endpoint of zigbeeGroup.getMembers()) {
|
|
|
|
if (!this.state.is(endpoint.deviceIeeeAddress, payload)) {
|
|
|
|
await this.publishEntityState(endpoint.deviceIeeeAddress, payload);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-09-17 09:33:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
async onMQTTMessage(topic, message) {
|
|
|
|
let type;
|
|
|
|
let group;
|
|
|
|
const topicMatch = topic.match(topicRegex);
|
|
|
|
if (topicMatch) {
|
2019-09-23 13:21:27 -07:00
|
|
|
group = this.zigbee.resolveEntity(topicMatch[1]);
|
2019-09-09 10:48:09 -07:00
|
|
|
type = topicMatch[2];
|
2019-04-29 11:38:40 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
if (!group || group.type !== 'group') {
|
|
|
|
logger.error(`Group '${topicMatch[1]}' does not exist`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (topic.match(topicRegexRemoveAll)) {
|
|
|
|
type = 'remove_all';
|
|
|
|
} else {
|
|
|
|
return;
|
2018-12-21 16:07:53 -07:00
|
|
|
}
|
|
|
|
|
2019-09-23 13:21:27 -07:00
|
|
|
const entity = this.zigbee.resolveEntity(message);
|
2019-09-09 10:48:09 -07:00
|
|
|
if (!entity || !entity.type === 'device') {
|
|
|
|
logger.error(`Device '${message}' does not exist`);
|
|
|
|
return;
|
2019-04-29 11:38:40 -07:00
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
const keys = [
|
|
|
|
`${entity.device.ieeeAddr}/${entity.endpoint.ID}`,
|
|
|
|
`${entity.name}/${entity.endpoint.ID}`,
|
|
|
|
];
|
2019-04-29 11:38:40 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
if (entity.endpointName) {
|
|
|
|
keys.push(`${entity.device.ieeeAddr}/${entity.endpointName}`);
|
|
|
|
keys.push(`${entity.name}/${entity.endpointName}`);
|
|
|
|
}
|
2018-12-21 16:07:53 -07:00
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
if (entity.isDefaultEndpoint) {
|
|
|
|
keys.push(entity.name);
|
|
|
|
keys.push(entity.device.ieeeAddr);
|
2018-12-21 16:07:53 -07:00
|
|
|
}
|
|
|
|
|
2019-09-09 10:48:09 -07:00
|
|
|
if (type === 'add') {
|
|
|
|
logger.info(`Adding '${entity.name}' to '${group.name}'`);
|
|
|
|
await entity.endpoint.addToGroup(group.group);
|
|
|
|
settings.addDeviceToGroup(group.settings.ID, keys);
|
2019-09-15 00:13:02 -07:00
|
|
|
this.mqtt.log('device_group_add', {friendly_name: entity.name, group: group.name});
|
2019-09-09 10:48:09 -07:00
|
|
|
} else if (type === 'remove') {
|
|
|
|
logger.info(`Removing '${entity.name}' from '${group.name}'`);
|
|
|
|
await entity.endpoint.removeFromGroup(group.group);
|
|
|
|
settings.removeDeviceFromGroup(group.settings.ID, keys);
|
2019-09-15 00:13:02 -07:00
|
|
|
this.mqtt.log('device_group_remove', {friendly_name: entity.name, group: group.name});
|
2019-09-09 10:48:09 -07:00
|
|
|
} else { // remove_all
|
|
|
|
logger.info(`Removing '${entity.name}' from all groups`);
|
|
|
|
await entity.endpoint.removeFromAllGroups();
|
|
|
|
for (const settingsGroup of settings.getGroups()) {
|
|
|
|
settings.removeDeviceFromGroup(settingsGroup.ID, keys);
|
2019-09-15 00:13:02 -07:00
|
|
|
this.mqtt.log('device_group_remove_all', {friendly_name: entity.name});
|
2019-06-17 12:17:29 -07:00
|
|
|
}
|
2018-12-21 16:07:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = Groups;
|