Group support #15 (#745)

* Start on group support. #15

* Implement publishing to zigbee groups. https://github.com/Koenkk/zigbee2mqtt/issues/15

* Complete first version of group support.

* Fix tests.
This commit is contained in:
Koen Kanters
2018-12-22 00:07:53 +01:00
committed by GitHub
parent bd8282a41a
commit d3f705162d
10 changed files with 413 additions and 157 deletions
+27
View File
@@ -0,0 +1,27 @@
# Groups
Zigbee2mqtt has support for Zigbee groups. By using Zigbee groups you can control multiple devices simultaneously.
## Configuration
Add the following to your `configuration.yaml`.
```yaml
groups:
# ID, each group should have a different numerical ID
'1':
# Name which will be used to control the group
friendly_name: group_1
```
## Adding a device to a group
Send an MQTT message to `zigbee2mqtt/bridge/groups/[GROUP_FRIENDLY_NAME]/add` with payload `DEVICE_FRIENDLY_NAME`
## Remove a device from a group
Send an MQTT message to `zigbee2mqtt/bridge/groups/[GROUP_FRIENDLY_NAME]/remove` with payload `DEVICE_FRIENDLY_NAME`
## Controlling
To control a group the following topic should be used. The payload is the same as is used for controlling devices.
```
zigbee2mqtt/group/[GROUP_FRIENDLY_NAME]/set
```
+2
View File
@@ -16,6 +16,7 @@ const ExtensionDeviceConfigure = require('./extension/deviceConfigure');
const ExtensionDeviceReceive = require('./extension/deviceReceive');
const ExtensionMarkOnlineXiaomi = require('./extension/markOnlineXiaomi');
const ExtensionBridgeConfig = require('./extension/bridgeConfig');
const ExtensionGroups = require('./extension/groups');
class Controller {
constructor() {
@@ -38,6 +39,7 @@ class Controller {
new ExtensionRouterPollXiaomi(this.zigbee, this.mqtt, this.state, this.publishDeviceState),
new ExtensionMarkOnlineXiaomi(this.zigbee, this.mqtt, this.state, this.publishDeviceState),
new ExtensionBridgeConfig(this.zigbee, this.mqtt, this.state, this.publishDeviceState),
new ExtensionGroups(this.zigbee, this.mqtt, this.state, this.publishDeviceState),
];
if (settings.get().homeassistant) {
+70 -31
View File
@@ -3,11 +3,20 @@ const settings = require('../util/settings');
const zigbeeShepherdConverters = require('zigbee-shepherd-converters');
const Queue = require('queue');
const logger = require('../util/logger');
const utils = require('../util/utils');
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/.+/(set|get)$`);
const postfixes = ['left', 'right', 'center', 'bottom_left', 'bottom_right', 'top_left', 'top_right'];
const maxDepth = 20;
const groupConverters = [
zigbeeShepherdConverters.toZigbeeConverters.on_off,
zigbeeShepherdConverters.toZigbeeConverters.light_brightness,
zigbeeShepherdConverters.toZigbeeConverters.light_colortemp,
zigbeeShepherdConverters.toZigbeeConverters.light_color,
zigbeeShepherdConverters.toZigbeeConverters.ignore_transition,
];
class DevicePublish {
constructor(zigbee, mqtt, state, publishDeviceState) {
this.zigbee = zigbee;
@@ -47,10 +56,10 @@ class DevicePublish {
topic = topic.replace(`${settings.get().mqtt.base_topic}/`, '');
// Parse type from topic
const type = topic.substr(topic.lastIndexOf('/') + 1, topic.length);
const cmdType = topic.substr(topic.lastIndexOf('/') + 1, topic.length);
// Remove type from topic
topic = topic.replace(`/${type}`, '');
topic = topic.replace(`/${cmdType}`, '');
// Check if we have to deal with a postfix.
let postfix = '';
@@ -61,9 +70,15 @@ class DevicePublish {
topic = topic.replace(`/${postfix}`, '');
}
const deviceID = topic;
let entityType = 'device';
if (topic.startsWith('group/')) {
topic = topic.replace('group/', '');
entityType = 'group';
}
return {type: type, deviceID: deviceID, postfix: postfix};
const ID = topic;
return {cmdType: cmdType, ID: ID, postfix: postfix, entityType: entityType};
}
onMQTTMessage(topic, message) {
@@ -73,22 +88,51 @@ class DevicePublish {
return false;
}
// Map friendlyName to ieeeAddr if possible.
const ieeeAddr = settings.getIeeeAddrByFriendlyName(topic.deviceID) || topic.deviceID;
// Get device
const device = this.zigbee.getDevice(ieeeAddr);
if (!device) {
logger.error(`Failed to find device with ieeAddr: '${ieeeAddr}'`);
return;
// Map friendlyName (ID) to entityID if possible.
let entityID = null;
if (topic.entityType === 'group') {
const groupID = settings.getGroupIDByFriendlyName(topic.ID);
if (groupID) {
entityID = Number(groupID);
} else if (utils.isNumeric(topic.ID)) {
entityID = Number(topic.ID);
} else {
logger.error(`Cannot find group '${topic.ID}'`);
return;
}
} else if (topic.entityType === 'device') {
entityID = settings.getIeeeAddrByFriendlyName(topic.ID) || topic.ID;
}
// Map device to a model
const model = zigbeeShepherdConverters.findByZigbeeModel(device.modelId);
if (!model) {
logger.warn(`Device with modelID '${device.modelId}' is not supported.`);
logger.warn(`Please see: https://koenkk.github.io/zigbee2mqtt/how_tos/how_to_support_new_devices.html`);
return;
// Get entity details
let endpoint = null;
let converters = null;
let device = null;
if (topic.entityType === 'device') {
device = this.zigbee.getDevice(entityID);
if (!device) {
logger.error(`Failed to find device with ieeAddr: '${entityID}'`);
return;
}
// Map device to a model
const model = zigbeeShepherdConverters.findByZigbeeModel(device.modelId);
if (!model) {
logger.warn(`Device with modelID '${device.modelId}' is not supported.`);
logger.warn(`Please see: https://koenkk.github.io/zigbee2mqtt/how_tos/how_to_support_new_devices.html`);
return;
}
// Determine endpoint to publish to.
if (model.hasOwnProperty('ep')) {
const eps = model.ep(device);
endpoint = eps.hasOwnProperty(topic.postfix) ? eps[topic.postfix] : null;
}
converters = model.toZigbee;
} else if (topic.entityType === 'group') {
converters = groupConverters;
}
// Convert the MQTT message to a Zigbee message.
@@ -100,13 +144,6 @@ class DevicePublish {
json = {state: message.toString()};
}
// Determine endpoint to publish to.
let endpoint = null;
if (model.hasOwnProperty('ep')) {
const eps = model.ep(device);
endpoint = eps.hasOwnProperty(topic.postfix) ? eps[topic.postfix] : null;
}
// When brightness is present skip state; brightness also handles state.
if (json.hasOwnProperty('brightness') && json.hasOwnProperty('state')) {
logger.debug(`Skipping 'state' because of 'brightness'`);
@@ -115,14 +152,14 @@ class DevicePublish {
// For each key in the JSON message find the matching converter.
Object.keys(json).forEach((key) => {
const converter = model.toZigbee.find((c) => c.key.includes(key));
const converter = converters.find((c) => c.key.includes(key));
if (!converter) {
logger.error(`No converter available for '${key}' (${json[key]})`);
return;
}
// Converter didn't return a result, skip
const converted = converter.convert(key, json[key], json, topic.type);
const converted = converter.convert(key, json[key], json, topic.cmdType);
if (!converted) {
return;
}
@@ -130,7 +167,8 @@ class DevicePublish {
// Add job to queue
this.queue.push((queueCallback) => {
this.zigbee.publish(
ieeeAddr,
entityID,
topic.entityType,
converted.cid,
converted.cmd,
converted.cmdType,
@@ -139,7 +177,8 @@ class DevicePublish {
endpoint,
(error, rsp) => {
// Devices do not report when they go off, this ensures state (on/off) is always in sync.
if (topic.type === 'set' && !error && (key.startsWith('state') || key === 'brightness')) {
if (topic.entityType === 'device' && topic.cmdType === 'set' &&
!error && (key.startsWith('state') || key === 'brightness')) {
const msg = {};
const _key = topic.postfix ? `state_${topic.postfix}` : 'state';
msg[_key] = key === 'brightness' ? 'ON' : json['state'];
@@ -153,14 +192,14 @@ class DevicePublish {
// When there is a transition in the message the state of the device gets out of sync.
// Therefore; at the end of the transition, read the new state from the device.
if (topic.type === 'set' && converted.zclData.transtime) {
if (topic.cmdType === 'set' && converted.zclData.transtime && topic.entityType === 'device') {
const time = converted.zclData.transtime * 100;
const getConverted = converter.convert(key, json[key], json, 'get');
setTimeout(() => {
// Add job to queue
this.queue.push((queueCallback) => {
this.zigbee.publish(
ieeeAddr, getConverted.cid, getConverted.cmd, getConverted.cmdType,
entityID, topic.entityType, getConverted.cid, getConverted.cmd, getConverted.cmdType,
getConverted.zclData, getConverted.cfg, endpoint, () => queueCallback()
);
});
+87
View File
@@ -0,0 +1,87 @@
const settings = require('../util/settings');
const logger = require('../util/logger');
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/groups/.+/(remove|add)$`);
class Groups {
constructor(zigbee, mqtt, state, publishDeviceState) {
this.zigbee = zigbee;
this.mqtt = mqtt;
this.state = state;
this.publishDeviceState = publishDeviceState;
}
stop() {
this.queue.stop();
}
onMQTTConnected() {
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/groups/+/remove`);
this.mqtt.subscribe(`${settings.get().mqtt.base_topic}/bridge/groups/+/add`);
}
parseTopic(topic) {
if (!topic.match(topicRegex)) {
return null;
}
// Remove base from topic
topic = topic.replace(`${settings.get().mqtt.base_topic}/bridge/groups/`, '');
// Parse type from topic
const type = topic.substr(topic.lastIndexOf('/') + 1, topic.length);
// Remove type from topic
topic = topic.replace(`/${type}`, '');
return {friendly_name: topic, type: type};
}
onMQTTMessage(topic, message) {
topic = this.parseTopic(topic);
if (!topic) {
return false;
}
// Find ID of this group.
const groupID = settings.getGroupIDByFriendlyName(topic.friendly_name);
if (!groupID) {
logger.error(`Group with friendly_name '${topic.friendly_name}' doesn't exist`);
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.
let payload = null;
if (topic.type === 'add') {
payload = {groupid: groupID, groupname: topic.friendly_name};
} else if (topic.type === 'remove') {
payload = {groupid: groupID};
}
const callback = (error, rsp) => {
if (error) {
logger.error(`Failed to ${topic.type} ${ieeeAddr} from ${topic.friendly_name}`);
} else {
logger.error(`Succesfully ${topic.type} ${ieeeAddr} to ${topic.friendly_name}`);
}
};
this.zigbee.publish(
ieeeAddr, 'device', 'genGroups', topic.type, 'functional',
payload, null, null, callback,
);
return true;
}
}
module.exports = Groups;
+12
View File
@@ -10,6 +10,7 @@ const defaults = {
mqtt: {
include_device_information: false,
},
groups: {},
device_options: {},
advanced: {
log_directory: path.join(data.getPath(), 'log', '%TIMESTAMP%'),
@@ -73,6 +74,16 @@ function getIeeeAddrByFriendlyName(friendlyName) {
);
}
function getGroupIDByFriendlyName(friendlyName) {
if (!settings.groups) {
return null;
}
return Object.keys(settings.groups).find((ID) =>
settings.groups[ID].friendly_name === friendlyName
);
}
function changeFriendlyName(old, new_) {
const ieeeAddr = getIeeeAddrByFriendlyName(old);
@@ -94,5 +105,6 @@ module.exports = {
removeDevice: (ieeeAddr) => removeDevice(ieeeAddr),
getIeeeAddrByFriendlyName: (friendlyName) => getIeeeAddrByFriendlyName(friendlyName),
getGroupIDByFriendlyName: (friendlyName) => getGroupIDByFriendlyName(friendlyName),
changeFriendlyName: (old, new_) => changeFriendlyName(old, new_),
};
+1
View File
@@ -5,4 +5,5 @@ module.exports = {
millisecondsToSeconds: (milliseconds) => milliseconds / 1000,
secondsToMilliseconds: (seconds) => seconds * 1000,
isXiaomiDevice: (device) => xiaomiManufacturerID.includes(device.manufId),
isNumeric: (string) => /^\d+$/.test(string),
};
+27 -10
View File
@@ -18,6 +18,11 @@ const shepherdSettings = {
},
};
const defaultCfg = {
manufSpec: 0,
disDefaultRsp: 0,
};
logger.debug(`Using zigbee-shepherd with settings: '${JSON.stringify(shepherdSettings)}'`);
class Zigbee {
@@ -187,22 +192,34 @@ class Zigbee {
return this.shepherd.find(device.ieeeAddr, 1);
}
publish(ieeAddr, cid, cmd, cmdType, zclData, cfg, ep, callback) {
const device = this.findDevice(ieeAddr, ep);
if (!device) {
logger.error(`Zigbee cannot publish message to device because '${ieeAddr}' not known by zigbee-shepherd`);
getGroup(ID) {
return this.shepherd.getGroup(ID);
}
publish(entityID, entityType, cid, cmd, cmdType, zclData, cfg=defaultCfg, ep, callback) {
let entity = null;
if (entityType === 'device') {
entity = this.findDevice(entityID, ep);
} else if (entityType === 'group') {
entity = this.getGroup(entityID);
}
if (!entity) {
logger.error(
`Zigbee cannot publish message to ${entityType} because '${entityID}' not known by zigbee-shepherd`
);
return;
}
logger.info(
`Zigbee publish to '${ieeAddr}', ${cid} - ${cmd} - ` +
`Zigbee publish to ${entityType} '${entityID}', ${cid} - ${cmd} - ` +
`${JSON.stringify(zclData)} - ${JSON.stringify(cfg)} - ${ep}`
);
const callback_ = (error, rsp) => {
if (error) {
logger.error(
`Zigbee publish to '${ieeAddr}', ${cid} - ${cmd} - ${JSON.stringify(zclData)} ` +
`Zigbee publish to ${entityType} '${entityID}', ${cid} - ${cmd} - ${JSON.stringify(zclData)} ` +
`- ${JSON.stringify(cfg)} - ${ep} ` +
`failed with error ${error}`);
}
@@ -210,10 +227,10 @@ class Zigbee {
callback(error, rsp);
};
if (cmdType === 'functional') {
device.functional(cid, cmd, zclData, cfg, callback_);
} else if (cmdType === 'foundation') {
device.foundation(cid, cmd, zclData, cfg, callback_);
if (cmdType === 'functional' && entity.functional) {
entity.functional(cid, cmd, zclData, cfg, callback_);
} else if (cmdType === 'foundation' && entity.foundation) {
entity.foundation(cid, cmd, zclData, cfg, callback_);
} else {
logger.error(`Unknown zigbee publish cmdType ${cmdType}`);
}
+13 -13
View File
@@ -67,9 +67,9 @@
}
},
"@babel/parser": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.2.2.tgz",
"integrity": "sha512-UNTmQ5cSLDeBGBl+s7JeowkqIHgmFAGBnLDdIzFmUNSuS5JF0XBcN59jsh/vJO/YjfsBqMxhMjoFGmNExmf0FA==",
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.2.3.tgz",
"integrity": "sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA==",
"dev": true
},
"@babel/template": {
@@ -84,16 +84,16 @@
}
},
"@babel/traverse": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.2.tgz",
"integrity": "sha512-E5Bn9FSwHpSkUhthw/XEuvFZxIgrqb9M8cX8j5EUQtrUG5DQUy6bFyl7G7iQ1D1Czudor+xkmp81JbLVVM0Sjg==",
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz",
"integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/generator": "^7.2.2",
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-split-export-declaration": "^7.0.0",
"@babel/parser": "^7.2.2",
"@babel/parser": "^7.2.3",
"@babel/types": "^7.2.2",
"debug": "^4.1.0",
"globals": "^11.1.0",
@@ -838,7 +838,7 @@
},
"concat-stream": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
"resolved": "http://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
"requires": {
"buffer-from": "^1.0.0",
@@ -4763,8 +4763,8 @@
}
},
"zigbee-shepherd": {
"version": "git+https://github.com/Koenkk/zigbee-shepherd.git#ce52ac4131e2a505af6197b4a26d2b5360e4eb80",
"from": "git+https://github.com/Koenkk/zigbee-shepherd.git#ce52ac4131e2a505af6197b4a26d2b5360e4eb80",
"version": "git+https://github.com/Koenkk/zigbee-shepherd.git#081227b1789a6b416a5ef4a61162d805272f1ef3",
"from": "git+https://github.com/Koenkk/zigbee-shepherd.git#081227b1789a6b416a5ef4a61162d805272f1ef3",
"requires": {
"areq": "^0.2.0",
"busyman": "^0.3.0",
@@ -4792,9 +4792,9 @@
}
},
"zigbee-shepherd-converters": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/zigbee-shepherd-converters/-/zigbee-shepherd-converters-7.0.4.tgz",
"integrity": "sha512-yFgF+TTQ5j6fjf0GG6JxIM5KsrKdlGnXM9wpJPR7W48WEwUwGAlB4x9ioRYCjWWEvX03ngR5RoOqTdGyr8P8Gw==",
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/zigbee-shepherd-converters/-/zigbee-shepherd-converters-7.0.5.tgz",
"integrity": "sha512-wXjWbtJzBpttRtDAS/sDT6KEOPjc6Zi19BIDNClb2K0TQbVmlIwpe3nZk5CsEXC1AtKzzyReO6VAUIa1zX8o+A==",
"requires": {
"debounce": "*",
"debug": "3.2.6",
+2 -2
View File
@@ -45,8 +45,8 @@
"semver": "*",
"winston": "2.4.2",
"ziee": "*",
"zigbee-shepherd": "git+https://github.com/Koenkk/zigbee-shepherd.git#ce52ac4131e2a505af6197b4a26d2b5360e4eb80",
"zigbee-shepherd-converters": "7.0.4",
"zigbee-shepherd": "git+https://github.com/Koenkk/zigbee-shepherd.git#081227b1789a6b416a5ef4a61162d805272f1ef3",
"zigbee-shepherd-converters": "7.0.5",
"zive": "*"
},
"devDependencies": {
+172 -101
View File
@@ -11,7 +11,7 @@ const mqtt = {
const zigbee = {
getDevice: null,
publish: sandbox.stub().callsFake((ieeAddr, cid, cmd, cmdType, zclData, cfg, ep, callback) => {
publish: sandbox.stub().callsFake((entityID, entityType, cid, cmd, cmdType, zclData, cfg, ep, callback) => {
callback(false, null);
}),
};
@@ -42,12 +42,13 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/set', JSON.stringify({brightness: '200'}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'genLevelCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'moveToLevelWithOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {level: '200', transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], null);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'genLevelCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'moveToLevelWithOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {level: '200', transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[7], null);
});
it('Should publish messages to zigbee devices when brightness is in %', () => {
@@ -56,12 +57,13 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/set', JSON.stringify({brightness_percent: '92'}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'genLevelCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'moveToLevelWithOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {level: '235', transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], null);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'genLevelCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'moveToLevelWithOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {level: '235', transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[7], null);
});
it('Should publish messages to zigbee devices when brightness is in number', () => {
@@ -70,12 +72,13 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/set', JSON.stringify({brightness: 230}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'genLevelCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'moveToLevelWithOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {level: 230, transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], null);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'genLevelCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'moveToLevelWithOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {level: 230, transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[7], null);
});
it('Should publish messages to zigbee devices with color_temp', () => {
@@ -84,12 +87,13 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/set', JSON.stringify({color_temp: '222'}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'lightingColorCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'moveToColorTemp');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {colortemp: '222', transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], null);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'lightingColorCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'moveToColorTemp');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {colortemp: '222', transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[7], null);
});
it('Should publish messages to zigbee devices with color_temp in %', () => {
@@ -98,12 +102,13 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/set', JSON.stringify({color_temp_percent: '100'}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'lightingColorCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'moveToColorTemp');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {colortemp: '500', transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], null);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'lightingColorCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'moveToColorTemp');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {colortemp: '500', transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[7], null);
});
it('Should publish messages to zigbee devices with non-default ep', () => {
@@ -112,12 +117,13 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/set', JSON.stringify({state: 'OFF'}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'genOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'off');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[6], 2);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'genOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'off');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[7], 2);
});
it('Should publish messages to zigbee devices with non-default ep and postfix', () => {
@@ -126,12 +132,13 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/right/set', JSON.stringify({state: 'OFF'}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'genOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'off');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[6], 3);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'genOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'off');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[7], 3);
});
it('Should publish messages to zigbee gledopto with [11,13]', () => {
@@ -140,12 +147,13 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/set', JSON.stringify({state: 'OFF'}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'genOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'off');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[6], null);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'genOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'off');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[7], null);
});
it('Should publish messages to zigbee gledopto with [11,12,13]', () => {
@@ -154,12 +162,13 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/set', JSON.stringify({state: 'OFF'}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'genOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'off');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[6], 12);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'genOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'off');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[7], 12);
});
it('Should publish messages to zigbee devices with color xy', () => {
@@ -168,12 +177,13 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/set', JSON.stringify({color: {x: 100, y: 50}}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'lightingColorCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'moveToColor');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {colorx: 6553500, colory: 3276750, transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], null);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'lightingColorCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'moveToColor');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {colorx: 6553500, colory: 3276750, transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[7], null);
});
it('Should publish messages to zigbee devices with color rgb', () => {
@@ -182,12 +192,13 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/set', JSON.stringify({color: {r: 100, g: 200, b: 10}}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'lightingColorCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'moveToColor');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {colorx: 17085, colory: 44000, transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], null);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'lightingColorCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'moveToColor');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {colorx: 17085, colory: 44000, transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[7], null);
});
it('Should publish messages to zigbee devices with color rgb string', () => {
@@ -196,12 +207,13 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/set', JSON.stringify({color: {rgb: '100,200,10'}}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'lightingColorCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'moveToColor');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {colorx: 17085, colory: 44000, transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], null);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'lightingColorCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'moveToColor');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {colorx: 17085, colory: 44000, transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[7], null);
});
it('Should publish 1 message when brightness with state is send', () => {
@@ -210,12 +222,42 @@ describe('DevicePublish', () => {
devicePublish.onMQTTMessage('zigbee2mqtt/0x12345678/set', JSON.stringify({state: 'ON', brightness: '50'}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], '0x12345678');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'genLevelCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'moveToLevelWithOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[4], {level: '50', transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], null);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'device');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'genLevelCtrl');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'moveToLevelWithOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {level: '50', transtime: 0});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[7], null);
});
it('Should publish messages to groups', () => {
sandbox.stub(settings, 'getGroupIDByFriendlyName').callsFake(() => '1');
zigbee.publish.resetHistory();
devicePublish.onMQTTMessage('zigbee2mqtt/group/group_1/set', JSON.stringify({state: 'ON'}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], 1);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'group');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'genOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'on');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[7], null);
});
it('Should publish messages to groups by ID', () => {
zigbee.publish.resetHistory();
devicePublish.onMQTTMessage('zigbee2mqtt/group/2/set', JSON.stringify({state: 'ON'}));
chai.assert.isTrue(zigbee.publish.calledOnce);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[0], 2);
chai.assert.strictEqual(zigbee.publish.getCall(0).args[1], 'group');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[2], 'genOnOff');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[3], 'on');
chai.assert.strictEqual(zigbee.publish.getCall(0).args[4], 'functional');
chai.assert.deepEqual(zigbee.publish.getCall(0).args[5], {});
chai.assert.deepEqual(zigbee.publish.getCall(0).args[6], cfg.default);
chai.assert.deepEqual(zigbee.publish.getCall(0).args[7], null);
});
});
@@ -253,16 +295,18 @@ describe('DevicePublish', () => {
it('Should parse set topic', () => {
const topic = 'zigbee2mqtt/my_device_id/set';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.type, 'set');
chai.assert.strictEqual(parsed.deviceID, 'my_device_id');
chai.assert.strictEqual(parsed.cmdType, 'set');
chai.assert.strictEqual(parsed.entityType, 'device');
chai.assert.strictEqual(parsed.ID, 'my_device_id');
chai.assert.strictEqual(parsed.postfix, '');
});
it('Should parse get topic', () => {
const topic = 'zigbee2mqtt/my_device_id2/get';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.type, 'get');
chai.assert.strictEqual(parsed.deviceID, 'my_device_id2');
chai.assert.strictEqual(parsed.cmdType, 'get');
chai.assert.strictEqual(parsed.entityType, 'device');
chai.assert.strictEqual(parsed.ID, 'my_device_id2');
chai.assert.strictEqual(parsed.postfix, '');
});
@@ -277,16 +321,18 @@ describe('DevicePublish', () => {
const topic = 'zigbee2mqtt/at/my/home/my_device_id2/get';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.type, 'get');
chai.assert.strictEqual(parsed.deviceID, 'my_device_id2');
chai.assert.strictEqual(parsed.cmdType, 'get');
chai.assert.strictEqual(parsed.entityType, 'device');
chai.assert.strictEqual(parsed.ID, 'my_device_id2');
chai.assert.strictEqual(parsed.postfix, '');
});
it('Should parse topic with when deviceID has multiple slashes', () => {
const topic = 'zigbee2mqtt/floor0/basement/my_device_id2/set';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.type, 'set');
chai.assert.strictEqual(parsed.deviceID, 'floor0/basement/my_device_id2');
chai.assert.strictEqual(parsed.cmdType, 'set');
chai.assert.strictEqual(parsed.entityType, 'device');
chai.assert.strictEqual(parsed.ID, 'floor0/basement/my_device_id2');
chai.assert.strictEqual(parsed.postfix, '');
});
@@ -301,48 +347,54 @@ describe('DevicePublish', () => {
const topic = 'zigbee2mqtt/at/my/basement/floor0/basement/my_device_id2/set';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.type, 'set');
chai.assert.strictEqual(parsed.deviceID, 'floor0/basement/my_device_id2');
chai.assert.strictEqual(parsed.cmdType, 'set');
chai.assert.strictEqual(parsed.entityType, 'device');
chai.assert.strictEqual(parsed.ID, 'floor0/basement/my_device_id2');
chai.assert.strictEqual(parsed.postfix, '');
});
it('Should parse set with ieeAddr topic', () => {
const topic = 'zigbee2mqtt/0x12345689/set';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.type, 'set');
chai.assert.strictEqual(parsed.deviceID, '0x12345689');
chai.assert.strictEqual(parsed.cmdType, 'set');
chai.assert.strictEqual(parsed.entityType, 'device');
chai.assert.strictEqual(parsed.ID, '0x12345689');
chai.assert.strictEqual(parsed.postfix, '');
});
it('Should parse set with postfix topic', () => {
const topic = 'zigbee2mqtt/0x12345689/left/set';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.type, 'set');
chai.assert.strictEqual(parsed.deviceID, '0x12345689');
chai.assert.strictEqual(parsed.cmdType, 'set');
chai.assert.strictEqual(parsed.entityType, 'device');
chai.assert.strictEqual(parsed.ID, '0x12345689');
chai.assert.strictEqual(parsed.postfix, 'left');
});
it('Should parse set with postfix topic', () => {
const topic = 'zigbee2mqtt/0x12345689/right/set';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.type, 'set');
chai.assert.strictEqual(parsed.deviceID, '0x12345689');
chai.assert.strictEqual(parsed.cmdType, 'set');
chai.assert.strictEqual(parsed.entityType, 'device');
chai.assert.strictEqual(parsed.ID, '0x12345689');
chai.assert.strictEqual(parsed.postfix, 'right');
});
it('Should parse set with postfix topic', () => {
const topic = 'zigbee2mqtt/0x12345689/bottom_left/set';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.type, 'set');
chai.assert.strictEqual(parsed.deviceID, '0x12345689');
chai.assert.strictEqual(parsed.cmdType, 'set');
chai.assert.strictEqual(parsed.entityType, 'device');
chai.assert.strictEqual(parsed.ID, '0x12345689');
chai.assert.strictEqual(parsed.postfix, 'bottom_left');
});
it('Shouldnt parse set with invalid postfix topic', () => {
const topic = 'zigbee2mqtt/0x12345689/invalid/set';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.type, 'set');
chai.assert.strictEqual(parsed.deviceID, '0x12345689/invalid');
chai.assert.strictEqual(parsed.cmdType, 'set');
chai.assert.strictEqual(parsed.entityType, 'device');
chai.assert.strictEqual(parsed.ID, '0x12345689/invalid');
chai.assert.strictEqual(parsed.postfix, '');
});
@@ -357,9 +409,28 @@ describe('DevicePublish', () => {
const topic = 'zigbee2mqtt/at/my/home/my/device/in/basement/sensor/bottom_left/get';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.type, 'get');
chai.assert.strictEqual(parsed.deviceID, 'my/device/in/basement/sensor');
chai.assert.strictEqual(parsed.cmdType, 'get');
chai.assert.strictEqual(parsed.entityType, 'device');
chai.assert.strictEqual(parsed.ID, 'my/device/in/basement/sensor');
chai.assert.strictEqual(parsed.postfix, 'bottom_left');
});
it('Should parse group topics', () => {
const topic = 'zigbee2mqtt/group/group_1/set';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.cmdType, 'set');
chai.assert.strictEqual(parsed.entityType, 'group');
chai.assert.strictEqual(parsed.ID, 'group_1');
chai.assert.strictEqual(parsed.postfix, '');
});
it('Should parse group topics with mutiple slashes', () => {
const topic = 'zigbee2mqtt/group/master/child/set';
const parsed = devicePublish.parseTopic(topic);
chai.assert.strictEqual(parsed.cmdType, 'set');
chai.assert.strictEqual(parsed.entityType, 'group');
chai.assert.strictEqual(parsed.ID, 'master/child');
chai.assert.strictEqual(parsed.postfix, '');
});
});
});