iobroker.zigbee2mqtt 2.13.6 → 2.13.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,7 +17,7 @@ class StatesController {
17
17
  return;
18
18
  }
19
19
 
20
- const device = this.groupCache.concat(this.deviceCache).find(x => x.id == messageObj.topic);
20
+ const device = this.groupCache.concat(this.deviceCache).find((x) => x.id == messageObj.topic);
21
21
  if (device) {
22
22
  try {
23
23
  this.setDeviceStateSafely(messageObj, device);
@@ -38,15 +38,14 @@ class StatesController {
38
38
  const actionStates = [];
39
39
 
40
40
  for (const [key, value] of Object.entries(messageObj.payload)) {
41
-
42
41
  if (value === undefined || value === null) {
43
42
  continue;
44
43
  }
45
44
 
46
- let states = device.states.filter(x => x.prop && x.prop == key);
45
+ let states = device.states.filter((x) => x.prop && x.prop == key);
47
46
 
48
47
  if (states.length == 0) {
49
- states = device.states.filter(x => x.id == key);
48
+ states = device.states.filter((x) => x.id == key);
50
49
  }
51
50
 
52
51
  if (states.length == 0) {
@@ -54,11 +53,14 @@ class StatesController {
54
53
  }
55
54
 
56
55
  for (const state of states) {
57
-
58
56
  const stateName = `${device.ieee_address}.${state.id}`;
59
57
 
60
58
  // It may be that the state has not yet been created!
61
- if (!this.createCache[device.ieee_address] || !this.createCache[device.ieee_address][state.id] || !this.createCache[device.ieee_address][state.id].created == true) {
59
+ if (
60
+ !this.createCache[device.ieee_address] ||
61
+ !this.createCache[device.ieee_address][state.id] ||
62
+ !this.createCache[device.ieee_address][state.id].created == true
63
+ ) {
62
64
  incStatsQueue[incStatsQueue.length] = messageObj;
63
65
  continue;
64
66
  }
@@ -69,6 +71,16 @@ class StatesController {
69
71
  actionStates.push(state);
70
72
  }
71
73
  // Is not an action
74
+ // check if its a motion sensor (occupancy state) and if configuration is set to update state every time
75
+ // if yes, use setStateSafelyAsync instead of setStateChangedSafelyAsync
76
+ else if (
77
+ this.adapter.config.allwaysUpdateOccupancyState === true &&
78
+ state.id === 'occupancy' &&
79
+ value === true
80
+ ) {
81
+ await this.setStateSafelyAsync(stateName, value);
82
+ }
83
+ // end section for motion sensor update
72
84
  else {
73
85
  if (state.getter) {
74
86
  await this.setStateChangedSafelyAsync(stateName, state.getter(messageObj.payload));
@@ -84,19 +96,16 @@ class StatesController {
84
96
  }
85
97
 
86
98
  for (const state of actionStates) {
87
-
88
99
  const stateName = `${device.ieee_address}.${state.id}`;
89
100
 
90
101
  try {
91
102
  if (state.isEvent && state.isEvent == true) {
92
103
  if (state.type == 'boolean') {
93
104
  await this.setStateWithTimeoutAsync(stateName, state.getter(messageObj.payload), 450);
94
- }
95
- else {
105
+ } else {
96
106
  await this.setStateSafelyAsync(stateName, state.getter(messageObj.payload));
97
107
  }
98
- }
99
- else {
108
+ } else {
100
109
  await this.setStateChangedSafelyAsync(stateName, state.getter(messageObj.payload));
101
110
  }
102
111
  } catch (err) {
@@ -179,5 +188,5 @@ class StatesController {
179
188
  }
180
189
 
181
190
  module.exports = {
182
- StatesController
183
- };
191
+ StatesController,
192
+ };
package/lib/utils.js CHANGED
@@ -18,7 +18,7 @@ function bulbLevelToAdapterLevel(bulbLevel) {
18
18
  // - Bulb level range [2...254] is linearly mapped to adapter level range [1...100].
19
19
  if (bulbLevel >= 2) {
20
20
  // Perform linear mapping of range [2...254] to [1...100]
21
- return (Math.round((bulbLevel - 2) * 99 / 252) + 1);
21
+ return Math.round(((bulbLevel - 2) * 99) / 252) + 1;
22
22
  } else {
23
23
  // The bulb is considered off. Even a bulb level of "1" is considered as off.
24
24
  return 0;
@@ -34,7 +34,7 @@ function adapterLevelToBulbLevel(adapterLevel) {
34
34
  // Please read the comments there regarding the rules applied here for mapping the values.
35
35
  if (adapterLevel) {
36
36
  // Perform linear mapping of range [1...100] to [2...254]
37
- return (Math.round((adapterLevel - 1) * 252 / 99) + 2);
37
+ return Math.round(((adapterLevel - 1) * 252) / 99) + 2;
38
38
  } else {
39
39
  // Switch the bulb off. Some bulbs need "0" (IKEA), others "1" (HUE), and according to the
40
40
  // ZigBee docs "1" is the "minimum possible level"... we choose "0" here which seems to work.
@@ -69,7 +69,7 @@ function miredKelvinConversion(t) {
69
69
  */
70
70
  function decimalToHex(decimal, padding) {
71
71
  let hex = Number(decimal).toString(16);
72
- padding = typeof (padding) === 'undefined' || padding === null ? padding = 2 : padding;
72
+ padding = typeof padding === 'undefined' || padding === null ? (padding = 2) : padding;
73
73
 
74
74
  while (hex.length < padding) {
75
75
  hex = `0${hex}`;
@@ -101,7 +101,7 @@ function moveArray(source, target) {
101
101
  }
102
102
 
103
103
  function isObject(item) {
104
- return (typeof item === 'object' && !Array.isArray(item) && item !== null);
104
+ return typeof item === 'object' && !Array.isArray(item) && item !== null;
105
105
  }
106
106
 
107
107
  function isJson(item) {
@@ -128,4 +128,4 @@ module.exports = {
128
128
  moveArray,
129
129
  isObject,
130
130
  isJson,
131
- };
131
+ };
@@ -19,7 +19,7 @@ class WebsocketController {
19
19
  wsURL += `?token=${this.adapter.config.wsToken}`;
20
20
  }
21
21
 
22
- wsClient = new WebSocket(wsURL), { rejectUnauthorized: false };
22
+ (wsClient = new WebSocket(wsURL)), { rejectUnauthorized: false };
23
23
 
24
24
  wsClient.on('open', () => {
25
25
  // Send ping to server
@@ -41,9 +41,11 @@ class WebsocketController {
41
41
  }
42
42
  });
43
43
 
44
- wsClient.on('message', () => { });
44
+ wsClient.on('message', () => {});
45
45
 
46
- wsClient.on('error', (err) => { this.adapter.log.debug(err); });
46
+ wsClient.on('error', (err) => {
47
+ this.adapter.log.debug(err);
48
+ });
47
49
 
48
50
  return wsClient;
49
51
  } catch (err) {
@@ -96,5 +98,5 @@ class WebsocketController {
96
98
  }
97
99
 
98
100
  module.exports = {
99
- WebsocketController
101
+ WebsocketController,
100
102
  };
@@ -20,12 +20,12 @@ class Z2mController {
20
20
  const ieee_address = splitedID[2];
21
21
  const stateName = splitedID[3];
22
22
 
23
- const device = this.groupCache.concat(this.deviceCache).find(d => d.ieee_address == ieee_address);
23
+ const device = this.groupCache.concat(this.deviceCache).find((d) => d.ieee_address == ieee_address);
24
24
  if (!device) {
25
25
  return;
26
26
  }
27
27
 
28
- const deviceState = device.states.find(s => s.id == stateName);
28
+ const deviceState = device.states.find((s) => s.id == stateName);
29
29
  if (!deviceState) {
30
30
  return;
31
31
  }
@@ -48,17 +48,18 @@ class Z2mController {
48
48
  payload: {
49
49
  [stateID]: stateVal,
50
50
  },
51
- topic: `${device.id}/set`
51
+ topic: `${device.id}/set`,
52
52
  };
53
53
 
54
54
  if (stateID == 'send_payload') {
55
55
  try {
56
56
  controlObj.payload = JSON.parse(stateVal);
57
57
  this.adapter.setState(id, state, true);
58
- }
59
- catch (error) {
58
+ } catch (error) {
60
59
  controlObj.payload = stateVal.replaceAll(' ', '').replaceAll('\n', '');
61
- this.adapter.log.warn(`${device.ieee_address} state: ${stateID} error: value passed is not a valid JSON`);
60
+ this.adapter.log.warn(
61
+ `${device.ieee_address} state: ${stateID} error: value passed is not a valid JSON`
62
+ );
62
63
  this.adapter.log.debug(`${device.ieee_address} states: ${JSON.stringify(controlObj)} error: ${error}`);
63
64
  return;
64
65
  }
@@ -69,7 +70,9 @@ class Z2mController {
69
70
  for (const option of deviceState.options) {
70
71
  // if optionsValues not set, set it!
71
72
  if (!device.optionsValues[option]) {
72
- const optionValue = (await this.adapter.getStateAsync(`${splitedID[0]}.${splitedID[1]}.${splitedID[2]}.${option}`)).val;
73
+ const optionValue = (
74
+ await this.adapter.getStateAsync(`${splitedID[0]}.${splitedID[1]}.${splitedID[2]}.${option}`)
75
+ ).val;
73
76
  // optionsValues Cache
74
77
  device.optionsValues[option] = optionValue;
75
78
  }
@@ -97,7 +100,11 @@ class Z2mController {
97
100
  this.adapter.setState(id, state, true);
98
101
  }
99
102
  // set stats with the mentioned ids always immediately to ack = true, because these are not reported back by Zigbee2MQTT
100
- if (['brightness_move', 'colortemp_move', 'brightness_move', 'brightness_step', 'effect'].includes(deviceState.id)) {
103
+ if (
104
+ ['brightness_move', 'colortemp_move', 'brightness_move', 'brightness_step', 'effect'].includes(
105
+ deviceState.id
106
+ )
107
+ ) {
101
108
  this.adapter.setState(id, state, true);
102
109
  }
103
110
 
@@ -109,7 +116,7 @@ class Z2mController {
109
116
 
110
117
  async proxyZ2MLogs(messageObj) {
111
118
  const logMessage = messageObj.payload.message;
112
- if (this.logCustomizations.logfilter.some(x => logMessage.includes(x))) {
119
+ if (this.logCustomizations.logfilter.some((x) => logMessage.includes(x))) {
113
120
  return;
114
121
  }
115
122
 
@@ -127,7 +134,6 @@ class Z2mController {
127
134
  }
128
135
  }
129
136
 
130
-
131
137
  module.exports = {
132
- Z2mController: Z2mController
133
- };
138
+ Z2mController: Z2mController,
139
+ };
package/main.js CHANGED
@@ -18,11 +18,10 @@ const StatesController = require('./lib/statesController').StatesController;
18
18
  const WebsocketController = require('./lib/websocketController').WebsocketController;
19
19
  const MqttServerController = require('./lib/mqttServerController').MqttServerController;
20
20
 
21
-
22
21
  let mqttClient;
23
- // eslint-disable-next-line prefer-const
22
+
24
23
  let deviceCache = [];
25
- // eslint-disable-next-line prefer-const
24
+
26
25
  let groupCache = [];
27
26
  const createCache = {};
28
27
  const logCustomizations = { debugDevices: '', logfilter: [] };
@@ -34,7 +33,6 @@ let websocketController;
34
33
  let mqttServerController;
35
34
 
36
35
  class Zigbee2mqtt extends core.Adapter {
37
-
38
36
  constructor(options) {
39
37
  super({
40
38
  ...options,
@@ -47,7 +45,14 @@ class Zigbee2mqtt extends core.Adapter {
47
45
 
48
46
  async onReady() {
49
47
  statesController = new StatesController(this, deviceCache, groupCache, logCustomizations, createCache);
50
- deviceController = new DeviceController(this, deviceCache, groupCache, this.config, logCustomizations, createCache);
48
+ deviceController = new DeviceController(
49
+ this,
50
+ deviceCache,
51
+ groupCache,
52
+ this.config,
53
+ logCustomizations,
54
+ createCache
55
+ );
51
56
  z2mController = new Z2mController(this, deviceCache, groupCache, logCustomizations);
52
57
 
53
58
  // Initialize your adapter here
@@ -63,12 +68,16 @@ class Zigbee2mqtt extends core.Adapter {
63
68
  const logfilterState = await this.getStateAsync('info.logfilter');
64
69
  if (logfilterState && logfilterState.val) {
65
70
  // @ts-ignore
66
- logCustomizations.logfilter = String(logfilterState.val).split(';').filter(x => x); // filter removes empty strings here
71
+ logCustomizations.logfilter = String(logfilterState.val)
72
+ .split(';')
73
+ .filter((x) => x); // filter removes empty strings here
67
74
  }
68
75
 
69
76
  if (this.config.coordinatorCheck == true) {
70
77
  try {
71
- schedule.scheduleJob('coordinatorCheck', this.config.coordinatorCheckCron, () => this.onStateChange('manual_trigger._.info.coordinator_check', { ack: false }));
78
+ schedule.scheduleJob('coordinatorCheck', this.config.coordinatorCheckCron, () =>
79
+ this.onStateChange('manual_trigger._.info.coordinator_check', { ack: false })
80
+ );
72
81
  } catch (e) {
73
82
  this.log.error(e);
74
83
  }
@@ -83,7 +92,11 @@ class Zigbee2mqtt extends core.Adapter {
83
92
  }
84
93
 
85
94
  // MQTT connection settings
86
- const mqttClientOptions = { clientId: `ioBroker.zigbee2mqtt_${Math.random().toString(16).slice(2, 8)}`, clean: true, reconnectPeriod: 500 };
95
+ const mqttClientOptions = {
96
+ clientId: `ioBroker.zigbee2mqtt_${Math.random().toString(16).slice(2, 8)}`,
97
+ clean: true,
98
+ reconnectPeriod: 500,
99
+ };
87
100
 
88
101
  // Set external mqtt credentials
89
102
  if (this.config.externalMqttServerCredentials == true) {
@@ -92,19 +105,28 @@ class Zigbee2mqtt extends core.Adapter {
92
105
  }
93
106
 
94
107
  // Init connection
95
- mqttClient = mqtt.connect(`mqtt://${this.config.externalMqttServerIP}:${this.config.externalMqttServerPort}`, mqttClientOptions);
108
+ mqttClient = mqtt.connect(
109
+ `mqtt://${this.config.externalMqttServerIP}:${this.config.externalMqttServerPort}`,
110
+ mqttClientOptions
111
+ );
96
112
  }
97
113
  // Internal MQTT-Server
98
114
  else {
99
115
  mqttServerController = new MqttServerController(this);
100
116
  await mqttServerController.createMQTTServer();
101
117
  await this.delay(1500);
102
- mqttClient = mqtt.connect(`mqtt://${this.config.mqttServerIPBind}:${this.config.mqttServerPort}`, { clientId: `ioBroker.zigbee2mqtt_${Math.random().toString(16).slice(2, 8)}`, clean: true, reconnectPeriod: 500 });
118
+ mqttClient = mqtt.connect(`mqtt://${this.config.mqttServerIPBind}:${this.config.mqttServerPort}`, {
119
+ clientId: `ioBroker.zigbee2mqtt_${Math.random().toString(16).slice(2, 8)}`,
120
+ clean: true,
121
+ reconnectPeriod: 500,
122
+ });
103
123
  }
104
124
 
105
125
  // MQTT Client
106
126
  mqttClient.on('connect', () => {
107
- this.log.info(`Connect to Zigbee2MQTT over ${this.config.connectionType == 'exmqtt' ? 'external mqtt' : 'internal mqtt'} connection.`);
127
+ this.log.info(
128
+ `Connect to Zigbee2MQTT over ${this.config.connectionType == 'exmqtt' ? 'external mqtt' : 'internal mqtt'} connection.`
129
+ );
108
130
  });
109
131
 
110
132
  mqttClient.subscribe('zigbee2mqtt/#');
@@ -237,7 +259,7 @@ class Zigbee2mqtt extends core.Adapter {
237
259
  // {"payload":{"state":"online"},"topic":"FL.Licht.Links/availability"} ----> {"payload":{"available":true},"topic":"FL.Licht.Links"}
238
260
  const newMessage = {
239
261
  payload: { available: messageObj.payload.state == 'online' },
240
- topic: messageObj.topic.replace('/availability', '')
262
+ topic: messageObj.topic.replace('/availability', ''),
241
263
  };
242
264
  statesController.processDeviceMessage(newMessage);
243
265
  }
@@ -328,12 +350,12 @@ class Zigbee2mqtt extends core.Adapter {
328
350
  return;
329
351
  }
330
352
  if (id.endsWith('info.logfilter')) {
331
- logCustomizations.logfilter = state.val.split(';').filter(x => x); // filter removes empty strings here
353
+ logCustomizations.logfilter = state.val.split(';').filter((x) => x); // filter removes empty strings here
332
354
  this.setState(id, state.val, true);
333
355
  return;
334
356
  }
335
357
 
336
- const message = await z2mController.createZ2MMessage(id, state) || { topic: '', payload: '' };
358
+ const message = (await z2mController.createZ2MMessage(id, state)) || { topic: '', payload: '' };
337
359
 
338
360
  if (['exmqtt', 'intmqtt'].includes(this.config.connectionType)) {
339
361
  mqttClient.publish(`zigbee2mqtt/${message.topic}`, JSON.stringify(message.payload));
@@ -344,12 +366,11 @@ class Zigbee2mqtt extends core.Adapter {
344
366
  }
345
367
  }
346
368
 
347
-
348
369
  if (require.main !== module) {
349
370
  // Export the constructor in compact mode
350
371
  /**
351
- * @param {Partial<core.AdapterOptions>} [options={}]
352
- */
372
+ * @param {Partial<core.AdapterOptions>} [options={}]
373
+ */
353
374
  module.exports = (options) => new Zigbee2mqtt(options);
354
375
  } else {
355
376
  // otherwise start the instance directly
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.zigbee2mqtt",
3
- "version": "2.13.6",
3
+ "version": "2.13.11",
4
4
  "description": "Zigbee2MQTT adapter for ioBroker",
5
5
  "author": {
6
6
  "name": "Dennis Rathjen and Arthur Rupp",
@@ -9,6 +9,8 @@
9
9
  "homepage": "https://github.com/arteck/ioBroker.zigbee2mqtt",
10
10
  "license": "MIT",
11
11
  "keywords": [
12
+ "zigbee2mqtt",
13
+ "zigbee",
12
14
  "ioBroker",
13
15
  "template",
14
16
  "Smart Home",
@@ -19,17 +21,17 @@
19
21
  "url": "https://github.com/arteck/ioBroker.zigbee2mqtt.git"
20
22
  },
21
23
  "engines": {
22
- "node": ">= 16"
24
+ "node": ">= 18"
23
25
  },
24
26
  "dependencies": {
25
- "@iobroker/adapter-core": "^3.0.4",
27
+ "@iobroker/adapter-core": "^3.2.1",
26
28
  "@iobroker/dm-utils": "^0.1.9",
27
- "aedes": "^0.51.0",
29
+ "aedes": "^0.51.3",
28
30
  "aedes-persistence-nedb": "^2.0.3",
29
- "mqtt": "~5.3.5",
31
+ "mqtt": "^5.9.0",
30
32
  "net": "^1.0.2",
31
33
  "node-schedule": "^2.1.1",
32
- "sharp": "^0.33.1",
34
+ "sharp": "^0.33.5",
33
35
  "ws": "^8.16.0"
34
36
  },
35
37
  "devDependencies": {
@@ -37,28 +39,28 @@
37
39
  "@alcalzone/release-script-plugin-iobroker": "^3.7.0",
38
40
  "@alcalzone/release-script-plugin-license": "^3.7.0",
39
41
  "@alcalzone/release-script-plugin-manual-review": "^3.7.0",
40
- "@iobroker/adapter-dev": "^1.2.0",
41
- "@iobroker/testing": "^4.1.0",
42
- "@tsconfig/node14": "^14.1.0",
42
+ "@iobroker/adapter-dev": "^1.3.0",
43
+ "@iobroker/testing": "^4.1.3",
44
+ "@tsconfig/node14": "^14.1.2",
43
45
  "@types/chai": "^4.3.5",
44
- "@types/chai-as-promised": "^7.1.8",
45
- "@types/mocha": "^10.0.6",
46
- "@types/node": "^20.11.24",
47
- "@types/node-schedule": "^2.1.5",
46
+ "@types/chai-as-promised": "^8.0.1",
47
+ "@types/mocha": "^10.0.7",
48
+ "@types/node": "^22.7.4",
49
+ "@types/node-schedule": "^2.1.7",
48
50
  "@types/proxyquire": "^1.3.31",
49
51
  "@types/sinon": "^17.0.3",
50
52
  "@types/sinon-chai": "^3.2.12",
51
- "chai": "^4.4.0",
52
- "chai-as-promised": "^7.1.1",
53
- "eslint": "^8.57.0",
53
+ "chai": "^4.4.1",
54
+ "chai-as-promised": "^8.0.0",
55
+ "eslint": "^9.11.1",
54
56
  "eslint-config-prettier": "^9.1.0",
55
- "eslint-plugin-prettier": "^5.1.2",
56
- "mocha": "^10.2.0",
57
- "prettier": "^3.2.5",
57
+ "eslint-plugin-prettier": "^5.2.1",
58
+ "mocha": "^10.5.2",
59
+ "prettier": "^3.3.3",
58
60
  "proxyquire": "^2.1.3",
59
- "sinon": "^17.0.1",
61
+ "sinon": "^18.0.0",
60
62
  "sinon-chai": "^3.7.0",
61
- "typescript": "~5.3.3"
63
+ "typescript": "~5.6.2"
62
64
  },
63
65
  "main": "main.js",
64
66
  "files": [