Skip to content

Commit

Permalink
Allow to specify endpoint when adding device to group. (#1515)
Browse files Browse the repository at this point in the history
* uu

* Update
  • Loading branch information
Koenkk authored May 29, 2019
1 parent 82ed260 commit c29bfe1
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 20 deletions.
50 changes: 30 additions & 20 deletions lib/extension/groups.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const settings = require('../util/settings');
const logger = require('../util/logger');
const data = require('../util/data');
const utils = require('../util/utils');
const fs = require('fs');
const diff = require('deep-diff');

Expand Down Expand Up @@ -44,15 +45,15 @@ class Groups {
const groupID = diff.path[0];

if (diff.kind === 'N') {
diff.rhs.forEach((ieeeAddr) => this.updateDeviceGroup(ieeeAddr, 'add', groupID));
diff.rhs.forEach((ID) => this.updateDeviceGroup(ID, '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));
diff.lhs.forEach((ID) => this.updateDeviceGroup(ID, 'remove', groupID));
} else if (diff.kind === 'E') {
this.updateDeviceGroup(diff.rhs, 'add', groupID);
this.updateDeviceGroup(diff.lhs, 'remove', groupID);
Expand All @@ -61,9 +62,28 @@ class Groups {
}
}

getGroupsOfDevice(ieeeAddr) {
parseID(ID) {
let entityID = ID;
let endpointID = null;
const postfix = utils.getPostfixes().find((p) => entityID.endsWith(`/${p}`));
if (postfix) {
// Found a postfix, retrieve the endpoint which correspodns to the postfix
entityID = entityID.substring(0, entityID.length - (postfix.length + 1));
const endpoint = utils.getEndpointByEntityID(this.zigbee, entityID, postfix);

if (!endpoint) {
return;
}

endpointID = endpoint.epId;
}

return {endpointID, entityID};
}

getGroupsOfDevice(entityID) {
return Object.keys(settings.getGroups()).filter((groupID) => {
return settings.getGroup(groupID).devices.includes(ieeeAddr);
return settings.getGroup(groupID).devices.includes(entityID);
});
}

Expand Down Expand Up @@ -113,7 +133,7 @@ class Groups {
return {friendly_name: topic, type};
}

updateDeviceGroup(ieeeAddr, cmd, groupID) {
updateDeviceGroup(ID, cmd, groupID) {
let payload = null;
const orignalCmd = cmd;
if (cmd === 'add') {
Expand All @@ -127,6 +147,9 @@ class Groups {
cmd = 'removeAll';
}

const {entityID, endpointID} = this.parseID(ID);
const ieeeAddr = settings.resolveEntity(entityID).ID;

const cb = (error, rsp) => {
if (error) {
logger.error(`Failed to ${cmd} ${ieeeAddr} from ${groupID}`);
Expand Down Expand Up @@ -176,7 +199,7 @@ class Groups {

this.zigbee.publish(
ieeeAddr, 'device', 'genGroups', cmd, 'functional',
payload, null, null, cb,
payload, null, endpointID, cb,
);
}

Expand All @@ -194,21 +217,8 @@ class Groups {
return;
}

if (groupID === 99) {
logger.error('Group 99 is reserved, please use a different groupID');
return;
}

// Map message to ieeeAddr and check if device exist.
message = message.toString();
const ieeeAddr = settings.getIeeeAddrByFriendlyName(message) || message;
if (!this.zigbee.getDevice(ieeeAddr)) {
logger.error(`Failed to find device '${message}'`);
return;
}

// Send command to the device.
this.updateDeviceGroup(ieeeAddr, topic.type, groupID);
this.updateDeviceGroup(message.toString(), topic.type, groupID.toString());

return true;
}
Expand Down
79 changes: 79 additions & 0 deletions test/group.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const Groups = require('../lib/extension/groups');
const settings = require('../lib/util/settings');

let groupExtension = null;
let zigbee = null;
Expand Down Expand Up @@ -175,4 +176,82 @@ describe('Groups', () => {
{groupid: '2', groupname: ''}, null, null, expect.any(Function)
);
});

it('Apply group updates add with postfix', async () => {
zigbee.publish.mockClear();
zigbee.getDevice = () => ({modelId: 'lumi.ctrl_neutral2'});
zigbee.getEndpoint = (entityID, ep) => ({epId: ep});
const from = {};
const to = {'1': ['0x12345689/right']};
groupExtension.apply(from, to);
expect(zigbee.publish).toHaveBeenCalledTimes(1);
expect(zigbee.publish).toHaveBeenCalledWith(
'0x12345689', 'device', 'genGroups', 'add', 'functional',
{groupid: '1', groupname: ''}, null, 3, expect.any(Function)
);
});

it('Apply group updates add and remove with postfix', async () => {
zigbee.publish.mockClear();
zigbee.getDevice = () => ({modelId: 'lumi.ctrl_neutral2'});
zigbee.getEndpoint = (entityID, ep) => ({epId: ep});
const from = {'1': ['0x12345689/right']};
const to = {'1': ['0x12345689'], '2': ['0x12345689/left']};
groupExtension.apply(from, to);
expect(zigbee.publish).toHaveBeenCalledTimes(3);
expect(zigbee.publish).toHaveBeenCalledWith(
'0x12345689', 'device', 'genGroups', 'add', 'functional',
{groupid: '2', groupname: ''}, null, 2, expect.any(Function)
);
expect(zigbee.publish).toHaveBeenCalledWith(
'0x12345689', 'device', 'genGroups', 'remove', 'functional',
{groupid: '1'}, null, 3, expect.any(Function)
);
expect(zigbee.publish).toHaveBeenCalledWith(
'0x12345689', 'device', 'genGroups', 'add', 'functional',
{groupid: '1', groupname: ''}, null, null, expect.any(Function)
);
});

it('Add to group via MQTT', async () => {
zigbee.publish.mockClear();
zigbee.getDevice = () => ({modelId: 'lumi.ctrl_neutral2'});
zigbee.getEndpoint = (entityID, ep) => ({epId: ep});
jest.spyOn(settings, 'getGroupIDByFriendlyName').mockReturnValue(1);
jest.spyOn(settings, 'getIeeeAddrByFriendlyName').mockReturnValue('0x12345689');
groupExtension.onMQTTMessage('zigbee2mqtt/bridge/group/my_group/add', 'my_switch');
expect(zigbee.publish).toHaveBeenCalledTimes(1);
expect(zigbee.publish).toHaveBeenCalledWith(
'0x12345689', 'device', 'genGroups', 'add', 'functional',
{groupid: '1', groupname: ''}, null, null, expect.any(Function)
);
});

it('Add to group via MQTT with postfix', async () => {
zigbee.publish.mockClear();
zigbee.getDevice = () => ({modelId: 'lumi.ctrl_neutral2'});
zigbee.getEndpoint = (entityID, ep) => ({epId: ep});
jest.spyOn(settings, 'getGroupIDByFriendlyName').mockReturnValue(1);
jest.spyOn(settings, 'getIeeeAddrByFriendlyName').mockReturnValue('0x12345689');
groupExtension.onMQTTMessage('zigbee2mqtt/bridge/group/my_group/add', 'my_switch/right');
expect(zigbee.publish).toHaveBeenCalledTimes(1);
expect(zigbee.publish).toHaveBeenCalledWith(
'0x12345689', 'device', 'genGroups', 'add', 'functional',
{groupid: '1', groupname: ''}, null, 3, expect.any(Function)
);
});

it('Remove from group via MQTT with postfix', async () => {
zigbee.publish.mockClear();
zigbee.getDevice = () => ({modelId: 'lumi.ctrl_neutral2'});
zigbee.getEndpoint = (entityID, ep) => ({epId: ep});
jest.spyOn(settings, 'getGroupIDByFriendlyName').mockReturnValue(1);
jest.spyOn(settings, 'getIeeeAddrByFriendlyName').mockReturnValue('0x12345689');
groupExtension.onMQTTMessage('zigbee2mqtt/bridge/group/my_group/remove', 'my_switch/left');
expect(zigbee.publish).toHaveBeenCalledTimes(1);
expect(zigbee.publish).toHaveBeenCalledWith(
'0x12345689', 'device', 'genGroups', 'remove', 'functional',
{groupid: '1'}, null, 2, expect.any(Function)
);
});
});

0 comments on commit c29bfe1

Please sign in to comment.