iobroker.airzone 2.0.2 → 3.0.0

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/LocalApi/Zone.js CHANGED
@@ -1,10 +1,12 @@
1
+ 'use strict';
2
+
1
3
  const Constants = require('./Constants');
2
4
 
3
5
  class Zone {
4
6
  constructor(adapter, localApi)
5
7
  {
6
8
  this.adapter = adapter;
7
- this.localApi = localApi;
9
+ this.localApi = localApi;
8
10
  }
9
11
 
10
12
  /**
@@ -14,8 +16,9 @@ class Zone {
14
16
  this.id = parseInt(zoneData['zoneID']);
15
17
  this.name = zoneData.hasOwnProperty('name') ? zoneData['name'] : '';
16
18
  this.min_temp = zoneData['minTemp'];
17
- this.max_temp = zoneData['maxTemp'];
18
-
19
+ this.max_temp = zoneData['maxTemp'];
20
+ this.isMaster = zoneData['master'] === 1;
21
+
19
22
  this.path = path+'.Zone'+this.id;
20
23
  await this.adapter.setObjectNotExistsAsync(this.path, {
21
24
  type: 'device',
@@ -28,25 +31,62 @@ class Zone {
28
31
  native: {},
29
32
  });
30
33
 
31
- var unitRaw = zoneData['units'];
32
- var unitName = Constants.UNIT_CONVERTER[unitRaw]["name"];
33
- var unitUnit = Constants.UNIT_CONVERTER[unitRaw]["unit"];
34
+ const unitRaw = zoneData['units'];
35
+ const unitName = Constants.UNIT_CONVERTER[unitRaw]?.['name'] || 'Unknown';
36
+ const unitUnit = Constants.UNIT_CONVERTER[unitRaw]?.['unit'] || '°C';
34
37
 
35
38
  await this.adapter.createPropertyAndInit(this.path, 'id', 'number', true, false, this.id, 'number');
36
- await this.adapter.createPropertyAndInit(this.path, 'name', 'string', true, false, this.name, 'text');
39
+ // Name is writable so user can set custom zone names (API may not return names)
40
+ await this.adapter.createPropertyAndInit(this.path, 'name', 'string', true, true, this.name || `Zone ${this.id}`, 'text');
37
41
  await this.adapter.createPropertyAndInit(this.path, 'min_temp', 'number', true, false, this.min_temp, 'value.min');
38
42
  await this.adapter.createPropertyAndInit(this.path, 'max_temp', 'number', true, false, this.max_temp, 'value.max');
39
- await this.adapter.createPropertyAndInit(this.path, 'unitRaw', 'string', true, false, unitRaw, 'number');
43
+ await this.adapter.createPropertyAndInit(this.path, 'unitRaw', 'number', true, false, unitRaw, 'value');
40
44
  await this.adapter.createPropertyAndInit(this.path, 'unitName', 'string', true, false, unitName, 'text');
41
45
  await this.adapter.createPropertyAndInit(this.path, 'unit', 'string', true, false, unitUnit, 'text');
42
- await this.adapter.createProperty(this.path, 'is_on', 'boolean', true, false, 'switch.power');
46
+ await this.adapter.createPropertyAndInit(this.path, 'master', 'boolean', true, false, this.isMaster, 'indicator');
47
+ await this.adapter.createProperty(this.path, 'is_on', 'boolean', true, true, 'switch.power');
43
48
  await this.adapter.createUnitProperty(this.path, 'current_temperature', 'number', 0, 100, unitUnit, true, false, 'value.temperature');
44
49
  await this.adapter.createUnitProperty(this.path, 'current_humidity', 'number', 0, 100, '%', true, false, 'value.humidity');
45
- await this.adapter.createUnitProperty(this.path, 'target_temperature', 'number', this.min_temp, this.max_temp, unitUnit, true, true, 'state');
46
-
50
+ await this.adapter.createUnitProperty(this.path, 'target_temperature', 'number', this.min_temp, this.max_temp, unitUnit, true, true, 'value.temperature');
51
+
52
+ // Mode property
53
+ await this.adapter.createProperty(this.path, 'mode', 'number', true, true, 'level.mode.hvac');
54
+ await this.adapter.createProperty(this.path, 'modeName', 'string', true, false, 'text');
55
+
56
+ // Double setpoint support (if available)
57
+ if (zoneData.hasOwnProperty('setpoint_air_cool')) {
58
+ await this.adapter.createUnitProperty(this.path, 'setpoint_cool', 'number', this.min_temp, this.max_temp, unitUnit, true, true, 'value.temperature');
59
+ await this.adapter.createUnitProperty(this.path, 'setpoint_heat', 'number', this.min_temp, this.max_temp, unitUnit, true, true, 'value.temperature');
60
+ this.adapter.subscribeState(this.path+'.setpoint_cool', this, this.reactToSetpointCoolChange);
61
+ this.adapter.subscribeState(this.path+'.setpoint_heat', this, this.reactToSetpointHeatChange);
62
+ }
63
+
64
+ // Fan speed (if available)
65
+ if (zoneData.hasOwnProperty('speed')) {
66
+ await this.adapter.createProperty(this.path, 'fan_speed', 'number', true, true, 'level');
67
+ await this.adapter.createProperty(this.path, 'fan_speed_name', 'string', true, false, 'text');
68
+ this.adapter.subscribeState(this.path+'.fan_speed', this, this.reactToFanSpeedChange);
69
+ }
70
+
71
+ // Sleep timer (if available)
72
+ if (zoneData.hasOwnProperty('sleep')) {
73
+ await this.adapter.createProperty(this.path, 'sleep_timer', 'number', true, true, 'level.timer');
74
+ this.adapter.subscribeState(this.path+'.sleep_timer', this, this.reactToSleepTimerChange);
75
+ }
76
+
77
+ // Air quality (if available)
78
+ if (zoneData.hasOwnProperty('air_quality')) {
79
+ await this.adapter.createProperty(this.path, 'air_quality', 'number', true, false, 'value');
80
+ await this.adapter.createProperty(this.path, 'air_quality_name', 'string', true, false, 'text');
81
+ }
82
+
83
+ // Error indicators
84
+ await this.adapter.createProperty(this.path, 'errors', 'string', true, false, 'text');
85
+
47
86
  // Register callbacks to react on value changes
48
87
  this.adapter.subscribeState(this.path+'.target_temperature', this, this.reactToTargetTemperatureChange);
49
88
  this.adapter.subscribeState(this.path+'.is_on', this, this.reactToIsOnChange);
89
+ this.adapter.subscribeState(this.path+'.mode', this, this.reactToModeChange);
50
90
 
51
91
  await this.updateData(zoneData);
52
92
  }
@@ -55,7 +95,12 @@ class Zone {
55
95
  * Synchronized the zone data from airzone into the iobroker data points
56
96
  */
57
97
  async updateData(zoneData)
58
- {
98
+ {
99
+ // Only update name if API returns one (don't overwrite user-set names)
100
+ if (zoneData['name']) {
101
+ await this.adapter.updatePropertyValue(this.path, 'name', zoneData['name']);
102
+ }
103
+
59
104
  this.current_temperature = zoneData['roomTemp'];
60
105
  await this.adapter.updatePropertyValue(this.path, 'current_temperature', this.current_temperature);
61
106
 
@@ -65,15 +110,61 @@ class Zone {
65
110
  this.target_temperature = zoneData['setpoint'];
66
111
  await this.adapter.updatePropertyValue(this.path, 'target_temperature', this.target_temperature);
67
112
 
68
- this.is_on = zoneData['on'] == '1';
113
+ this.is_on = zoneData['on'] === 1;
69
114
  await this.adapter.updatePropertyValue(this.path, 'is_on', this.is_on);
70
- }
115
+
116
+ // Update mode
117
+ const mode = zoneData['mode'];
118
+ if (mode !== undefined) {
119
+ await this.adapter.updatePropertyValue(this.path, 'mode', mode);
120
+ const modeName = Constants.MODES_CONVERTER[String(mode)]?.['name'] || 'Unknown';
121
+ await this.adapter.updatePropertyValue(this.path, 'modeName', modeName);
122
+ }
123
+
124
+ // Update double setpoint values (if available)
125
+ if (zoneData.hasOwnProperty('setpoint_air_cool')) {
126
+ await this.adapter.updatePropertyValue(this.path, 'setpoint_cool', zoneData['setpoint_air_cool']);
127
+ }
128
+ if (zoneData.hasOwnProperty('setpoint_air_heat')) {
129
+ await this.adapter.updatePropertyValue(this.path, 'setpoint_heat', zoneData['setpoint_air_heat']);
130
+ }
131
+
132
+ // Update fan speed (if available)
133
+ if (zoneData.hasOwnProperty('speed')) {
134
+ const speed = zoneData['speed'];
135
+ await this.adapter.updatePropertyValue(this.path, 'fan_speed', speed);
136
+ const speedName = Constants.FAN_SPEED_CONVERTER[String(speed)]?.['name'] || 'Unknown';
137
+ await this.adapter.updatePropertyValue(this.path, 'fan_speed_name', speedName);
138
+ }
139
+
140
+ // Update sleep timer (if available)
141
+ if (zoneData.hasOwnProperty('sleep')) {
142
+ await this.adapter.updatePropertyValue(this.path, 'sleep_timer', zoneData['sleep']);
143
+ }
144
+
145
+ // Update air quality (if available)
146
+ if (zoneData.hasOwnProperty('air_quality')) {
147
+ const quality = zoneData['air_quality'];
148
+ await this.adapter.updatePropertyValue(this.path, 'air_quality', quality);
149
+ const qualityName = Constants.IAQ_SCORE_CONVERTER[String(quality)]?.['name'] || 'Unknown';
150
+ await this.adapter.updatePropertyValue(this.path, 'air_quality_name', qualityName);
151
+ }
152
+
153
+ // Update errors (if present)
154
+ if (zoneData.hasOwnProperty('errors')) {
155
+ const errors = zoneData['errors'];
156
+ const errorStr = Array.isArray(errors) ? errors.join(', ') : String(errors);
157
+ await this.adapter.updatePropertyValue(this.path, 'errors', errorStr);
158
+ } else {
159
+ await this.adapter.updatePropertyValue(this.path, 'errors', '');
160
+ }
161
+ }
71
162
 
72
163
  /**
73
164
  * Is called when the state of target_temperature was changed
74
165
  */
75
166
  async reactToTargetTemperatureChange(self, id, state) {
76
- var temperature = state.val;
167
+ let temperature = state.val;
77
168
  if(self.min_temp != undefined && temperature < self.min_temp)
78
169
  temperature = self.min_temp;
79
170
  if(self.max_temp != undefined && temperature > self.max_temp)
@@ -85,18 +176,73 @@ class Zone {
85
176
  /**
86
177
  * Is called when the state of is_on was changed
87
178
  */
88
- async reactToIsOnChange(self, id, state) {
179
+ async reactToIsOnChange(self, id, state) {
89
180
  if(state.val)
90
181
  await self.turn_on();
91
182
  else
92
183
  await self.turn_off();
93
184
  }
94
185
 
186
+ /**
187
+ * Is called when the mode was changed
188
+ */
189
+ async reactToModeChange(self, id, state) {
190
+ await self.sendEvent('mode', state.val);
191
+ }
192
+
193
+ /**
194
+ * Is called when the cooling setpoint was changed
195
+ */
196
+ async reactToSetpointCoolChange(self, id, state) {
197
+ let temperature = state.val;
198
+ if(self.min_temp != undefined && temperature < self.min_temp)
199
+ temperature = self.min_temp;
200
+ if(self.max_temp != undefined && temperature > self.max_temp)
201
+ temperature = self.max_temp;
202
+ await self.sendEvent('setpoint_air_cool', temperature);
203
+ }
204
+
205
+ /**
206
+ * Is called when the heating setpoint was changed
207
+ */
208
+ async reactToSetpointHeatChange(self, id, state) {
209
+ let temperature = state.val;
210
+ if(self.min_temp != undefined && temperature < self.min_temp)
211
+ temperature = self.min_temp;
212
+ if(self.max_temp != undefined && temperature > self.max_temp)
213
+ temperature = self.max_temp;
214
+ await self.sendEvent('setpoint_air_heat', temperature);
215
+ }
216
+
217
+ /**
218
+ * Is called when the fan speed was changed
219
+ */
220
+ async reactToFanSpeedChange(self, id, state) {
221
+ const speed = Math.max(0, Math.min(7, Math.round(state.val)));
222
+ await self.sendEvent('speed', speed);
223
+ }
224
+
225
+ /**
226
+ * Is called when the sleep timer was changed
227
+ */
228
+ async reactToSleepTimerChange(self, id, state) {
229
+ // Sleep timer values: 0 = off, 30, 60, 90 minutes
230
+ const validValues = [0, 30, 60, 90];
231
+ let value = state.val;
232
+ if (!validValues.includes(value)) {
233
+ // Find the closest valid value
234
+ value = validValues.reduce((prev, curr) =>
235
+ Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev
236
+ );
237
+ }
238
+ await self.sendEvent('sleep', value);
239
+ }
240
+
95
241
  /**
96
242
  * Send event to the airzone cloud
97
243
  */
98
244
  async sendEvent(option, value) {
99
- await this.localApi.sendUpdate(this.id, option, value)
245
+ await this.localApi.sendUpdate(this.id, option, value);
100
246
  }
101
247
 
102
248
  /**
@@ -109,7 +255,7 @@ class Zone {
109
255
  /**
110
256
  * Turn zone off
111
257
  */
112
- async turn_off() {
258
+ async turn_off() {
113
259
  await this.sendEvent('on', 0);
114
260
  }
115
261
  }
package/README.md CHANGED
@@ -11,6 +11,26 @@ Control and monitor airzone devices with ioBroker.
11
11
  [![Build Status](https://travis-ci.com/SilentPhoenix11/ioBroker.airzone.svg?branch=master)](https://travis-ci.com/github/SilentPhoenix11/ioBroker.airzone)
12
12
 
13
13
  ## Changelog
14
+ ### 3.0.0
15
+ * (SilentPhoenix11) **BREAKING**: Requires Node.js >= 18 and js-controller >= 5.0.0
16
+ * (SilentPhoenix11) Migrated from deprecated `request` to `axios` HTTP client
17
+ * (SilentPhoenix11) Updated to @iobroker/adapter-core 3.x
18
+ * (SilentPhoenix11) Added `info.connection` state for connection status
19
+ * (SilentPhoenix11) Added support for fan speed control (`fanSpeed`, `fanSpeedRaw`)
20
+ * (SilentPhoenix11) Added sleep timer support (`sleepTime`)
21
+ * (SilentPhoenix11) Added double setpoint support (`setpointCool`, `setpointHeat`)
22
+ * (SilentPhoenix11) Added zone mode control (`mode`, `modeRaw`)
23
+ * (SilentPhoenix11) Added zone error tracking (`errors`)
24
+ * (SilentPhoenix11) Zone names are now editable
25
+ * (SilentPhoenix11) Added IAQ sensor support (CO2, PM2.5, PM10, TVOC)
26
+ * (SilentPhoenix11) Added API version and webserver info endpoints
27
+ * (SilentPhoenix11) Fixed HTTP parser issue with Airzone devices (non-standard LF line endings)
28
+ * (SilentPhoenix11) Uses adapter timers instead of global setTimeout
29
+ * (SilentPhoenix11) Migrated to ESLint 9 flat config
30
+
31
+ ### 2.0.3
32
+ * (SilentPhoenix11) Small fixes
33
+
14
34
  ### 2.0.2
15
35
  * (SilentPhoenix11) Small fixes
16
36
 
@@ -1,94 +1,137 @@
1
- const util = require('util');
2
- const request = require("request");
3
- const asyncRequest = util.promisify(request);
1
+ 'use strict';
2
+
3
+ const axios = require('axios');
4
+
5
+ // Create axios instance with default config for Airzone API
6
+ const axiosInstance = axios.create({
7
+ timeout: 10000,
8
+ headers: {
9
+ 'Content-Type': 'application/json'
10
+ }
11
+ });
4
12
 
5
13
  class AsyncRequest {
6
14
 
15
+ /**
16
+ * Perform a POST request to the Airzone API
17
+ * @param {string} url - The URL to send the request to
18
+ * @param {object} data - The data to send (will be serialized to JSON)
19
+ * @returns {Promise<object>} - Response object with statusCode and body/errors
20
+ */
7
21
  static async jsonPostRequest(url, data) {
8
-
9
- const response = await asyncRequest({
10
- method: 'POST',
11
- uri: url,
12
- headers: {
13
- 'Content-Type': 'application/json'
14
- },
15
- body: data
16
- });
22
+ try {
23
+ // Parse data if it's a string
24
+ const requestData = typeof data === 'string' ? JSON.parse(data) : data;
17
25
 
18
- var result;
19
-
20
- if(response.error)
21
- {
22
- result = JSON.stringify({statusCode:response.statusCode,errors:error});
23
- }
24
- else
25
- {
26
- var body = response.body;
27
- var errorMsg = JSON.parse(body)["errors"];
28
- if(errorMsg)
29
- result = JSON.stringify({statusCode:response.statusCode,errors:errorMsg});
30
- else
31
- result = JSON.stringify({statusCode:response.statusCode,body:response.body});
32
- }
26
+ const response = await axiosInstance.post(url, requestData);
27
+
28
+ // Check for API-level errors in response
29
+ if (response.data && response.data.errors) {
30
+ return {
31
+ statusCode: response.status,
32
+ errors: response.data.errors
33
+ };
34
+ }
33
35
 
34
- return JSON.parse(result);
36
+ return {
37
+ statusCode: response.status,
38
+ body: JSON.stringify(response.data)
39
+ };
40
+ } catch (error) {
41
+ if (error.response) {
42
+ // Server responded with error status
43
+ const errorData = error.response.data;
44
+ return {
45
+ statusCode: error.response.status,
46
+ errors: errorData?.errors || errorData?.message || error.message
47
+ };
48
+ }
49
+ // Network error or timeout
50
+ return {
51
+ statusCode: 0,
52
+ errors: error.message
53
+ };
54
+ }
35
55
  }
36
56
 
57
+ /**
58
+ * Perform a PUT request to the Airzone API
59
+ * @param {string} url - The URL to send the request to
60
+ * @param {object} data - The data to send (will be serialized to JSON)
61
+ * @returns {Promise<object>} - Response object with statusCode and body/errors
62
+ */
37
63
  static async jsonPutRequest(url, data) {
38
-
39
- const response = await asyncRequest({
40
- method: 'PUT',
41
- uri: url,
42
- headers: {
43
- 'Content-Type': 'application/json'
44
- },
45
- body: data
46
- });
64
+ try {
65
+ // Parse data if it's a string
66
+ const requestData = typeof data === 'string' ? JSON.parse(data) : data;
47
67
 
48
- var result;
49
-
50
- if(response.error)
51
- {
52
- result = JSON.stringify({statusCode:response.statusCode,errors:error});
53
- }
54
- else
55
- {
56
- var body = response.body;
57
- var errorMsg = JSON.parse(body)["errors"];
58
- if(errorMsg)
59
- result = JSON.stringify({statusCode:response.statusCode,errors:errorMsg});
60
- else
61
- result = JSON.stringify({statusCode:response.statusCode,body:response.body});
62
- }
68
+ const response = await axiosInstance.put(url, requestData);
63
69
 
64
- return JSON.parse(result);
65
- }
70
+ // Check for API-level errors in response
71
+ if (response.data && response.data.errors) {
72
+ return {
73
+ statusCode: response.status,
74
+ errors: response.data.errors
75
+ };
76
+ }
66
77
 
78
+ return {
79
+ statusCode: response.status,
80
+ body: JSON.stringify(response.data)
81
+ };
82
+ } catch (error) {
83
+ if (error.response) {
84
+ // Server responded with error status
85
+ const errorData = error.response.data;
86
+ return {
87
+ statusCode: error.response.status,
88
+ errors: errorData?.errors || errorData?.message || error.message
89
+ };
90
+ }
91
+ // Network error or timeout
92
+ return {
93
+ statusCode: 0,
94
+ errors: error.message
95
+ };
96
+ }
97
+ }
67
98
 
99
+ /**
100
+ * Perform a GET request to the Airzone API
101
+ * @param {string} url - The URL to send the request to
102
+ * @returns {Promise<object>} - Response object with statusCode and body/errors
103
+ */
68
104
  static async jsonGetRequest(url) {
69
-
70
- const response = await asyncRequest({
71
- method: 'GET',
72
- uri: url
73
- });
105
+ try {
106
+ const response = await axiosInstance.get(url);
74
107
 
75
- var result;
76
-
77
- if(response.error)
78
- {
79
- result = JSON.stringify({statusCode:response.statusCode,errors:error});
80
- }
81
- else
82
- {
83
- var body = response.body;
84
- var errorMsg = JSON.parse(body)["errors"];
85
- if(errorMsg)
86
- result = JSON.stringify({statusCode:response.statusCode,errors:errorMsg});
87
- else
88
- result = JSON.stringify({statusCode:response.statusCode,body:response.body});
89
- }
108
+ // Check for API-level errors in response
109
+ if (response.data && response.data.errors) {
110
+ return {
111
+ statusCode: response.status,
112
+ errors: response.data.errors
113
+ };
114
+ }
90
115
 
91
- return JSON.parse(result);
116
+ return {
117
+ statusCode: response.status,
118
+ body: JSON.stringify(response.data)
119
+ };
120
+ } catch (error) {
121
+ if (error.response) {
122
+ // Server responded with error status
123
+ const errorData = error.response.data;
124
+ return {
125
+ statusCode: error.response.status,
126
+ errors: errorData?.errors || errorData?.message || error.message
127
+ };
128
+ }
129
+ // Network error or timeout
130
+ return {
131
+ statusCode: 0,
132
+ errors: error.message
133
+ };
134
+ }
92
135
  }
93
136
  }
94
137
 
@@ -0,0 +1,30 @@
1
+ const js = require('@eslint/js');
2
+ const globals = require('globals');
3
+
4
+ module.exports = [
5
+ js.configs.recommended,
6
+ {
7
+ languageOptions: {
8
+ ecmaVersion: 2022,
9
+ sourceType: 'commonjs',
10
+ globals: {
11
+ ...globals.node,
12
+ ...globals.mocha
13
+ }
14
+ },
15
+ rules: {
16
+ 'indent': ['error', 4, { 'SwitchCase': 1 }],
17
+ 'no-console': 'off',
18
+ 'no-var': 'error',
19
+ 'prefer-const': 'error',
20
+ 'no-trailing-spaces': 'error',
21
+ 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }],
22
+ 'semi': ['error', 'always'],
23
+ 'no-prototype-builtins': 'off',
24
+ 'no-unused-vars': ['error', { 'argsIgnorePattern': '^_', 'varsIgnorePattern': '^_', 'caughtErrorsIgnorePattern': '^_' }]
25
+ }
26
+ },
27
+ {
28
+ ignores: ['node_modules/**', '.git/**', 'gulpfile.js', 'admin/words.js']
29
+ }
30
+ ];
package/io-package.json CHANGED
@@ -1,8 +1,20 @@
1
1
  {
2
2
  "common":{
3
3
  "name":"airzone",
4
- "version":"2.0.2",
4
+ "version":"3.0.0",
5
5
  "news":{
6
+ "3.0.0":{
7
+ "en": "Migrated from request to axios, Node.js 18+, added info.connection, extended API support",
8
+ "de": "Migration von request zu axios, Node.js 18+, info.connection hinzugefügt, erweiterte API-Unterstützung",
9
+ "ru": "Переход с request на axios, Node.js 18+, добавлен info.connection, расширенная поддержка API",
10
+ "pt": "Migração de request para axios, Node.js 18+, info.connection adicionado, suporte estendido à API",
11
+ "nl": "Migratie van request naar axios, Node.js 18+, info.connection toegevoegd, uitgebreide API-ondersteuning",
12
+ "fr": "Migration de request vers axios, Node.js 18+, info.connection ajouté, support API étendu",
13
+ "it": "Migrazione da request ad axios, Node.js 18+, aggiunto info.connection, supporto API esteso",
14
+ "es": "Migración de request a axios, Node.js 18+, info.connection añadido, soporte API extendido",
15
+ "pl": "Migracja z request do axios, Node.js 18+, dodano info.connection, rozszerzona obsługa API",
16
+ "zh-cn": "从request迁移到axios,Node.js 18+,添加info.connection,扩展API支持"
17
+ },
6
18
  "1.0.0":{
7
19
  "en": "initial release",
8
20
  "de": "Erstveröffentlichung",
@@ -98,6 +110,18 @@
98
110
  "es": "Pequeñas correcciones",
99
111
  "pl": "Małe poprawki",
100
112
  "zh-cn": "小修正"
113
+ },
114
+ "2.0.3":{
115
+ "en": "Small fixes",
116
+ "de": "Kleine Korrekturen",
117
+ "ru": "Небольшие исправления",
118
+ "pt": "Pequenas correções",
119
+ "nl": "Kleine reparaties",
120
+ "fr": "Petits correctifs",
121
+ "it": "Piccole correzioni",
122
+ "es": "Pequeñas correcciones",
123
+ "pl": "Małe poprawki",
124
+ "zh-cn": "小修正"
101
125
  }
102
126
  },
103
127
  "title":"Airzone Local API",
@@ -144,13 +168,14 @@
144
168
  "loglevel":"info",
145
169
  "mode":"daemon",
146
170
  "type":"climate-control",
147
- "compact":true,
171
+ "compact":false,
148
172
  "connectionType":"local",
149
- "dataSource":"poll",
150
- "materialize":true,
173
+ "dataSource":"poll",
174
+ "materialize":true,
175
+ "nodeProcessParams": ["--insecure-http-parser"],
151
176
  "dependencies":[
152
177
  {
153
- "js-controller":">=2.0.0"
178
+ "js-controller":">=5.0.0"
154
179
  }
155
180
  ]
156
181
  },
@@ -162,5 +187,27 @@
162
187
  "encryptedNative": [],
163
188
  "protectedNative": [],
164
189
  "objects":[],
165
- "instanceObjects":[]
190
+ "instanceObjects":[
191
+ {
192
+ "_id": "info",
193
+ "type": "channel",
194
+ "common": {
195
+ "name": "Information"
196
+ },
197
+ "native": {}
198
+ },
199
+ {
200
+ "_id": "info.connection",
201
+ "type": "state",
202
+ "common": {
203
+ "role": "indicator.connected",
204
+ "name": "Device or service connected",
205
+ "type": "boolean",
206
+ "read": true,
207
+ "write": false,
208
+ "def": false
209
+ },
210
+ "native": {}
211
+ }
212
+ ]
166
213
  }