Merge branch 'dev'

This commit is contained in:
Koen Kanters committed 2022-09-01 08:37:54 +02:00
commit 96d72103f2
9 files changed
+648 -459

No files matched your search

+8 -2
View File
@@ -292,6 +292,7 @@ export default class Groups extends Extension {
groupKey, deviceKey, skipDisableReporting, resolvedEntityEndpoint,
} = parsed;
const message = utils.parseJSON(data.message, data.message);
let changedGroups: Group[] = [];
const responseData: KeyValue = {device: deviceKey};
if (groupKey) {
@@ -320,6 +321,7 @@ export default class Groups extends Extension {
logger.info(`Adding '${resolvedEntityDevice.name}' to '${resolvedEntityGroup.name}'`);
await resolvedEntityEndpoint.addToGroup(resolvedEntityGroup.zh);
settings.addDeviceToGroup(resolvedEntityGroup.ID.toString(), keys);
changedGroups.push(resolvedEntityGroup);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
@@ -333,6 +335,7 @@ export default class Groups extends Extension {
logger.info(`Removing '${resolvedEntityDevice.name}' from '${resolvedEntityGroup.name}'`);
await resolvedEntityEndpoint.removeFromGroup(resolvedEntityGroup.zh);
settings.removeDeviceFromGroup(resolvedEntityGroup.ID.toString(), keys);
changedGroups.push(resolvedEntityGroup);
/* istanbul ignore else */
if (settings.get().advanced.legacy_api) {
@@ -344,6 +347,7 @@ export default class Groups extends Extension {
}
} else { // remove_all
logger.info(`Removing '${resolvedEntityDevice.name}' from all groups`);
changedGroups = this.zigbee.groups().filter((g) => g.zh.members.includes(resolvedEntityEndpoint));
await resolvedEntityEndpoint.removeFromAllGroups();
for (const settingsGroup of settings.getGroups()) {
settings.removeDeviceFromGroup(settingsGroup.ID.toString(), keys);
@@ -372,8 +376,10 @@ export default class Groups extends Extension {
if (error) {
logger.error(error);
} else {
this.eventBus.emitGroupMembersChanged({
group: resolvedEntityGroup, action: type, endpoint: resolvedEntityEndpoint, skipDisableReporting});
for (const group of changedGroups) {
this.eventBus.emitGroupMembersChanged({
group, action: type, endpoint: resolvedEntityEndpoint, skipDisableReporting});
}
}
}
}
+53 -58
View File
@@ -375,6 +375,7 @@ export default class HomeAssistant extends Extension {
const tilt = exposes.find((expose) => expose.features.find((e) => e.name === 'tilt'));
const motorState = definitionExposes?.find((e) => e.type === 'enum' && e.name === 'motor_state' &&
e.access === ACCESS_STATE);
const running = definitionExposes?.find((e) => e.type === 'binary' && e.name === 'running');
const discoveryEntry: DiscoveryEntry = {
type: 'cover',
@@ -382,48 +383,38 @@ export default class HomeAssistant extends Extension {
object_id: endpoint ? `cover_${endpoint}` : 'cover',
discovery_payload: {
command_topic_prefix: endpoint,
command_topic: true,
state_topic: true,
},
};
// For covers only supporting tilt don't discover the command/state_topic, otherwise
// HA does not correctly reflect the state
// - https://github.com/home-assistant/core/issues/51793
// - https://github.com/Koenkk/zigbee-herdsman-converters/pull/2663
if (!tilt || (tilt && position)) {
discoveryEntry.discovery_payload.command_topic = true;
discoveryEntry.discovery_payload.state_topic = !position;
discoveryEntry.discovery_payload.command_topic_prefix = endpoint;
// For curtains that have `motor_state` lookup a possible state names and make this
// available for discovery. If the curtains only support the `running` value,
// then we use it anyway. The movement direction is calculated (assumed) in this case.
if (motorState) {
const openingLookup = ['opening', 'open', 'forward', 'up', 'rising'];
const closingLookup = ['closing', 'close', 'backward', 'back', 'reverse', 'down', 'declining'];
const stoppedLookup = ['stopped', 'stop', 'pause', 'paused'];
// For curtains that have `motor_state` lookup a possible state names and make this
// available for discovery. If the curtains only support the `running` value,
// then we use it anyway. The movement direction is calculated (assumed) in this case.
if (motorState) {
const openingLookup = ['opening', 'open', 'forward', 'up', 'rising'];
const closingLookup = ['closing', 'close', 'backward', 'back', 'reverse', 'down', 'declining'];
const stoppedLookup = ['stopped', 'stop', 'pause', 'paused'];
const openingState = motorState.values.find((s) => openingLookup.includes(s.toLowerCase()));
const closingState = motorState.values.find((s) => closingLookup.includes(s.toLowerCase()));
const stoppedState = motorState.values.find((s) => stoppedLookup.includes(s.toLowerCase()));
const openingState = motorState.values.find((s) => openingLookup.includes(s.toLowerCase()));
const closingState = motorState.values.find((s) => closingLookup.includes(s.toLowerCase()));
const stoppedState = motorState.values.find((s) => stoppedLookup.includes(s.toLowerCase()));
if (openingState && closingState && stoppedState) {
discoveryEntry.discovery_payload.state_topic = true;
discoveryEntry.discovery_payload.state_opening = openingState;
discoveryEntry.discovery_payload.state_closing = closingState;
discoveryEntry.discovery_payload.state_stopped = stoppedState;
discoveryEntry.discovery_payload.value_template = `{% if not value_json.motor_state %} ` +
`${stoppedState} {% else %} {{ value_json.motor_state }} {% endif %}`;
}
} else {
const running = definitionExposes?.find((e) => e.type === 'binary' && e.name === 'running');
if (running) {
discoveryEntry.discovery_payload.state_topic = true;
discoveryEntry.discovery_payload.value_template = `{% if not value_json.running %} ` +
`stopped {% else %} {% if value_json.position > 0 %} closing {% else %} ` +
`opening {% endif %} {% endif %}`;
}
if (openingState && closingState && stoppedState) {
discoveryEntry.discovery_payload.state_opening = openingState;
discoveryEntry.discovery_payload.state_closing = closingState;
discoveryEntry.discovery_payload.state_stopped = stoppedState;
discoveryEntry.discovery_payload.value_template = `{% if not value_json.motor_state %} ` +
`${stoppedState} {% else %} {{ value_json.motor_state }} {% endif %}`;
}
} else if (running) {
discoveryEntry.discovery_payload.value_template = `{% if not value_json.running %} ` +
`stopped {% else %} {% if value_json.position > 0 %} closing {% else %} ` +
`opening {% endif %} {% endif %}`;
} else {
discoveryEntry.discovery_payload.value_template = `{{ value_json.state }}`;
discoveryEntry.discovery_payload.state_open = 'OPEN';
discoveryEntry.discovery_payload.state_closed = 'CLOSE';
}
if (!position && !tilt) {
@@ -529,6 +520,7 @@ export default class HomeAssistant extends Extension {
garage_door_contact: {device_class: 'garage_door', payload_on: false, payload_off: true},
eco_mode: {entity_category: 'config', icon: 'mdi:leaf'},
expose_pin: {entity_category: 'config', icon: 'mdi:pin'},
flip_indicator_light: {entity_category: 'config', icon: 'mdi:arrow-left-right'},
gas: {device_class: 'gas'},
invert_cover: {entity_category: 'config', icon: 'mdi:arrow-left-right'},
led_disabled_night: {entity_category: 'config', icon: 'mdi:led-off'},
@@ -802,39 +794,42 @@ export default class HomeAssistant extends Extension {
week: {entity_category: 'config', icon: 'mdi:calendar-clock'},
};
const valueTemplate = firstExpose.access & ACCESS_STATE ?
`{{ value_json.${firstExpose.property} }}` : undefined;
if (firstExpose.access & ACCESS_STATE) {
discoveryEntries.push({
type: 'sensor',
object_id: firstExpose.property,
mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: {
value_template: `{{ value_json.${firstExpose.property} }}`,
value_template: valueTemplate,
enabled_by_default: !(firstExpose.access & ACCESS_SET),
...lookup[firstExpose.name],
},
});
}
/**
* If enum attribute has SET access then expose as SELECT entity too.
* Note: currently both sensor and select are discovered, this is to avoid
* breaking changes for sensors already existing in HA (legacy).
*/
if ((firstExpose.access & ACCESS_SET)) {
discoveryEntries.push({
type: 'select',
object_id: firstExpose.property,
mockProperties: [{property: firstExpose.property, value: null}],
discovery_payload: {
value_template: `{{ value_json.${firstExpose.property} }}`,
state_topic: true,
command_topic_prefix: endpoint,
command_topic: true,
command_topic_postfix: firstExpose.property,
options: firstExpose.values.map((v) => v.toString()),
...lookup[firstExpose.name],
},
});
}
/**
* If enum attribute has SET access then expose as SELECT entity too.
* Note: currently both sensor and select are discovered, this is to avoid
* breaking changes for sensors already existing in HA (legacy).
*/
if ((firstExpose.access & ACCESS_SET)) {
discoveryEntries.push({
type: 'select',
object_id: firstExpose.property,
mockProperties: [], // Already mocked above in case access STATE is supported
discovery_payload: {
value_template: valueTemplate,
state_topic: !!(firstExpose.access & ACCESS_STATE),
command_topic_prefix: endpoint,
command_topic: true,
command_topic_postfix: firstExpose.property,
options: firstExpose.values.map((v) => v.toString()),
...lookup[firstExpose.name],
},
});
}
} else if (firstExpose.type === 'text' || firstExpose.type === 'composite') {
if (firstExpose.access & ACCESS_STATE) {
+2 -3
View File
@@ -98,10 +98,9 @@ export default class MQTT {
}, utils.seconds(10));
logger.info('Connected to MQTT server');
await this.publishStateOnline();
if (this.initialConnect) {
await this.publishStateOnline();
} else {
if (!this.initialConnect) {
this.republishRetainedTimer = setTimeout(() => {
// Republish retained messages in case MQTT broker does not persist them.
// https://github.com/Koenkk/zigbee2mqtt/issues/9629
+558 -374
View File
File diff suppressed because it is too large. Load diff
+13 -13
View File
@@ -1,6 +1,6 @@
{
"name": "zigbee2mqtt",
"version": "1.27.0",
"version": "1.27.0-dev",
"description": "Zigbee to MQTT bridge using Zigbee-herdsman",
"main": "index.js",
"repository": {
@@ -47,7 +47,7 @@
"humanize-duration": "^3.27.2",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"jszip": "^3.10.0",
"jszip": "^3.10.1",
"mkdir-recursive": "^0.4.0",
"moment": "^2.29.4",
"mqtt": "4.3.7",
@@ -60,29 +60,29 @@
"winston-syslog": "^2.6.0",
"winston-transport": "^4.5.0",
"ws": "^8.8.1",
"zigbee-herdsman": "0.14.46",
"zigbee-herdsman-converters": "14.0.583",
"zigbee2mqtt-frontend": "0.6.107"
"zigbee-herdsman": "0.14.51",
"zigbee-herdsman-converters": "14.0.610",
"zigbee2mqtt-frontend": "0.6.110"
},
"devDependencies": {
"@babel/core": "^7.18.9",
"@babel/plugin-proposal-decorators": "^7.18.9",
"@babel/preset-env": "^7.18.9",
"@babel/core": "^7.18.10",
"@babel/plugin-proposal-decorators": "^7.18.10",
"@babel/preset-env": "^7.18.10",
"@babel/preset-typescript": "^7.18.6",
"@types/debounce": "^1.2.1",
"@types/finalhandler": "^1.1.1",
"@types/humanize-duration": "^3.27.1",
"@types/jest": "^28.1.6",
"@types/jest": "^28.1.7",
"@types/js-yaml": "^4.0.5",
"@types/object-assign-deep": "^0.4.0",
"@types/rimraf": "^3.0.2",
"@types/ws": "^8.5.3",
"@typescript-eslint/eslint-plugin": "^5.31.0",
"@typescript-eslint/parser": "^5.31.0",
"@typescript-eslint/eslint-plugin": "^5.33.1",
"@typescript-eslint/parser": "^5.33.1",
"babel-jest": "^28.1.3",
"eslint": "^8.20.0",
"eslint": "^8.22.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-jest": "^26.7.0",
"eslint-plugin-jest": "^26.8.5",
"jest": "^28.1.3",
"tmp": "^0.2.1",
"typescript": "^4.7.4"
+1 -1
View File
@@ -90,7 +90,7 @@ describe('Bridge', () => {
await resetExtension();
expect(MQTT.publish).toHaveBeenCalledWith(
'zigbee2mqtt/bridge/devices',
stringify([{"date_code":null,"definition":null,"endpoints":{"1":{"bindings":[],"clusters":{"input":[],"output":[]},"configured_reportings":[],"scenes":[]}},"friendly_name":"Coordinator","ieee_address":"0x00124b00120144ae","interview_completed":false,"interviewing":false,"model_id":null,"network_address":0,"power_source":null,"software_build_id":null,"supported":false,"type":"Coordinator"},{"date_code":null,"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","exposes":[{"features":[{"access":7,"description":"On/off state of this light","name":"state","property":"state","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","name":"brightness","property":"brightness","type":"numeric","value_max":254,"value_min":0},{"access":7,"description":"Color temperature of this light","name":"color_temp","presets":[{"description":"Coolest temperature supported","name":"coolest","value":250},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":454}],"property":"color_temp","type":"numeric","unit":"mired","value_max":454,"value_min":250},{"access":7,"description":"Color temperature after cold power on of this light","name":"color_temp_startup","presets":[{"description":"Coolest temperature supported","name":"coolest","value":250},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":454},{"description":"Restore previous color_temp on cold power on","name":"previous","value":65535}],"property":"color_temp_startup","type":"numeric","unit":"mired","value_max":454,"value_min":250}],"type":"light"},{"access":2,"description":"Triggers an effect on the light (e.g. make light blink for a few seconds)","name":"effect","property":"effect","type":"enum","values":["blink","breathe","okay","channel_change","finish_effect","stop_effect"]},{"access":7,"description":"Controls the behavior when the device is powered on","name":"power_on_behavior","property":"power_on_behavior","type":"enum","values":["off","previous","on"]},{"access":1,"description":"Link quality (signal strength)","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"LED1545G12","options":[{"access":2,"description":"Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).","name":"transition","property":"transition","type":"numeric","value_min":0},{"access":2,"description":"When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).","name":"color_sync","property":"color_sync","type":"binary","value_off":false,"value_on":true}],"supports_ota":true,"vendor":"IKEA"},"description":"this is my bulb","endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl","lightingColorCtrl"],"output":["genScenes","genOta"]},"configured_reportings":[{"attribute":"onOff","cluster":"genOnOff","maximum_report_interval":10,"minimum_report_interval":1,"reportable_change":20}],"scenes":[]}},"friendly_name":"bulb","ieee_address":"0x000b57fffec6a5b2","interview_completed":true,"interviewing":false,"model_id":"TRADFRI bulb E27 WS opal 980lm","network_address":40369,"power_source":"Mains (single phase)","software_build_id":null,"supported":true,"type":"Router"},{"date_code":"2019.09","definition":{"description":"Hue Go","exposes":[{"features":[{"access":7,"description":"On/off state of this light","name":"state","property":"state","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","name":"brightness","property":"brightness","type":"numeric","value_max":254,"value_min":0},{"access":7,"description":"Color temperature of this light","name":"color_temp","presets":[{"description":"Coolest temperature supported","name":"coolest","value":150},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value"Line truncated
stringify([{"date_code":null,"definition":null,"endpoints":{"1":{"bindings":[],"clusters":{"input":[],"output":[]},"configured_reportings":[],"scenes":[]}},"friendly_name":"Coordinator","ieee_address":"0x00124b00120144ae","interview_completed":false,"interviewing":false,"model_id":null,"network_address":0,"power_source":null,"software_build_id":null,"supported":false,"type":"Coordinator"},{"date_code":null,"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","exposes":[{"features":[{"access":7,"description":"On/off state of this light","name":"state","property":"state","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","name":"brightness","property":"brightness","type":"numeric","value_max":254,"value_min":0},{"access":7,"description":"Color temperature of this light","name":"color_temp","presets":[{"description":"Coolest temperature supported","name":"coolest","value":250},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":454}],"property":"color_temp","type":"numeric","unit":"mired","value_max":454,"value_min":250},{"access":7,"description":"Color temperature after cold power on of this light","name":"color_temp_startup","presets":[{"description":"Coolest temperature supported","name":"coolest","value":250},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value":454},{"description":"Restore previous color_temp on cold power on","name":"previous","value":65535}],"property":"color_temp_startup","type":"numeric","unit":"mired","value_max":454,"value_min":250}],"type":"light"},{"access":2,"description":"Triggers an effect on the light (e.g. make light blink for a few seconds)","name":"effect","property":"effect","type":"enum","values":["blink","breathe","okay","channel_change","finish_effect","stop_effect"]},{"access":7,"description":"Controls the behavior when the device is powered on","name":"power_on_behavior","property":"power_on_behavior","type":"enum","values":["off","previous","on"]},{"access":1,"description":"Link quality (signal strength)","name":"linkquality","property":"linkquality","type":"numeric","unit":"lqi","value_max":255,"value_min":0}],"model":"LED1545G12","options":[{"access":2,"description":"Controls the transition time (in seconds) of on/off, brightness, color temperature (if applicable) and color (if applicable) changes. Defaults to `0` (no transition).","name":"transition","property":"transition","type":"numeric","value_min":0},{"access":2,"description":"When enabled colors will be synced, e.g. if the light supports both color x/y and color temperature a conversion from color x/y to color temperature will be done when setting the x/y color (default true).","name":"color_sync","property":"color_sync","type":"binary","value_off":false,"value_on":true}],"supports_ota":true,"vendor":"IKEA"},"description":"this is my bulb","endpoints":{"1":{"bindings":[],"clusters":{"input":["genBasic","genScenes","genOnOff","genLevelCtrl","lightingColorCtrl"],"output":["genScenes","genOta"]},"configured_reportings":[{"attribute":"onOff","cluster":"genOnOff","maximum_report_interval":10,"minimum_report_interval":1,"reportable_change":20}],"scenes":[]}},"friendly_name":"bulb","ieee_address":"0x000b57fffec6a5b2","interview_completed":true,"interviewing":false,"model_id":"TRADFRI bulb E27 WS opal 980lm","network_address":40369,"power_source":"Mains (single phase)","software_build_id":null,"supported":true,"type":"Router"},{"date_code":"2019.09","definition":{"description":"Hue Go","exposes":[{"features":[{"access":7,"description":"On/off state of this light","name":"state","property":"state","type":"binary","value_off":"OFF","value_on":"ON","value_toggle":"TOGGLE"},{"access":7,"description":"Brightness of this light","name":"brightness","property":"brightness","type":"numeric","value_max":254,"value_min":0},{"access":7,"description":"Color temperature of this light","name":"color_temp","presets":[{"description":"Coolest temperature supported","name":"coolest","value":150},{"description":"Cool temperature (250 mireds / 4000 Kelvin)","name":"cool","value":250},{"description":"Neutral temperature (370 mireds / 2700 Kelvin)","name":"neutral","value":370},{"description":"Warm temperature (454 mireds / 2200 Kelvin)","name":"warm","value":454},{"description":"Warmest temperature supported","name":"warmest","value"Line truncated
{ retain: true, qos: 0 },
expect.any(Function)
);
+3 -2
View File
@@ -651,7 +651,7 @@ describe('Controller', () => {
MQTT.events['connect']();
await flushPromises();
jest.runOnlyPendingTimers();
expect(MQTT.publish).toHaveBeenCalledTimes(12);
expect(MQTT.publish).toHaveBeenCalledTimes(13);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), { retain: true, qos: 0 }, expect.any(Function));
});
@@ -662,7 +662,8 @@ describe('Controller', () => {
await flushPromises();
await MQTT.events.message('zigbee2mqtt/bridge/state', 'online');
jest.runOnlyPendingTimers();
expect(MQTT.publish).toHaveBeenCalledTimes(0);
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/state', expect.any(String), { retain: true, qos: 0 }, expect.any(Function));
});
it('Should prevent any message being published with retain flag when force_disable_retain is set', async () => {
+4
View File
@@ -811,6 +811,10 @@ describe('HomeAssistant extension', () => {
set_position_topic: 'zigbee2mqtt/smart vent/set',
set_position_template: '{ "position": {{ position }} }',
position_template: '{{ value_json.position }}',
state_topic: 'zigbee2mqtt/smart vent',
value_template: `{{ value_json.state }}`,
state_open: 'OPEN',
state_closed: 'CLOSE',
json_attributes_topic: 'zigbee2mqtt/smart vent',
name: 'smart vent',
unique_id: '0x0017880104e45551_cover_zigbee2mqtt',
+6 -6
View File
@@ -84,7 +84,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', stringify({position: 10, tilt: 20}), {retain: false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/J1_cover', stringify({position: 10, tilt: 20, state: 'OPEN'}), {retain: false, qos: 0}, expect.any(Function));
// Inverted
MQTT.publish.mockClear();
@@ -92,7 +92,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', stringify({position: 90, tilt: 80}), {retain: false, qos: 0}, expect.any(Function));
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/J1_cover', stringify({position: 90, tilt: 80, state: 'OPEN'}), {retain: false, qos: 0}, expect.any(Function));
});
it('Should allow to disable the legacy integration', async () => {
@@ -242,15 +242,15 @@ describe('Receive', () => {
expect(MQTT.publish.mock.calls[0][2]).toStrictEqual({"qos": 1, "retain": false});
});
it('Should handle a zigbee message with voltage 3010', async () => {
it('Should handle a zigbee message with voltage 2990', async () => {
const device = zigbeeHerdsman.devices.WXKG02LM_rev1;
const data = {'65281': {'1': 3010}}
const data = {'65281': {'1': 2990}}
const payload = {data, cluster: 'genBasic', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
await zigbeeHerdsman.events.message(payload);
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/button_double_key');
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({battery: 46, voltage: 3010});
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({battery: 93, voltage: 2990});
expect(MQTT.publish.mock.calls[0][2]).toStrictEqual({"qos": 0, "retain": false});
});
@@ -262,7 +262,7 @@ describe('Receive', () => {
await flushPromises();
expect(MQTT.publish).toHaveBeenCalledTimes(1);
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/occupancy_sensor');
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({'battery': 56, 'illuminance': 381, "illuminance_lux": 381, 'voltage': 3045, 'device_temperature': 19, 'power_outage_count': 34});
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({'battery': 100, 'illuminance': 381, "illuminance_lux": 381, 'voltage': 3045, 'device_temperature': 19, 'power_outage_count': 34});
expect(MQTT.publish.mock.calls[0][2]).toStrictEqual({"qos": 0, "retain": false});
});