Sort logged and published JSON objects alphabetically. #4091

This commit is contained in:
Koen Kanters
2020-08-13 20:00:35 +02:00
parent c66e65e0fb
commit 9774031169
27 changed files with 836 additions and 837 deletions
+9 -11
View File
@@ -6,6 +6,7 @@ const logger = require('./util/logger');
const settings = require('./util/settings');
const objectAssignDeep = require('object-assign-deep');
const utils = require('./util/utils');
const stringify = require('json-stable-stringify');
const fs = require('fs');
const data = require('./util/data');
const path = require('path');
@@ -218,7 +219,7 @@ class Controller {
if (type === 'message') {
logger.debug(
`Received Zigbee message from '${name}', type '${data.type}', cluster '${data.cluster}'` +
`, data '${JSON.stringify(data.data)}' from endpoint ${data.endpoint.ID}` +
`, data '${stringify(data.data)}' from endpoint ${data.endpoint.ID}` +
(data.hasOwnProperty('groupID') ? ` with groupID ${data.groupID}` : ``),
);
} else if (type === 'deviceJoined') {
@@ -325,16 +326,13 @@ class Controller {
}
if (Object.entries(messagePayload).length) {
if (settings.get().experimental.output === 'attribute_and_json') {
await this.mqtt.publish(resolvedEntity.name, JSON.stringify(messagePayload), options);
const output = settings.get().experimental.output;
if (output === 'attribute_and_json' || output === 'json') {
await this.mqtt.publish(resolvedEntity.name, stringify(messagePayload), options);
}
if (output === 'attribute_and_json' || output === 'attribute') {
await this.iteratePayloadAttributeOutput(`${resolvedEntity.name}/`, messagePayload, options);
} else if (settings.get().experimental.output === 'json') {
await this.mqtt.publish(resolvedEntity.name, JSON.stringify(messagePayload), options);
} else {
/* istanbul ignore else */
if (settings.get().experimental.output === 'attribute') {
await this.iteratePayloadAttributeOutput(`${resolvedEntity.name}/`, messagePayload, options);
}
}
}
@@ -359,7 +357,7 @@ class Controller {
} else if (typeof subPayload === 'object') {
this.iteratePayloadAttributeOutput(`${topicRoot}${key}-`, subPayload, options);
} else {
message = typeof subPayload === 'string' ? subPayload : JSON.stringify(subPayload);
message = typeof subPayload === 'string' ? subPayload : stringify(subPayload);
}
if (message !== null) {
+5 -4
View File
@@ -4,6 +4,7 @@ const utils = require('../util/utils');
const legacyTopicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/(bind|unbind)/.+$`);
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/request/device/(bind|unbind)`);
const Extension = require('./extension');
const stringify = require('json-stable-stringify');
const clusterCandidates = ['genScenes', 'genOnOff', 'genLevelCtrl', 'lightingColorCtrl', 'closuresWindowCovering'];
@@ -106,7 +107,7 @@ class Bind extends Extension {
if (settings.get().advanced.legacy_api) {
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `device_${type}`,
stringify({type: `device_${type}`,
message: {from: sourceName, to: targetName, cluster}}),
);
}
@@ -121,7 +122,7 @@ class Bind extends Extension {
if (settings.get().advanced.legacy_api) {
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `device_${type}_failed`,
stringify({type: `device_${type}_failed`,
message: {from: sourceName, to: targetName, cluster}}),
);
}
@@ -137,7 +138,7 @@ class Bind extends Extension {
if (settings.get().advanced.legacy_api) {
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `device_${type}_failed`, message: {from: sourceName, to: targetName}}),
stringify({type: `device_${type}_failed`, message: {from: sourceName, to: targetName}}),
);
}
} else if (failedClusters.length === attemptedClusters.length) {
@@ -151,7 +152,7 @@ class Bind extends Extension {
const triggeredViaLegacyApi = topic.match(legacyTopicRegex);
if (!triggeredViaLegacyApi) {
const response = utils.getResponse(message, responseData, error);
await this.mqtt.publish(`bridge/response/device/${type}`, JSON.stringify(response));
await this.mqtt.publish(`bridge/response/device/${type}`, stringify(response));
}
if (error) {
+8 -7
View File
@@ -4,6 +4,7 @@ const Extension = require('./extension');
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const settings = require('../util/settings');
const Transport = require('winston-transport');
const stringify = require('json-stable-stringify');
const requestRegex = new RegExp(`${settings.get().mqtt.base_topic}/bridge/request/(.*)`);
@@ -47,7 +48,7 @@ class Bridge extends Extension {
const mqtt = this.mqtt;
class EventTransport extends Transport {
log(info, callback) {
const payload = JSON.stringify({message: info.message, level: info.level});
const payload = stringify({message: info.message, level: info.level});
mqtt.publish(`bridge/logging`, payload, {}, settings.get().mqtt.base_topic, true);
callback();
}
@@ -63,11 +64,11 @@ class Bridge extends Extension {
try {
const response = await this.requestLookup[match[1].toLowerCase()](message);
await this.mqtt.publish(`bridge/response/${match[1]}`, JSON.stringify(response));
await this.mqtt.publish(`bridge/response/${match[1]}`, stringify(response));
} catch (error) {
logger.error(`Request '${topic}' failed with error: '${error.message}'`);
const response = utils.getResponse(message, {}, error.message);
await this.mqtt.publish(`bridge/response/${match[1]}`, JSON.stringify(response));
await this.mqtt.publish(`bridge/response/${match[1]}`, stringify(response));
}
}
}
@@ -99,7 +100,7 @@ class Bridge extends Extension {
await this.mqtt.publish(
'bridge/event',
JSON.stringify({type: utils.toSnakeCase(type), data: payload}),
stringify({type: utils.toSnakeCase(type), data: payload}),
{retain: false, qos: 0},
);
}
@@ -366,7 +367,7 @@ class Bridge extends Extension {
permit_join: await this.zigbee.getPermitJoin(),
};
await this.mqtt.publish('bridge/info', JSON.stringify(payload), {retain: true, qos: 0});
await this.mqtt.publish('bridge/info', stringify(payload), {retain: true, qos: 0});
}
async publishDevices() {
@@ -395,7 +396,7 @@ class Bridge extends Extension {
};
});
await this.mqtt.publish('bridge/devices', JSON.stringify(devices), {retain: true, qos: 0});
await this.mqtt.publish('bridge/devices', stringify(devices), {retain: true, qos: 0});
}
async publishGroups() {
@@ -413,7 +414,7 @@ class Bridge extends Extension {
};
});
await this.mqtt.publish('bridge/groups', JSON.stringify(groups), {retain: true, qos: 0});
await this.mqtt.publish('bridge/groups', stringify(groups), {retain: true, qos: 0});
}
}
+2 -1
View File
@@ -2,6 +2,7 @@ const settings = require('../util/settings');
const utils = require('../util/utils');
const logger = require('../util/logger');
const Extension = require('./extension');
const stringify = require('json-stable-stringify');
/**
* This extension calls the zigbee-herdsman-converters definition configure() method
@@ -97,7 +98,7 @@ class Configure extends Extension {
}
const response = utils.getResponse(message, {id: ID}, error);
await this.mqtt.publish(`bridge/response/device/configure`, JSON.stringify(response));
await this.mqtt.publish(`bridge/response/device/configure`, stringify(response));
}
}
+7 -6
View File
@@ -3,6 +3,7 @@ const logger = require('../util/logger');
const Extension = require('./extension');
const utils = require('../util/utils');
const postfixes = utils.getEndpointNames();
const stringify = require('json-stable-stringify');
const topicRegex =
new RegExp(`^${settings.get().mqtt.base_topic}/bridge/request/group/members/(remove|add|remove_all)$`);
@@ -188,7 +189,7 @@ class Groups extends Extension {
};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `device_group_${type}_failed`, message: payload}),
stringify({type: `device_group_${type}_failed`, message: payload}),
);
}
@@ -209,7 +210,7 @@ class Groups extends Extension {
};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `device_group_${type}_failed`, message: payload}),
stringify({type: `device_group_${type}_failed`, message: payload}),
);
}
@@ -289,7 +290,7 @@ class Groups extends Extension {
const payload = {friendly_name: resolvedEntityDevice.name, group: resolvedEntityGroup.name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `device_group_add`, message: payload}),
stringify({type: `device_group_add`, message: payload}),
);
}
} else if (type === 'remove') {
@@ -302,7 +303,7 @@ class Groups extends Extension {
const payload = {friendly_name: resolvedEntityDevice.name, group: resolvedEntityGroup.name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `device_group_remove`, message: payload}),
stringify({type: `device_group_remove`, message: payload}),
);
}
} else { // remove_all
@@ -316,7 +317,7 @@ class Groups extends Extension {
const payload = {friendly_name: resolvedEntityDevice.name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `device_group_remove_all`, message: payload}),
stringify({type: `device_group_remove_all`, message: payload}),
);
}
}
@@ -328,7 +329,7 @@ class Groups extends Extension {
if (!triggeredViaLegacyApi) {
const response = utils.getResponse(message, responseData, error);
await this.mqtt.publish(`bridge/response/group/members/${type}`, JSON.stringify(response));
await this.mqtt.publish(`bridge/response/group/members/${type}`, stringify(response));
}
if (error) {
+4 -3
View File
@@ -5,6 +5,7 @@ const utils = require('../util/utils');
const zigbee2mqttVersion = require('../../package.json').version;
const Extension = require('./extension');
const objectAssignDeep = require(`object-assign-deep`);
const stringify = require('json-stable-stringify');
const cfg = {
// Binary sensor
@@ -1947,7 +1948,7 @@ class HomeAssistant extends Extension {
}
await this.mqtt.publish(
`${data.entity.name}/${endpoint}`, JSON.stringify(payload), {},
`${data.entity.name}/${endpoint}`, stringify(payload), {},
);
}
}
@@ -1993,7 +1994,7 @@ class HomeAssistant extends Extension {
device: this.getDevicePayload(data.entity),
};
await this.mqtt.publish(topic, JSON.stringify(payload), {retain: true, qos: 0}, this.discoveryTopic);
await this.mqtt.publish(topic, stringify(payload), {retain: true, qos: 0}, this.discoveryTopic);
this.discoveredTriggers[device.ieeeAddr].add(discoveredKey);
}
@@ -2237,7 +2238,7 @@ class HomeAssistant extends Extension {
}
}
this.mqtt.publish(topic, JSON.stringify(payload), {retain: true, qos: 0}, this.discoveryTopic);
this.mqtt.publish(topic, stringify(payload), {retain: true, qos: 0}, this.discoveryTopic);
});
this.discovered[device.ieeeAddr] = true;
+23 -22
View File
@@ -4,6 +4,7 @@ const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const utils = require('../../util/utils');
const assert = require('assert');
const Extension = require('../extension');
const stringify = require('json-stable-stringify');
const configRegex =
new RegExp(`${settings.get().mqtt.base_topic}/bridge/config/((?:\\w+/get)|(?:\\w+/factory_reset)|(?:\\w+))`);
@@ -66,7 +67,7 @@ class BridgeLegacy extends Extension {
logger.info(`Whitelisted '${entity.friendlyName}'`);
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: 'device_whitelisted', message: {friendly_name: entity.friendlyName}}),
stringify({type: 'device_whitelisted', message: {friendly_name: entity.friendlyName}}),
);
} catch (error) {
logger.error(`Failed to whitelist '${message}' '${error}'`);
@@ -90,7 +91,7 @@ class BridgeLegacy extends Extension {
const entity = settings.getEntity(json.friendly_name);
assert(entity, `Entity '${json.friendly_name}' does not exist`);
settings.changeEntityOptions(entity.ID, json.options);
logger.info(`Changed device specific options of '${json.friendly_name}' (${JSON.stringify(json.options)})`);
logger.info(`Changed device specific options of '${json.friendly_name}' (${stringify(json.options)})`);
}
async permitJoin(topic, message) {
@@ -176,9 +177,9 @@ class BridgeLegacy extends Extension {
});
if (topic.split('/').pop() == 'get') {
this.mqtt.publish(`bridge/config/devices`, JSON.stringify(devices), {});
this.mqtt.publish(`bridge/config/devices`, stringify(devices), {});
} else {
this.mqtt.publish('bridge/log', JSON.stringify({type: 'devices', message: devices}));
this.mqtt.publish('bridge/log', stringify({type: 'devices', message: devices}));
}
}
@@ -189,7 +190,7 @@ class BridgeLegacy extends Extension {
return group;
});
this.mqtt.publish('bridge/log', JSON.stringify({type: 'groups', message: payload}));
this.mqtt.publish('bridge/log', stringify({type: 'groups', message: payload}));
}
rename(topic, message) {
@@ -233,7 +234,7 @@ class BridgeLegacy extends Extension {
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `${isGroup ? 'group' : 'device'}_renamed`, message: {from, to}}),
stringify({type: `${isGroup ? 'group' : 'device'}_renamed`, message: {from, to}}),
);
} catch (error) {
logger.error(`Failed to rename - ${from} to ${to}`);
@@ -265,7 +266,7 @@ class BridgeLegacy extends Extension {
const group = settings.addGroup(name, id);
this.zigbee.createGroup(group.ID);
this.mqtt.publish('bridge/log', JSON.stringify({type: `group_added`, message: name}));
this.mqtt.publish('bridge/log', stringify({type: `group_added`, message: name}));
logger.info(`Added group '${name}'`);
}
@@ -281,7 +282,7 @@ class BridgeLegacy extends Extension {
}
settings.removeGroup(message);
this.mqtt.publish('bridge/log', JSON.stringify({type: `group_removed`, message}));
this.mqtt.publish('bridge/log', stringify({type: `group_removed`, message}));
logger.info(`Removed group '${name}'`);
}
@@ -308,7 +309,7 @@ class BridgeLegacy extends Extension {
if (!entity) {
logger.error(`Cannot ${lookup[action][2]}, device '${message}' does not exist`);
this.mqtt.publish('bridge/log', JSON.stringify({type: `device_${lookup[action][0]}_failed`, message}));
this.mqtt.publish('bridge/log', stringify({type: `device_${lookup[action][0]}_failed`, message}));
return;
}
@@ -323,7 +324,7 @@ class BridgeLegacy extends Extension {
this.state.remove(entity.settings.ID);
logger.info(`Successfully ${lookup[action][0]} ${entity.settings.friendlyName}`);
this.mqtt.publish('bridge/log', JSON.stringify({type: `device_${lookup[action][0]}`, message}));
this.mqtt.publish('bridge/log', stringify({type: `device_${lookup[action][0]}`, message}));
};
try {
@@ -340,7 +341,7 @@ class BridgeLegacy extends Extension {
// eslint-disable-next-line
logger.error(`See https://www.zigbee2mqtt.io/information/mqtt_topics_and_message_structure.html#zigbee2mqttbridgeconfigremove for more info`);
this.mqtt.publish('bridge/log', JSON.stringify({type: `device_${lookup[action][0]}_failed`, message}));
this.mqtt.publish('bridge/log', stringify({type: `device_${lookup[action][0]}_failed`, message}));
}
if (action === 'ban') {
@@ -383,7 +384,7 @@ class BridgeLegacy extends Extension {
permit_join: await this.zigbee.getPermitJoin(),
};
await this.mqtt.publish(topic, JSON.stringify(payload), {retain: true, qos: 0});
await this.mqtt.publish(topic, stringify(payload), {retain: true, qos: 0});
}
onZigbeeEvent(type, data, resolvedEntity) {
@@ -394,7 +395,7 @@ class BridgeLegacy extends Extension {
if (type === 'deviceJoined') {
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `device_connected`, message: {friendly_name: resolvedEntity.name}}),
stringify({type: `device_connected`, message: {friendly_name: resolvedEntity.name}}),
);
} else if (type === 'deviceInterview') {
if (data.status === 'successful') {
@@ -403,20 +404,20 @@ class BridgeLegacy extends Extension {
const log = {friendly_name: resolvedEntity.name, model, vendor, description, supported: true};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `pairing`, message: 'interview_successful', meta: log}),
stringify({type: `pairing`, message: 'interview_successful', meta: log}),
);
} else {
const meta = {friendly_name: resolvedEntity.name, supported: false};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `pairing`, message: 'interview_successful', meta}),
stringify({type: `pairing`, message: 'interview_successful', meta}),
);
}
} else if (data.status === 'failed') {
const meta = {friendly_name: resolvedEntity.name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `pairing`, message: 'interview_failed', meta}),
stringify({type: `pairing`, message: 'interview_failed', meta}),
);
} else {
/* istanbul ignore else */
@@ -424,13 +425,13 @@ class BridgeLegacy extends Extension {
const meta = {friendly_name: resolvedEntity.name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `pairing`, message: 'interview_started', meta}),
stringify({type: `pairing`, message: 'interview_started', meta}),
);
}
}
} else if (type === 'deviceAnnounce') {
const meta = {friendly_name: resolvedEntity.name};
this.mqtt.publish('bridge/log', JSON.stringify({type: `device_announced`, message: 'announce', meta}));
this.mqtt.publish('bridge/log', stringify({type: `device_announced`, message: 'announce', meta}));
} else {
/* istanbul ignore else */
if (type === 'deviceLeave') {
@@ -438,7 +439,7 @@ class BridgeLegacy extends Extension {
const meta = {friendly_name: name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `device_removed`, message: 'left_network', meta}),
stringify({type: `device_removed`, message: 'left_network', meta}),
);
}
}
@@ -448,7 +449,7 @@ class BridgeLegacy extends Extension {
logger.info('Starting touchlink factory reset...');
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `touchlink`, message: 'reset_started', meta: {status: 'started'}}),
stringify({type: `touchlink`, message: 'reset_started', meta: {status: 'started'}}),
);
const result = await this.zigbee.touchlinkFactoryReset();
@@ -456,13 +457,13 @@ class BridgeLegacy extends Extension {
logger.info('Successfully factory reset device through Touchlink');
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `touchlink`, message: 'reset_success', meta: {status: 'success'}}),
stringify({type: `touchlink`, message: 'reset_success', meta: {status: 'success'}}),
);
} else {
logger.warn('Failed to factory reset device through Touchlink');
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `touchlink`, message: 'reset_failed', meta: {status: 'failed'}}),
stringify({type: `touchlink`, message: 'reset_failed', meta: {status: 'failed'}}),
);
}
}
+4 -3
View File
@@ -2,6 +2,7 @@ const settings = require('../util/settings');
const utils = require('../util/utils');
const logger = require('../util/logger');
const Extension = require('./extension');
const stringify = require('json-stable-stringify');
/**
* This extension creates a network map
@@ -65,19 +66,19 @@ class NetworkMap extends Extension {
const value = this.supportedFormats[type](topology);
await this.mqtt.publish(
'bridge/response/networkmap',
JSON.stringify(utils.getResponse(message, {routes, type, value}, null)),
stringify(utils.getResponse(message, {routes, type, value}, null)),
);
} catch (error) {
await this.mqtt.publish(
'bridge/response/networkmap',
JSON.stringify(utils.getResponse(message, {}, error.message)),
stringify(utils.getResponse(message, {}, error.message)),
);
}
}
}
raw(topology) {
return JSON.stringify(topology);
return stringify(topology);
}
graphviz(topology) {
+12 -11
View File
@@ -1,5 +1,6 @@
const settings = require('../util/settings');
const logger = require('../util/logger');
const stringify = require('json-stable-stringify');
const utils = require('../util/utils');
const legacyTopicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/ota_update/.+$`);
const topicRegex =
@@ -67,7 +68,7 @@ class OTAUpdate extends Extension {
const meta = {status: 'available', device: resolvedEntity.settings.friendly_name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `ota_update`, message, meta}),
stringify({type: `ota_update`, message, meta}),
);
}
}
@@ -140,7 +141,7 @@ class OTAUpdate extends Extension {
const meta = {status: `not_supported`, device: resolvedEntity.name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `ota_update`, message: error, meta}),
stringify({type: `ota_update`, message: error, meta}),
);
}
} else if (this.inProgress.has(resolvedEntity.device.ieeeAddr)) {
@@ -157,7 +158,7 @@ class OTAUpdate extends Extension {
const meta = {status: `checking_if_available`, device: resolvedEntity.name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `ota_update`, message: msg, meta}),
stringify({type: `ota_update`, message: msg, meta}),
);
}
@@ -173,7 +174,7 @@ class OTAUpdate extends Extension {
const meta = {status: available ? 'available' : 'not_available', device: resolvedEntity.name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `ota_update`, message: msg, meta}),
stringify({type: `ota_update`, message: msg, meta}),
);
}
@@ -189,7 +190,7 @@ class OTAUpdate extends Extension {
const meta = {status: `check_failed`, device: resolvedEntity.name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `ota_update`, message: error, meta}),
stringify({type: `ota_update`, message: error, meta}),
);
}
}
@@ -202,7 +203,7 @@ class OTAUpdate extends Extension {
const meta = {status: `update_in_progress`, device: resolvedEntity.name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `ota_update`, msg, meta}),
stringify({type: `ota_update`, msg, meta}),
);
}
@@ -221,14 +222,14 @@ class OTAUpdate extends Extension {
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `update_progress`, device: resolvedEntity.name, progress};
this.mqtt.publish('bridge/log', JSON.stringify({type: `ota_update`, message: msg, meta}));
this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: msg, meta}));
}
};
const from_ = await this.readSoftwareBuildIDAndDateCode(resolvedEntity.device, false);
await resolvedEntity.definition.ota.updateToLatest(resolvedEntity.device, logger, onProgress);
const to = await this.readSoftwareBuildIDAndDateCode(resolvedEntity.device, true);
const [fromS, toS] = [JSON.stringify(from_), JSON.stringify(to)];
const [fromS, toS] = [stringify(from_), stringify(to)];
const msg = `Finished update of '${resolvedEntity.name}'` +
(to ? `, from '${fromS}' to '${toS}'` : ``);
logger.info(msg);
@@ -241,7 +242,7 @@ class OTAUpdate extends Extension {
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `update_succeeded`, device: resolvedEntity.name, from: from_, to};
this.mqtt.publish('bridge/log', JSON.stringify({type: `ota_update`, message, meta}));
this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message, meta}));
}
} catch (e) {
error = `Update of '${resolvedEntity.name}' failed (${e.message})`;
@@ -253,7 +254,7 @@ class OTAUpdate extends Extension {
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
const meta = {status: `update_failed`, device: resolvedEntity.name};
this.mqtt.publish('bridge/log', JSON.stringify({type: `ota_update`, message: error, meta}));
this.mqtt.publish('bridge/log', stringify({type: `ota_update`, message: error, meta}));
}
}
}
@@ -264,7 +265,7 @@ class OTAUpdate extends Extension {
const triggeredViaLegacyApi = topic.match(legacyTopicRegex);
if (!triggeredViaLegacyApi) {
const response = utils.getResponse(message, responseData, error);
await this.mqtt.publish(`bridge/response/device/ota_update/${type}`, JSON.stringify(response));
await this.mqtt.publish(`bridge/response/device/ota_update/${type}`, stringify(response));
}
if (error) {
+3 -2
View File
@@ -5,6 +5,7 @@ const logger = require('../util/logger');
const utils = require('../util/utils');
const assert = require('assert');
const Extension = require('./extension');
const stringify = require('json-stable-stringify');
const topicRegex = new RegExp(`^(.+?)(?:/(${utils.getEndpointNames().join('|')}))?/(get|set)(?:/(.+))?`);
@@ -64,7 +65,7 @@ class EntityPublish extends Extension {
const message = {friendly_name: entityKey};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `entity_not_found`, message}),
stringify({type: `entity_not_found`, message}),
);
}
@@ -245,7 +246,7 @@ class EntityPublish extends Extension {
const meta = {friendly_name: resolvedEntity.name};
this.mqtt.publish(
'bridge/log',
JSON.stringify({type: `zigbee_publish_error`, message, meta}),
stringify({type: `zigbee_publish_error`, message, meta}),
);
}
}
+2 -1
View File
@@ -3,6 +3,7 @@ const logger = require('../util/logger');
const utils = require('../util/utils');
const debounce = require('debounce');
const Extension = require('./extension');
const stringify = require('json-stable-stringify');
class Receive extends Extension {
constructor(zigbee, mqtt, state, publishEntityState, eventBus) {
@@ -125,7 +126,7 @@ class Receive extends Extension {
if (!converters.length && data.cluster !== 'genOta' && data.cluster !== 'genTime') {
logger.debug(
`No converter available for '${resolvedEntity.definition.model}' with cluster '${data.cluster}' ` +
`and type '${data.type}' and data '${JSON.stringify(data.data)}'`,
`and type '${data.type}' and data '${stringify(data.data)}'`,
);
return;
}
+2 -1
View File
@@ -3,6 +3,7 @@ const data = require('./util/data');
const settings = require('./util/settings');
const fs = require('fs');
const objectAssignDeep = require('object-assign-deep');
const stringify = require('json-stable-stringify');
const saveInterval = 1000 * 60 * 5; // 5 minutes
@@ -56,7 +57,7 @@ class State {
save() {
if (settings.get().advanced.cache_state_persistent) {
logger.debug(`Saving state to file ${this.file}`);
const json = JSON.stringify(this.state, null, 4);
const json = stringify(this.state, null, 4);
fs.writeFileSync(this.file, json, 'utf8');
} else {
logger.debug(`Not saving state`);
+4 -3
View File
@@ -7,6 +7,7 @@ const utils = require('./util/utils');
const events = require('events');
const objectAssignDeep = require('object-assign-deep');
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const stringify = require('json-stable-stringify');
const endpointNames = utils.getEndpointNames();
const keyEndpointByNumber = new RegExp(`.*/([0-9]*)$`);
@@ -42,7 +43,7 @@ class Zigbee extends events.EventEmitter {
const herdsmanSettingsLog = objectAssignDeep.noMutate(herdsmanSettings);
herdsmanSettingsLog.network.networkKey = 'HIDDEN';
logger.debug(`Using zigbee-herdsman with settings: '${JSON.stringify(herdsmanSettingsLog)}'`);
logger.debug(`Using zigbee-herdsman with settings: '${stringify(herdsmanSettingsLog)}'`);
if (herdsmanSettings.network.networkKey === 'GENERATE') {
const newKey = Array.from({length: 16}, () => Math.floor(Math.random() * 255));
@@ -67,8 +68,8 @@ class Zigbee extends events.EventEmitter {
this.herdsman.on('message', (data) => this.emit('event', 'message', data));
logger.info('zigbee-herdsman started');
logger.info(`Coordinator firmware version: '${JSON.stringify(await this.getCoordinatorVersion())}'`);
logger.debug(`Zigbee network parameters: ${JSON.stringify(await this.herdsman.getNetworkParameters())}`);
logger.info(`Coordinator firmware version: '${stringify(await this.getCoordinatorVersion())}'`);
logger.debug(`Zigbee network parameters: ${stringify(await this.herdsman.getNetworkParameters())}`);
for (const device of this.getClients()) {
// If a passlist is used, all other device will be removed from the network.
+329 -353
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -39,6 +39,7 @@
"git-last-commit": "^1.0.0",
"humanize-duration": "^3.23.1",
"js-yaml": "^3.14.0",
"json-stable-stringify": "^1.0.1",
"mkdir-recursive": "^0.4.0",
"moment": "^2.27.0",
"mqtt": "^4.1.0",
+27 -26
View File
@@ -5,6 +5,7 @@ const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const stringify = require('json-stable-stringify');
describe('Bind', () => {
let controller;
@@ -43,7 +44,7 @@ describe('Bind', () => {
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
const endpoint = device.getEndpoint(1);
mockClear(device);
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', JSON.stringify({from: 'remote', to: 'bulb_color'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'bulb_color'}));
await flushPromises();
expect(endpoint.bind).toHaveBeenCalledTimes(3);
expect(endpoint.bind).toHaveBeenCalledWith("genOnOff", target);
@@ -51,7 +52,7 @@ describe('Bind', () => {
expect(endpoint.bind).toHaveBeenCalledWith("genScenes", target);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/bind',
JSON.stringify({"data":{"from":"remote","to":"bulb_color","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
stringify({"data":{"from":"remote","to":"bulb_color","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -61,13 +62,13 @@ describe('Bind', () => {
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
const endpoint = device.getEndpoint(1);
mockClear(device);
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', JSON.stringify({from: 'remote', to: 'bulb_color', clusters: ["genOnOff"]}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'bulb_color', clusters: ["genOnOff"]}));
await flushPromises();
expect(endpoint.bind).toHaveBeenCalledTimes(1);
expect(endpoint.bind).toHaveBeenCalledWith("genOnOff", target);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/bind',
JSON.stringify({"data":{"from":"remote","to":"bulb_color","clusters":["genOnOff"],"failed":[]},"status":"ok"}),
stringify({"data":{"from":"remote","to":"bulb_color","clusters":["genOnOff"],"failed":[]},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -77,12 +78,12 @@ describe('Bind', () => {
const endpoint = device.getEndpoint(1);
mockClear(device);
logger.error.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', JSON.stringify({from: 'remote', to: 'button'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'button'}));
await flushPromises();
expect(endpoint.bind).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/bind',
JSON.stringify({"data":{"from":"remote","to":"button","clusters":[],"failed":[]},"status":"error","error":"Nothing to bind"}),
stringify({"data":{"from":"remote","to":"button","clusters":[],"failed":[]},"status":"error","error":"Nothing to bind"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -92,7 +93,7 @@ describe('Bind', () => {
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
const endpoint = device.getEndpoint(1);
mockClear(device);
MQTT.events.message('zigbee2mqtt/bridge/request/device/unbind', JSON.stringify({from: 'remote', to: 'bulb_color'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/unbind', stringify({from: 'remote', to: 'bulb_color'}));
await flushPromises();
expect(endpoint.unbind).toHaveBeenCalledTimes(3);
expect(endpoint.unbind).toHaveBeenCalledWith("genOnOff", target);
@@ -100,7 +101,7 @@ describe('Bind', () => {
expect(endpoint.unbind).toHaveBeenCalledWith("genScenes", target);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/unbind',
JSON.stringify({"data":{"from":"remote","to":"bulb_color","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
stringify({"data":{"from":"remote","to":"bulb_color","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -111,7 +112,7 @@ describe('Bind', () => {
const endpoint = device.getEndpoint(1);
mockClear(device);
endpoint.unbind.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/unbind', JSON.stringify({from: 'remote', to: 'Coordinator'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/unbind', stringify({from: 'remote', to: 'Coordinator'}));
await flushPromises();
expect(endpoint.unbind).toHaveBeenCalledTimes(3);
expect(endpoint.unbind).toHaveBeenCalledWith("genOnOff", target);
@@ -119,7 +120,7 @@ describe('Bind', () => {
expect(endpoint.unbind).toHaveBeenCalledWith("genScenes", target);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/unbind',
JSON.stringify({"data":{"from":"remote","to":"Coordinator","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
stringify({"data":{"from":"remote","to":"Coordinator","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -129,7 +130,7 @@ describe('Bind', () => {
const target = zigbeeHerdsman.groups.group_1;
const endpoint = device.getEndpoint(1);
mockClear(device);
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', JSON.stringify({from: 'remote', to: 'group_1'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'group_1'}));
await flushPromises();
expect(endpoint.bind).toHaveBeenCalledTimes(3);
expect(endpoint.bind).toHaveBeenCalledWith("genOnOff", target);
@@ -137,7 +138,7 @@ describe('Bind', () => {
expect(endpoint.bind).toHaveBeenCalledWith("genScenes", target);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/bind',
JSON.stringify({"data":{"from":"remote","to":"group_1","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
stringify({"data":{"from":"remote","to":"group_1","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -147,7 +148,7 @@ describe('Bind', () => {
const target = zigbeeHerdsman.groups.group_1;
const endpoint = device.getEndpoint(1);
mockClear(device);
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', JSON.stringify({from: 'remote', to: '1'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: '1'}));
await flushPromises();
expect(endpoint.bind).toHaveBeenCalledTimes(3);
expect(endpoint.bind).toHaveBeenCalledWith("genOnOff", target);
@@ -155,7 +156,7 @@ describe('Bind', () => {
expect(endpoint.bind).toHaveBeenCalledWith("genScenes", target);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/bind',
JSON.stringify({"data":{"from":"remote","to":"1","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
stringify({"data":{"from":"remote","to":"1","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -166,12 +167,12 @@ describe('Bind', () => {
const endpoint = device.getEndpoint(1);
mockClear(device);
endpoint.bind.mockImplementation(() => {throw new Error('failed')});
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', JSON.stringify({from: 'remote', to: 'bulb_color'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'bulb_color'}));
await flushPromises();
expect(endpoint.bind).toHaveBeenCalledTimes(3);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/bind',
JSON.stringify({"data":{"from":"remote","to":"bulb_color","clusters":[],"failed":["genScenes","genOnOff","genLevelCtrl"]},"status":"error","error":"Failed to bind"}),
stringify({"data":{"from":"remote","to":"bulb_color","clusters":[],"failed":["genScenes","genOnOff","genLevelCtrl"]},"status":"error","error":"Failed to bind"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -181,13 +182,13 @@ describe('Bind', () => {
const target = zigbeeHerdsman.devices.QBKG03LM.getEndpoint(3);
const endpoint = device.getEndpoint(2);
mockClear(device);
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', JSON.stringify({from: 'remote/ep2', to: 'wall_switch_double/right'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote/ep2', to: 'wall_switch_double/right'}));
await flushPromises();
expect(endpoint.bind).toHaveBeenCalledTimes(1);
expect(endpoint.bind).toHaveBeenCalledWith("genOnOff", target);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/bind',
JSON.stringify({"data":{"from":"remote/ep2","to":"wall_switch_double/right","clusters":["genOnOff"],"failed":[]},"status":"ok"}),
stringify({"data":{"from":"remote/ep2","to":"wall_switch_double/right","clusters":["genOnOff"],"failed":[]},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -197,13 +198,13 @@ describe('Bind', () => {
const target = zigbeeHerdsman.devices.QBKG04LM.getEndpoint(2);
const endpoint = device.getEndpoint(2);
mockClear(device);
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', JSON.stringify({from: 'remote/ep2', to: 'wall_switch'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote/ep2', to: 'wall_switch'}));
await flushPromises();
expect(endpoint.bind).toHaveBeenCalledTimes(1);
expect(endpoint.bind).toHaveBeenCalledWith("genOnOff", target);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/bind',
JSON.stringify({"data":{"from":"remote/ep2","to":"wall_switch","clusters":["genOnOff"],"failed":[]},"status":"ok"}),
stringify({"data":{"from":"remote/ep2","to":"wall_switch","clusters":["genOnOff"],"failed":[]},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -213,7 +214,7 @@ describe('Bind', () => {
const target = 'default_bind_group';
const endpoint = device.getEndpoint(1);
mockClear(device);
MQTT.events.message('zigbee2mqtt/bridge/request/device/unbind', JSON.stringify({from: 'remote', to: target}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/unbind', stringify({from: 'remote', to: target}));
await flushPromises();
expect(endpoint.unbind).toHaveBeenCalledTimes(3);
expect(endpoint.unbind).toHaveBeenCalledWith("genOnOff", 901);
@@ -221,7 +222,7 @@ describe('Bind', () => {
expect(endpoint.unbind).toHaveBeenCalledWith("genScenes", 901);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/unbind',
JSON.stringify({"data":{"from":"remote","to":"default_bind_group","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
stringify({"data":{"from":"remote","to":"default_bind_group","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -231,11 +232,11 @@ describe('Bind', () => {
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
const endpoint = device.getEndpoint(1);
mockClear(device);
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', JSON.stringify({from: 'remote_not_existing', to: 'bulb_color'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote_not_existing', to: 'bulb_color'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/bind',
JSON.stringify({"data":{"from":"remote_not_existing","to":"bulb_color"},"status":"error","error":"Source device 'remote_not_existing' does not exist"}),
stringify({"data":{"from":"remote_not_existing","to":"bulb_color"},"status":"error","error":"Source device 'remote_not_existing' does not exist"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -245,11 +246,11 @@ describe('Bind', () => {
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
const endpoint = device.getEndpoint(1);
mockClear(device);
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', JSON.stringify({from: 'remote', to: 'bulb_color_not_existing'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'bulb_color_not_existing'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/bind',
JSON.stringify({"data":{"from":"remote","to":"bulb_color_not_existing"},"status":"error","error":"Target device or group 'bulb_color_not_existing' does not exist"}),
stringify({"data":{"from":"remote","to":"bulb_color_not_existing"},"status":"error","error":"Target device or group 'bulb_color_not_existing' does not exist"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
+74 -73
View File
@@ -5,6 +5,7 @@ const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const stringify = require('json-stable-stringify');
const {coordinator, bulb, unsupported, WXKG11LM} = zigbeeHerdsman.devices;
zigbeeHerdsman.returnDevices.push(coordinator.ieeeAddr);
@@ -38,7 +39,7 @@ describe('Bridge', () => {
const version = await require('../lib/util/utils').getZigbee2mqttVersion();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/info',
JSON.stringify({"version":version.version,"commit":version.commitHash,"coordinator":{"type":"z-Stack","meta":{"version":1,"revision":20190425}},"network":{"channel":15,"pan_id":5674,"extended_pan_id":[0,11,22]},"log_level":"info","permit_join":false}),
stringify({"version":version.version,"commit":version.commitHash,"coordinator":{"type":"z-Stack","meta":{"version":1,"revision":20190425}},"network":{"channel":15,"pan_id":5674,"extended_pan_id":[0,11,22]},"log_level":"info","permit_join":false}),
{ retain: true, qos: 0 },
expect.any(Function)
);
@@ -47,7 +48,7 @@ describe('Bridge', () => {
it('Should publish devices on startup', async () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/devices',
JSON.stringify([{"ieee_address":"0x000b57fffec6a5b2","type":"Router","network_address":40369,"supported":true,"friendly_name":"bulb","definition":{"model":"LED1545G12","vendor":"IKEA","description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","supports":"on/off, brightness, color temperature"},"power_source":"Mains (single phase)","date_code":null,"interviewing":false,"interview_completed":true},{"ieee_address":"0x0017880104e45518","type":"EndDevice","network_address":6536,"supported":false,"friendly_name":"0x0017880104e45518","definition":null,"power_source":"Battery","date_code":null,"interviewing":false,"interview_completed":true},{"ieee_address":"0x0017880104e45520","type":"EndDevice","network_address":6537,"supported":true,"friendly_name":"button","definition":{"model":"WXKG11LM","vendor":"Xiaomi","description":"Aqara wireless switch","supports":"single, double click (and triple, quadruple, hold, release depending on model)"},"power_source":"Battery","date_code":null,"interviewing":false,"interview_completed":true}]),
stringify([{"ieee_address":"0x000b57fffec6a5b2","type":"Router","network_address":40369,"supported":true,"friendly_name":"bulb","definition":{"model":"LED1545G12","vendor":"IKEA","description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","supports":"on/off, brightness, color temperature"},"power_source":"Mains (single phase)","date_code":null,"interviewing":false,"interview_completed":true},{"ieee_address":"0x0017880104e45518","type":"EndDevice","network_address":6536,"supported":false,"friendly_name":"0x0017880104e45518","definition":null,"power_source":"Battery","date_code":null,"interviewing":false,"interview_completed":true},{"ieee_address":"0x0017880104e45520","type":"EndDevice","network_address":6537,"supported":true,"friendly_name":"button","definition":{"model":"WXKG11LM","vendor":"Xiaomi","description":"Aqara wireless switch","supports":"single, double click (and triple, quadruple, hold, release depending on model)"},"power_source":"Battery","date_code":null,"interviewing":false,"interview_completed":true}]),
{ retain: true, qos: 0 },
expect.any(Function)
);
@@ -60,7 +61,7 @@ describe('Bridge', () => {
logger.info("this is a test");
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/logging',
JSON.stringify({message: 'this is a test', level: 'info'}),
stringify({message: 'this is a test', level: 'info'}),
{ retain: false, qos: 0 },
expect.any(Function)
);
@@ -83,7 +84,7 @@ describe('Bridge', () => {
logger.setTransportsEnabled(true);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/groups',
JSON.stringify([{"id":1,"friendly_name":"group_1","members":[]},{"id":15071,"friendly_name":"group_tradfri_remote","members":[]},{"id":99,"friendly_name":99,"members":[]},{"id":11,"friendly_name":"group_with_tradfri","members":[]},{"id":2,"friendly_name":"group_2","members":[]}]),
stringify([{"id":1,"friendly_name":"group_1","members":[]},{"id":15071,"friendly_name":"group_tradfri_remote","members":[]},{"id":99,"friendly_name":99,"members":[]},{"id":11,"friendly_name":"group_with_tradfri","members":[]},{"id":2,"friendly_name":"group_2","members":[]}]),
{ retain: true, qos: 0 },
expect.any(Function)
);
@@ -96,7 +97,7 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/event',
JSON.stringify({"type":"device_joined","data":{"friendly_name":"bulb","ieee_address":"0x000b57fffec6a5b2"}}),
stringify({"type":"device_joined","data":{"friendly_name":"bulb","ieee_address":"0x000b57fffec6a5b2"}}),
{ retain: false, qos: 0 },
expect.any(Function)
);
@@ -109,7 +110,7 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/event',
JSON.stringify({"type":"device_interview","data":{"friendly_name":"bulb","status":"started","ieee_address":"0x000b57fffec6a5b2"}}),
stringify({"type":"device_interview","data":{"friendly_name":"bulb","status":"started","ieee_address":"0x000b57fffec6a5b2"}}),
{ retain: false, qos: 0 },
expect.any(Function)
);
@@ -122,7 +123,7 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/event',
JSON.stringify({"type":"device_interview","data":{"friendly_name":"bulb","status":"failed","ieee_address":"0x000b57fffec6a5b2"}}),
stringify({"type":"device_interview","data":{"friendly_name":"bulb","status":"failed","ieee_address":"0x000b57fffec6a5b2"}}),
{ retain: false, qos: 0 },
expect.any(Function)
);
@@ -142,13 +143,13 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(4);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/event',
JSON.stringify({"type":"device_interview","data":{"friendly_name":"bulb","status":"successful","ieee_address":"0x000b57fffec6a5b2","supported":true,"definition":{"model":"LED1545G12","vendor":"IKEA","description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","supports":"on/off, brightness, color temperature"}}}),
stringify({"type":"device_interview","data":{"friendly_name":"bulb","status":"successful","ieee_address":"0x000b57fffec6a5b2","supported":true,"definition":{"model":"LED1545G12","vendor":"IKEA","description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","supports":"on/off, brightness, color temperature"}}}),
{ retain: false, qos: 0 },
expect.any(Function)
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/event',
JSON.stringify({"type":"device_interview","data":{"friendly_name":"0x0017880104e45518","status":"successful","ieee_address":"0x0017880104e45518","supported":false,"definition":null}}),
stringify({"type":"device_interview","data":{"friendly_name":"0x0017880104e45518","status":"successful","ieee_address":"0x0017880104e45518","supported":false,"definition":null}}),
{ retain: false, qos: 0 },
expect.any(Function)
);
@@ -167,7 +168,7 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/event',
JSON.stringify({"type":"device_leave","data":{"ieee_address":"0x000b57fffec6a5b2"}}),
stringify({"type":"device_leave","data":{"ieee_address":"0x000b57fffec6a5b2"}}),
{ retain: false, qos: 0 },
expect.any(Function)
);
@@ -189,31 +190,31 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), { retain: true, qos: 0 }, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
JSON.stringify({"data":{"value":true},"status":"ok"}),
stringify({"data":{"value":true},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
zigbeeHerdsman.permitJoin.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', JSON.stringify({"value": false}));
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({"value": false}));
await flushPromises();
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledTimes(1);
expect(zigbeeHerdsman.permitJoin).toHaveBeenCalledWith(false);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), { retain: true, qos: 0 }, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
JSON.stringify({"data":{"value":false},"status":"ok"}),
stringify({"data":{"value":false},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should put transaction in response when request is done with transaction', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', JSON.stringify({"value": false, "transaction": 22}));
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({"value": false, "transaction": 22}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
JSON.stringify({"data":{"value":false},"status":"ok", "transaction": 22}),
stringify({"data":{"value":false},"status":"ok", "transaction": 22}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -221,29 +222,29 @@ describe('Bridge', () => {
it('Should put error in response when request fails', async () => {
zigbeeHerdsman.permitJoin.mockImplementationOnce(() => {throw new Error('Failed to connect to adapter')});
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', JSON.stringify({"value": false}));
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({"value": false}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
JSON.stringify({"data":{},"status":"error","error": "Failed to connect to adapter"}),
stringify({"data":{},"status":"error","error": "Failed to connect to adapter"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should put error in response when format is incorrect', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', JSON.stringify({"value_not_good": false}));
MQTT.events.message('zigbee2mqtt/bridge/request/permit_join', stringify({"value_not_good": false}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/permit_join',
JSON.stringify({"data":{},"status":"error","error": "No value given"}),
stringify({"data":{},"status":"error","error": "No value given"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Coverage satisfaction', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/random', JSON.stringify({"value": false}));
MQTT.events.message('zigbee2mqtt/bridge/request/random', stringify({"value": false}));
const device = zigbeeHerdsman.devices.bulb;
await zigbeeHerdsman.events.message({data: {onOff: 1}, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10});
await flushPromises();
@@ -255,7 +256,7 @@ describe('Bridge', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/health_check',
JSON.stringify({"data":{"healthy": true},"status":"ok"}),
stringify({"data":{"healthy": true},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -273,7 +274,7 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/remove',
JSON.stringify({"data":{"id": "bulb","block":false,"force":false},"status":"ok"}),
stringify({"data":{"id": "bulb","block":false,"force":false},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
expect(settings.get().blocklist).toStrictEqual([]);
@@ -282,7 +283,7 @@ describe('Bridge', () => {
it('Should allow to remove device by object ID', async () => {
const device = zigbeeHerdsman.devices.bulb;
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', JSON.stringify({id: "bulb"}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: "bulb"}));
await flushPromises();
expect(device.removeFromNetwork).toHaveBeenCalledTimes(1);
expect(device.removeFromDatabase).not.toHaveBeenCalled();
@@ -290,7 +291,7 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/remove',
JSON.stringify({"data":{"id": "bulb","block":false,"force":false},"status":"ok"}),
stringify({"data":{"id": "bulb","block":false,"force":false},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -298,7 +299,7 @@ describe('Bridge', () => {
it('Should allow to force remove device', async () => {
const device = zigbeeHerdsman.devices.bulb;
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', JSON.stringify({id: "bulb", force: true}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: "bulb", force: true}));
await flushPromises();
expect(device.removeFromDatabase).toHaveBeenCalledTimes(1);
expect(device.removeFromNetwork).not.toHaveBeenCalled();
@@ -306,7 +307,7 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/remove',
JSON.stringify({"data":{"id": "bulb","block":false,"force":true},"status":"ok"}),
stringify({"data":{"id": "bulb","block":false,"force":true},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -314,14 +315,14 @@ describe('Bridge', () => {
it('Should allow to block device', async () => {
const device = zigbeeHerdsman.devices.bulb;
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', JSON.stringify({id: "bulb", block: true, force: true}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: "bulb", block: true, force: true}));
await flushPromises();
expect(device.removeFromDatabase).toHaveBeenCalledTimes(1);
expect(settings.getDevice('bulb')).toBeNull();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/remove',
JSON.stringify({"data":{"id": "bulb","block":true,"force":true},"status":"ok"}),
stringify({"data":{"id": "bulb","block":true,"force":true},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
expect(settings.get().blocklist).toStrictEqual(["0x000b57fffec6a5b2"]);
@@ -337,7 +338,7 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/remove',
JSON.stringify({"data":{"id": "group_1", "force": false},"status":"ok"}),
stringify({"data":{"id": "group_1", "force": false},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -345,14 +346,14 @@ describe('Bridge', () => {
it('Should allow to force remove group', async () => {
const group = zigbeeHerdsman.groups.group_1;
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/remove', JSON.stringify({id: "group_1", force: true}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/remove', stringify({id: "group_1", force: true}));
await flushPromises();
expect(group.removeFromDatabase).toHaveBeenCalledTimes(1);
expect(settings.getGroup('group_1')).toBeNull();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/remove',
JSON.stringify({"data":{"id": "group_1", "force": true},"status":"ok"}),
stringify({"data":{"id": "group_1", "force": true},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -360,11 +361,11 @@ describe('Bridge', () => {
it('Should throw error on removing non-existing device', async () => {
const device = zigbeeHerdsman.devices.bulb;
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', JSON.stringify({id: "non-existing-device"}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: "non-existing-device"}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/remove',
JSON.stringify({"data":{},"status":"error","error":"Device 'non-existing-device' does not exist"}),
stringify({"data":{},"status":"error","error":"Device 'non-existing-device' does not exist"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -373,61 +374,61 @@ describe('Bridge', () => {
const device = zigbeeHerdsman.devices.bulb;
MQTT.publish.mockClear();
device.removeFromNetwork.mockImplementationOnce(() => {throw new Error('device timeout')})
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', JSON.stringify({id: "bulb"}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: "bulb"}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/remove',
JSON.stringify({"data":{},"status":"error","error":"Failed to remove device 'bulb' (block: false, force: false) (Error: device timeout)"}),
stringify({"data":{},"status":"error","error":"Failed to remove device 'bulb' (block: false, force: false) (Error: device timeout)"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should allow rename device', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', JSON.stringify({from: 'bulb', to: 'bulb_new_name'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb', to: 'bulb_new_name'}));
await flushPromises();
expect(settings.getDevice('bulb')).toBeNull();
expect(settings.getDevice('bulb_new_name')).toStrictEqual({"ID": "0x000b57fffec6a5b2", "friendly_name": "bulb_new_name", "friendlyName": "bulb_new_name", "retain": true});
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/rename',
JSON.stringify({"data":{"from":"bulb","to":"bulb_new_name"},"status":"ok"}),
stringify({"data":{"from":"bulb","to":"bulb_new_name"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should allow rename group', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/rename', JSON.stringify({from: 'group_1', to: 'group_new_name'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/rename', stringify({from: 'group_1', to: 'group_new_name'}));
await flushPromises();
expect(settings.getGroup('group_1')).toBeNull();
expect(settings.getGroup('group_new_name')).toStrictEqual({"ID": 1, "devices": [], "friendly_name": "group_new_name", "friendlyName": "group_new_name", "optimistic": true, "retain": false});
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/rename',
JSON.stringify({"data":{"from":"group_1","to":"group_new_name"},"status":"ok"}),
stringify({"data":{"from":"group_1","to":"group_new_name"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should throw error on invalid device rename payload', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', JSON.stringify({from_bla: 'bulb', to: 'bulb_new_name'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({from_bla: 'bulb', to: 'bulb_new_name'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/rename',
JSON.stringify({"data":{},"status":"error","error":"Invalid payload"}),
stringify({"data":{},"status":"error","error":"Invalid payload"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should throw error on non-existing device rename', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', JSON.stringify({from: 'bulb_not_existing', to: 'bulb_new_name'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb_not_existing', to: 'bulb_new_name'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/rename',
JSON.stringify({"data":{},"status":"error","error":"Device 'bulb_not_existing' does not exist"}),
stringify({"data":{},"status":"error","error":"Device 'bulb_not_existing' does not exist"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -435,25 +436,25 @@ describe('Bridge', () => {
it('Should allow to rename last joined device', async () => {
MQTT.publish.mockClear();
await zigbeeHerdsman.events.deviceJoined({device: zigbeeHerdsman.devices.bulb});
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', JSON.stringify({last: true, to: 'bulb_new_name'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({last: true, to: 'bulb_new_name'}));
await flushPromises();
expect(settings.getDevice('bulb')).toBeNull();
expect(settings.getDevice('bulb_new_name')).toStrictEqual({"ID": "0x000b57fffec6a5b2", "friendly_name": "bulb_new_name", "friendlyName": "bulb_new_name", "retain": true});
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/rename',
JSON.stringify({"data":{"from":"bulb","to":"bulb_new_name"},"status":"ok"}),
stringify({"data":{"from":"bulb","to":"bulb_new_name"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should throw error when renaming last joined device but none has joined', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', JSON.stringify({last: true, to: 'bulb_new_name'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/rename', stringify({last: true, to: 'bulb_new_name'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/rename',
JSON.stringify({"data":{},"status":"error","error":"No device has joined since start"}),
stringify({"data":{},"status":"error","error":"No device has joined since start"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -461,12 +462,12 @@ describe('Bridge', () => {
it('Should allow change device options', async () => {
MQTT.publish.mockClear();
expect(settings.getDevice('bulb')).toStrictEqual({"ID": "0x000b57fffec6a5b2", "friendly_name": "bulb", "friendlyName": "bulb", "retain": true});
MQTT.events.message('zigbee2mqtt/bridge/request/device/options', JSON.stringify({options: {retain: false, transition: 1}, id: 'bulb'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/options', stringify({options: {retain: false, transition: 1}, id: 'bulb'}));
await flushPromises();
expect(settings.getDevice('bulb')).toStrictEqual({"ID": "0x000b57fffec6a5b2", "friendly_name": "bulb", "friendlyName": "bulb", "retain": false, "transition": 1});
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/options',
JSON.stringify({"data":{"from":{"retain": true},"to":{"retain": false,"transition":1}, "id":"bulb"},"status":"ok"}),
stringify({"data":{"from":{"retain": true},"to":{"retain": false,"transition":1}, "id":"bulb"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -474,23 +475,23 @@ describe('Bridge', () => {
it('Should allow change group options', async () => {
MQTT.publish.mockClear();
expect(settings.getGroup('group_1')).toStrictEqual({"ID": 1, "devices": [], "friendly_name": "group_1", "retain": false, "friendlyName": "group_1", "optimistic": true});
MQTT.events.message('zigbee2mqtt/bridge/request/group/options', JSON.stringify({options: {retain: true, transition: 1}, id: 'group_1'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/options', stringify({options: {retain: true, transition: 1}, id: 'group_1'}));
await flushPromises();
expect(settings.getGroup('group_1')).toStrictEqual({"ID": 1, "devices": [], "friendly_name": "group_1", "retain": true, "friendlyName": "group_1", "optimistic": true, "transition": 1});
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/options',
JSON.stringify({"data":{"from":{"optimistic": true,"retain": false},"to":{"optimistic": true,"retain": true,"transition":1}, "id":"group_1"},"status":"ok"}),
stringify({"data":{"from":{"optimistic": true,"retain": false},"to":{"optimistic": true,"retain": true,"transition":1}, "id":"group_1"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should throw error on invalid device change options payload', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/device/options', JSON.stringify({options_: {retain: true, transition: 1}, id: 'bulb'}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/options', stringify({options_: {retain: true, transition: 1}, id: 'bulb'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/options',
JSON.stringify({"data":{},"status":"error","error":"Invalid payload"}),
stringify({"data":{},"status":"error","error":"Invalid payload"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -503,31 +504,31 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/add',
JSON.stringify({"data":{"friendly_name":"group_193","id": 3},"status":"ok"}),
stringify({"data":{"friendly_name":"group_193","id": 3},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should allow to add group with ID', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/add', JSON.stringify({friendly_name: "group_193", id: 9}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/add', stringify({friendly_name: "group_193", id: 9}));
await flushPromises();
expect(settings.getGroup('group_193')).toStrictEqual({"ID": 9, "devices": [], "friendly_name": "group_193", "friendlyName": "group_193", "optimistic": true});
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/add',
JSON.stringify({"data":{"friendly_name":"group_193","id": 9},"status":"ok"}),
stringify({"data":{"friendly_name":"group_193","id": 9},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Should throw error when add with invalid payload', async () => {
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/add', JSON.stringify({friendly_name9: "group_193"}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/add', stringify({friendly_name9: "group_193"}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/add',
JSON.stringify({"data":{},"status":"error","error":"Invalid payload"}),
stringify({"data":{},"status":"error","error":"Invalid payload"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -542,21 +543,21 @@ describe('Bridge', () => {
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/button/action', 'single', {retain: false, qos: 0}, expect.any(Function));
// Disable when already disabled should go OK
MQTT.events.message('zigbee2mqtt/bridge/request/config/homeassistant', JSON.stringify({value: false}));
MQTT.events.message('zigbee2mqtt/bridge/request/config/homeassistant', stringify({value: false}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/config/homeassistant',
JSON.stringify({"data":{"value":false},"status":"ok"}),
stringify({"data":{"value":false},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
expect(settings.get().homeassistant).toBeFalsy();
// Enable
MQTT.events.message('zigbee2mqtt/bridge/request/config/homeassistant', JSON.stringify({value: true}));
MQTT.events.message('zigbee2mqtt/bridge/request/config/homeassistant', stringify({value: true}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/config/homeassistant',
JSON.stringify({"data":{"value":true},"status":"ok"}),
stringify({"data":{"value":true},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
expect(settings.get().homeassistant).toBeTruthy();
@@ -566,11 +567,11 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/button/action', 'single', {retain: false, qos: 0}, expect.any(Function));
// Disable
MQTT.events.message('zigbee2mqtt/bridge/request/config/homeassistant', JSON.stringify({value: false}));
MQTT.events.message('zigbee2mqtt/bridge/request/config/homeassistant', stringify({value: false}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/config/homeassistant',
JSON.stringify({"data":{"value":false},"status":"ok"}),
stringify({"data":{"value":false},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
expect(settings.get().homeassistant).toBeFalsy();
@@ -587,7 +588,7 @@ describe('Bridge', () => {
expect(settings.get().homeassistant).toBeFalsy();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/config/homeassistant',
JSON.stringify({"data":{},"status":"error","error":"'invalid_one' is not an allowed value, allowed: true,false"}),
stringify({"data":{},"status":"error","error":"'invalid_one' is not an allowed value, allowed: true,false"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -599,7 +600,7 @@ describe('Bridge', () => {
expect(settings.get().advanced.last_seen).toBe('ISO_8601');
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/config/last_seen',
JSON.stringify({"data":{"value":"ISO_8601"},"status":"ok"}),
stringify({"data":{"value":"ISO_8601"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -611,7 +612,7 @@ describe('Bridge', () => {
expect(settings.get().advanced.last_seen).toBe('disable');
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/config/last_seen',
JSON.stringify({"data":{},"status":"error","error":"'invalid_one' is not an allowed value, allowed: disable,ISO_8601,epoch,ISO_8601_local"}),
stringify({"data":{},"status":"error","error":"'invalid_one' is not an allowed value, allowed: disable,ISO_8601,epoch,ISO_8601_local"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -623,7 +624,7 @@ describe('Bridge', () => {
expect(settings.get().advanced.elapsed).toBe(true);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/config/elapsed',
JSON.stringify({"data":{"value":true},"status":"ok"}),
stringify({"data":{"value":true},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -635,7 +636,7 @@ describe('Bridge', () => {
expect(settings.get().advanced.elapsed).toBe(false);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/config/elapsed',
JSON.stringify({"data":{},"status":"error","error":"'not_valid' is not an allowed value, allowed: true,false"}),
stringify({"data":{},"status":"error","error":"'not_valid' is not an allowed value, allowed: true,false"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -648,7 +649,7 @@ describe('Bridge', () => {
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/config/log_level',
JSON.stringify({"data":{"value":'debug'},"status":"ok"}),
stringify({"data":{"value":'debug'},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -659,7 +660,7 @@ describe('Bridge', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/config/log_level',
JSON.stringify({"data":{},"status":"error","error":"'not_valid' is not an allowed value, allowed: error,warn,info,debug"}),
stringify({"data":{},"status":"error","error":"'not_valid' is not an allowed value, allowed: error,warn,info,debug"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -673,7 +674,7 @@ describe('Bridge', () => {
expect(zigbeeHerdsman.touchlinkFactoryReset).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/touchlink/factory_reset',
JSON.stringify({"data":{},"status":"ok"}),
stringify({"data":{},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -687,7 +688,7 @@ describe('Bridge', () => {
expect(zigbeeHerdsman.touchlinkFactoryReset).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/touchlink/factory_reset',
JSON.stringify({"data":{},"status":"error","error":"Failed to factory reset device through Touchlink"}),
stringify({"data":{},"status":"error","error":"Failed to factory reset device through Touchlink"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
+8 -7
View File
@@ -6,6 +6,7 @@ const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
const stringify = require('json-stable-stringify');
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
@@ -109,38 +110,38 @@ describe('Configure', () => {
expectRemoteConfigured();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/configure',
JSON.stringify({"data":{"id": "remote"},"status":"ok"}),
stringify({"data":{"id": "remote"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Fail to configure via MQTT when device does not exist', async () => {
await MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', JSON.stringify({id: "not_existing_device"}));
await MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: "not_existing_device"}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/configure',
JSON.stringify({"data":{"id": "not_existing_device"},"status":"error","error": "Device 'not_existing_device' does not exist"}),
stringify({"data":{"id": "not_existing_device"},"status":"error","error": "Device 'not_existing_device' does not exist"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Fail to configure via MQTT when configure fails', async () => {
zigbeeHerdsman.devices.remote.getEndpoint(1).bind.mockImplementationOnce(async () => {throw new Error('Bind timeout after 10s')});
await MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', JSON.stringify({id: "remote"}));
await MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: "remote"}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/configure',
JSON.stringify({"data":{"id": "remote"},"status":"error","error": "Failed to configure (Bind timeout after 10s)"}),
stringify({"data":{"id": "remote"},"status":"error","error": "Failed to configure (Bind timeout after 10s)"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
it('Fail to configure via MQTT when device has no configure', async () => {
await MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', JSON.stringify({id: "bulb", transaction: 20}));
await MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: "bulb", transaction: 20}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/configure',
JSON.stringify({"data":{"id": "bulb"},"status":"error","error": "Device 'bulb' cannot be configured","transaction":20}),
stringify({"data":{"id": "bulb"},"status":"error","error": "Device 'bulb' cannot be configured","transaction":20}),
{retain: false, qos: 0}, expect.any(Function)
);
});
+29 -28
View File
@@ -6,6 +6,7 @@ const path = require('path');
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const stringify = require('json-stable-stringify');
const flushPromises = () => new Promise(setImmediate);
const tmp = require('tmp');
const mocksClear = [
@@ -42,8 +43,8 @@ describe('Controller', () => {
expect(logger.info).toHaveBeenCalledWith('0x0017880104e45518 (0x0017880104e45518): Not supported (EndDevice)');
expect(MQTT.connect).toHaveBeenCalledTimes(1);
expect(MQTT.connect).toHaveBeenCalledWith("mqtt://localhost", {"will": {"payload": "offline", "retain": true, "topic": "zigbee2mqtt/bridge/state"}});
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', '{"state":"ON","brightness":50,"color_temp":370,"linkquality":99}',{ retain: true, qos: 0 }, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote', '{"brightness":255}', { retain: true, qos: 0 }, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99}),{ retain: true, qos: 0 }, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({"brightness":255}), { retain: true, qos: 0 }, expect.any(Function));
});
it('Start controller when permit join fails', async () => {
@@ -104,8 +105,8 @@ describe('Controller', () => {
data.writeDefaultState();
await controller.start();
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", `{"state":"ON","brightness":50,"color_temp":370,"linkquality":99}`, {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/remote", `{"brightness":255}`, {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99}), {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/remote", stringify({"brightness":255}), {"qos": 0, "retain": true}, expect.any(Function));
});
it('Start controller should not publish cached states when disabled', async () => {
@@ -148,7 +149,7 @@ describe('Controller', () => {
await flushPromises();
expect(logger.error).toHaveBeenCalledTimes(2);
expect(logger.error).toHaveBeenCalledWith("Not connected to MQTT server!");
expect(logger.error).toHaveBeenCalledWith("Cannot send message: topic: 'zigbee2mqtt/bulb', payload: '{\"state\":\"ON\",\"brightness\":50,\"color_temp\":370,\"linkquality\":99,\"color\":{\"r\":100,\"g\":50,\"b\":10},\"dummy\":{\"1\":\"yes\",\"2\":\"no\"}}");
expect(logger.error).toHaveBeenCalledWith("Cannot send message: topic: 'zigbee2mqtt/bulb', payload: '{\"brightness\":50,\"color\":{\"b\":10,\"g\":50,\"r\":100},\"color_temp\":370,\"dummy\":{\"1\":\"yes\",\"2\":\"no\"},\"linkquality\":99,\"state\":\"ON\"}");
controller.mqtt.client.reconnecting = false;
});
@@ -296,7 +297,7 @@ describe('Controller', () => {
const payload = {device};
await zigbeeHerdsman.events.deviceJoined(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bridge/log", '{"type":"device_connected","message":{"friendly_name":"bulb"}}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bridge/log", stringify({"type":"device_connected","message":{"friendly_name":"bulb"}}), {"retain": false, qos: 0}, expect.any(Function));
});
it('acceptJoiningDeviceHandler reject device on blocklist', async () => {
@@ -354,7 +355,7 @@ describe('Controller', () => {
zigbeeHerdsman.events.deviceJoined(payload);
zigbeeHerdsman.events.deviceJoined(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bridge/log", '{"type":"device_connected","message":{"friendly_name":"bulb"}}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bridge/log", stringify({"type":"device_connected","message":{"friendly_name":"bulb"}}), {"retain": false, qos: 0}, expect.any(Function));
});
it('On zigbee deviceInterview started', async () => {
@@ -363,7 +364,7 @@ describe('Controller', () => {
const payload = {device, status: 'started'};
await zigbeeHerdsman.events.deviceInterview(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', '{"type":"pairing","message":"interview_started","meta":{"friendly_name":"bulb"}}', { retain: false, qos: 0 }, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', stringify({"type":"pairing","message":"interview_started","meta":{"friendly_name":"bulb"}}), { retain: false, qos: 0 }, expect.any(Function));
});
it('On zigbee deviceInterview failed', async () => {
@@ -372,7 +373,7 @@ describe('Controller', () => {
const payload = {device, status: 'failed'};
await zigbeeHerdsman.events.deviceInterview(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', '{"type":"pairing","message":"interview_failed","meta":{"friendly_name":"bulb"}}', { retain: false, qos: 0 }, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', stringify({"type":"pairing","message":"interview_failed","meta":{"friendly_name":"bulb"}}), { retain: false, qos: 0 }, expect.any(Function));
});
it('On zigbee deviceInterview successful supported', async () => {
@@ -381,7 +382,7 @@ describe('Controller', () => {
const payload = {device, status: 'successful'};
await zigbeeHerdsman.events.deviceInterview(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', '{"type":"pairing","message":"interview_successful","meta":{"friendly_name":"bulb","model":"LED1545G12","vendor":"IKEA","description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","supported":true}}', { retain: false, qos: 0 }, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', stringify({"type":"pairing","message":"interview_successful","meta":{"friendly_name":"bulb","model":"LED1545G12","vendor":"IKEA","description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","supported":true}}), { retain: false, qos: 0 }, expect.any(Function));
});
it('On zigbee deviceInterview successful not supported', async () => {
@@ -390,7 +391,7 @@ describe('Controller', () => {
const payload = {device, status: 'successful'};
await zigbeeHerdsman.events.deviceInterview(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', '{"type":"pairing","message":"interview_successful","meta":{"friendly_name":"0x0017880104e45518","supported":false}}', { retain: false, qos: 0 }, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', stringify({"type":"pairing","message":"interview_successful","meta":{"friendly_name":"0x0017880104e45518","supported":false}}), { retain: false, qos: 0 }, expect.any(Function));
});
it('On zigbee event device announce', async () => {
@@ -400,7 +401,7 @@ describe('Controller', () => {
await zigbeeHerdsman.events.deviceAnnounce(payload);
await flushPromises();
expect(logger.debug).toHaveBeenCalledWith(`Device 'bulb' announced itself`);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', '{"type":"device_announced","message":"announce","meta":{"friendly_name":"bulb"}}', { retain: false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', stringify({"type":"device_announced","message":"announce","meta":{"friendly_name":"bulb"}}), { retain: false, qos: 0}, expect.any(Function));
});
it('On zigbee event device leave (removed from database and settings)', async () => {
@@ -412,7 +413,7 @@ describe('Controller', () => {
const payload = {ieeeAddr: device.ieeeAddr};
await zigbeeHerdsman.events.deviceLeave(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', '{"type":"device_removed","message":"left_network","meta":{"friendly_name":"0x000b57fffec6a5b2"}}', { retain: false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', stringify({"type":"device_removed","message":"left_network","meta":{"friendly_name":"0x000b57fffec6a5b2"}}), { retain: false, qos: 0}, expect.any(Function));
});
it('On zigbee event device leave (removed from database and NOT settings)', async () => {
@@ -423,7 +424,7 @@ describe('Controller', () => {
const payload = {ieeeAddr: device.ieeeAddr};
await zigbeeHerdsman.events.deviceLeave(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', '{"type":"device_removed","message":"left_network","meta":{"friendly_name":"0x000b57fffec6a5b2"}}', { retain: false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/log', stringify({"type":"device_removed","message":"left_network","meta":{"friendly_name":"0x000b57fffec6a5b2"}}), { retain: false, qos: 0}, expect.any(Function));
});
it('Publish entity state attribute output', async () => {
@@ -436,8 +437,8 @@ describe('Controller', () => {
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/brightness", "50", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/color_temp", "370", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/color", '100,50,10', {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/dummy-1", 'yes', {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/dummy-2", 'no', {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/dummy-1", "yes", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/dummy-2", "no", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/test1", '', {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/test", '', {"qos": 0, "retain": true}, expect.any(Function));
});
@@ -453,7 +454,7 @@ describe('Controller', () => {
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/brightness", "200", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/color_temp", "370", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/linkquality", "99", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", '{"state":"ON","brightness":200,"color_temp":370,"linkquality":99}', {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON","brightness":200,"color_temp":370,"linkquality":99}), {"qos": 0, "retain": true}, expect.any(Function));
});
@@ -467,7 +468,7 @@ describe('Controller', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(3);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/state", "ON", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/brightness", "200", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", '{"state":"ON","brightness":200}', {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON","brightness":200}), {"qos": 0, "retain": true}, expect.any(Function));
});
it('Publish entity state attribute_json output filtered (device_options)', async () => {
@@ -480,7 +481,7 @@ describe('Controller', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(3);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/state", "ON", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb/brightness", "200", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", '{"state":"ON","brightness":200}', {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON","brightness":200}), {"qos": 0, "retain": true}, expect.any(Function));
});
it('Publish entity state with device information', async () => {
@@ -489,12 +490,12 @@ describe('Controller', () => {
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON'});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', '{"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"device":{"friendlyName":"bulb","model":"LED1545G12","ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369,"type":"Router","manufacturerID":4476,"powerSource":"Mains (single phase)","dateCode":null}}', {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"device":{"friendlyName":"bulb","model":"LED1545G12","ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369,"type":"Router","manufacturerID":4476,"powerSource":"Mains (single phase)","dateCode":null}}), {"qos": 0, "retain": true}, expect.any(Function));
// Unsupported device should have model "unknown"
await controller.publishEntityState('unsupported2', {state: 'ON'});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/unsupported2', '{"state":"ON","device":{"friendlyName":"unsupported2","model":"unknown","ieeeAddr":"0x0017880104e45529","networkAddress":6536,"type":"EndDevice","manufacturerID":0,"powerSource":"Battery","dateCode":null}}', {"qos": 0, "retain": false}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/unsupported2', stringify({"state":"ON","device":{"friendlyName":"unsupported2","model":"unknown","ieeeAddr":"0x0017880104e45529","networkAddress":6536,"type":"EndDevice","manufacturerID":0,"powerSource":"Battery","dateCode":null}}), {"qos": 0, "retain": false}, expect.any(Function));
});
it('Should publish entity state without retain', async () => {
@@ -503,7 +504,7 @@ describe('Controller', () => {
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON'});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', '{"state":"ON","brightness":50,"color_temp":370,"linkquality":99}', {"qos": 0, "retain": false}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99}), {"qos": 0, "retain": false}, expect.any(Function));
});
it('Should publish entity state with retain', async () => {
@@ -512,7 +513,7 @@ describe('Controller', () => {
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON'});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', '{"state":"ON","brightness":50,"color_temp":370,"linkquality":99}', {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99}), {"qos": 0, "retain": true}, expect.any(Function));
});
it('Should publish entity state with expiring retention', async () => {
@@ -523,7 +524,7 @@ describe('Controller', () => {
MQTT.publish.mockClear();
await controller.publishEntityState('bulb', {state: 'ON'});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', '{"state":"ON","brightness":50,"color_temp":370,"linkquality":99}', {"qos": 0, "retain": true, "properties": {messageExpiryInterval: 37}}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99}), {"qos": 0, "retain": true, "properties": {messageExpiryInterval: 37}}, expect.any(Function));
});
it('Publish entity state no empty messages', async () => {
@@ -544,8 +545,8 @@ describe('Controller', () => {
await controller.publishEntityState('bulb', {brightness: 200});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", JSON.stringify({state: "ON"}), {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", JSON.stringify({state: "ON", brightness: 200}), {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({state: "ON"}), {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({state: "ON", brightness: 200}), {"qos": 0, "retain": true}, expect.any(Function));
await controller.stop();
expect(data.stateExists()).toBeFalsy();
});
@@ -559,8 +560,8 @@ describe('Controller', () => {
await controller.publishEntityState('bulb', {brightness: 200});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", "{\"state\":\"ON\"}", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", "{\"brightness\":200}", {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"ON"}), {"qos": 0, "retain": true}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"brightness":200}), {"qos": 0, "retain": true}, expect.any(Function));
});
it('Publish should not do anything for unknown entity', async () => {
+62 -61
View File
@@ -1,6 +1,7 @@
const data = require('./stub/data');
const logger = require('./stub/logger');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const stringify = require('json-stable-stringify');
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
zigbeeHerdsman.returnDevices.push('0x00124b00120144ae');
zigbeeHerdsman.returnDevices.push('0x000b57fffec6a5b3');
@@ -107,7 +108,7 @@ describe('Groups', () => {
await flushPromises();
expect(group.members).toStrictEqual([endpoint]);
expect(settings.getGroup('group_1').devices).toStrictEqual([`${device.ieeeAddr}/1`]);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bridge/log", '{"type":"device_group_add","message":{"friendly_name":"bulb_color","group":"group_1"}}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bridge/log", stringify({"type":"device_group_add","message":{"friendly_name":"bulb_color","group":"group_1"}}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Legacy api: Add to group with slashes via MQTT', async () => {
@@ -122,7 +123,7 @@ describe('Groups', () => {
await flushPromises();
expect(group.members).toStrictEqual([endpoint]);
expect(settings.getGroup('group/with/slashes').devices).toStrictEqual([`${device.ieeeAddr}/1`]);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bridge/log", '{"type":"device_group_add","message":{"friendly_name":"bulb_color","group":"group/with/slashes"}}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bridge/log", stringify({"type":"device_group_add","message":{"friendly_name":"bulb_color","group":"group/with/slashes"}}), {"retain": false, qos: 0}, expect.any(Function));
// Test if subscribed to topics with slashes
expect(MQTT.subscribe).toHaveBeenCalledWith('zigbee2mqtt/bridge/group/+/remove');
@@ -171,7 +172,7 @@ describe('Groups', () => {
await flushPromises();
expect(group.members).toStrictEqual([]);
expect(settings.getGroup('group_1').devices).toStrictEqual([]);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bridge/log", '{"type":"device_group_remove","message":{"friendly_name":"bulb_color","group":"group_1"}}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bridge/log", stringify({"type":"device_group_remove","message":{"friendly_name":"bulb_color","group":"group_1"}}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Legacy api: Remove from group via MQTT when in zigbee but not in settings', async () => {
@@ -242,7 +243,7 @@ describe('Groups', () => {
await flushPromises();
expect(group.members).toStrictEqual([]);
expect(settings.getGroup('group_1').devices).toStrictEqual([]);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bridge/log", '{"type":"device_group_remove_all","message":{"friendly_name":"wall_switch_double"}}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bridge/log", stringify({"type":"device_group_remove_all","message":{"friendly_name":"wall_switch_double"}}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Remove from group all deprecated', async () => {
@@ -292,8 +293,8 @@ describe('Groups', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", '{"state":"ON","linkquality":10}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", '{"state":"ON"}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", stringify({"state":"ON","linkquality":10}), {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should publish state change of all members when a group changes its state', async () => {
@@ -306,11 +307,11 @@ describe('Groups', () => {
await flushPromises();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", '{"state":"ON"}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", '{"state":"ON"}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should publish state change of all members when a group changes its state, filtered', async () => {
@@ -323,11 +324,11 @@ describe('Groups', () => {
await flushPromises();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'ON', brightness: 100}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON', brightness: 100}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", '{"state":"ON","brightness":100}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", '{"state":"ON"}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", stringify({"state":"ON","brightness":100}), {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Shouldnt publish group state change when a group is not optimistic', async () => {
@@ -345,7 +346,7 @@ describe('Groups', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", '{"state":"ON","linkquality":10}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", stringify({"state":"ON","linkquality":10}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should publish state change of another group with shared device when a group changes its state', async () => {
@@ -362,12 +363,12 @@ describe('Groups', () => {
await flushPromises();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(3);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", '{"state":"ON"}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", '{"state":"ON"}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_2", '{"state":"ON"}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_2", stringify({"state":"ON"}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should not publish state change off if any lights within are still on when changed via device', async () => {
@@ -384,14 +385,14 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'OFF'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", '{"state":"OFF","brightness":0}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", stringify({"state":"OFF","brightness":0}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should not publish state change off if any lights within are still on when changed via shared group', async () => {
@@ -409,15 +410,15 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/group_2/set', JSON.stringify({state: 'OFF'}));
await MQTT.events.message('zigbee2mqtt/group_2/set', stringify({state: 'OFF'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_2", '{"state":"OFF","brightness":0}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", '{"state":"OFF","brightness":0}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_2", stringify({"state":"OFF","brightness":0}), {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", stringify({"state":"OFF","brightness":0}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should publish state change off if all lights within turn off', async () => {
@@ -434,17 +435,17 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'OFF'}));
await MQTT.events.message('zigbee2mqtt/bulb/set', JSON.stringify({state: 'OFF'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'}));
await MQTT.events.message('zigbee2mqtt/bulb/set', stringify({state: 'OFF'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(3);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", '{"state":"OFF","brightness":0}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", '{"state":"OFF","brightness":0}', {"retain": true, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", '{"state":"OFF","brightness":0}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", stringify({"state":"OFF","brightness":0}), {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb", stringify({"state":"OFF","brightness":0}), {"retain": true, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({"state":"OFF","brightness":0}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Should publish state change off even when missing current state', async () => {
@@ -461,17 +462,17 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
MQTT.publish.mockClear();
controller.state.state = {};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'OFF'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", '{"state":"OFF","brightness":0}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", '{"state":"OFF","brightness":0}', {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/bulb_color", stringify({"state":"OFF","brightness":0}), {"retain": false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith("zigbee2mqtt/group_1", stringify({"state":"OFF","brightness":0}), {"retain": false, qos: 0}, expect.any(Function));
});
it('Add to group via MQTT', async () => {
@@ -483,14 +484,14 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', JSON.stringify({group: 'group_1', device: 'bulb_color'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'bulb_color'}));
await flushPromises();
expect(group.members).toStrictEqual([endpoint]);
expect(settings.getGroup('group_1').devices).toStrictEqual([`${device.ieeeAddr}/1`]);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/add',
JSON.stringify({"data":{"device":"bulb_color","group":"group_1"},"status":"ok"}),
stringify({"data":{"device":"bulb_color","group":"group_1"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -505,14 +506,14 @@ describe('Groups', () => {
endpoint.addToGroup.mockImplementationOnce(() => {throw new Error('timeout')});
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', JSON.stringify({group: 'group_1', device: 'bulb_color'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'bulb_color'}));
await flushPromises();
expect(group.members).toStrictEqual([]);
expect(settings.getGroup('group_1').devices).toStrictEqual([]);
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/add',
JSON.stringify({"data":{"device":"bulb_color","group":"group_1"},"status":"error","error":"Failed to add from group (timeout)"}),
stringify({"data":{"device":"bulb_color","group":"group_1"},"status":"error","error":"Failed to add from group (timeout)"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -526,14 +527,14 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', JSON.stringify({group: 'group/with/slashes', device: 'bulb_color'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group/with/slashes', device: 'bulb_color'}));
await flushPromises();
expect(group.members).toStrictEqual([endpoint]);
expect(settings.getGroup('group/with/slashes').devices).toStrictEqual([`${device.ieeeAddr}/1`]);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/add',
JSON.stringify({"data":{"device":"bulb_color","group":"group/with/slashes"},"status":"ok"}),
stringify({"data":{"device":"bulb_color","group":"group/with/slashes"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -546,14 +547,14 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', JSON.stringify({group: 'group_1', device: 'wall_switch_double/right'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'wall_switch_double/right'}));
await flushPromises();
expect(group.members).toStrictEqual([endpoint]);
expect(settings.getGroup('group_1').devices).toStrictEqual([`${device.ieeeAddr}/${endpoint.ID}`]);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/add',
JSON.stringify({"data":{"device":"wall_switch_double/right","group":"group_1"},"status":"ok"}),
stringify({"data":{"device":"wall_switch_double/right","group":"group_1"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -566,16 +567,16 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', JSON.stringify({group: 'group_1', device: 'wall_switch_double/right'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'wall_switch_double/right'}));
await flushPromises();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', JSON.stringify({group: 'group_1', device: '0x0017880104e45542/3'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: '0x0017880104e45542/3'}));
await flushPromises();
expect(group.members).toStrictEqual([endpoint]);
expect(settings.getGroup('group_1').devices).toStrictEqual([`${device.ieeeAddr}/${endpoint.ID}`]);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/add',
JSON.stringify({"data":{"device":"wall_switch_double/right","group":"group_1"},"status":"ok"}),
stringify({"data":{"device":"wall_switch_double/right","group":"group_1"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -589,14 +590,14 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', JSON.stringify({group: 'group_1', device: 'bulb_color'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: 'bulb_color'}));
await flushPromises();
expect(group.members).toStrictEqual([]);
expect(settings.getGroup('group_1').devices).toStrictEqual([]);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/remove',
JSON.stringify({"data":{"device":"bulb_color","group":"group_1"},"status":"ok"}),
stringify({"data":{"device":"bulb_color","group":"group_1"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -610,14 +611,14 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', JSON.stringify({group: 'group_1', device: 'bulb_color'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: 'bulb_color'}));
await flushPromises();
expect(group.members).toStrictEqual([]);
expect(settings.getGroup('group_1').devices).toStrictEqual(['dummy']);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/remove',
JSON.stringify({"data":{"device":"bulb_color","group":"group_1"},"status":"ok"}),
stringify({"data":{"device":"bulb_color","group":"group_1"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -631,14 +632,14 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', JSON.stringify({group: 'group_1', device: '0x0017880104e45542/3'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: '0x0017880104e45542/3'}));
await flushPromises();
expect(group.members).toStrictEqual([]);
expect(settings.getGroup('group_1').devices).toStrictEqual([]);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/remove',
JSON.stringify({"data":{"device":"0x0017880104e45542/3","group":"group_1"},"status":"ok"}),
stringify({"data":{"device":"0x0017880104e45542/3","group":"group_1"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -652,14 +653,14 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', JSON.stringify({group: 'group_1', device: 'wall_switch_double/3'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: 'wall_switch_double/3'}));
await flushPromises();
expect(group.members).toStrictEqual([]);
expect(settings.getGroup('group_1').devices).toStrictEqual([]);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/remove',
JSON.stringify({"data":{"device":"wall_switch_double/3","group":"group_1"},"status":"ok"}),
stringify({"data":{"device":"wall_switch_double/3","group":"group_1"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -673,14 +674,14 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', JSON.stringify({group: 'group_1', device: '0x0017880104e45542/right'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: '0x0017880104e45542/right'}));
await flushPromises();
expect(group.members).toStrictEqual([]);
expect(settings.getGroup('group_1').devices).toStrictEqual([]);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/remove',
JSON.stringify({"data":{"device":"0x0017880104e45542/right","group":"group_1"},"status":"ok"}),
stringify({"data":{"device":"0x0017880104e45542/right","group":"group_1"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -694,14 +695,14 @@ describe('Groups', () => {
await controller.start();
await flushPromises();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove_all', JSON.stringify({device: '0x0017880104e45542/right'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove_all', stringify({device: '0x0017880104e45542/right'}));
await flushPromises();
expect(group.members).toStrictEqual([]);
expect(settings.getGroup('group_1').devices).toStrictEqual([]);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/remove_all',
JSON.stringify({"data":{"device":"0x0017880104e45542/right"},"status":"ok"}),
stringify({"data":{"device":"0x0017880104e45542/right"},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -711,12 +712,12 @@ describe('Groups', () => {
await flushPromises();
logger.error.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', JSON.stringify({group: 'group_1_not_existing', device: 'bulb_color'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1_not_existing', device: 'bulb_color'}));
await flushPromises();
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/remove',
JSON.stringify({"data":{"device":"bulb_color","group":"group_1_not_existing"},"status":"error","error":"Group 'group_1_not_existing' does not exist"}),
stringify({"data":{"device":"bulb_color","group":"group_1_not_existing"},"status":"error","error":"Group 'group_1_not_existing' does not exist"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -726,12 +727,12 @@ describe('Groups', () => {
await flushPromises();
logger.error.mockClear();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', JSON.stringify({group: 'group_1', device: 'bulb_color_not_existing'}));
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'bulb_color_not_existing'}));
await flushPromises();
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/group/members/add',
JSON.stringify({"data":{"device":"bulb_color_not_existing","group":"group_1"},"status":"error","error":"Device 'bulb_color_not_existing' does not exist"}),
stringify({"data":{"device":"bulb_color_not_existing","group":"group_1"},"status":"error","error":"Device 'bulb_color_not_existing' does not exist"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
+33 -32
View File
@@ -1,5 +1,6 @@
const data = require('./stub/data');
const settings = require('../lib/util/settings');
const stringify = require('json-stable-stringify');
const logger = require('./stub/logger');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const flushPromises = () => new Promise(setImmediate);
@@ -81,7 +82,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -106,7 +107,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/humidity/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -131,7 +132,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/pressure/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -156,7 +157,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/battery/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -181,7 +182,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/linkquality/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -222,7 +223,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -247,7 +248,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/humidity/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -272,7 +273,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/pressure/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -330,7 +331,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -356,7 +357,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/humidity/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -419,7 +420,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/fan/0x0017880104e45548/fan/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -454,7 +455,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/cover/0x0017880104e45551/cover/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -488,7 +489,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'my_custom_discovery_topic/sensor/0x0017880104e45522/temperature/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -553,7 +554,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
JSON.stringify(payloadHA),
stringify(payloadHA),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -587,13 +588,13 @@ describe('HomeAssistant extension', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
'{"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"update_available":false}',
stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"update_available":false}),
{ retain: true, qos: 0 },
expect.any(Function)
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/remote',
'{"brightness":255,"update_available":false}',
stringify({"brightness":255,"update_available":false}),
{ retain: true, qos: 0 },
expect.any(Function)
);
@@ -613,13 +614,13 @@ describe('HomeAssistant extension', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
'{"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"update_available":false}',
stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"update_available":false}),
{ retain: true, qos: 0 },
expect.any(Function)
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/remote',
'{"brightness":255,"update_available":false}',
stringify({"brightness":255,"update_available":false}),
{ retain: true, qos: 0 },
expect.any(Function)
);
@@ -681,7 +682,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -765,7 +766,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/sensor/0x0017880104e45522/temperature/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -797,7 +798,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/binary_sensor/0x000b57fffec6a5b2/update_available/config',
JSON.stringify(payload),
stringify(payload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -840,7 +841,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/device_automation/0x0017880104e45520/action_single/config',
JSON.stringify(discoverPayload),
stringify(discoverPayload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -854,14 +855,14 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/button',
JSON.stringify({action: "single", linkquality: 10}),
stringify({action: "single", linkquality: 10}),
{ retain: false, qos: 0 },
expect.any(Function),
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/button',
JSON.stringify({linkquality: 10, action: ""}),
stringify({linkquality: 10, action: ""}),
{ retain: false, qos: 0 },
expect.any(Function),
);
@@ -872,7 +873,7 @@ describe('HomeAssistant extension', () => {
await flushPromises();
expect(MQTT.publish).not.toHaveBeenCalledWith(
'homeassistant/device_automation/0x0017880104e45520/action_single/config',
JSON.stringify(discoverPayload),
stringify(discoverPayload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -940,7 +941,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'homeassistant/device_automation/0x0017880104e45520/action_single/config',
JSON.stringify(discoverPayload),
stringify(discoverPayload),
{ retain: true, qos: 0 },
expect.any(Function),
);
@@ -954,7 +955,7 @@ describe('HomeAssistant extension', () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/button',
JSON.stringify({action: "single", linkquality: 10}),
stringify({action: "single", linkquality: 10}),
{ retain: false, qos: 0 },
expect.any(Function),
);
@@ -968,11 +969,11 @@ describe('HomeAssistant extension', () => {
await flushPromises();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/U202DST600ZB/l2/set', JSON.stringify({state: 'ON', brightness: 20}));
await MQTT.events.message('zigbee2mqtt/U202DST600ZB/l2/set', stringify({state: 'ON', brightness: 20}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/U202DST600ZB', JSON.stringify({state_l2:"ON", brightness_l2:20}), {"qos": 0, "retain": false}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/U202DST600ZB/l2', JSON.stringify({state:"ON", brightness:20}), {"qos": 0, "retain": false}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/U202DST600ZB/l1', JSON.stringify({}), {"qos": 0, "retain": false}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/U202DST600ZB', stringify({state_l2:"ON", brightness_l2:20}), {"qos": 0, "retain": false}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/U202DST600ZB/l2', stringify({state:"ON", brightness:20}), {"qos": 0, "retain": false}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/U202DST600ZB/l1', stringify({}), {"qos": 0, "retain": false}, expect.any(Function));
});
it('Shouldnt crash in onPublishEntityState on group publish', async () => {
@@ -982,7 +983,7 @@ describe('HomeAssistant extension', () => {
logger.error.mockClear();
MQTT.publish.mockClear();
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
expect(logger.error).toHaveBeenCalledTimes(0);
});
+24 -23
View File
@@ -2,6 +2,7 @@ const data = require('../stub/data');
const logger = require('../stub/logger');
const zigbeeHerdsman = require('../stub/zigbeeHerdsman');
const MQTT = require('../stub/mqtt');
const stringify = require('json-stable-stringify');
const path = require('path');
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
const settings = require('../../lib/util/settings');
@@ -29,7 +30,7 @@ describe('Bridge legacy', () => {
it('Should publish bridge configuration on startup', async () => {
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/config',
JSON.stringify({"version":this.version.version,"commit":this.version.commitHash,"coordinator":{"type":"z-Stack","meta":{"version":1, "revision": 20190425}},"network":{"panID":5674,"extendedPanID":[0,11,22],"channel":15},"log_level":'info',"permit_join":false}),
stringify({"version":this.version.version,"commit":this.version.commitHash,"coordinator":{"type":"z-Stack","meta":{"version":1, "revision": 20190425}},"network":{"panID":5674,"extendedPanID":[0,11,22],"channel":15},"log_level":'info',"permit_join":false}),
{ retain: true, qos: 0 },
expect.any(Function)
);
@@ -56,7 +57,7 @@ describe('Bridge legacy', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: "device_whitelisted", "message": {friendly_name: "bulb_color"}}),
stringify({type: "device_whitelisted", "message": {friendly_name: "bulb_color"}}),
{ retain: false, qos: 0 },
expect.any(Function)
);
@@ -67,7 +68,7 @@ describe('Bridge legacy', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: "device_whitelisted", "message": {friendly_name: "bulb"}}),
stringify({type: "device_whitelisted", "message": {friendly_name: "bulb"}}),
{ retain: false, qos: 0 },
expect.any(Function)
);
@@ -85,12 +86,12 @@ describe('Bridge legacy', () => {
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "retain": false}
);
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', JSON.stringify({friendly_name: 'bulb_color', options: {retain: true}}));
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', stringify({friendly_name: 'bulb_color', options: {retain: true}}));
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "retain": true}
);
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', JSON.stringify({friendly_name: 'bulb_color', optionswrong: {retain: true}}));
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', stringify({friendly_name: 'bulb_color', optionswrong: {retain: true}}));
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "retain": true}
@@ -100,17 +101,17 @@ describe('Bridge legacy', () => {
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "retain": true}
);
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', JSON.stringify({friendly_name: 'bulb_color', options: {random_setting: true}}));
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', stringify({friendly_name: 'bulb_color', options: {random_setting: true}}));
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "random_setting": true, "retain": true}
);
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', JSON.stringify({friendly_name: 'bulb_color', options: {options: {random_1: true}}}));
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', stringify({friendly_name: 'bulb_color', options: {options: {random_1: true}}}));
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "random_setting": true, "retain": true, options: {random_1: true}}
);
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', JSON.stringify({friendly_name: 'bulb_color', options: {options: {random_2: false}}}));
MQTT.events.message('zigbee2mqtt/bridge/config/device_options', stringify({friendly_name: 'bulb_color', options: {options: {random_2: false}}}));
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(
{"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "random_setting": true, "retain": true, options: {random_1: true, random_2: false}}
@@ -194,18 +195,18 @@ describe('Bridge legacy', () => {
const bulb_color2 = {"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color2", "friendly_name": "bulb_color2", "retain": false};
MQTT.publish.mockClear();
expect(settings.getDevice('bulb_color')).toStrictEqual({"ID": "0x000b57fffec6a5b3", "friendlyName": "bulb_color", "friendly_name": "bulb_color", "retain": false});
MQTT.events.message('zigbee2mqtt/bridge/config/rename', JSON.stringify({old: 'bulb_color', new: 'bulb_color2'}));
MQTT.events.message('zigbee2mqtt/bridge/config/rename', stringify({old: 'bulb_color', new: 'bulb_color2'}));
await flushPromises();
expect(settings.getDevice('bulb_color')).toStrictEqual(null);
expect(settings.getDevice('bulb_color2')).toStrictEqual(bulb_color2);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: 'device_renamed', message: {from: 'bulb_color', to: 'bulb_color2'}}),
stringify({type: 'device_renamed', message: {from: 'bulb_color', to: 'bulb_color2'}}),
{qos: 0, retain: false},
expect.any(Function)
);
MQTT.events.message('zigbee2mqtt/bridge/config/rename', JSON.stringify({old: 'bulb_color2', newmalformed: 'bulb_color3'}));
MQTT.events.message('zigbee2mqtt/bridge/config/rename', stringify({old: 'bulb_color2', newmalformed: 'bulb_color3'}));
await flushPromises();
expect(settings.getDevice('bulb_color2')).toStrictEqual(bulb_color2);
@@ -213,7 +214,7 @@ describe('Bridge legacy', () => {
await flushPromises();
expect(settings.getDevice('bulb_color2')).toStrictEqual(bulb_color2);
MQTT.events.message('zigbee2mqtt/bridge/config/rename', JSON.stringify({old: 'bulb_color', new: 'bulb_color3'}));
MQTT.events.message('zigbee2mqtt/bridge/config/rename', stringify({old: 'bulb_color', new: 'bulb_color3'}));
await flushPromises();
expect(settings.getDevice('bulb_color2')).toStrictEqual(bulb_color2);
});
@@ -221,12 +222,12 @@ describe('Bridge legacy', () => {
it('Should allow rename groups', async () => {
MQTT.publish.mockClear();
expect(settings.getGroup(1)).toStrictEqual({"ID": 1, devices: [], friendlyName: "group_1", "friendly_name": "group_1", optimistic: true, retain: false});
MQTT.events.message('zigbee2mqtt/bridge/config/rename', JSON.stringify({old: 'group_1', new: 'group_1_renamed'}));
MQTT.events.message('zigbee2mqtt/bridge/config/rename', stringify({old: 'group_1', new: 'group_1_renamed'}));
await flushPromises();
expect(settings.getGroup(1)).toStrictEqual({"ID": 1, devices: [], friendlyName: "group_1_renamed", "friendly_name": "group_1_renamed", optimistic: true, retain: false});
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: 'group_renamed', message: {from: 'group_1', to: 'group_1_renamed'}}),
stringify({type: 'group_renamed', message: {from: 'group_1', to: 'group_1_renamed'}}),
{qos: 0, retain: false},
expect.any(Function)
);
@@ -243,7 +244,7 @@ describe('Bridge legacy', () => {
expect(settings.getDevice('0x000b57fffec6a5b2').friendlyName).toStrictEqual('bulb_new_name');
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: 'device_renamed', message: {from: 'bulb', to: 'bulb_new_name'}}),
stringify({type: 'device_renamed', message: {from: 'bulb', to: 'bulb_new_name'}}),
{qos: 0, retain: false},
expect.any(Function)
);
@@ -265,7 +266,7 @@ describe('Bridge legacy', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: 'group_added', message: 'new_group'}),
stringify({type: 'group_added', message: 'new_group'}),
{qos: 0, retain: false},
expect.any(Function)
);
@@ -283,7 +284,7 @@ describe('Bridge legacy', () => {
expect(zigbeeHerdsman.createGroup).toHaveBeenCalledWith(3);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: 'group_added', message: 'new_group'}),
stringify({type: 'group_added', message: 'new_group'}),
{qos: 0, retain: false},
expect.any(Function)
);
@@ -298,7 +299,7 @@ describe('Bridge legacy', () => {
expect(zigbeeHerdsman.createGroup).toHaveBeenCalledWith(42);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: 'group_added', message: 'new_group'}),
stringify({type: 'group_added', message: 'new_group'}),
{qos: 0, retain: false},
expect.any(Function)
);
@@ -321,7 +322,7 @@ describe('Bridge legacy', () => {
expect(group.removeFromNetwork).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: 'group_removed', message: 'group_1'}),
stringify({type: 'group_removed', message: 'group_1'}),
{qos: 0, retain: false},
expect.any(Function)
);
@@ -335,7 +336,7 @@ describe('Bridge legacy', () => {
expect(group.removeFromDatabase).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: 'group_removed', message: 'group_1'}),
stringify({type: 'group_removed', message: 'group_1'}),
{qos: 0, retain: false},
expect.any(Function)
);
@@ -370,7 +371,7 @@ describe('Bridge legacy', () => {
expect(settings.getDevice('bulb_color')).toBeNull();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: 'device_removed', message: 'bulb_color'}),
stringify({type: 'device_removed', message: 'bulb_color'}),
{qos: 0, retain: false},
expect.any(Function)
);
@@ -392,7 +393,7 @@ describe('Bridge legacy', () => {
expect(settings.getDevice('bulb_color')).toBeNull();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: 'device_force_removed', message: 'bulb_color'}),
stringify({type: 'device_force_removed', message: 'bulb_color'}),
{qos: 0, retain: false},
expect.any(Function)
);
@@ -413,7 +414,7 @@ describe('Bridge legacy', () => {
expect(settings.getDevice('bulb_color')).toBeNull();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/log',
JSON.stringify({type: 'device_banned', message: 'bulb_color'}),
stringify({type: 'device_banned', message: 'bulb_color'}),
{qos: 0, retain: false},
expect.any(Function)
);
+3 -2
View File
@@ -1,6 +1,7 @@
const data = require('./stub/data');
const logger = require('./stub/logger');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const stringify = require('json-stable-stringify');
const {coordinator, bulb, bulb_color, WXKG02LM, CC2530_ROUTER, unsupported_router} = zigbeeHerdsman.devices;
@@ -258,7 +259,7 @@ describe('Networkmap', () => {
it('Should output raw networkmap', async () => {
mock();
MQTT.publish.mockClear();
MQTT.events.message('zigbee2mqtt/bridge/request/networkmap', JSON.stringify({type: 'raw', routes: true}));
MQTT.events.message('zigbee2mqtt/bridge/request/networkmap', stringify({type: 'raw', routes: true}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
let call = MQTT.publish.mock.calls[0];
@@ -303,7 +304,7 @@ describe('Networkmap', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/networkmap',
JSON.stringify({"data":{},"status":"error","error":"Type 'not_existing' not supported, allowed are: raw,graphviz,plantuml"}),
stringify({"data":{},"status":"error","error":"Type 'not_existing' not supported, allowed are: raw,graphviz,plantuml"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
+18 -17
View File
@@ -6,6 +6,7 @@ const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
const flushPromises = () => new Promise(setImmediate);
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const stringify = require('json-stable-stringify');
describe('OTA update', () => {
let controller;
@@ -58,28 +59,28 @@ describe('OTA update', () => {
expect(mapped.ota.updateToLatest).toHaveBeenCalledWith(device, logger, expect.any(Function));
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 0.00%`);
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 10.00%, +- 60 minutes remaining`);
expect(logger.info).toHaveBeenCalledWith(`Finished update of 'bulb', from '{"softwareBuildID":1,"dateCode":"20190101"}' to '{"softwareBuildID":2,"dateCode":"20190102"}'`);
expect(logger.info).toHaveBeenCalledWith(`Finished update of 'bulb', from '{"dateCode":"20190101","softwareBuildID":1}' to '{"dateCode":"20190102","softwareBuildID":2}'`);
expect(device.save).toHaveBeenCalledTimes(1);
expect(device.dateCode).toBe('20190102');
expect(device.softwareBuildID).toBe(2);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
JSON.stringify({"update_available":false,"update":{"state":"updating","progress":0}}),
stringify({"update_available":false,"update":{"state":"updating","progress":0}}),
{retain: true, qos: 0}, expect.any(Function)
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
JSON.stringify({"update_available":false,"update":{"state":"updating","progress":10,"remaining":3600}}),
stringify({"update_available":false,"update":{"state":"updating","progress":10,"remaining":3600}}),
{retain: true, qos: 0}, expect.any(Function)
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
JSON.stringify({"update_available":false,"update":{"state":"idle"}}),
stringify({"update_available":false,"update":{"state":"idle"}}),
{retain: true, qos: 0}, expect.any(Function)
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/ota_update/update',
JSON.stringify({"data":{"id": "bulb","from":{"software_build_id":1,"date_code":"20190101"},"to":{"software_build_id":2,"date_code":"20190102"}},"status":"ok"}),
stringify({"data":{"id": "bulb","from":{"software_build_id":1,"date_code":"20190101"},"to":{"software_build_id":2,"date_code":"20190102"}},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -95,16 +96,16 @@ describe('OTA update', () => {
throw new Error('Update failed');
});
MQTT.events.message('zigbee2mqtt/bridge/request/device/ota_update/update', JSON.stringify({id: "bulb"}));
MQTT.events.message('zigbee2mqtt/bridge/request/device/ota_update/update', stringify({id: "bulb"}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
JSON.stringify({"update_available":true,"update":{"state":"available"}}),
stringify({"update_available":true,"update":{"state":"available"}}),
{retain: true, qos: 0}, expect.any(Function)
);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/ota_update/update',
JSON.stringify({"data":{"id": "bulb"},"status":"error","error":"Update of 'bulb' failed (Update failed)"}),
stringify({"data":{"id": "bulb"},"status":"error","error":"Update of 'bulb' failed (Update failed)"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -121,7 +122,7 @@ describe('OTA update', () => {
expect(mapped.ota.updateToLatest).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/ota_update/check',
JSON.stringify({"data":{"id": "bulb","updateAvailable":false},"status":"ok"}),
stringify({"data":{"id": "bulb","updateAvailable":false},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
@@ -133,7 +134,7 @@ describe('OTA update', () => {
expect(mapped.ota.updateToLatest).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/ota_update/check',
JSON.stringify({"data":{"id": "bulb","updateAvailable":true},"status":"ok"}),
stringify({"data":{"id": "bulb","updateAvailable":true},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -150,7 +151,7 @@ describe('OTA update', () => {
expect(mapped.ota.updateToLatest).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/ota_update/check',
JSON.stringify({"data":{"id": "bulb"},"status":"error","error": `Failed to check if update available for 'bulb' (RF singals disturbed because of dogs barking)`}),
stringify({"data":{"id": "bulb"},"status":"error","error": `Failed to check if update available for 'bulb' (RF singals disturbed because of dogs barking)`}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -160,7 +161,7 @@ describe('OTA update', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/ota_update/check',
JSON.stringify({"data":{"id": "not_existing_deviceooo"},"status":"error","error": `Device 'not_existing_deviceooo' does not exist`}),
stringify({"data":{"id": "not_existing_deviceooo"},"status":"error","error": `Device 'not_existing_deviceooo' does not exist`}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -170,7 +171,7 @@ describe('OTA update', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/ota_update/check',
JSON.stringify({"data":{"id": "ZNLDP12LM"},"status":"error","error": `Device 'ZNLDP12LM' does not support OTA updates`}),
stringify({"data":{"id": "ZNLDP12LM"},"status":"error","error": `Device 'ZNLDP12LM' does not support OTA updates`}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -193,7 +194,7 @@ describe('OTA update', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/ota_update/check',
JSON.stringify({"data":{"id": "bulb"},"status":"error","error": `Update or check for update already in progress for 'bulb'`}),
stringify({"data":{"id": "bulb"},"status":"error","error": `Update or check for update already in progress for 'bulb'`}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -210,7 +211,7 @@ describe('OTA update', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/response/device/ota_update/update',
JSON.stringify({"data":{"id":"bulb","from":null,"to":null},"status":"ok"}),
stringify({"data":{"id":"bulb","from":null,"to":null},"status":"ok"}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -245,7 +246,7 @@ describe('OTA update', () => {
expect(logger.info).not.toHaveBeenCalledWith(`Update available for 'bulb'`);
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bulb',
JSON.stringify({"update_available":true,"update":{"state":"available"}}),
stringify({"update_available":true,"update":{"state":"available"}}),
{retain: true, qos: 0}, expect.any(Function)
);
});
@@ -297,7 +298,7 @@ describe('OTA update', () => {
expect(mapped.ota.updateToLatest).toHaveBeenCalledWith(device, logger, expect.any(Function));
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 0.00%`);
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 10.00%, +- 60 minutes remaining`);
expect(logger.info).toHaveBeenCalledWith(`Finished update of 'bulb', from '{"softwareBuildID":1,"dateCode":"20190101"}' to '{"softwareBuildID":2,"dateCode":"20190102"}'`);
expect(logger.info).toHaveBeenCalledWith(`Finished update of 'bulb', from '{"dateCode":"20190101","softwareBuildID":1}' to '{"dateCode":"20190102","softwareBuildID":2}'`);
expect(logger.error).toHaveBeenCalledTimes(0);
expect(device.save).toHaveBeenCalledTimes(1);
expect(device.dateCode).toBe('20190102');
+134 -133
View File
@@ -2,6 +2,7 @@ const data = require('./stub/data');
const logger = require('./stub/logger');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
const stringify = require('json-stable-stringify');
const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
const Controller = require('../lib/controller');
@@ -55,7 +56,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices', async () => {
const endpoint = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness: '200'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({brightness: '200'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genLevelCtrl", "moveToLevelWithOnOff", {"level": 200, "transtime": 0}, {});
@@ -66,13 +67,13 @@ describe('Publish', () => {
});
it('Should publish messages to zigbee devices when there is no converters', async () => {
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness_no: '200'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({brightness_no: '200'}));
await flushPromises();
expectNothingPublished();
});
it('Should publish messages to zigbee devices when there is a get converter but no set', async () => {
await MQTT.events.message('zigbee2mqtt/thermostat/set', JSON.stringify({relay_status_log_rsp: '200'}));
await MQTT.events.message('zigbee2mqtt/thermostat/set', stringify({relay_status_log_rsp: '200'}));
await flushPromises();
expectNothingPublished();
});
@@ -81,7 +82,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['devices', device.ieeeAddr, 'friendly_name'], 'wohnzimmer.light.wall.right')
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/wohnzimmer.light.wall.right/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/wohnzimmer.light.wall.right/set', stringify({state: 'ON'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genOnOff", "on", {}, {});
@@ -94,7 +95,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices when brightness is in %', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness_percent: '92'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({brightness_percent: '92'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genLevelCtrl", "moveToLevelWithOnOff", {level: 235, transtime: 0}, {});
@@ -107,7 +108,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices when brightness is in number', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness: 230}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({brightness: 230}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genLevelCtrl", "moveToLevelWithOnOff", {level: 230, transtime: 0}, {});
@@ -120,7 +121,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices with color_temp', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({color_temp: '222'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({color_temp: '222'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("lightingColorCtrl", "moveToColorTemp", {colortemp: 222, transtime: 0}, {});
@@ -133,7 +134,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices with color_temp in %', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({color_temp_percent: '100'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({color_temp_percent: '100'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("lightingColorCtrl", "moveToColorTemp", {colortemp: 500, transtime: 0}, {});
@@ -146,7 +147,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices with non-default ep', async () => {
const device = zigbeeHerdsman.devices.QBKG04LM;
const endpoint = device.getEndpoint(2);
await MQTT.events.message('zigbee2mqtt/wall_switch/set', JSON.stringify({state: 'OFF'}));
await MQTT.events.message('zigbee2mqtt/wall_switch/set', stringify({state: 'OFF'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genOnOff", "off", {}, {});
@@ -159,7 +160,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices with non-default ep and postfix', async () => {
const device = zigbeeHerdsman.devices.QBKG03LM;
const endpoint = device.getEndpoint(3);
await MQTT.events.message('zigbee2mqtt/wall_switch_double/right/set', JSON.stringify({state: 'OFF'}));
await MQTT.events.message('zigbee2mqtt/wall_switch_double/right/set', stringify({state: 'OFF'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genOnOff", "off", {}, {});
@@ -172,7 +173,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices to non default-ep with state_[EP]', async () => {
const device = zigbeeHerdsman.devices.QBKG03LM;
const endpoint = device.getEndpoint(3);
await MQTT.events.message('zigbee2mqtt/wall_switch_double/set', JSON.stringify({state_right: 'OFF'}));
await MQTT.events.message('zigbee2mqtt/wall_switch_double/set', stringify({state_right: 'OFF'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genOnOff", "off", {}, {});
@@ -185,7 +186,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices with color xy', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({color: {x: 100, y: 50}}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({color: {x: 100, y: 50}}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("lightingColorCtrl", "moveToColor", {colorx: 6553500, colory: 3276750, transtime: 0}, {});
@@ -198,7 +199,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices with color xy and state', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({color: {x: 100, y: 50}, state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({color: {x: 100, y: 50}, state: 'ON'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command).toHaveBeenCalledWith("genOnOff", "on", {}, {});
@@ -215,7 +216,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices with color xy and brightness', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({color: {x: 100, y: 50}, brightness: 20}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({color: {x: 100, y: 50}, brightness: 20}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command).toHaveBeenCalledWith("genLevelCtrl", "moveToLevelWithOnOff", {level: 20, transtime: 0}, {});
@@ -232,7 +233,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices with color xy, brightness and state on', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({color: {x: 100, y: 50}, brightness: 20, state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({color: {x: 100, y: 50}, brightness: 20, state: 'ON'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command).toHaveBeenCalledWith("genLevelCtrl", "moveToLevelWithOnOff", {level: 20, transtime: 0}, {});
@@ -249,7 +250,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices with color xy, brightness and state off', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({color: {x: 100, y: 50}, brightness: 20, state: 'OFF'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({color: {x: 100, y: 50}, brightness: 20, state: 'OFF'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command).toHaveBeenNthCalledWith(1, "lightingColorCtrl", "moveToColor", {colorx: 6553500, colory: 3276750, transtime: 0}, {});
@@ -266,7 +267,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices with color rgb', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({color: {r: 100, g: 200, b: 10}}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({color: {r: 100, g: 200, b: 10}}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("lightingColorCtrl", "moveToColor", {colorx: 16764, colory: 40979, transtime: 0}, {});
@@ -278,7 +279,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices with color rgb', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({color: {rgb: '100,200,10'}}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({color: {rgb: '100,200,10'}}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("lightingColorCtrl", "moveToColor", {colorx: 16764, colory: 40979, transtime: 0}, {});
@@ -290,7 +291,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices with color rgb', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'ON', brightness: '50'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON', brightness: '50'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genLevelCtrl", "moveToLevelWithOnOff", {level: 50, transtime: 0}, {});
@@ -301,7 +302,7 @@ describe('Publish', () => {
it('Should publish messages groups', async () => {
const group = zigbeeHerdsman.groups.group_1;
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'}));
await flushPromises();
expect(group.command).toHaveBeenCalledTimes(1);
expect(group.command).toHaveBeenCalledWith("genOnOff", "on", {}, {});
@@ -312,7 +313,7 @@ describe('Publish', () => {
it('Should publish messages to groups with brightness_percent', async () => {
const group = zigbeeHerdsman.groups.group_1;
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({brightness_percent: 50}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({brightness_percent: 50}));
await flushPromises();
expect(group.command).toHaveBeenCalledTimes(1);
expect(group.command).toHaveBeenCalledWith("genLevelCtrl", "moveToLevelWithOnOff", {level: 127, transtime: 0}, {});
@@ -323,7 +324,7 @@ describe('Publish', () => {
it('Should publish messages to groups with on and brightness', async () => {
const group = zigbeeHerdsman.groups.group_1;
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'ON', brightness: 50}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'ON', brightness: 50}));
await flushPromises();
expect(group.command).toHaveBeenCalledTimes(1);
expect(group.command).toHaveBeenCalledWith("genLevelCtrl", "moveToLevelWithOnOff", {level: 50, transtime: 0}, {});
@@ -334,7 +335,7 @@ describe('Publish', () => {
it('Should publish messages to groups with off and brightness', async () => {
const group = zigbeeHerdsman.groups.group_1;
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({state: 'OFF', brightness: 50}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({state: 'OFF', brightness: 50}));
await flushPromises();
expect(group.command).toHaveBeenCalledTimes(1);
expect(group.command).toHaveBeenCalledWith("genOnOff", "off", {}, {});
@@ -345,7 +346,7 @@ describe('Publish', () => {
it('Should publish messages to groups color', async () => {
const group = zigbeeHerdsman.groups.group_1;
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({color: {x: 0.37, y: 0.28}}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({color: {x: 0.37, y: 0.28}}));
await flushPromises();
expect(group.command).toHaveBeenCalledTimes(1);
expect(group.command).toHaveBeenCalledWith("lightingColorCtrl", "moveToColor", {colorx: 24248, colory: 18350, transtime: 0}, {});
@@ -356,7 +357,7 @@ describe('Publish', () => {
it('Should publish messages to groups color temperature', async () => {
const group = zigbeeHerdsman.groups.group_1;
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({color_temp: 100}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({color_temp: 100}));
await flushPromises();
expect(group.command).toHaveBeenCalledTimes(1);
expect(group.command).toHaveBeenCalledWith("lightingColorCtrl", "moveToColorTemp", {colortemp: 100, transtime: 0}, {});
@@ -368,7 +369,7 @@ describe('Publish', () => {
it('Should create and publish to group which is in configuration.yaml but not in zigbee-herdsman', async () => {
delete zigbeeHerdsman.groups.group_2;
expect(Object.values(zigbeeHerdsman.groups).length).toBe(4);
await MQTT.events.message('zigbee2mqtt/group_2/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/group_2/set', stringify({state: 'ON'}));
await flushPromises();
expect(Object.values(zigbeeHerdsman.groups).length).toBe(5);
expect(zigbeeHerdsman.groups.group_2.command).toHaveBeenCalledTimes(1);
@@ -376,31 +377,31 @@ describe('Publish', () => {
});
it('Should handle non-valid topics', async () => {
await MQTT.events.message('zigbee2mqtt1/bulb_color/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt1/bulb_color/set', stringify({state: 'ON'}));
await flushPromises();
expectNothingPublished();
});
it('Should handle non-valid topics', async () => {
await MQTT.events.message('zigbee2mqtt1/bulb_color/sett', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt1/bulb_color/sett', stringify({state: 'ON'}));
await flushPromises();
expectNothingPublished();
});
it('Should handle non-valid topics', async () => {
await MQTT.events.message('zigbee2mqtt/bulb_color/write', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/write', stringify({state: 'ON'}));
await flushPromises();
expectNothingPublished();
});
it('Should handle non-valid topics', async () => {
await MQTT.events.message('zigbee2mqtt/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/set', stringify({state: 'ON'}));
await flushPromises();
expectNothingPublished();
});
it('Should handle non-valid topics', async () => {
await MQTT.events.message('set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('set', stringify({state: 'ON'}));
await flushPromises();
expectNothingPublished();
});
@@ -408,7 +409,7 @@ describe('Publish', () => {
it('Should handle get', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/get', JSON.stringify({state: ''}));
await MQTT.events.message('zigbee2mqtt/bulb_color/get', stringify({state: ''}));
await flushPromises();
expect(endpoint.read).toHaveBeenCalledTimes(1);
expect(endpoint.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
@@ -418,7 +419,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.QBKG03LM;
const endpoint2 = device.getEndpoint(2);
const endpoint3 = device.getEndpoint(3);
await MQTT.events.message('zigbee2mqtt/0x0017880104e45542/get', JSON.stringify({state_left: '', state_right: ''}));
await MQTT.events.message('zigbee2mqtt/0x0017880104e45542/get', stringify({state_left: '', state_right: ''}));
await flushPromises();
expect(endpoint2.read).toHaveBeenCalledTimes(1);
expect(endpoint2.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
@@ -431,7 +432,7 @@ describe('Publish', () => {
const endpoint2 = device.getEndpoint(2);
const endpoint3 = device.getEndpoint(3);
logger.error.mockClear();
await MQTT.events.message('zigbee2mqtt/0x0017880104e45542/get', JSON.stringify({state_center: '', state_right: ''}));
await MQTT.events.message('zigbee2mqtt/0x0017880104e45542/get', stringify({state_center: '', state_right: ''}));
await flushPromises();
expect(logger.error).toHaveBeenCalledWith(`Device 'wall_switch_double' has no endpoint 'center'`);
expect(endpoint2.read).toHaveBeenCalledTimes(0);
@@ -440,19 +441,19 @@ describe('Publish', () => {
});
it('Should not respond to bridge/config/devices/get', async () => {
await MQTT.events.message('zigbee2mqtt/bridge/config/devices/get', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/bridge/config/devices/get', stringify({state: 'ON'}));
await flushPromises();
expectNothingPublished();
});
it('Should not respond to bridge/config/devices/set', async () => {
await MQTT.events.message('zigbee2mqtt/bridge/config/devices/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/bridge/config/devices/set', stringify({state: 'ON'}));
await flushPromises();
expectNothingPublished();
});
it('Should not respond to bridge/config/devices', async () => {
await MQTT.events.message('zigbee2mqtt/bridge/config/devices', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/bridge/config/devices', stringify({state: 'ON'}));
await flushPromises();
expectNothingPublished();
});
@@ -461,7 +462,7 @@ describe('Publish', () => {
settings.set(['mqtt', 'base_topic'], 'zigbee2mqtt/at/my/home');
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/at/my/home/bulb_color/get', JSON.stringify({state: ''}));
await MQTT.events.message('zigbee2mqtt/at/my/home/bulb_color/get', stringify({state: ''}));
await flushPromises();
expect(endpoint.read).toHaveBeenCalledTimes(1);
expect(endpoint.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
@@ -471,7 +472,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['devices', device.ieeeAddr, 'friendly_name'], 'floor0/basement/my_device_id2');
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/floor0/basement/my_device_id2/get', JSON.stringify({state: ''}));
await MQTT.events.message('zigbee2mqtt/floor0/basement/my_device_id2/get', stringify({state: ''}));
await flushPromises();
expect(endpoint.read).toHaveBeenCalledTimes(1);
expect(endpoint.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
@@ -482,7 +483,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb_color;
settings.set(['devices', device.ieeeAddr, 'friendly_name'], 'floor0/basement/my_device_id2');
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/at/my/basement/floor0/basement/my_device_id2/get', JSON.stringify({state: ''}));
await MQTT.events.message('zigbee2mqtt/at/my/basement/floor0/basement/my_device_id2/get', stringify({state: ''}));
await flushPromises();
expect(endpoint.read).toHaveBeenCalledTimes(1);
expect(endpoint.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
@@ -512,14 +513,14 @@ describe('Publish', () => {
it('Should parse set with ieeeAddr topic', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/0x000b57fffec6a5b3/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/0x000b57fffec6a5b3/set', stringify({state: 'ON'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genOnOff", "on", {}, {});
});
it('Should parse set with non-existing postfix', async () => {
await MQTT.events.message('zigbee2mqtt/wall_switch_double/invalid/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/wall_switch_double/invalid/set', stringify({state: 'ON'}));
await flushPromises();
expectNothingPublished();
});
@@ -529,7 +530,7 @@ describe('Publish', () => {
const endpoint = device.getEndpoint(1);
// Non-inverted (open = 100, close = 0)
await MQTT.events.message('zigbee2mqtt/J1_cover/set', JSON.stringify({position: 90}));
await MQTT.events.message('zigbee2mqtt/J1_cover/set', stringify({position: 90}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("closuresWindowCovering", "goToLiftPercentage", {percentageliftvalue: 10}, {});
@@ -537,7 +538,7 @@ describe('Publish', () => {
// // Inverted
endpoint.command.mockClear();
settings.set(['devices', device.ieeeAddr, 'invert_cover'], true);
await MQTT.events.message('zigbee2mqtt/J1_cover/set', JSON.stringify({position: 90}));
await MQTT.events.message('zigbee2mqtt/J1_cover/set', stringify({position: 90}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("closuresWindowCovering", "goToLiftPercentage", {percentageliftvalue: 90}, {});
@@ -554,8 +555,8 @@ describe('Publish', () => {
expect(endpoint.command).toHaveBeenCalledWith("genOnOff", "on", {}, {});
expect(endpoint.command).toHaveBeenCalledWith("genOnOff", "toggle", {}, {});
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish.mock.calls[0]).toEqual(["zigbee2mqtt/wall_switch_double", JSON.stringify({state_left: 'ON'}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[1]).toEqual(["zigbee2mqtt/wall_switch_double", JSON.stringify({state_left: 'OFF'}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[0]).toEqual(["zigbee2mqtt/wall_switch_double", stringify({state_left: 'ON'}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[1]).toEqual(["zigbee2mqtt/wall_switch_double", stringify({state_left: 'OFF'}), {"qos": 0, "retain": false}, expect.any(Function)]);
});
it('Should parse set with postfix topic and attribute', async () => {
@@ -572,7 +573,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.QBKG03LM;
settings.set(['devices', device.ieeeAddr, 'friendly_name'], 'in/basement/wall_switch_double');
const endpoint = device.getEndpoint(2);
await MQTT.events.message('zigbee2mqtt/at/my/home/in/basement/wall_switch_double/left/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/at/my/home/in/basement/wall_switch_double/left/set', stringify({state: 'ON'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genOnOff", "on", {}, {});
@@ -582,7 +583,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.QBKG03LM;
settings.set(['devices', device.ieeeAddr, 'friendly_name'], 'ground_floor/kitchen/wall_switch/2');
const endpoint = device.getEndpoint(2);
await MQTT.events.message('zigbee2mqtt/ground_floor/kitchen/wall_switch/2/left/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/ground_floor/kitchen/wall_switch/2/left/set', stringify({state: 'ON'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genOnOff", "on", {}, {});
@@ -591,8 +592,8 @@ describe('Publish', () => {
it('Should not publish messages to zigbee devices when payload is invalid', async () => {
const device = zigbeeHerdsman.devices.QBKG03LM;
const endpoint = device.getEndpoint(2);
await MQTT.events.message('zigbee2mqtt/wall_switch_double/left/set', JSON.stringify({state: true}));
await MQTT.events.message('zigbee2mqtt/wall_switch_double/left/set', JSON.stringify({state: 1}));
await MQTT.events.message('zigbee2mqtt/wall_switch_double/left/set', stringify({state: true}));
await MQTT.events.message('zigbee2mqtt/wall_switch_double/left/set', stringify({state: 1}));
await flushPromises();
expectNothingPublished();
});
@@ -601,7 +602,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
const payload = {'state': 'ON', 'color': {'x': 0.701, 'y': 0.299}};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command.mock.calls[0]).toEqual(["genOnOff", "on", {}, {}]);
@@ -614,7 +615,7 @@ describe('Publish', () => {
settings.set(['devices', device.ieeeAddr, 'retrieve_state'], true);
const endpoint = device.getEndpoint(1);
const payload = {'state': 'ON', 'color': {'x': 0.701, 'y': 0.299}};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
jest.runAllTimers();
expect(endpoint.command).toHaveBeenCalledTimes(2);
@@ -628,7 +629,7 @@ describe('Publish', () => {
it('Should use transition when brightness with group', async () => {
const group = zigbeeHerdsman.groups.group_1;
settings.set(['groups', '1', 'transition'], 20);
await MQTT.events.message('zigbee2mqtt/group_1/set', JSON.stringify({brightness: 100}));
await MQTT.events.message('zigbee2mqtt/group_1/set', stringify({brightness: 100}));
await flushPromises();
expect(group.command).toHaveBeenCalledTimes(1);
expect(group.command).toHaveBeenCalledWith("genLevelCtrl", "moveToLevelWithOnOff", {level: 100, transtime: 200}, {});
@@ -642,7 +643,7 @@ describe('Publish', () => {
settings.set(['devices', device.ieeeAddr, 'transition'], 20);
const endpoint = device.getEndpoint(1);
const payload = {brightness: 20};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 20, transtime: 200}, {}]);
@@ -653,7 +654,7 @@ describe('Publish', () => {
settings.set(['device_options'], {transition: 20});
const endpoint = device.getEndpoint(1);
const payload = {brightness: 20};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 20, transtime: 200}, {}]);
@@ -663,18 +664,18 @@ describe('Publish', () => {
// https://github.com/Koenkk/zigbee2mqtt/issues/3799
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({"state": "OFF", "transition": 0.5}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({"state": "OFF", "transition": 0.5}));
await flushPromises();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({"state": "ON", "transition": 0.5}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({"state": "ON", "transition": 0.5}));
await flushPromises();
expect(MQTT.publish).toHaveBeenNthCalledWith(1,
'zigbee2mqtt/bulb_color',
JSON.stringify({state: 'OFF', brightness: 0}),
stringify({state: 'OFF', brightness: 0}),
{retain: false, qos: 0}, expect.any(Function)
);
expect(MQTT.publish).toHaveBeenNthCalledWith(2,
'zigbee2mqtt/bulb_color',
JSON.stringify({state: 'ON', brightness: 254}),
stringify({state: 'ON', brightness: 254}),
{retain: false, qos: 0}, expect.any(Function)
);
expect(endpoint.command).toHaveBeenCalledTimes(2);
@@ -686,11 +687,11 @@ describe('Publish', () => {
// https://github.com/Koenkk/zigbee2mqtt/issues/3563
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({"state": "ON", "brightness": 50}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({"state": "ON", "brightness": 50}));
await flushPromises();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({"state": "ON"}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({"state": "ON"}));
await flushPromises();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({"state": "ON", "transition": 1}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({"state": "ON", "transition": 1}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(3);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 50, transtime: 0}, {}]);
@@ -703,7 +704,7 @@ describe('Publish', () => {
settings.set(['devices', device.ieeeAddr, 'transition'], 20);
const endpoint = device.getEndpoint(1);
const payload = {color_temp: 200};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["lightingColorCtrl", "moveToColorTemp", {colortemp: 200, transtime: 200}, {}]);
@@ -713,7 +714,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb;
const endpoint = device.getEndpoint(1);
const payload = {state: 'ON', brightness: 20, color_temp: 200, transition: 20};
await MQTT.events.message('zigbee2mqtt/bulb/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 20, transtime: 0}, {}]);
@@ -722,7 +723,7 @@ describe('Publish', () => {
it('Should use transition only once when setting brightness and color temperature for group which contains TRADFRI', async () => {
const group = zigbeeHerdsman.groups.group_with_tradfri;
await MQTT.events.message('zigbee2mqtt/group_with_tradfri/set', JSON.stringify({"state": "ON", "transition": 60, "brightness": 20, "color_temp": 400}));
await MQTT.events.message('zigbee2mqtt/group_with_tradfri/set', stringify({"state": "ON", "transition": 60, "brightness": 20, "color_temp": 400}));
await flushPromises();
expect(group.command).toHaveBeenCalledTimes(2);
expect(group.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 20, transtime: 0}, {}]);
@@ -734,7 +735,7 @@ describe('Publish', () => {
settings.set(['devices', device.ieeeAddr, 'transition'], 20);
const endpoint = device.getEndpoint(1);
const payload = {brightness: 200, transition: 10};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 200, transtime: 100}, {}]);
@@ -744,7 +745,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
const payload = {'state': 'ON', 'color': {'x': 0.701, 'y': 0.299}, 'transition': 3, 'brightness': 100};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 100, transtime: 30}, {}]);
@@ -754,27 +755,27 @@ describe('Publish', () => {
it('Should turn device off when brightness 0 is send', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness: 50, state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({brightness: 50, state: 'ON'}));
await flushPromises();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness: 0}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({brightness: 0}));
await flushPromises();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(3);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 50, transtime: 0}, {}]);
expect(endpoint.command.mock.calls[1]).toEqual(["genOnOff", "off", {}, {}]);
expect(endpoint.command.mock.calls[2]).toEqual(["genOnOff", "on", {}, {}]);
expect(MQTT.publish).toHaveBeenCalledTimes(3);
expect(MQTT.publish.mock.calls[0]).toEqual(["zigbee2mqtt/bulb_color", JSON.stringify({state: 'ON', brightness: 50}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[1]).toEqual(["zigbee2mqtt/bulb_color", JSON.stringify({state: 'OFF', brightness: 0}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[2]).toEqual(["zigbee2mqtt/bulb_color", JSON.stringify({state: 'ON', brightness: 50}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[0]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'ON', brightness: 50}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[1]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'OFF', brightness: 0}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[2]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'ON', brightness: 50}), {"qos": 0, "retain": false}, expect.any(Function)]);
});
it('Should turn device off when brightness 0 is send with light_brightness converter', async () => {
const device = zigbeeHerdsman.devices.HGZB04D;
const endpoint = device.getEndpoint(1);
const payload = {'brightness': 0};
await MQTT.events.message('zigbee2mqtt/dimmer_wall_switch/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/dimmer_wall_switch/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["genOnOff", "off", {}, {}]);
@@ -784,7 +785,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
const payload = {"color":{"hue":250, "saturation":50}};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["lightingColorCtrl", "enhancedMoveToHueAndSaturation", {"direction": 0, "enhancehue": 44891.475, "saturation": 199.21474, "transtime": 0,}, {}]);
@@ -797,7 +798,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.ZNCLDJ11LM;
const endpoint = device.getEndpoint(1);
const payload = {'state': 'OPEN'};
await MQTT.events.message('zigbee2mqtt/curtain/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
await flushPromises();
expect(endpoint.write).toHaveBeenCalledTimes(1);
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {"85": {"type": 57, "value": 100}});
@@ -807,7 +808,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.ZNCLDJ11LM;
const endpoint = device.getEndpoint(1);
const payload = {'position': 10};
await MQTT.events.message('zigbee2mqtt/curtain/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
await flushPromises();
expect(endpoint.write).toHaveBeenCalledTimes(1);
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {"85": {"type": 57, "value": 10}});
@@ -817,7 +818,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.ZNCLDJ11LM;
const endpoint = device.getEndpoint(1);
const payload = {'state': 'CLOSE'};
await MQTT.events.message('zigbee2mqtt/curtain/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
await flushPromises();
expect(endpoint.write).toHaveBeenCalledTimes(1);
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {"85": {"type": 57, "value": 0}});
@@ -827,7 +828,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.ZNCLDJ11LM;
const endpoint = device.getEndpoint(1);
const payload = {'state': 'STOP'};
await MQTT.events.message('zigbee2mqtt/curtain/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("closuresWindowCovering", "stop", {}, {});
@@ -837,7 +838,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
const payload = {state: 'ON', transition: 3};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 254, transtime: 30}, {}]);
@@ -847,7 +848,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
const payload = {state: 'ON', transition: 0};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 254, transtime: 0}, {}]);
@@ -857,7 +858,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
const payload = {state: 'OFF', transition: 1};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 0, transtime: 10}, {}]);
@@ -870,58 +871,58 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
// Set initial brightness in state
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness: 200}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({brightness: 200}));
await flushPromises();
endpoint.command.mockClear();
MQTT.publish.mockClear();
// Turn off
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'OFF', transition: 3}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF', transition: 3}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 0, transtime: 30}, {}]);
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0]).toEqual(["zigbee2mqtt/bulb_color", JSON.stringify({state: 'OFF', brightness: 0}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[0]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'OFF', brightness: 0}), {"qos": 0, "retain": false}, expect.any(Function)]);
// Bulb reports brightness while decreasing brightness
await zigbeeHerdsman.events.message({data: {currentLevel: 1}, cluster: 'genLevelCtrl', device, endpoint, type: 'attributeReport', linkquality: 10});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish.mock.calls[1]).toEqual(["zigbee2mqtt/bulb_color", JSON.stringify({state: 'OFF', brightness: 1, linkquality: 10}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[1]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'OFF', brightness: 1, linkquality: 10}), {"qos": 0, "retain": false}, expect.any(Function)]);
// Turn on again
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'ON', transition: 3}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON', transition: 3}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command.mock.calls[1]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 200, transtime: 30}, {}]);
expect(MQTT.publish).toHaveBeenCalledTimes(3);
expect(MQTT.publish.mock.calls[2]).toEqual(["zigbee2mqtt/bulb_color", JSON.stringify({state: 'ON', brightness: 200, linkquality: 10}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[2]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'ON', brightness: 200, linkquality: 10}), {"qos": 0, "retain": false}, expect.any(Function)]);
});
it('When device is turned off with transition and turned on WITHOUT transition it should restore the brightness', async () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
// Set initial brightness in state
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness: 200}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({brightness: 200}));
await flushPromises();
endpoint.command.mockClear();
MQTT.publish.mockClear();
// Turn off
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'OFF', transition: 3}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF', transition: 3}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 0, transtime: 30}, {}]);
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0]).toEqual(["zigbee2mqtt/bulb_color", JSON.stringify({state: 'OFF', brightness: 0}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[0]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'OFF', brightness: 0}), {"qos": 0, "retain": false}, expect.any(Function)]);
// Turn on again
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command.mock.calls[1]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 200, transtime: 0}, {}]);
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish.mock.calls[1]).toEqual(["zigbee2mqtt/bulb_color", JSON.stringify({state: 'ON', brightness: 200}), {"qos": 0, "retain": false}, expect.any(Function)]);
expect(MQTT.publish.mock.calls[1]).toEqual(["zigbee2mqtt/bulb_color", stringify({state: 'ON', brightness: 200}), {"qos": 0, "retain": false}, expect.any(Function)]);
});
it('Home Assistant: should set state', async () => {
@@ -929,7 +930,7 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
const payload = {state: 'ON'};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["genOnOff", "on", {}, {}]);
@@ -944,7 +945,7 @@ describe('Publish', () => {
controller.state.set(device.ieeeAddr, {state: 'ON'})
const endpoint = device.getEndpoint(1);
const payload = {state: 'ON', color_temp: 100};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["lightingColorCtrl", "moveToColorTemp", {colortemp: 100, transtime: 0}, {}]);
@@ -959,7 +960,7 @@ describe('Publish', () => {
controller.state.set(device.ieeeAddr, {state: 'OFF'})
const endpoint = device.getEndpoint(1);
const payload = {state: 'ON', color_temp: 100};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command.mock.calls[0]).toEqual(["genOnOff", "on", {}, {}]);
@@ -971,13 +972,13 @@ describe('Publish', () => {
expect(JSON.parse(MQTT.publish.mock.calls[1][1])).toStrictEqual({state: 'ON', color: {x: 0.280632719756407, y: 0.288286029784579}, color_temp: 100});
});
it('Home Assistant: should not set state when color is also set', async () => {
it('onlythis Home Assistant: should not set state when color is also set', async () => {
settings.set(['homeassistant'], true);
const device = zigbeeHerdsman.devices.bulb_color;
controller.state.set(device.ieeeAddr, {state: 'ON'})
const endpoint = device.getEndpoint(1);
const payload = {state: 'ON', color: {x: 0.41, y: 0.25}};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["lightingColorCtrl", "moveToColor", {colorx: 26869, colory: 16384, transtime: 0}, {}]);
@@ -988,7 +989,7 @@ describe('Publish', () => {
it('Should publish correct state on toggle command to zigbee bulb', async () => {
const endpoint = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'TOGGLE'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'TOGGLE'}));
await flushPromises();
// At this point the bulb has no state yet, so we cannot determine the next state and therefore shouldn't publish it to MQTT.
@@ -997,12 +998,12 @@ describe('Publish', () => {
expect(MQTT.publish).toHaveBeenCalledTimes(0);
// Turn bulb off so that the bulb gets a state.
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'OFF'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenNthCalledWith(1,
'zigbee2mqtt/bulb_color',
JSON.stringify({state: 'OFF', brightness: 0}),
stringify({state: 'OFF', brightness: 0}),
{retain: false, qos: 0}, expect.any(Function)
);
@@ -1012,13 +1013,13 @@ describe('Publish', () => {
return {currentLevel: 100};
}
});
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'TOGGLE'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'TOGGLE'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(3);
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenNthCalledWith(2,
'zigbee2mqtt/bulb_color',
JSON.stringify({state: 'ON', brightness: 100}),
stringify({state: 'ON', brightness: 100}),
{retain: false, qos: 0}, expect.any(Function)
);
});
@@ -1032,13 +1033,13 @@ describe('Publish', () => {
}
});
controller.state.state = {[device.ieeeAddr]: {state: "OFF", brightness: 0,}};
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: "OFF"}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: "OFF"}));
await flushPromises();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: "ON"}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: "ON"}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
expect(MQTT.publish).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/bulb_color', JSON.stringify({state: 'OFF', brightness: 0}), {retain: false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', JSON.stringify({state: 'ON', brightness: 100}), {retain: false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF', brightness: 0}), {retain: false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', stringify({state: 'ON', brightness: 100}), {retain: false, qos: 0}, expect.any(Function));
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command.mock.calls[0]).toEqual(["genOnOff", "off", {}, {}]);
expect(endpoint.command.mock.calls[1]).toEqual(["genOnOff", "on", {}, {}]);
@@ -1047,7 +1048,7 @@ describe('Publish', () => {
it('Should publish messages with options disableDefaultResponse', async () => {
const device = zigbeeHerdsman.devices.GLEDOPTO1112;
const endpoint = device.getEndpoint(11);
await MQTT.events.message('zigbee2mqtt/led_controller_1/set', JSON.stringify({state: 'OFF'}));
await MQTT.events.message('zigbee2mqtt/led_controller_1/set', stringify({state: 'OFF'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genOnOff", "off", {}, {disableDefaultResponse: true});
@@ -1060,7 +1061,7 @@ describe('Publish', () => {
it('Should publish messages to zigbee devices', async () => {
settings.set(['advanced', 'last_seen'], 'ISO_8601')
const endpoint = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness: '200'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({brightness: '200'}));
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
@@ -1070,7 +1071,7 @@ describe('Publish', () => {
it('Should publish brightness_move up to zigbee devices', async () => {
const endpoint = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness_move: -40}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({brightness_move: -40}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genLevelCtrl", "move", {"movemode": 1, "rate": 40}, {});
@@ -1078,7 +1079,7 @@ describe('Publish', () => {
it('Should publish brightness_move down to zigbee devices', async () => {
const endpoint = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({brightness_move: 30}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({brightness_move: 30}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genLevelCtrl", "move", {"movemode": 0, "rate": 30}, {});
@@ -1087,7 +1088,7 @@ describe('Publish', () => {
it('HS2WD-E burglar warning', async () => {
const endpoint = zigbeeHerdsman.devices.HS2WD.getEndpoint(1);
const payload = {warning: {duration: 100, mode: 'burglar', strobe: true, level: 'high'}};
await MQTT.events.message('zigbee2mqtt/siren/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/siren/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("ssIasWd", "startWarning", {"startwarninginfo": 22, "warningduration": 100}, {disableDefaultResponse: true});
@@ -1096,7 +1097,7 @@ describe('Publish', () => {
it('HS2WD-E emergency warning', async () => {
const endpoint = zigbeeHerdsman.devices.HS2WD.getEndpoint(1);
const payload = {warning: {duration: 10, mode: 'emergency', strobe: false, level: 'very_high'}};
await MQTT.events.message('zigbee2mqtt/siren/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/siren/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("ssIasWd", "startWarning", {"startwarninginfo": 51, "warningduration": 10}, {disableDefaultResponse: true});
@@ -1105,7 +1106,7 @@ describe('Publish', () => {
it('HS2WD-E emergency without level', async () => {
const endpoint = zigbeeHerdsman.devices.HS2WD.getEndpoint(1);
const payload = {warning: {duration: 10, mode: 'emergency', strobe: false}};
await MQTT.events.message('zigbee2mqtt/siren/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/siren/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("ssIasWd", "startWarning", {"startwarninginfo": 49, "warningduration": 10}, {disableDefaultResponse: true});
@@ -1114,7 +1115,7 @@ describe('Publish', () => {
it('HS2WD-E wrong payload (should use defaults)', async () => {
const endpoint = zigbeeHerdsman.devices.HS2WD.getEndpoint(1);
const payload = {warning: 'wrong'};
await MQTT.events.message('zigbee2mqtt/siren/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/siren/set', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("ssIasWd", "startWarning", {"startwarninginfo": 53, "warningduration": 10}, {disableDefaultResponse: true});
@@ -1122,14 +1123,14 @@ describe('Publish', () => {
it('Shouldnt do anythign when device is not supported', async () => {
const payload = {state: 'ON'};
await MQTT.events.message('zigbee2mqtt/unsupported2/set', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/unsupported2/set', stringify(payload));
await flushPromises();
expectNothingPublished();
});
it('Should publish state to roller shutter', async () => {
const endpoint = zigbeeHerdsman.devices.roller_shutter.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/roller_shutter/set', JSON.stringify({state: 'OPEN'}));
await MQTT.events.message('zigbee2mqtt/roller_shutter/set', stringify({state: 'OPEN'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("genLevelCtrl", "moveToLevelWithOnOff", {"level": "255", "transtime": 0}, {});
@@ -1142,7 +1143,7 @@ describe('Publish', () => {
it('Should publish to MKS-CM-W5', async () => {
const device = zigbeeHerdsman.devices['MKS-CM-W5'];
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/MKS-CM-W5/l3/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/MKS-CM-W5/l3/set', stringify({state: 'ON'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuyaDimmer", "setData", {data: [1,1], dp: 259, fn: 0, status: 0, transid: expect.any(Number)}, {disableDefaultResponse: true});
@@ -1157,7 +1158,7 @@ describe('Publish', () => {
jest.useFakeTimers();
const device = zigbeeHerdsman.devices['GL-S-007ZS'];
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/GL-S-007ZS/set', JSON.stringify({state: 'ON', brightness: 20}));
await MQTT.events.message('zigbee2mqtt/GL-S-007ZS/set', stringify({state: 'ON', brightness: 20}));
await flushPromises();
jest.runAllTimers();
await flushPromises();
@@ -1165,7 +1166,7 @@ describe('Publish', () => {
expect(endpoint.command.mock.calls[0]).toEqual([ 'genOnOff', 'on', {}, {} ]);
expect(endpoint.command.mock.calls[1]).toEqual([ 'genLevelCtrl', 'moveToLevelWithOnOff', { level: 20, transtime: 0 }, {} ]);
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0]).toEqual([ 'zigbee2mqtt/GL-S-007ZS', '{"state":"ON","brightness":20}', { qos: 0, retain: false }, expect.any(Function)]);
expect(MQTT.publish.mock.calls[0]).toEqual([ 'zigbee2mqtt/GL-S-007ZS', stringify({"state":"ON","brightness":20}), { qos: 0, retain: false }, expect.any(Function)]);
jest.useRealTimers();
});
@@ -1174,7 +1175,7 @@ describe('Publish', () => {
const endpoint = device.getEndpoint(1);
const payload = {'brightness_move': 20};
logger.error.mockClear();
await MQTT.events.message('zigbee2mqtt/bulb_color/get', JSON.stringify(payload));
await MQTT.events.message('zigbee2mqtt/bulb_color/get', stringify(payload));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(0);
expect(logger.error).toHaveBeenCalledWith("No converter available for 'get' 'brightness_move' (20)");
@@ -1186,22 +1187,22 @@ describe('Publish', () => {
const endpoint = device.getEndpoint(1);
endpoint.command.mockClear();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(1);
expect(endpoint.command.mock.calls[0]).toEqual(["genOnOff", "on", {}, {}]);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({"state": "ON", "brightness": 123}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({"state": "ON", "brightness": 123}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(2);
expect(endpoint.command.mock.calls[1]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 123, transtime: 0}, {}]);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({"state": "OFF"}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({"state": "OFF"}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(3);
expect(endpoint.command.mock.calls[2]).toEqual(["genOnOff", "off", {}, {}]);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({"state": "ON", "transition": 1.0}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({"state": "ON", "transition": 1.0}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(4);
expect(endpoint.command.mock.calls[3]).toEqual(["genLevelCtrl", "moveToLevelWithOnOff", {level: 123, transtime: 10}, {}]);
@@ -1212,13 +1213,13 @@ describe('Publish', () => {
const device = zigbeeHerdsman.devices.bulb_color;
const endpoint = device.getEndpoint(1);
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({'state': 'ON'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({'state': 'ON'}));
await flushPromises();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({'brightness': 150}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({'brightness': 150}));
await flushPromises();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({'state': 'OFF'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({'state': 'OFF'}));
await flushPromises();
await MQTT.events.message('zigbee2mqtt/bulb_color/set', JSON.stringify({'state': 'ON'}));
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({'state': 'ON'}));
await flushPromises();
expect(endpoint.command).toHaveBeenCalledTimes(4);
@@ -1228,9 +1229,9 @@ describe('Publish', () => {
expect(endpoint.command.mock.calls[3]).toEqual(["genOnOff", "on", {}, {}]);
expect(MQTT.publish).toHaveBeenCalledTimes(4);
expect(MQTT.publish.mock.calls[0]).toEqual([ 'zigbee2mqtt/bulb_color', '{"state":"ON"}', { qos: 0, retain: false }, expect.any(Function)]);
expect(MQTT.publish.mock.calls[1]).toEqual([ 'zigbee2mqtt/bulb_color', '{"state":"ON","brightness":150}', { qos: 0, retain: false }, expect.any(Function)]);
expect(MQTT.publish.mock.calls[2]).toEqual([ 'zigbee2mqtt/bulb_color', '{"state":"OFF","brightness":0}', { qos: 0, retain: false }, expect.any(Function)]);
expect(MQTT.publish.mock.calls[3]).toEqual([ 'zigbee2mqtt/bulb_color', '{"state":"ON","brightness":150}', { qos: 0, retain: false }, expect.any(Function)]);
expect(MQTT.publish.mock.calls[0]).toEqual([ 'zigbee2mqtt/bulb_color', stringify({"state":"ON"}), { qos: 0, retain: false }, expect.any(Function)]);
expect(MQTT.publish.mock.calls[1]).toEqual([ 'zigbee2mqtt/bulb_color', stringify({"state":"ON","brightness":150}), { qos: 0, retain: false }, expect.any(Function)]);
expect(MQTT.publish.mock.calls[2]).toEqual([ 'zigbee2mqtt/bulb_color', stringify({"state":"OFF","brightness":0}), { qos: 0, retain: false }, expect.any(Function)]);
expect(MQTT.publish.mock.calls[3]).toEqual([ 'zigbee2mqtt/bulb_color', stringify({"state":"ON","brightness":150}), { qos: 0, retain: false }, expect.any(Function)]);
});
});
+6 -5
View File
@@ -1,5 +1,6 @@
const data = require('./stub/data');
const logger = require('./stub/logger');
const stringify = require('json-stable-stringify');
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
const MQTT = require('./stub/mqtt');
const settings = require('../lib/util/settings');
@@ -28,7 +29,7 @@ describe('Receive', () => {
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/button', JSON.stringify({action: 'single', click: 'single', linkquality: 10}), {retain: false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/button', stringify({action: 'single', click: 'single', linkquality: 10}), {retain: false, qos: 0}, expect.any(Function));
});
it('Should handle a zigbee message which uses ep (left)', async () => {
@@ -74,7 +75,7 @@ describe('Receive', () => {
await zigbeeHerdsman.events.message({data: {currentPositionLiftPercentage: 90, currentPositionTiltPercentage: 80}, cluster: 'closuresWindowCovering', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/J1_cover', JSON.stringify({position: 10, tilt: 20, linkquality: 10}), {retain: false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/J1_cover', stringify({position: 10, tilt: 20, linkquality: 10}), {retain: false, qos: 0}, expect.any(Function));
// Inverted
MQTT.publish.mockClear();
@@ -82,7 +83,7 @@ describe('Receive', () => {
await zigbeeHerdsman.events.message({data: {currentPositionLiftPercentage: 90, currentPositionTiltPercentage: 80}, cluster: 'closuresWindowCovering', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10});
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/J1_cover', JSON.stringify({position: 90, tilt: 80, linkquality: 10}), {retain: false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/J1_cover', stringify({position: 90, tilt: 80, linkquality: 10}), {retain: false, qos: 0}, expect.any(Function));
});
it('Should allow to disable the legacy integration', async () => {
@@ -93,7 +94,7 @@ describe('Receive', () => {
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/button', JSON.stringify({action: 'single', linkquality: 10}), {retain: false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/button', stringify({action: 'single', linkquality: 10}), {retain: false, qos: 0}, expect.any(Function));
});
it('Should debounce messages', async () => {
@@ -175,7 +176,7 @@ describe('Receive', () => {
const device = zigbeeHerdsman.devices.bulb;
settings.set(['devices', device.ieeeAddr, 'debounce'], 0.1);
await zigbeeHerdsman.events.message({data: {onOff: 0}, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10});
await MQTT.events.message('zigbee2mqtt/bulb/set', JSON.stringify({state: 'ON'}));
await MQTT.events.message('zigbee2mqtt/bulb/set', stringify({state: 'ON'}));
await flushPromises();
jest.runAllTimers();
expect(MQTT.publish).toHaveBeenCalledTimes(2);
+3 -2
View File
@@ -2,6 +2,7 @@ const tmp = require('tmp');
const yaml = require('../../lib/util/yaml');
const path = require('path');
const fs = require('fs');
const stringify = require('json-stable-stringify');
const mockDir = tmp.dirSync().name;
const mockDirStorage = tmp.dirSync().name;
@@ -186,7 +187,7 @@ function writeDefaultConfiguration() {
}
function writeEmptyState() {
fs.writeFileSync(stateFile, JSON.stringify({}));
fs.writeFileSync(stateFile, stringify({}));
}
function removeState() {
@@ -212,7 +213,7 @@ function writeDefaultState() {
},
}
fs.writeFileSync(path.join(mockDir, 'state.json'), JSON.stringify(state));
fs.writeFileSync(path.join(mockDir, 'state.json'), stringify(state));
}
jest.mock('../../lib/util/data', () => ({