mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-08-27 21:20:03 +00:00
Group configuration via configuration.yaml and group state (#1464)
Group configuration via configuration.yaml and group state.
This commit is contained in:
+2
-1
@@ -64,4 +64,5 @@ data/log*.txt
|
||||
data/state.json
|
||||
data/log
|
||||
data-backup/
|
||||
data/coordinator_backup.json
|
||||
data/coordinator_backup.json
|
||||
data/.storage
|
||||
+147
-33
@@ -1,5 +1,8 @@
|
||||
const settings = require('../util/settings');
|
||||
const logger = require('../util/logger');
|
||||
const data = require('../util/data');
|
||||
const fs = require('fs');
|
||||
const diff = require('deep-diff');
|
||||
|
||||
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/.+/(remove|add|remove_all)$`);
|
||||
|
||||
@@ -9,6 +12,18 @@ class Groups {
|
||||
this.mqtt = mqtt;
|
||||
this.state = state;
|
||||
this.publishEntityState = publishEntityState;
|
||||
this.onStateChange = this.onStateChange.bind(this);
|
||||
|
||||
this.groupsCacheFile = data.joinPathStorage('groups_cache.json');
|
||||
this.groupsCache = this.readGroupsCache();
|
||||
}
|
||||
|
||||
readGroupsCache() {
|
||||
return fs.existsSync(this.groupsCacheFile) ? JSON.parse(fs.readFileSync(this.groupsCacheFile, 'utf8')) : {};
|
||||
}
|
||||
|
||||
writeGroupsCache() {
|
||||
fs.writeFileSync(this.groupsCacheFile, JSON.stringify(this.groupsCache), 'utf8');
|
||||
}
|
||||
|
||||
onMQTTConnected() {
|
||||
@@ -17,6 +32,70 @@ class Groups {
|
||||
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/group/+/remove_all`);
|
||||
}
|
||||
|
||||
apply(from, to) {
|
||||
const sortGroups = (obj) => Object.keys(obj).forEach((key) => obj[key] = obj[key].sort());
|
||||
|
||||
sortGroups(from);
|
||||
sortGroups(to);
|
||||
|
||||
const differences = diff(from, to);
|
||||
if (differences) {
|
||||
differences.forEach((diff) => {
|
||||
const groupID = diff.path[0];
|
||||
|
||||
if (diff.kind === 'N') {
|
||||
diff.rhs.forEach((ieeeAddr) => this.updateDeviceGroup(ieeeAddr, 'add', groupID));
|
||||
} else if (diff.kind === 'A') {
|
||||
if (diff.item.lhs) {
|
||||
this.updateDeviceGroup(diff.item.lhs, 'remove', groupID);
|
||||
} else {
|
||||
this.updateDeviceGroup(diff.item.rhs, 'add', groupID);
|
||||
}
|
||||
} else if (diff.kind === 'D') {
|
||||
diff.lhs.forEach((ieeeAddr) => this.updateDeviceGroup(ieeeAddr, 'remove', groupID));
|
||||
} else if (diff.kind === 'E') {
|
||||
this.updateDeviceGroup(diff.rhs, 'add', groupID);
|
||||
this.updateDeviceGroup(diff.lhs, 'remove', groupID);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getGroupsOfDevice(ieeeAddr) {
|
||||
return Object.keys(settings.getGroups()).filter((groupID) => {
|
||||
return settings.getGroup(groupID).devices.includes(ieeeAddr);
|
||||
});
|
||||
}
|
||||
|
||||
onStateChange(ieeeAddr, from, to) {
|
||||
const properties = ['state', 'brightness', 'color_temp', 'color'];
|
||||
const payload = {};
|
||||
|
||||
properties.forEach((prop) => {
|
||||
if (to.hasOwnProperty(prop) && from[prop] != to[prop]) {
|
||||
payload[prop] = to[prop];
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(payload)) {
|
||||
const groups = this.getGroupsOfDevice(ieeeAddr);
|
||||
groups.forEach((groupID) => {
|
||||
this.publishEntityState(groupID, payload);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onZigbeeStarted() {
|
||||
this.state.registerOnStateChangeListener(this.onStateChange);
|
||||
|
||||
const settingsGroups = {};
|
||||
Object.keys(settings.getGroups()).forEach((groupID) => {
|
||||
settingsGroups[groupID] = settings.getGroup(groupID).devices;
|
||||
});
|
||||
|
||||
this.apply(this.groupsCache, settingsGroups);
|
||||
}
|
||||
|
||||
parseTopic(topic) {
|
||||
if (!topic.match(topicRegex)) {
|
||||
return null;
|
||||
@@ -34,6 +113,73 @@ class Groups {
|
||||
return {friendly_name: topic, type};
|
||||
}
|
||||
|
||||
updateDeviceGroup(ieeeAddr, cmd, groupID) {
|
||||
let payload = null;
|
||||
const orignalCmd = cmd;
|
||||
if (cmd === 'add') {
|
||||
payload = {groupid: groupID, groupname: ''};
|
||||
cmd = 'add';
|
||||
} else if (cmd === 'remove') {
|
||||
payload = {groupid: groupID};
|
||||
cmd = 'remove';
|
||||
} else if (cmd === 'remove_all') {
|
||||
payload = {};
|
||||
cmd = 'removeAll';
|
||||
}
|
||||
|
||||
const cb = (error, rsp) => {
|
||||
if (error) {
|
||||
logger.error(`Failed to ${cmd} ${ieeeAddr} from ${groupID}`);
|
||||
} else {
|
||||
logger.info(`Successfully ${cmd} ${ieeeAddr} to ${groupID}`);
|
||||
|
||||
// Log to MQTT
|
||||
this.mqtt.log({
|
||||
device: settings.getDevice(ieeeAddr).friendly_name,
|
||||
group: groupID,
|
||||
action: orignalCmd,
|
||||
});
|
||||
|
||||
// Update group cache
|
||||
if (cmd === 'add') {
|
||||
if (!this.groupsCache[groupID]) {
|
||||
this.groupsCache[groupID] = [];
|
||||
}
|
||||
|
||||
if (!this.groupsCache[groupID].includes(ieeeAddr)) {
|
||||
this.groupsCache[groupID].push(ieeeAddr);
|
||||
}
|
||||
} else if (cmd === 'remove') {
|
||||
if (this.groupsCache[groupID]) {
|
||||
this.groupsCache[groupID] = this.groupsCache[groupID].filter((device) => device != ieeeAddr);
|
||||
}
|
||||
} else if (cmd === 'removeAll') {
|
||||
Object.keys(this.groupsCache).forEach((groupID_) => {
|
||||
this.groupsCache[groupID_] = this.groupsCache[groupID_].filter((device) => device != ieeeAddr);
|
||||
});
|
||||
}
|
||||
|
||||
this.writeGroupsCache();
|
||||
|
||||
// Update settings
|
||||
if (cmd === 'add') {
|
||||
settings.addDeviceToGroup(groupID, ieeeAddr);
|
||||
} else if (cmd === 'remove') {
|
||||
settings.removeDeviceFromGroup(groupID, ieeeAddr);
|
||||
} else if (cmd === 'removeAll') {
|
||||
Object.keys(settings.get().groups).forEach((groupID) => {
|
||||
settings.removeDeviceFromGroup(groupID, ieeeAddr);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.zigbee.publish(
|
||||
ieeeAddr, 'device', 'genGroups', cmd, 'functional',
|
||||
payload, null, null, cb,
|
||||
);
|
||||
}
|
||||
|
||||
onMQTTMessage(topic, message) {
|
||||
topic = this.parseTopic(topic);
|
||||
|
||||
@@ -62,39 +208,7 @@ class Groups {
|
||||
}
|
||||
|
||||
// Send command to the device.
|
||||
let payload = null;
|
||||
let cmd = null;
|
||||
if (topic.type === 'add') {
|
||||
payload = {groupid: groupID, groupname: ''};
|
||||
cmd = 'add';
|
||||
} else if (topic.type === 'remove') {
|
||||
payload = {groupid: groupID};
|
||||
cmd = 'remove';
|
||||
} else if (topic.type === 'remove_all') {
|
||||
payload = {};
|
||||
cmd = 'removeAll';
|
||||
}
|
||||
|
||||
const callback = (error, rsp) => {
|
||||
if (error) {
|
||||
logger.error(`Failed to ${topic.type} ${ieeeAddr} from ${topic.friendly_name}`);
|
||||
} else {
|
||||
logger.info(`Successfully ${topic.type} ${ieeeAddr} to ${topic.friendly_name}`);
|
||||
|
||||
// Log to MQTT
|
||||
const log = {device: message};
|
||||
if (['remove', 'add'].includes(topic.type)) {
|
||||
log.group = topic.friendly_name;
|
||||
}
|
||||
|
||||
this.mqtt.log(log);
|
||||
}
|
||||
};
|
||||
|
||||
this.zigbee.publish(
|
||||
ieeeAddr, 'device', 'genGroups', cmd, 'functional',
|
||||
payload, null, null, callback,
|
||||
);
|
||||
this.updateDeviceGroup(ieeeAddr, topic.type, groupID);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+12
-4
@@ -17,6 +17,7 @@ class State {
|
||||
this.state = {};
|
||||
this.file = data.joinPath('state.json');
|
||||
this.timer = null;
|
||||
this.stateChangeListeners = [];
|
||||
|
||||
this.handleSettingsChanged = this.handleSettingsChanged.bind(this);
|
||||
}
|
||||
@@ -38,6 +39,10 @@ class State {
|
||||
this.checkLastSeen();
|
||||
}
|
||||
|
||||
registerOnStateChangeListener(listener) {
|
||||
this.stateChangeListeners.push(listener);
|
||||
}
|
||||
|
||||
checkLastSeen() {
|
||||
if (settings.get().advanced.last_seen === 'disable') {
|
||||
Object.values(this.state).forEach((s) => {
|
||||
@@ -90,14 +95,17 @@ class State {
|
||||
}
|
||||
|
||||
set(ieeeAddr, state) {
|
||||
const s = objectAssignDeep.noMutate(state);
|
||||
const toState = objectAssignDeep.noMutate(state);
|
||||
dontCacheProperties.forEach((property) => {
|
||||
if (s.hasOwnProperty(property)) {
|
||||
delete s[property];
|
||||
if (toState.hasOwnProperty(property)) {
|
||||
delete toState[property];
|
||||
}
|
||||
});
|
||||
|
||||
this.state[ieeeAddr] = s;
|
||||
const fromState = this.state[ieeeAddr];
|
||||
this.stateChangeListeners.forEach((listener) => listener(ieeeAddr, fromState, toState));
|
||||
|
||||
this.state[ieeeAddr] = toState;
|
||||
}
|
||||
|
||||
remove(ieeeAddr) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
let dataPath = null;
|
||||
|
||||
@@ -13,8 +14,18 @@ function load() {
|
||||
|
||||
load();
|
||||
|
||||
function joinPathStorage(file) {
|
||||
const storagePath = path.join(dataPath, '.storage');
|
||||
if (!fs.existsSync(storagePath)) {
|
||||
fs.mkdirSync(storagePath);
|
||||
}
|
||||
|
||||
return path.join(storagePath, file);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
joinPath: (file) => path.join(dataPath, file),
|
||||
joinPathStorage: (file) => joinPathStorage(file),
|
||||
getPath: () => dataPath,
|
||||
|
||||
// For test only.
|
||||
|
||||
+41
-2
@@ -153,9 +153,17 @@ const getDevices = () => getSettings().devices || [];
|
||||
|
||||
const getDevice = (ieeeAddr) => getDevices()[ieeeAddr];
|
||||
|
||||
const getGroups = () => getSettings().groups || [];
|
||||
const getGroups = () => getSettings().groups || {};
|
||||
|
||||
const getGroup = (ID) => getGroups()[ID];
|
||||
const getGroup = (ID) => {
|
||||
const group = getGroups()[ID];
|
||||
|
||||
if (group && !group.hasOwnProperty('devices')) {
|
||||
group.devices = [];
|
||||
}
|
||||
|
||||
return group;
|
||||
};
|
||||
|
||||
|
||||
function addDevice(ieeeAddr) {
|
||||
@@ -204,6 +212,35 @@ function addGroup(groupName) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function addDeviceToGroup(ID, ieeeAddr) {
|
||||
const settings = getSettings();
|
||||
const group = settings.groups[ID];
|
||||
if (!group.devices) {
|
||||
group.devices = [];
|
||||
}
|
||||
|
||||
if (!group.devices.includes(ieeeAddr)) {
|
||||
group.devices.push(ieeeAddr);
|
||||
writeRead();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function removeDeviceFromGroup(ID, ieeeAddr) {
|
||||
const settings = getSettings();
|
||||
const group = settings.groups[ID];
|
||||
|
||||
if (group.devices && group.devices.includes(ieeeAddr)) {
|
||||
group.devices = group.devices.filter((d) => d != ieeeAddr);
|
||||
writeRead();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function removeGroup(name) {
|
||||
const settings = getSettings();
|
||||
if (!settings.groups) return;
|
||||
@@ -308,6 +345,8 @@ module.exports = {
|
||||
removeDevice: (ieeeAddr) => removeDevice(ieeeAddr),
|
||||
addGroup: (name) => addGroup(name),
|
||||
removeGroup: (name) => removeGroup(name),
|
||||
addDeviceToGroup: (ID, ieeeAddr) => addDeviceToGroup(ID, ieeeAddr),
|
||||
removeDeviceFromGroup: (ID, ieeeAddr) => removeDeviceFromGroup(ID, ieeeAddr),
|
||||
|
||||
getIeeeAddrByFriendlyName: (friendlyName) => getIeeeAddrByFriendlyName(friendlyName),
|
||||
getGroupIDByFriendlyName: (friendlyName) => getGroupIDByFriendlyName(friendlyName),
|
||||
|
||||
Generated
+11
-6
@@ -440,9 +440,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/jest": {
|
||||
"version": "24.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.11.tgz",
|
||||
"integrity": "sha512-2kLuPC5FDnWIDvaJBzsGTBQaBbnDweznicvK7UGYzlIJP4RJR2a4A/ByLUXEyEgag6jz8eHdlWExGDtH3EYUXQ==",
|
||||
"version": "24.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.12.tgz",
|
||||
"integrity": "sha512-60sjqMhat7i7XntZckcSGV8iREJyXXI6yFHZkSZvCPUeOnEJ/VP1rU/WpEWQ56mvoh8NhC+sfKAuJRTyGtCOow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/jest-diff": "*"
|
||||
@@ -2077,6 +2077,11 @@
|
||||
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
|
||||
"dev": true
|
||||
},
|
||||
"deep-diff": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz",
|
||||
"integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg=="
|
||||
},
|
||||
"deep-eql": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
|
||||
@@ -2901,9 +2906,9 @@
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.8.tgz",
|
||||
"integrity": "sha512-tPvHgPGB7m40CZ68xqFGkKuzN+RnpGmSV+hgeKxhRpbxdqKXUFJGC3yonBOLzQBcJyGpdZFDfCsdOC2KFsXzeA==",
|
||||
"version": "1.2.9",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
|
||||
"integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
"ziee": "*",
|
||||
"zigbee-shepherd": "git+https://github.com/Koenkk/zigbee-shepherd.git#30d08aacf50327dc1e2c3f146076b9efb8581192",
|
||||
"zigbee-shepherd-converters": "8.1.5",
|
||||
"deep-diff": "*",
|
||||
"zive": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
const Groups = require('../lib/extension/groups');
|
||||
|
||||
let groupExtension = null;
|
||||
let zigbee = null;
|
||||
|
||||
describe('Groups', () => {
|
||||
beforeEach(() => {
|
||||
zigbee = {
|
||||
publish: jest.fn(),
|
||||
};
|
||||
|
||||
groupExtension = new Groups(zigbee, null, null, null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('Apply group updates add', async () => {
|
||||
zigbee.publish.mockClear();
|
||||
const from = {};
|
||||
const to = {'1': ['1', '2']};
|
||||
groupExtension.apply(from, to);
|
||||
expect(zigbee.publish).toHaveBeenCalledTimes(2);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'1', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '1', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'2', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '1', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Apply group updates remove', async () => {
|
||||
zigbee.publish.mockClear();
|
||||
const from = {'1': ['1', '2', '3']};
|
||||
const to = {'1': ['1']};
|
||||
groupExtension.apply(from, to);
|
||||
expect(zigbee.publish).toHaveBeenCalledTimes(2);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'3', 'device', 'genGroups', 'remove', 'functional',
|
||||
{groupid: '1'}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'2', 'device', 'genGroups', 'remove', 'functional',
|
||||
{groupid: '1'}, null, null, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Apply group updates add 1', async () => {
|
||||
zigbee.publish.mockClear();
|
||||
const from = {'1': ['1']};
|
||||
const to = {'1': ['1', '2', '3']};
|
||||
groupExtension.apply(from, to);
|
||||
expect(zigbee.publish).toHaveBeenCalledTimes(2);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'3', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '1', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'2', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '1', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Apply group updates add and remove group', async () => {
|
||||
zigbee.publish.mockClear();
|
||||
const from = {'1': ['1', '2']};
|
||||
const to = {'2': ['1', '2']};
|
||||
groupExtension.apply(from, to);
|
||||
expect(zigbee.publish).toHaveBeenCalledTimes(4);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'1', 'device', 'genGroups', 'remove', 'functional',
|
||||
{groupid: '1'}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'2', 'device', 'genGroups', 'remove', 'functional',
|
||||
{groupid: '1'}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'1', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '2', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'2', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '2', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Apply group updates change 1', async () => {
|
||||
zigbee.publish.mockClear();
|
||||
const from = {'1': ['1', '4', '2']};
|
||||
const to = {'1': ['1', '2', '3']};
|
||||
groupExtension.apply(from, to);
|
||||
expect(zigbee.publish).toHaveBeenCalledTimes(2);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'3', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '1', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'4', 'device', 'genGroups', 'remove', 'functional',
|
||||
{groupid: '1'}, null, null, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Apply group updates change 2', async () => {
|
||||
zigbee.publish.mockClear();
|
||||
const from = {'1': ['1', '2', '3']};
|
||||
const to = {'1': ['3', '1', '2', '4']};
|
||||
groupExtension.apply(from, to);
|
||||
expect(zigbee.publish).toHaveBeenCalledTimes(1);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'4', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '1', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Apply group updates change 3', async () => {
|
||||
zigbee.publish.mockClear();
|
||||
const from = {'1': ['1', '2']};
|
||||
const to = {'1': ['3', '1', '4', '2']};
|
||||
groupExtension.apply(from, to);
|
||||
expect(zigbee.publish).toHaveBeenCalledTimes(2);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'3', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '1', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'4', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '1', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Apply group updates change 4', async () => {
|
||||
zigbee.publish.mockClear();
|
||||
const from = {'1': ['1']};
|
||||
const to = {'2': ['3', '1']};
|
||||
groupExtension.apply(from, to);
|
||||
expect(zigbee.publish).toHaveBeenCalledTimes(3);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'1', 'device', 'genGroups', 'remove', 'functional',
|
||||
{groupid: '1'}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'1', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '2', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'3', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '2', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Apply group updates change 5', async () => {
|
||||
zigbee.publish.mockClear();
|
||||
const from = {'1': ['1']};
|
||||
const to = {'1': ['3'], '2': ['3', '1']};
|
||||
groupExtension.apply(from, to);
|
||||
expect(zigbee.publish).toHaveBeenCalledTimes(4);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'1', 'device', 'genGroups', 'remove', 'functional',
|
||||
{groupid: '1'}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'3', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '1', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'1', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '2', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
expect(zigbee.publish).toHaveBeenCalledWith(
|
||||
'3', 'device', 'genGroups', 'add', 'functional',
|
||||
{groupid: '2', groupname: ''}, null, null, expect.any(Function)
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -203,12 +203,13 @@ describe('Settings', () => {
|
||||
const group = settings.getGroup('1');
|
||||
const expected = {
|
||||
friendly_name: '123',
|
||||
devices: [],
|
||||
};
|
||||
|
||||
expect(group).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it('Should read groups form a separate file', () => {
|
||||
it('Should read groups from a separate file', () => {
|
||||
const contentConfiguration = {
|
||||
groups: 'groups.yaml',
|
||||
};
|
||||
@@ -225,6 +226,7 @@ describe('Settings', () => {
|
||||
const group = settings.getGroup('1');
|
||||
const expected = {
|
||||
friendly_name: '123',
|
||||
devices: [],
|
||||
};
|
||||
|
||||
expect(group).toStrictEqual(expected);
|
||||
@@ -239,6 +241,7 @@ describe('Settings', () => {
|
||||
const contentGroups = {
|
||||
'1': {
|
||||
friendly_name: '123',
|
||||
devices: [],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -268,6 +271,7 @@ describe('Settings', () => {
|
||||
const group = settings.getGroup('1');
|
||||
const expectedGroup = {
|
||||
friendly_name: '123',
|
||||
devices: [],
|
||||
};
|
||||
|
||||
expect(group).toStrictEqual(expectedGroup);
|
||||
|
||||
Reference in New Issue
Block a user