iobroker.zigbee2mqtt 2.2.0 → 2.2.1

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.
package/README.md CHANGED
@@ -30,6 +30,12 @@ This adapter allows to control the data points of the devices of a Zigbee2MQTT i
30
30
  Placeholder for the next version (at the beginning of the line):
31
31
  ### **WORK IN PROGRESS**
32
32
  -->
33
+ ### 2.2.1 (2022-10-25)
34
+
35
+ - (o0shojo0o) fix state roles and access
36
+ - (o0shojo0o) fix state handling
37
+ - (o0shojo0o) fix createZ2MMessage
38
+
33
39
  ### 2.2.0 (2022-10-20)
34
40
 
35
41
  - (o0shojo0o) added support for [Lidl HG06467 effects](https://www.zigbee2mqtt.io/devices/HG06467.html#trigger-effects)
@@ -13,7 +13,7 @@
13
13
  "External MQTT-Server Port": "Externer MQTT-Server Port",
14
14
  "MQTT-Server IP-Address bind": "MQTT-Server IP-Adresse binden",
15
15
  "MQTT-Server Port": "MQTT-Server-Port",
16
- "Configure your Zigbee2MQTT WebUi connection": "Zigbee2MQTT WebUi Verbindung Konfiguration",
16
+ "Configure your Zigbee2MQTT WebUi connection": "Konfiguration der Zigbee2MQTT WebUi Verbindung",
17
17
  "WebUi Address": "WebUi Adresse",
18
18
  "WebUi Port": "WebUi Port",
19
19
  "Color configurations": "Farbkonfigurationen",
package/io-package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "zigbee2mqtt",
4
- "version": "2.2.0",
4
+ "version": "2.2.1",
5
5
  "news": {
6
+ "2.2.1": {
7
+ "en": "fix state roles and access\nfix state handling\nfix createZ2MMessage",
8
+ "de": "zustandsrollen und zugriff beheben\nmanuelle zustandsbearbeitung\nfix erstellenZ2MMessage",
9
+ "ru": "исправить государственные роли и доступ\nисправить государственную обработку\nисправить CreateZ2MMessage",
10
+ "pt": "corrigir funções estaduais e acesso\ncorrigir o manuseio do estado\ncorrigir createZ2MMessage",
11
+ "nl": "vertaling:\nvertaling:\nvertaling:",
12
+ "fr": "fixer les rôles de l'état et l'accès\nmaniement de l ' état\nfix createZ2MMessage",
13
+ "it": "fissare i ruoli dello stato e l'accesso\ncorretta gestione dello stato\nfix createZ2MMessage",
14
+ "es": "fijar funciones estatales y acceso\ncorrección del estado\nfix createZ2MMessage",
15
+ "pl": "uchwała rola państwowa i dostęp\nnaprawa\ntworzenie Z2MM",
16
+ "uk": "фіксувати державні ролі і доступ\nфіксувати стан обробки\nфіксатор творуZ2MMessage",
17
+ "zh-cn": "fix 国家作用和准入\nfix 国家处理\nfix 建立Z2MMessage"
18
+ },
6
19
  "2.2.0": {
7
20
  "en": "added support for [Lidl HG06467 effects](https://www.zigbee2mqtt.io/devices/HG06467.html#trigger-effects)\nadded support for hs color\nsimulated_brightness data point is added only for supported devices",
8
21
  "de": "unterstützung für [Lidl HG06467-Effekte](https://www.zigbee2mqt.io/devices/HG06467.html#trigger-Effekte)\nunterstützung für hs farbe\nsimuld_brightness-datenpunkt wird nur für unterstützte geräte hinzugefügt",
@@ -78,18 +91,6 @@
78
91
  "es": "estados del grupo corregidos\nopción agregada 'Use Kelvin en lugar de mired para el color temps '\neliminar la lógica disponible, ahora utilizará la información de z2m\nnoLogDevices to logfilter\nmuchos bugfixes\nnoLogDevices funcionalidad\ndebugmesage añadido para la funcionalidad específica del dispositivo\nañadido algunos estados son falsos\nsoporte añadido para escenas definidas en un dispositivo\nfijar la función estatal disponible\nfijar suscriptosEstados disponibles",
79
92
  "pl": "państwa grupowe poprawiły\ndodał opcję 'Use Kelvin' zamiast śmiać kolorowe tempo. '\nzdejmowanie dostępnej logiki wykorzystuje obecnie informacje z 2 m\nnazwa NoLogDevices (ang.)\nwiele błędów\ndodano obsługę NoLogDevices\ndodanie debugmessage dla konkretnej funkcjonalności\ndodanie niektórych stanów jest fałszywe\ndodano wsparcie dla scen zdefiniowanych na urządzenie\nfunkcja stanowa\nrecovery",
80
93
  "zh-cn": "更正\n加上“Use Kelvin”的备选办法,而不是对色彩的诱惑。 评 注\n删除现有逻辑,现在将使用兹2m的信息。\n目 录\n批发\n增加无车辆功能\n增加特定装置功能的碎片\n另有一些国家的违约情况\n对一个装置所界定的场地的更多支持\nf 现有国家作用\nfix 有线性的国家"
81
- },
82
- "0.1.0": {
83
- "en": "first release",
84
- "de": "erste veröffentlichung",
85
- "ru": "первый релиз",
86
- "pt": "primeiro lançamento",
87
- "nl": "eerste vrijlating",
88
- "fr": "première version",
89
- "it": "primo rilascio",
90
- "es": "primera liberación",
91
- "pl": "pierwsze wydanie",
92
- "zh-cn": "首次释放"
93
94
  }
94
95
  },
95
96
  "titleLang": {
@@ -3,14 +3,15 @@ const defineDeviceFromExposes = require('./exposes').defineDeviceFromExposes;
3
3
  const utils = require('./utils');
4
4
  const colors = require('./colors.js');
5
5
  const rgb = require('./rgb.js');
6
- const createCache = {};
6
+ //const createCache = {};
7
7
 
8
8
  class DeviceController {
9
- constructor(adapter, deviceCache, groupCache, config) {
9
+ constructor(adapter, deviceCache, groupCache, config, createCache) {
10
10
  this.adapter = adapter;
11
11
  this.groupCache = groupCache;
12
12
  this.deviceCache = deviceCache;
13
13
  this.config = config;
14
+ this.createCache = createCache;
14
15
  }
15
16
 
16
17
  async createDeviceDefinitions(exposes) {
@@ -142,7 +143,7 @@ class DeviceController {
142
143
  async createOrUpdateDevices() {
143
144
  for (const device of this.groupCache.concat(this.deviceCache)) {
144
145
  const deviceName = device.id == device.ieee_address ? '' : device.id;
145
- if (!createCache[device.ieee_address] || createCache[device.ieee_address].common.name != deviceName) {
146
+ if (!this.createCache[device.ieee_address] || this.createCache[device.ieee_address].name != deviceName) {
146
147
  const deviceObj = {
147
148
  type: 'device',
148
149
  common: {
@@ -160,7 +161,7 @@ class DeviceController {
160
161
 
161
162
  //@ts-ignore
162
163
  await this.adapter.extendObjectAsync(device.ieee_address, deviceObj);
163
- createCache[device.ieee_address] = deviceObj;
164
+ this.createCache[device.ieee_address] = { name: deviceName };
164
165
  }
165
166
 
166
167
  // Here it is checked whether the scenes match the current data from z2m.
@@ -175,14 +176,15 @@ class DeviceController {
175
176
  }
176
177
 
177
178
  for (const state of device.states) {
178
- if (!createCache[device.ieee_address][state.id] || createCache[device.ieee_address][state.id].name != state.name) {
179
- const iobState = await this.copyAndCleanStateObj(state);
180
- await this.adapter.extendObjectAsync(`${device.ieee_address}.${state.id}`, {
179
+ if (!this.createCache[device.ieee_address][state.id] || this.createCache[device.ieee_address][state.id].name != state.name) {
180
+ const iobState = {
181
181
  type: 'state',
182
- common: iobState,
182
+ common: await this.copyAndCleanStateObj(state),
183
183
  native: {},
184
- });
185
- createCache[device.ieee_address][state.id] = state.name;
184
+ };
185
+
186
+ await this.adapter.extendObjectAsync(`${device.ieee_address}.${state.id}`, iobState);
187
+ this.createCache[device.ieee_address][state.id] = { name: state.name, created: true };
186
188
  }
187
189
  }
188
190
  }
@@ -212,6 +214,7 @@ class DeviceController {
212
214
  if (ieee_address != undefined) {
213
215
  this.adapter.setStateAsync(`${ieee_address}.available`, false, true);
214
216
  this.adapter.extendObject(`${ieee_address}`, { common: { name: 'Device removed!', } });
217
+ delete this.createCache[ieee_address];
215
218
  }
216
219
  }
217
220
 
package/lib/exposes.js CHANGED
@@ -8,10 +8,38 @@ const utils = require('./utils');
8
8
  const colors = require('./colors');
9
9
  const getNonGenDevStatesDefs = require('./nonGenericDevicesExtension').getStateDefinition;
10
10
 
11
+ // https://www.zigbee2mqtt.io/guide/usage/exposes.html#access
12
+ const z2mAccess = {
13
+ /**
14
+ * Bit 0: The property can be found in the published state of this device
15
+ */
16
+ STATE: 1,
17
+ /**
18
+ * Bit 1: The property can be set with a /set command
19
+ */
20
+ SET: 2,
21
+ /**
22
+ * Bit 2: The property can be retrieved with a /get command
23
+ */
24
+ GET: 4,
25
+ /**
26
+ * Bitwise inclusive OR of STATE and SET : 0b001 | 0b010
27
+ */
28
+ STATE_SET: 3,
29
+ /**
30
+ * Bitwise inclusive OR of STATE and GET : 0b001 | 0b100
31
+ */
32
+ STATE_GET: 5,
33
+ /**
34
+ * Bitwise inclusive OR of STATE and GET and SET : 0b001 | 0b100 | 0b010
35
+ */
36
+ ALL: 7,
37
+ };
38
+
11
39
  function genState(expose, role, name, desc) {
12
40
  let state;
13
- const readable = true; //expose.access > 0;
14
- const writable = expose.access > 1;
41
+ const readable = (expose.access & z2mAccess.STATE) > 0;
42
+ const writable = (expose.access & z2mAccess.SET) > 0;
15
43
  const stname = (name || expose.property);
16
44
 
17
45
  if (typeof stname !== 'string') {
@@ -134,9 +162,9 @@ function createFromExposes(deviceID, ieee_address, definitions, power_source, sc
134
162
  if (state === undefined) {
135
163
  return 0;
136
164
  }
137
-
138
- state.readable = true;
139
- state.writable = access > 1;
165
+ if (access === undefined) access = z2mAccess.ALL;
166
+ state.readable = (access & z2mAccess.STATE) > 0;
167
+ state.writable = (access & z2mAccess.SET) > 0;
140
168
  const stateExists = states.findIndex((x, _index, _array) => (x.id === state.id));
141
169
 
142
170
  if (stateExists < 0) {
@@ -379,7 +407,7 @@ function createFromExposes(deviceID, ieee_address, definitions, power_source, sc
379
407
  },
380
408
  epname: expose.endpoint,
381
409
  setattr: 'color',
382
- }, 2);
410
+ }, prop.access);
383
411
  break;
384
412
  }
385
413
  case 'color_hs': {
@@ -415,7 +443,7 @@ function createFromExposes(deviceID, ieee_address, definitions, power_source, sc
415
443
  },
416
444
  epname: expose.endpoint,
417
445
  setattr: 'color',
418
- }, 2);
446
+ }, prop.access);
419
447
  // pushToStates({
420
448
  // id: expose.endpoint ? `hue_${expose.endpoint}` : 'hue',
421
449
  // prop: expose.endpoint ? `color_${expose.endpoint}` : 'color',
@@ -539,7 +567,7 @@ function createFromExposes(deviceID, ieee_address, definitions, power_source, sc
539
567
  break;
540
568
  }
541
569
  default:
542
- pushToStates(genState(prop), 2);
570
+ pushToStates(genState(prop), prop.access);
543
571
  break;
544
572
  }
545
573
  }
@@ -701,7 +729,7 @@ function createFromExposes(deviceID, ieee_address, definitions, power_source, sc
701
729
  getter: payload => {
702
730
  return utils.bulbLevelToAdapterLevel(payload.brightness);
703
731
  },
704
- }, 1);
732
+ }, expose.access);
705
733
  }
706
734
  state = null;
707
735
  break;
@@ -720,7 +748,7 @@ function createFromExposes(deviceID, ieee_address, definitions, power_source, sc
720
748
  switch (expose.name) {
721
749
  case 'contact':
722
750
  state = statesDefs.contact;
723
- pushToStates(statesDefs.opened, 1);
751
+ pushToStates(statesDefs.opened, expose.access);
724
752
  break;
725
753
 
726
754
  case 'battery_low':
@@ -783,6 +811,12 @@ function createFromExposes(deviceID, ieee_address, definitions, power_source, sc
783
811
  case 'running_mode':
784
812
  pushToStates(statesDefs.climate_running_mode, prop.access);
785
813
  break;
814
+ case 'local_temperature':
815
+ pushToStates(statesDefs.hvacThermostat_local_temp, prop.access);
816
+ break;
817
+ case 'local_temperature_calibration':
818
+ pushToStates(statesDefs.hvacThermostat_local_temp_calibration, prop.access);
819
+ break;
786
820
  default:
787
821
  {
788
822
  if (prop.name.includes('heating_setpoint')) {
@@ -809,7 +843,7 @@ function createFromExposes(deviceID, ieee_address, definitions, power_source, sc
809
843
  return result;
810
844
  };
811
845
  // if we have a composite expose, the value have to be an object {expose.property : {prop.property: value}}
812
- if (prop.access & 2) {
846
+ if (prop.access & z2mAccess.SET) {
813
847
  st.setter = (value, options) => {
814
848
  const result = {};
815
849
  options[prop.property] = value;
@@ -819,7 +853,7 @@ function createFromExposes(deviceID, ieee_address, definitions, power_source, sc
819
853
  st.setattr = expose.property;
820
854
  }
821
855
  // if we have a composite expose, the payload will be an object {expose.property : {prop.property: value}}
822
- if (prop.access & 1) {
856
+ if (prop.access & z2mAccess.STATE) {
823
857
  st.getter = payload => {
824
858
  if ((payload.hasOwnProperty(expose.property)) && (payload[expose.property] !== null) && payload[expose.property].hasOwnProperty(prop.property)) {
825
859
  return !isNaN(payload[expose.property][prop.property]) ? payload[expose.property][prop.property] : undefined;
@@ -841,11 +875,11 @@ function createFromExposes(deviceID, ieee_address, definitions, power_source, sc
841
875
  // If necessary, add states defined for this device model.
842
876
  // Unfortunately this is necessary for some device models because they do not adhere to the standard
843
877
  for (const state of getNonGenDevStatesDefs(definitions.model)) {
844
- pushToStates(state, state.write ? 2 : 1);
878
+ pushToStates(state, state.write ? z2mAccess.SET : z2mAccess.STATE);
845
879
  }
846
880
 
847
881
  // Add default states
848
- pushToStates(statesDefs.available, 1);
882
+ pushToStates(statesDefs.available, z2mAccess.STATE);
849
883
 
850
884
  const newDevice = {
851
885
  id: deviceID,
package/lib/states.js CHANGED
@@ -2153,7 +2153,7 @@ const states = {
2153
2153
 
2154
2154
  // hvac Thermostat cluster - generic states
2155
2155
  hvacThermostat_local_temp: {
2156
- id: 'local_temp',
2156
+ id: 'local_temperature',
2157
2157
  prop: 'local_temperature',
2158
2158
  name: 'Local Temperature',
2159
2159
  icon: undefined,
@@ -2164,7 +2164,7 @@ const states = {
2164
2164
  unit: '°C',
2165
2165
  },
2166
2166
  hvacThermostat_local_temp_calibration: {
2167
- id: 'local_temp_calibration',
2167
+ id: 'local_temperature_calibration',
2168
2168
  prop: 'local_temperature_calibration',
2169
2169
  name: 'Temperature Calibration',
2170
2170
  icon: undefined,
@@ -2173,6 +2173,7 @@ const states = {
2173
2173
  read: true,
2174
2174
  type: 'number',
2175
2175
  unit: '°C',
2176
+ def: 0
2176
2177
  },
2177
2178
  hvacThermostat_remote_sensing: {
2178
2179
  id: 'remote_sensing',
@@ -3,11 +3,12 @@ const incStatsQueue = [];
3
3
  const timeOutCache = {};
4
4
 
5
5
  class StatesController {
6
- constructor(adapter, deviceCache, groupCache, logCustomizations) {
6
+ constructor(adapter, deviceCache, groupCache, logCustomizations, createCache) {
7
7
  this.adapter = adapter;
8
8
  this.groupCache = groupCache;
9
9
  this.deviceCache = deviceCache;
10
10
  this.logCustomizations = logCustomizations;
11
+ this.createCache = createCache;
11
12
  }
12
13
 
13
14
  processDeviceMessage(messageObj) {
@@ -49,6 +50,13 @@ class StatesController {
49
50
 
50
51
  const stateName = `${device.ieee_address}.${state.id}`;
51
52
 
53
+ // It may be that the state has not yet been created!
54
+ if (!this.createCache[device.ieee_address] || !this.createCache[device.ieee_address][state.id] || !this.createCache[device.ieee_address][state.id].created) {
55
+ incStatsQueue[incStatsQueue.length] = messageObj;
56
+ //this.adapter.log.debug(`State ${stateName} is not yet created, queue state in incStatsQueue!`);
57
+ continue;
58
+ }
59
+
52
60
  try {
53
61
  if (state.isEvent) {
54
62
  if (state.getter) {
@@ -36,16 +36,11 @@ class Z2mController {
36
36
  stateID = deviceState.prop;
37
37
  }
38
38
 
39
- let topic = `${device.ieee_address}/set`;
40
- if (device.ieee_address.includes('group_')) {
41
- topic = `${device.id}/set`;
42
- }
43
-
44
39
  const controlObj = {
45
40
  payload: {
46
41
  [stateID]: stateVal
47
42
  },
48
- topic: topic
43
+ topic: `${device.id}/set`
49
44
  };
50
45
 
51
46
  // set stats with the mentioned role or ids always immediately to ack = true, because these are not reported back by Zigbee2MQTT
package/main.js CHANGED
@@ -23,6 +23,7 @@ let mqttClient;
23
23
  let deviceCache = [];
24
24
  // eslint-disable-next-line prefer-const
25
25
  let groupCache = [];
26
+ const createCache = {};
26
27
  const logCustomizations = { debugDevices: '', logfilter: [] };
27
28
  let showInfo = true;
28
29
  let statesController;
@@ -44,8 +45,8 @@ class Zigbee2mqtt extends core.Adapter {
44
45
  }
45
46
 
46
47
  async onReady() {
47
- statesController = new StatesController(this, deviceCache, groupCache, logCustomizations);
48
- deviceController = new DeviceController(this, deviceCache, groupCache, this.config);
48
+ statesController = new StatesController(this, deviceCache, groupCache, logCustomizations, createCache);
49
+ deviceController = new DeviceController(this, deviceCache, groupCache, this.config, createCache);
49
50
  z2mController = new Z2mController(this, deviceCache, groupCache, logCustomizations);
50
51
 
51
52
  // Initialize your adapter here
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.zigbee2mqtt",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Zigbee2MQTT adapter for ioBroker",
5
5
  "author": {
6
6
  "name": "Dennis Rathjen",
@@ -36,13 +36,13 @@
36
36
  "@types/chai": "^4.3.3",
37
37
  "@types/chai-as-promised": "^7.1.5",
38
38
  "@types/mocha": "^10.0.0",
39
- "@types/node": "^18.11.0",
39
+ "@types/node": "^18.11.3",
40
40
  "@types/proxyquire": "^1.3.28",
41
41
  "@types/sinon": "^10.0.13",
42
42
  "@types/sinon-chai": "^3.2.8",
43
43
  "chai": "^4.3.6",
44
44
  "chai-as-promised": "^7.1.1",
45
- "eslint": "^8.25.0",
45
+ "eslint": "^8.26.0",
46
46
  "eslint-config-prettier": "^8.5.0",
47
47
  "eslint-plugin-prettier": "^4.2.1",
48
48
  "mocha": "^10.1.0",