mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-08-29 23:28:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e43c371d76 | ||
|
|
a7e85a0bd9 | ||
|
|
7f6f6a69c7 | ||
|
|
87667bdac7 | ||
|
|
bd8edd70bb | ||
|
|
4a3e6b9561 | ||
|
|
f92134fc5f | ||
|
|
9dba2d7f11 | ||
|
|
9289a26f11 | ||
|
|
17054627e9 | ||
|
|
66c4ca072e | ||
|
|
3b221d4fb0 | ||
|
|
4464554f90 | ||
|
|
b15cd433c4 | ||
|
|
4b5a03c6c5 | ||
|
|
ca4badca4d | ||
|
|
1b3dd7540b | ||
|
|
c7183ca080 | ||
|
|
119531e9e2 | ||
|
|
85be7d28d5 | ||
|
|
6c147dae6c | ||
|
|
1fe138c34c | ||
|
|
1258aee464 | ||
|
|
fbb0ed0e67 | ||
|
|
8510e99010 | ||
|
|
342955666a | ||
|
|
50cb421f64 | ||
|
|
16398b7810 | ||
|
|
f3b5aee173 | ||
|
|
e8f85b35c1 | ||
|
|
dd8f13aa8d | ||
|
|
ced2ba7344 | ||
|
|
a2f8534823 | ||
|
|
d05ef70901 | ||
|
|
4bc0c3ed6b | ||
|
|
489d1de756 | ||
|
|
499b993410 | ||
|
|
9f0b16291a | ||
|
|
98a6a2dfe8 | ||
|
|
fb14ba20ca | ||
|
|
b68b984de6 | ||
|
|
1d36dfd932 | ||
|
|
672da64913 | ||
|
|
cb29e164f8 | ||
|
|
1e6a457190 | ||
|
|
cd22dafc89 | ||
|
|
83d8a0e6a2 | ||
|
|
e0b765ba88 | ||
|
|
5e2df18b75 | ||
|
|
9b48334e0e | ||
|
|
c8ae15e92d | ||
|
|
f88ac154a2 | ||
|
|
e66fe8534e | ||
|
|
c2e95bdb6a | ||
|
|
7c5ef8a3d6 | ||
|
|
4a2190f969 | ||
|
|
a3ed236524 | ||
|
|
f4d8e6f4c5 | ||
|
|
360a777a4e | ||
|
|
5ed8b36cf2 | ||
|
|
9540b6c182 | ||
|
|
31f002294e | ||
|
|
ecfc7e8906 | ||
|
|
60bf855e74 | ||
|
|
c3f6e793fa | ||
|
|
48117fddfa | ||
|
|
7e70a5984f | ||
|
|
4589c9f691 | ||
|
|
d81c15b5af | ||
|
|
043ae738d2 | ||
|
|
68184d669e | ||
|
|
fb3ee70c7a | ||
|
|
c5697e6c2b | ||
|
|
58c800181a | ||
|
|
fd023268c5 | ||
|
|
9dc93c7d27 | ||
|
|
c943992e49 | ||
|
|
dd9e92f81f | ||
|
|
60de5b7676 | ||
|
|
6c0430b6e2 | ||
|
|
ddc4345f01 | ||
|
|
41053aa024 | ||
|
|
08ceae235e | ||
|
|
599380dd63 | ||
|
|
a426ee1041 | ||
|
|
939fdf0496 | ||
|
|
3cffc2fdd8 | ||
|
|
22d2170d41 | ||
|
|
50a9de4e2b | ||
|
|
2c12cbaf18 | ||
|
|
ef6826f263 | ||
|
|
ceeaabafbc | ||
|
|
c49f5b157d | ||
|
|
0a71e39253 | ||
|
|
52a0f68c87 | ||
|
|
457fc9da7c | ||
|
|
5b39884c13 | ||
|
|
4d7b1a6c03 | ||
|
|
50b5c67dde | ||
|
|
005ada0237 | ||
|
|
e9b1080734 | ||
|
|
56cc1432e9 | ||
|
|
e3dcc7b0a6 | ||
|
|
37aea8df8f | ||
|
|
f8d64b67ad | ||
|
|
77fc3a9308 | ||
|
|
db3730ea44 | ||
|
|
98ceaa04bb | ||
|
|
e3e4a0a440 | ||
|
|
52150b7257 | ||
|
|
5727fe3a49 |
@@ -80,7 +80,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
node: [14, 16, 18]
|
||||
node: [14, 16, 18, 19]
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: true
|
||||
steps:
|
||||
|
||||
@@ -11,4 +11,4 @@ jobs:
|
||||
- name: 'Checkout repository'
|
||||
uses: actions/checkout@v3
|
||||
- name: 'Dependency review'
|
||||
uses: actions/dependency-review-action@v2
|
||||
uses: actions/dependency-review-action@v3
|
||||
|
||||
@@ -3,6 +3,7 @@ on:
|
||||
|
||||
name: GHCR cleanup
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -3,8 +3,13 @@ on:
|
||||
repository_dispatch:
|
||||
types: update_frontend
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
update_frontend:
|
||||
permissions:
|
||||
contents: write # to create branch (peter-evans/create-pull-request)
|
||||
pull-requests: write # to create a PR (peter-evans/create-pull-request)
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -3,8 +3,13 @@ on:
|
||||
repository_dispatch:
|
||||
types: update_zh
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
update_zh:
|
||||
permissions:
|
||||
contents: write # to create branch (peter-evans/create-pull-request)
|
||||
pull-requests: write # to create a PR (peter-evans/create-pull-request)
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -3,8 +3,13 @@ on:
|
||||
repository_dispatch:
|
||||
types: update_zhc
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
update_zhc:
|
||||
permissions:
|
||||
contents: write # to create branch (peter-evans/create-pull-request)
|
||||
pull-requests: write # to create a PR (peter-evans/create-pull-request)
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -54,7 +54,7 @@ Zigbee2MQTT integrates well with (almost) every home automation solution because
|
||||
|
||||
### [Home Assistant](https://www.home-assistant.io/)
|
||||
- [Home Assistant](https://www.home-assistant.io/hassio/): Using [the official addon](https://github.com/zigbee2mqtt/hassio-zigbee2mqtt)
|
||||
- Generic install or Hassbian: Using instructions [here](https://www.zigbee2mqtt.io/guide/usage/integrations/home_assistant.html)
|
||||
- Generic install: using instructions [here](https://www.zigbee2mqtt.io/guide/usage/integrations/home_assistant.html)
|
||||
|
||||
<img align="left" height="100px" width="100px" src="https://user-images.githubusercontent.com/2734836/47615848-b8dd8700-dabd-11e8-9d77-175002dd8987.png">
|
||||
|
||||
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
FROM node:16-alpine3.15 as base
|
||||
FROM node:18-alpine3.16 as base
|
||||
|
||||
WORKDIR /app
|
||||
RUN apk add --no-cache tzdata eudev tini
|
||||
@@ -28,5 +28,7 @@ RUN mkdir /app/data
|
||||
ARG COMMIT
|
||||
RUN echo "$COMMIT" > dist/.hash
|
||||
|
||||
ENV NODE_ENV production
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
CMD [ "/sbin/tini", "--", "node", "index.js"]
|
||||
@@ -1,5 +1,3 @@
|
||||
require('core-js/features/object/from-entries');
|
||||
require('core-js/features/array/flat');
|
||||
const semver = require('semver');
|
||||
const engines = require('./package.json').engines;
|
||||
const fs = require('fs');
|
||||
@@ -120,10 +118,16 @@ async function handleQuit() {
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv.length === 3 && process.argv[2] === 'writehash') {
|
||||
writeHash();
|
||||
if (require.main === module || require.main.filename.endsWith(path.sep + 'cli.js')) {
|
||||
if (process.argv.length === 3 && process.argv[2] === 'writehash') {
|
||||
writeHash();
|
||||
} else {
|
||||
process.on('SIGINT', handleQuit);
|
||||
process.on('SIGTERM', handleQuit);
|
||||
start();
|
||||
}
|
||||
} else {
|
||||
process.on('SIGINT', handleQuit);
|
||||
process.on('SIGTERM', handleQuit);
|
||||
start();
|
||||
module.exports = {start};
|
||||
}
|
||||
|
||||
+4
-2
@@ -39,7 +39,7 @@ const AllExtensions = [
|
||||
type ExtensionArgs = [Zigbee, MQTT, State, PublishEntityState, EventBus,
|
||||
(enable: boolean, name: string) => Promise<void>, () => void, (extension: Extension) => Promise<void>];
|
||||
|
||||
class Controller {
|
||||
export class Controller {
|
||||
private eventBus: EventBus;
|
||||
private zigbee: Zigbee;
|
||||
private state: State;
|
||||
@@ -49,7 +49,7 @@ class Controller {
|
||||
private extensions: Extension[];
|
||||
private extensionArgs: ExtensionArgs;
|
||||
|
||||
constructor(restartCallback: () => void, exitCallback: (code: number) => void) {
|
||||
constructor(restartCallback: () => void, exitCallback: (code: number, restart: boolean) => void) {
|
||||
logger.init();
|
||||
this.eventBus = new EventBus( /* istanbul ignore next */ (error) => {
|
||||
logger.error(`Error: ${error.message}`);
|
||||
@@ -163,6 +163,8 @@ class Controller {
|
||||
|
||||
this.eventBus.onLastSeenChanged(this,
|
||||
(data) => utils.publishLastSeen(data, settings.get(), false, this.publishEntityState));
|
||||
|
||||
logger.info(`Zigbee2MQTT started!`);
|
||||
}
|
||||
|
||||
@bind async enableDisableExtension(enable: boolean, name: string): Promise<void> {
|
||||
|
||||
+16
-2
@@ -68,7 +68,8 @@ const reportClusters: {[s: string]:
|
||||
type PollOnMessage = {
|
||||
cluster: {[s: string]: {type: string, data: KeyValue}[]}
|
||||
read: {cluster: string, attributes: string[], attributesForEndpoint?: (endpoint: zh.Endpoint) => Promise<string[]>}
|
||||
manufacturerIDs: number[]
|
||||
manufacturerIDs: number[],
|
||||
manufacturerNames: string [],
|
||||
}[];
|
||||
|
||||
const pollOnMessage: PollOnMessage = [
|
||||
@@ -102,6 +103,10 @@ const pollOnMessage: PollOnMessage = [
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.TELINK,
|
||||
],
|
||||
manufacturerNames: [
|
||||
'GLEDOPTO',
|
||||
'Trust International B.V.\u0000',
|
||||
],
|
||||
},
|
||||
{
|
||||
cluster: {
|
||||
@@ -133,6 +138,10 @@ const pollOnMessage: PollOnMessage = [
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.TELINK,
|
||||
],
|
||||
manufacturerNames: [
|
||||
'GLEDOPTO',
|
||||
'Trust International B.V.\u0000',
|
||||
],
|
||||
},
|
||||
{
|
||||
cluster: {
|
||||
@@ -160,6 +169,10 @@ const pollOnMessage: PollOnMessage = [
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.TELINK,
|
||||
],
|
||||
manufacturerNames: [
|
||||
'GLEDOPTO',
|
||||
'Trust International B.V.\u0000',
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -456,7 +469,8 @@ export default class Bind extends Extension {
|
||||
|
||||
for (const endpoint of toPoll) {
|
||||
for (const poll of polls) {
|
||||
if (!poll.manufacturerIDs.includes(endpoint.getDevice().manufacturerID) ||
|
||||
if ((!poll.manufacturerIDs.includes(endpoint.getDevice().manufacturerID) &&
|
||||
!poll.manufacturerNames.includes(endpoint.getDevice().manufacturerName)) ||
|
||||
!endpoint.supportsInputCluster(poll.read.cluster)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,9 @@ export default class Bridge extends Extension {
|
||||
};
|
||||
cleanupDeleted(message.options, diff.deleted);
|
||||
|
||||
const newSettings = objectAssignDeep({}, diff.added, diff.updated, diff.deleted);
|
||||
// objectAssignDeep requires object prototype which is missing from detailedDiff, therefore clone
|
||||
const newSettings = objectAssignDeep({}, utils.clone(diff.added), utils.clone(diff.updated),
|
||||
utils.clone(diff.deleted));
|
||||
|
||||
// deep-object-diff converts arrays to objects, set original array back here
|
||||
const convertBackArray = (before: KeyValue, after: KeyValue): void => {
|
||||
|
||||
@@ -111,7 +111,7 @@ export default class HomeAssistant extends Extension {
|
||||
}
|
||||
|
||||
private exposeToConfig(exposes: zhc.DefinitionExpose[], entityType: 'device' | 'group',
|
||||
definition?: zhc.Definition, definitionExposes?: zhc.DefinitionExpose[]): DiscoveryEntry[] {
|
||||
allExposes: zhc.DefinitionExpose[], definition?: zhc.Definition): DiscoveryEntry[] {
|
||||
// For groups an array of exposes (of the same type) is passed, this is to determine e.g. what features
|
||||
// to use for a bulb (e.g. color_xy/color_temp)
|
||||
assert(entityType === 'group' || exposes.length === 1, 'Multiple exposes for device not allowed');
|
||||
@@ -171,10 +171,11 @@ export default class HomeAssistant extends Extension {
|
||||
discoveryEntry.discovery_payload.min_mireds = min;
|
||||
}
|
||||
|
||||
const effect = definitionExposes?.find((e) => e.type === 'enum' && e.name === 'effect');
|
||||
if (effect) {
|
||||
const effects = utils.arrayUnique(utils.flatten(
|
||||
allExposes.filter((e) => e.type === 'enum' && e.name === 'effect').map((e) => e.values)));
|
||||
if (effects.length) {
|
||||
discoveryEntry.discovery_payload.effect = true;
|
||||
discoveryEntry.discovery_payload.effect_list = effect.values;
|
||||
discoveryEntry.discovery_payload.effect_list = effects;
|
||||
}
|
||||
|
||||
discoveryEntries.push(discoveryEntry);
|
||||
@@ -320,6 +321,9 @@ export default class HomeAssistant extends Extension {
|
||||
|
||||
if (tempCalibration.value_min != null) discoveryEntry.discovery_payload.min = tempCalibration.value_min;
|
||||
if (tempCalibration.value_max != null) discoveryEntry.discovery_payload.max = tempCalibration.value_max;
|
||||
if (tempCalibration.value_step != null) {
|
||||
discoveryEntry.discovery_payload.step = tempCalibration.value_step;
|
||||
}
|
||||
discoveryEntries.push(discoveryEntry);
|
||||
}
|
||||
|
||||
@@ -386,9 +390,9 @@ export default class HomeAssistant extends Extension {
|
||||
?.features.find((f) => f.name === 'position');
|
||||
const tilt = exposes.find((expose) => expose.features.find((e) => e.name === 'tilt'))
|
||||
?.features.find((f) => f.name === 'tilt');
|
||||
const motorState = definitionExposes?.find((e) => e.type === 'enum' && e.name === 'motor_state' &&
|
||||
const motorState = allExposes?.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 running = allExposes?.find((e) => e.type === 'binary' && e.name === 'running');
|
||||
|
||||
const discoveryEntry: DiscoveryEntry = {
|
||||
type: 'cover',
|
||||
@@ -550,8 +554,10 @@ export default class HomeAssistant extends Extension {
|
||||
tamper: {device_class: 'tamper'},
|
||||
temperature_scale: {entity_category: 'config', icon: 'mdi:temperature-celsius'},
|
||||
test: {entity_category: 'diagnostic', icon: 'mdi:test-tube'},
|
||||
valve_state: {device_class: 'opening'},
|
||||
vibration: {device_class: 'vibration'},
|
||||
water_leak: {device_class: 'moisture'},
|
||||
window: {device_class: 'window'},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -611,6 +617,8 @@ export default class HomeAssistant extends Extension {
|
||||
battery: {device_class: 'battery', entity_category: 'diagnostic', state_class: 'measurement'},
|
||||
battery2: {device_class: 'battery', entity_category: 'diagnostic', state_class: 'measurement'},
|
||||
battery_voltage: {device_class: 'voltage', entity_category: 'diagnostic', state_class: 'measurement'},
|
||||
boost_heating_countdown: {device_class: 'duration'},
|
||||
boost_heating_countdown_time_set: {entity_category: 'config', icon: 'mdi:timer'},
|
||||
boost_time: {entity_category: 'config', icon: 'mdi:timer'},
|
||||
calibration: {entity_category: 'config', icon: 'mdi:wrench-clock'},
|
||||
calibration_time: {entity_category: 'config', icon: 'mdi:wrench-clock'},
|
||||
@@ -663,9 +671,10 @@ export default class HomeAssistant extends Extension {
|
||||
state_class: 'measurement',
|
||||
},
|
||||
local_temperature: {device_class: 'temperature', state_class: 'measurement'},
|
||||
max_temperature: {entity_category: 'config', icon: 'mdi:thermometer'},
|
||||
max_temperature_limit: {entity_category: 'config', icon: 'mdi:thermometer'},
|
||||
min_temperature: {entity_category: 'config', icon: 'mdi:thermometer'},
|
||||
max_temperature: {entity_category: 'config', icon: 'mdi:thermometer-high'},
|
||||
max_temperature_limit: {entity_category: 'config', icon: 'mdi:thermometer-high'},
|
||||
min_temperature_limit: {entity_category: 'config', icon: 'mdi:thermometer-low'},
|
||||
min_temperature: {entity_category: 'config', icon: 'mdi:thermometer-low'},
|
||||
measurement_poll_interval: {entity_category: 'config', icon: 'mdi:clock-out'},
|
||||
occupancy_timeout: {entity_category: 'config', icon: 'mdi:timer'},
|
||||
pm10: {device_class: 'pm10', state_class: 'measurement'},
|
||||
@@ -756,6 +765,7 @@ export default class HomeAssistant extends Extension {
|
||||
command_topic_prefix: endpoint,
|
||||
command_topic_postfix: firstExpose.property,
|
||||
...(firstExpose.unit && {unit_of_measurement: firstExpose.unit}),
|
||||
...(firstExpose.value_step && {step: firstExpose.value_step}),
|
||||
...lookup[firstExpose.name],
|
||||
},
|
||||
};
|
||||
@@ -802,6 +812,7 @@ export default class HomeAssistant extends Extension {
|
||||
sound_volume: {entity_category: 'config', icon: 'mdi:volume-high'},
|
||||
status: {icon: 'mdi:state-machine'},
|
||||
switch_type: {entity_category: 'config', icon: 'mdi:tune'},
|
||||
temperature_sensor_select: {entity_category: 'config', icon: 'mdi:home-thermometer'},
|
||||
thermostat_unit: {entity_category: 'config', icon: 'mdi:thermometer'},
|
||||
volume: {entity_category: 'config', icon: 'mdi: volume-high'},
|
||||
week: {entity_category: 'config', icon: 'mdi:calendar-clock'},
|
||||
@@ -844,10 +855,11 @@ export default class HomeAssistant extends Extension {
|
||||
},
|
||||
});
|
||||
}
|
||||
} else if (firstExpose.type === 'text' || firstExpose.type === 'composite') {
|
||||
} else if (firstExpose.type === 'text' || firstExpose.type === 'composite' || firstExpose.type === 'list') {
|
||||
if (firstExpose.access & ACCESS_STATE) {
|
||||
const lookup: {[s: string]: KeyValue} = {
|
||||
action: {icon: 'mdi:gesture-double-tap'},
|
||||
programming_mode: {icon: 'mdi:calendar-clock'},
|
||||
};
|
||||
|
||||
const discoveryEntry: DiscoveryEntry = {
|
||||
@@ -980,7 +992,7 @@ export default class HomeAssistant extends Extension {
|
||||
if (isDevice) {
|
||||
const exposes = entity.exposes(); // avoid calling it hundred of times/s
|
||||
for (const expose of exposes) {
|
||||
configs.push(...this.exposeToConfig([expose], 'device', entity.definition, exposes));
|
||||
configs.push(...this.exposeToConfig([expose], 'device', exposes, entity.definition));
|
||||
}
|
||||
|
||||
for (const mapping of legacyMapping) {
|
||||
@@ -997,10 +1009,13 @@ export default class HomeAssistant extends Extension {
|
||||
}
|
||||
} else { // group
|
||||
const exposesByType: {[s: string]: zhc.DefinitionExpose[]} = {};
|
||||
const allExposes: zhc.DefinitionExpose[] = [];
|
||||
|
||||
entity.zh.members.map((e) => this.zigbee.resolveEntity(e.getDevice()) as Device)
|
||||
.filter((d) => d.definition).forEach((device) => {
|
||||
for (const expose of device.exposes().filter((e) => groupSupportedTypes.includes(e.type))) {
|
||||
const exposes = device.exposes();
|
||||
allExposes.push(...exposes);
|
||||
for (const expose of exposes.filter((e) => groupSupportedTypes.includes(e.type))) {
|
||||
let key = expose.type;
|
||||
if (['switch', 'lock', 'cover'].includes(expose.type) && expose.endpoint) {
|
||||
// A device can have multiple of these types which have to discovered seperately.
|
||||
@@ -1015,7 +1030,7 @@ export default class HomeAssistant extends Extension {
|
||||
});
|
||||
|
||||
configs = [].concat(...Object.values(exposesByType)
|
||||
.map((exposes) => this.exposeToConfig(exposes, 'group')));
|
||||
.map((exposes) => this.exposeToConfig(exposes, 'group', allExposes)));
|
||||
}
|
||||
|
||||
if (isDevice && settings.get().advanced.last_seen !== 'disable') {
|
||||
@@ -1061,12 +1076,29 @@ export default class HomeAssistant extends Extension {
|
||||
payload_on: true,
|
||||
payload_off: false,
|
||||
value_template: `{{ value_json['update']['state'] == "available" }}`,
|
||||
enabled_by_default: true,
|
||||
enabled_by_default: false,
|
||||
device_class: 'update',
|
||||
entity_category: 'diagnostic',
|
||||
},
|
||||
};
|
||||
configs.push(updateAvailableSensor);
|
||||
const updateSensor: DiscoveryEntry = {
|
||||
type: 'update',
|
||||
object_id: 'update',
|
||||
mockProperties: [{property: 'update', value: {state: null}}],
|
||||
discovery_payload: {
|
||||
entity_picture: 'https://github.com/Koenkk/zigbee2mqtt/raw/master/images/logo.png',
|
||||
latest_version_topic: true,
|
||||
state_topic: true,
|
||||
device_class: 'firmware',
|
||||
command_topic: `${settings.get().mqtt.base_topic}/bridge/request/device/ota_update/update`,
|
||||
payload_install: `{"id": "${entity.ieeeAddr}"}`,
|
||||
value_template: `{{ value_json['update']['installed_version'] }}`,
|
||||
latest_version_template: `{% if value_json['update']['state'] == "available" %}{{ 'newer' }}` +
|
||||
`{% else %}{{ value_json['update']['installed_version'] }}{% endif %}`,
|
||||
},
|
||||
};
|
||||
configs.push(updateSensor);
|
||||
}
|
||||
|
||||
if (isDevice && entity.options.hasOwnProperty('legacy') && !entity.options.legacy) {
|
||||
@@ -1178,7 +1210,7 @@ export default class HomeAssistant extends Extension {
|
||||
delete payload.command_topic_postfix;
|
||||
const commandTopic = `${baseTopic}/${commandTopicPrefix}set${commandTopicPostfix}`;
|
||||
|
||||
if (payload.command_topic) {
|
||||
if (payload.command_topic && typeof payload.command_topic !== 'string') {
|
||||
payload.command_topic = commandTopic;
|
||||
}
|
||||
|
||||
@@ -1233,6 +1265,10 @@ export default class HomeAssistant extends Extension {
|
||||
payload.fan_mode_state_topic = stateTopic;
|
||||
}
|
||||
|
||||
if (payload.latest_version_topic) {
|
||||
payload.latest_version_topic = stateTopic;
|
||||
}
|
||||
|
||||
if (payload.fan_mode_command_topic) {
|
||||
payload.fan_mode_command_topic = `${baseTopic}/${commandTopicPrefix}set/fan_mode`;
|
||||
}
|
||||
@@ -1424,6 +1460,10 @@ export default class HomeAssistant extends Extension {
|
||||
message.color.s = message.color.saturation;
|
||||
}
|
||||
}
|
||||
|
||||
if (entity.isDevice() && entity.definition?.ota && message.hasOwnProperty('update')) {
|
||||
message['update']['installed_version'] = entity.zh.softwareBuildID || 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
private getEncodedBaseTopic(): string {
|
||||
|
||||
@@ -112,13 +112,11 @@ export default class OTAUpdate extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
// Respond to the OTA request:
|
||||
// - In case we don't support OTA: respond with NO_IMAGE_AVAILABLE (0x98) (so the client stops requesting OTAs)
|
||||
// - In case we do support OTA: respond with ABORT (0x95) as we don't want to update now.
|
||||
// Respond to the OTA request: respond with NO_IMAGE_AVAILABLE (0x98) (so the client stops requesting OTAs)
|
||||
const endpoint = data.device.zh.endpoints.find((e) => e.supportsOutputCluster('genOta'));
|
||||
if (endpoint) {
|
||||
// Some devices send OTA requests without defining OTA cluster as input cluster.
|
||||
await endpoint.commandResponse('genOta', 'queryNextImageResponse', {status: supportsOTA ? 0x95 : 0x98});
|
||||
await endpoint.commandResponse('genOta', 'queryNextImageResponse', {status: 0x98});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -100,7 +100,7 @@ declare global {
|
||||
|
||||
interface DefinitionExpose {
|
||||
type: string, name?: string, features?: DefinitionExposeFeature[],
|
||||
endpoint?: string, values?: string[], value_off?: string, value_on?: string,
|
||||
endpoint?: string, values?: string[], value_off?: string, value_on?: string, value_step?: number,
|
||||
access: number, property: string, unit?: string,
|
||||
value_min?: number, value_max?: number}
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ function load(): void {
|
||||
load();
|
||||
|
||||
function joinPath(file: string): string {
|
||||
return path.join(dataPath, file);
|
||||
return path.resolve(dataPath, file);
|
||||
}
|
||||
|
||||
function getPath(): string {
|
||||
|
||||
@@ -842,7 +842,7 @@
|
||||
"icon": {
|
||||
"type": "string",
|
||||
"title": "Icon",
|
||||
"description": "The user-defined device icon for the frontend. It can be a link to an image (not a path to a file) or base64 encoded data URL like: image/svg+xml;base64,PHN2ZyB3aW....R0aD"
|
||||
"description": "The user-defined device icon for the frontend. It can be a full URL link to an image (e.g. https://SOME.SITE/MODEL123.jpg) (you cannot use a path to a local file) or base64 encoded data URL (e.g. image/svg+xml;base64,PHN2ZyB3aW....R0aD)"
|
||||
},
|
||||
"homeassistant": {
|
||||
"type": ["object", "null"],
|
||||
|
||||
+20
-9
@@ -187,6 +187,20 @@ function loadSettingsWithDefaults(): void {
|
||||
_settingsWithDefaults.whitelist && _settingsWithDefaults.passlist.push(..._settingsWithDefaults.whitelist);
|
||||
}
|
||||
|
||||
function parseValueRef(text: string): {filename: string, key: string} | null {
|
||||
const match = /!(.*) (.*)/g.exec(text);
|
||||
if (match) {
|
||||
let filename = match[1];
|
||||
// This is mainly for backward compatibility.
|
||||
if (!filename.endsWith('.yaml') && !filename.endsWith('.yml')) {
|
||||
filename += '.yaml';
|
||||
}
|
||||
return {filename, key: match[2]};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function write(): void {
|
||||
const settings = getInternalSettings();
|
||||
const toWrite: KeyValue = objectAssignDeep({}, settings);
|
||||
@@ -203,9 +217,9 @@ function write(): void {
|
||||
['frontend', 'auth_token'],
|
||||
]) {
|
||||
if (actual[path[0]] && actual[path[0]][path[1]]) {
|
||||
const match = /!(.*) (.*)/g.exec(actual[path[0]][path[1]]);
|
||||
if (match) {
|
||||
yaml.updateIfChanged(data.joinPath(`${match[1]}.yaml`), match[2], toWrite[path[0]][path[1]]);
|
||||
const ref = parseValueRef(actual[path[0]][path[1]]);
|
||||
if (ref) {
|
||||
yaml.updateIfChanged(data.joinPath(ref.filename), ref.key, toWrite[path[0]][path[1]]);
|
||||
toWrite[path[0]][path[1]] = actual[path[0]][path[1]];
|
||||
}
|
||||
}
|
||||
@@ -314,12 +328,9 @@ function read(): Settings {
|
||||
// Read !secret MQTT username and password if set
|
||||
// eslint-disable-next-line
|
||||
const interpetValue = (value: any): any => {
|
||||
const re = /!(.*) (.*)/g;
|
||||
const match = re.exec(value);
|
||||
if (match) {
|
||||
const file = data.joinPath(`${match[1]}.yaml`);
|
||||
const key = match[2];
|
||||
return yaml.read(file)[key];
|
||||
const ref = parseValueRef(value);
|
||||
if (ref) {
|
||||
return yaml.read(data.joinPath(ref.filename))[ref.key];
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
|
||||
+14
-2
@@ -77,7 +77,7 @@ async function getZigbee2MQTTVersion(includeCommitHash=true): Promise<{commitHas
|
||||
}
|
||||
|
||||
async function getDependencyVersion(depend: string): Promise<{version: string}> {
|
||||
const packageJSON = await import(path.join(__dirname, '..', '..', 'node_modules', depend, 'package.json'));
|
||||
const packageJSON = await import(path.join(require.resolve(depend), '..', '..', 'package.json'));
|
||||
const version = packageJSON.version;
|
||||
return {version};
|
||||
}
|
||||
@@ -314,6 +314,14 @@ function isEndpoint(obj: unknown): obj is zh.Endpoint {
|
||||
return obj.constructor.name.toLowerCase() === 'endpoint';
|
||||
}
|
||||
|
||||
function flatten<Type>(arr: Type[][]): Type[] {
|
||||
return [].concat(...arr);
|
||||
}
|
||||
|
||||
function arrayUnique<Type>(arr: Type[]): Type[] {
|
||||
return [...new Set(arr)];
|
||||
}
|
||||
|
||||
function isZHGroup(obj: unknown): obj is zh.Group {
|
||||
return obj.constructor.name.toLowerCase() === 'group';
|
||||
}
|
||||
@@ -351,11 +359,15 @@ function filterProperties(filter: string[], data: KeyValue): void {
|
||||
}
|
||||
}
|
||||
|
||||
function clone(obj: KeyValue): KeyValue {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
export default {
|
||||
endpointNames, capitalize, getZigbee2MQTTVersion, getDependencyVersion, formatDate, objectHasProperties,
|
||||
equalsPartial, getObjectProperty, getResponse, parseJSON, loadModuleFromText, loadModuleFromFile,
|
||||
getExternalConvertersDefinitions, removeNullPropertiesFromObject, toNetworkAddressHex, toSnakeCase,
|
||||
parseEntityID, isEndpoint, isZHGroup, hours, minutes, seconds, validateFriendlyName, sleep,
|
||||
sanitizeImageParameter, isAvailabilityEnabledForEntity, publishLastSeen, availabilityPayload,
|
||||
getAllFiles, filterProperties,
|
||||
getAllFiles, filterProperties, flatten, arrayUnique, clone,
|
||||
};
|
||||
|
||||
Generated
+1261
-2378
File diff suppressed because it is too large
Load Diff
+21
-22
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zigbee2mqtt",
|
||||
"version": "1.28.0",
|
||||
"version": "1.28.3",
|
||||
"description": "Zigbee to MQTT bridge using Zigbee-herdsman",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -8,7 +8,7 @@
|
||||
"url": "git+https://github.com/Koenkk/zigbee2mqtt.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14 || ^16 || ^18"
|
||||
"node": "^14 || ^16 || ^18 || ^19"
|
||||
},
|
||||
"keywords": [
|
||||
"xiaomi",
|
||||
@@ -35,12 +35,11 @@
|
||||
},
|
||||
"homepage": "https://koenkk.github.io/zigbee2mqtt",
|
||||
"dependencies": {
|
||||
"ajv": "^8.11.0",
|
||||
"ajv": "^8.11.2",
|
||||
"bind-decorator": "^1.0.11",
|
||||
"connect-gzip-static": "2.1.1",
|
||||
"core-js": "^3.25.2",
|
||||
"debounce": "^1.2.1",
|
||||
"deep-object-diff": "^1.1.7",
|
||||
"deep-object-diff": "^1.1.9",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"finalhandler": "^1.2.0",
|
||||
"git-last-commit": "^1.0.1",
|
||||
@@ -53,39 +52,39 @@
|
||||
"mqtt": "4.3.7",
|
||||
"object-assign-deep": "^0.4.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.7",
|
||||
"semver": "^7.3.8",
|
||||
"source-map-support": "^0.5.21",
|
||||
"uri-js": "^4.4.1",
|
||||
"winston": "^3.8.2",
|
||||
"winston-syslog": "^2.6.0",
|
||||
"winston-syslog": "^2.7.0",
|
||||
"winston-transport": "^4.5.0",
|
||||
"ws": "^8.9.0",
|
||||
"zigbee-herdsman": "0.14.62",
|
||||
"zigbee-herdsman-converters": "14.0.635",
|
||||
"zigbee2mqtt-frontend": "0.6.112"
|
||||
"ws": "^8.11.0",
|
||||
"zigbee-herdsman": "0.14.76",
|
||||
"zigbee-herdsman-converters": "14.0.680",
|
||||
"zigbee2mqtt-frontend": "0.6.119"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19.1",
|
||||
"@babel/plugin-proposal-decorators": "^7.19.1",
|
||||
"@babel/preset-env": "^7.19.1",
|
||||
"@babel/core": "^7.20.2",
|
||||
"@babel/plugin-proposal-decorators": "^7.20.2",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@types/debounce": "^1.2.1",
|
||||
"@types/finalhandler": "^1.2.0",
|
||||
"@types/humanize-duration": "^3.27.1",
|
||||
"@types/jest": "^29.0.3",
|
||||
"@types/jest": "^29.2.3",
|
||||
"@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.38.0",
|
||||
"@typescript-eslint/parser": "^5.38.0",
|
||||
"babel-jest": "^29.0.3",
|
||||
"eslint": "^8.24.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.44.0",
|
||||
"@typescript-eslint/parser": "^5.44.0",
|
||||
"babel-jest": "^29.3.1",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-jest": "^27.0.4",
|
||||
"jest": "^29.0.3",
|
||||
"eslint-plugin-jest": "^27.1.6",
|
||||
"jest": "^29.3.1",
|
||||
"tmp": "^0.2.1",
|
||||
"typescript": "^4.8.3"
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"jest": {
|
||||
"coverageThreshold": {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
const assert = require('assert');
|
||||
const vm = require('vm');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const filename = process.argv[2];
|
||||
const moduleCode = fs.readFileSync(filename);
|
||||
const moduleFakePath = path.join(__dirname, 'externally-loaded.js');
|
||||
const sandbox = {
|
||||
require: require,
|
||||
module: {},
|
||||
console,
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
setInterval,
|
||||
clearInterval,
|
||||
setImmediate,
|
||||
clearImmediate,
|
||||
};
|
||||
vm.runInNewContext(moduleCode, sandbox, moduleFakePath);
|
||||
const converter = sandbox.module.exports;
|
||||
assert(!converter.toZigbee.includes(undefined));
|
||||
assert(!converter.fromZigbee.includes(undefined));
|
||||
+2
-2
File diff suppressed because one or more lines are too long
@@ -19,6 +19,7 @@ describe('Data', () => {
|
||||
const actual = data.getPath();
|
||||
expect(actual).toBe(expected);
|
||||
expect(data.joinPath('test')).toStrictEqual(path.join(expected, 'test'));
|
||||
expect(data.joinPath('/test')).toStrictEqual(path.resolve(expected, '/test'));
|
||||
delete process.env.ZIGBEE2MQTT_DATA;
|
||||
data.testingOnlyReload();
|
||||
});
|
||||
|
||||
@@ -84,7 +84,12 @@ describe('User extensions', () => {
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/extension/save', stringify({"name": "foo.js", "code": extensionCode}));
|
||||
await flushPromises();
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/extension/save', stringify({"data":{},"error":"Unexpected identifier","status":"error"}), { retain: false, qos: 0 }, expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/extension/save', expect.any(String), { retain: false, qos: 0 }, expect.any(Function));
|
||||
const payload = JSON.parse(MQTT.publish.mock.calls[0][1]);
|
||||
expect(payload).toEqual(
|
||||
expect.objectContaining({"data":{},"status":"error"})
|
||||
);
|
||||
expect(payload.error).toMatch("Unexpected identifier");
|
||||
});
|
||||
|
||||
it('Removes user extension', async () => {
|
||||
|
||||
@@ -140,7 +140,7 @@ describe('Frontend', () => {
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({state: 'ON', linkquality: null, update_available: null, update: {state: null}}),
|
||||
stringify({state: 'ON', power_on_behavior:null, linkquality: null, update_available: null, update: {state: null, installed_version: "unknown"}}),
|
||||
{ retain: false, qos: 0 },
|
||||
expect.any(Function)
|
||||
);
|
||||
@@ -151,7 +151,7 @@ describe('Frontend', () => {
|
||||
|
||||
// Received message on socket
|
||||
expect(mockWSClient.implementation.send).toHaveBeenCalledTimes(1);
|
||||
expect(mockWSClient.implementation.send).toHaveBeenCalledWith(stringify({topic: 'bulb_color', payload: {state: 'ON', linkquality: null, update_available: null, update: {state: null}}}));
|
||||
expect(mockWSClient.implementation.send).toHaveBeenCalledWith(stringify({topic: 'bulb_color', payload: {state: 'ON', power_on_behavior:null, linkquality: null, update_available: null, update: {state: null, installed_version: "unknown"}}}));
|
||||
|
||||
// Shouldnt set when not ready
|
||||
mockWSClient.implementation.send.mockClear();
|
||||
|
||||
@@ -98,6 +98,15 @@ describe('HomeAssistant extension', () => {
|
||||
"xy",
|
||||
"color_temp"
|
||||
],
|
||||
"effect":true,
|
||||
"effect_list":[
|
||||
"blink",
|
||||
"breathe",
|
||||
"okay",
|
||||
"channel_change",
|
||||
"finish_effect",
|
||||
"stop_effect"
|
||||
],
|
||||
"unique_id":"9_light_zigbee2mqtt"
|
||||
};
|
||||
|
||||
@@ -770,7 +779,8 @@ describe('HomeAssistant extension', () => {
|
||||
"boost",
|
||||
"complex",
|
||||
"comfort",
|
||||
"eco"
|
||||
"eco",
|
||||
"away"
|
||||
],
|
||||
"preset_mode_value_template":"{{ value_json.preset }}",
|
||||
"preset_mode_state_topic":"zigbee2mqtt/TS0601_thermostat",
|
||||
@@ -946,7 +956,7 @@ describe('HomeAssistant extension', () => {
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({"color":{"hue": 0, "saturation": 100, "h": 0, "s": 100}, "color_mode": "hs", "linkquality": null, "state": null, "update_available": null, "update": {"state": null}}),
|
||||
stringify({"color":{"hue": 0, "saturation": 100, "h": 0, "s": 100}, "color_mode": "hs", "linkquality": null, "state": null, "update_available": null, "power_on_behavior":null, "update": {"state": null, "installed_version": "unknown"}}),
|
||||
{ retain: false, qos: 0 },
|
||||
expect.any(Function),
|
||||
);
|
||||
@@ -962,7 +972,7 @@ describe('HomeAssistant extension', () => {
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({"color": {"x": 0.4576,"y": 0.41}, "color_mode": "xy", "linkquality": null,"state": null, "update_available": null, "update": {"state": null}}),
|
||||
stringify({"color": {"x": 0.4576,"y": 0.41}, "color_mode": "xy", "linkquality": null,"state": null, "update_available": null, "power_on_behavior":null, "update": {"state": null, "installed_version": "unknown"}}),
|
||||
{ retain: false, qos: 0 },
|
||||
expect.any(Function),
|
||||
);
|
||||
@@ -978,7 +988,7 @@ describe('HomeAssistant extension', () => {
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb_color',
|
||||
stringify({"linkquality": null,"state": "ON", "update_available": null, "update": {"state": null}}),
|
||||
stringify({"linkquality": null,"state": "ON", "update_available": null, "power_on_behavior": null, "update": {"state": null, "installed_version": "unknown"}}),
|
||||
{ retain: false, qos: 0 },
|
||||
expect.any(Function),
|
||||
);
|
||||
@@ -1066,13 +1076,13 @@ describe('HomeAssistant extension', () => {
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"power_on_behavior":null, "update_available": null, "update": {"state": null}}),
|
||||
stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"power_on_behavior":null, "update_available": null, "update": {"state": null, "installed_version": "unknown"}}),
|
||||
{ retain: true, qos: 0 },
|
||||
expect.any(Function)
|
||||
);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/remote',
|
||||
stringify({"action":null,"action_duration":null,"battery":null,"brightness":255,"linkquality":null, "update_available": null, "update": {"state": null}}),
|
||||
stringify({"action":null,"action_duration":null,"battery":null,"brightness":255,"linkquality":null, "update_available": null, "update": {"state": null, "installed_version": "unknown"}}),
|
||||
{ retain: true, qos: 0 },
|
||||
expect.any(Function)
|
||||
);
|
||||
@@ -1096,13 +1106,13 @@ describe('HomeAssistant extension', () => {
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"power_on_behavior":null, "update_available": null, "update": {"state": null}}),
|
||||
stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99,"power_on_behavior":null, "update_available": null, "update": {"state": null, "installed_version": "unknown"}}),
|
||||
{ retain: true, qos: 0 },
|
||||
expect.any(Function)
|
||||
);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/remote',
|
||||
stringify({"action":null,"action_duration":null,"battery":null,"brightness":255,"linkquality":null, "update_available": null, "update": {"state": null}}),
|
||||
stringify({"action":null,"action_duration":null,"battery":null,"brightness":255,"linkquality":null, "update_available": null, "update": {"state": null, "installed_version": "unknown"}}),
|
||||
{ retain: true, qos: 0 },
|
||||
expect.any(Function)
|
||||
);
|
||||
@@ -1306,6 +1316,15 @@ describe('HomeAssistant extension', () => {
|
||||
"xy",
|
||||
"color_temp"
|
||||
],
|
||||
"effect":true,
|
||||
"effect_list":[
|
||||
"blink",
|
||||
"breathe",
|
||||
"okay",
|
||||
"channel_change",
|
||||
"finish_effect",
|
||||
"stop_effect"
|
||||
],
|
||||
"unique_id":"9_light_zigbee2mqtt"
|
||||
};
|
||||
|
||||
@@ -1369,7 +1388,7 @@ describe('HomeAssistant extension', () => {
|
||||
"payload_on":true,
|
||||
"payload_off":false,
|
||||
"value_template":`{{ value_json['update']['state'] == "available" }}`,
|
||||
"enabled_by_default": true,
|
||||
"enabled_by_default": false,
|
||||
"state_topic":"zigbee2mqtt/bulb",
|
||||
"json_attributes_topic":"zigbee2mqtt/bulb",
|
||||
"name":"bulb update available",
|
||||
@@ -1822,6 +1841,15 @@ describe('HomeAssistant extension', () => {
|
||||
"xy",
|
||||
"color_temp"
|
||||
],
|
||||
"effect":true,
|
||||
"effect_list":[
|
||||
"blink",
|
||||
"breathe",
|
||||
"okay",
|
||||
"channel_change",
|
||||
"finish_effect",
|
||||
"stop_effect"
|
||||
],
|
||||
"unique_id":"9_light_zigbee2mqtt"
|
||||
};
|
||||
|
||||
@@ -1858,6 +1886,15 @@ describe('HomeAssistant extension', () => {
|
||||
"xy",
|
||||
"color_temp"
|
||||
],
|
||||
"effect":true,
|
||||
"effect_list":[
|
||||
"blink",
|
||||
"breathe",
|
||||
"okay",
|
||||
"channel_change",
|
||||
"finish_effect",
|
||||
"stop_effect"
|
||||
],
|
||||
"unique_id":"9_light_zigbee2mqtt"
|
||||
};
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ describe('Networkmap', () => {
|
||||
let call = MQTT.publish.mock.calls[0];
|
||||
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/networkmap/raw');
|
||||
|
||||
const expected = {"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description": "external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]};
|
||||
const expected = {"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, power_on_behavior, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description": "external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]};
|
||||
expect(JSON.parse(call[1])).toStrictEqual(expected);
|
||||
|
||||
/**
|
||||
@@ -276,7 +276,7 @@ describe('Networkmap', () => {
|
||||
let call = MQTT.publish.mock.calls[0];
|
||||
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
|
||||
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
|
||||
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, power_on_behavior, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
|
||||
const actual = JSON.parse(call[1]);
|
||||
expect(actual).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
@@ -258,7 +258,7 @@ describe('OTA update', () => {
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382});
|
||||
expect(logger.info).toHaveBeenCalledWith(`Update available for 'bulb'`);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x95});
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98});
|
||||
|
||||
// Should not request again when device asks again after a short time
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
@@ -313,7 +313,7 @@ describe('OTA update', () => {
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382});
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x95});
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98});
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({"update_available":false,"update":{"state":"idle"}}),
|
||||
|
||||
@@ -419,7 +419,7 @@ describe('Receive', () => {
|
||||
const data = {instantaneousDemand:496,currentSummDelivered:[0,6648]}
|
||||
|
||||
const SP600_NEW = zigbeeHerdsman.devices.SP600_NEW;
|
||||
await zigbeeHerdsman.events.message({data, cluster: 'seMetering', device: SP600_NEW, endpoint: SP600_NEW.getEndpoint(1), type: 'attributeReport', linkquality: 10});
|
||||
await zigbeeHerdsman.events.message({data, cluster: 'seMetering', device: SP600_NEW, endpoint: SP600_NEW.getEndpoint(1), type: 'attributeReport', linkquality: 10, meta: {zclTransactionSequenceNumber: 1}});
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
||||
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/SP600_NEW');
|
||||
@@ -427,7 +427,7 @@ describe('Receive', () => {
|
||||
|
||||
MQTT.publish.mockClear();
|
||||
const SP600_OLD = zigbeeHerdsman.devices.SP600_OLD;
|
||||
await zigbeeHerdsman.events.message({data, cluster: 'seMetering', device: SP600_OLD, endpoint: SP600_OLD.getEndpoint(1), type: 'attributeReport', linkquality: 10});
|
||||
await zigbeeHerdsman.events.message({data, cluster: 'seMetering', device: SP600_OLD, endpoint: SP600_OLD.getEndpoint(1), type: 'attributeReport', linkquality: 10, meta: {zclTransactionSequenceNumber: 2}});
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
||||
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/SP600_OLD');
|
||||
|
||||
@@ -199,7 +199,7 @@ describe('Settings', () => {
|
||||
mqtt: {
|
||||
server: '!secret server',
|
||||
user: '!secret username',
|
||||
password: '!secret password',
|
||||
password: '!secret.yaml password',
|
||||
},
|
||||
advanced: {
|
||||
network_key: '!secret network_key',
|
||||
|
||||
Reference in New Issue
Block a user