iobroker.airzone 2.0.3 → 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.
@@ -1,32 +1,32 @@
1
- ---
2
- name: Bug report
3
- about: Something is not working as it should
4
- title: ''
5
- labels: ''
6
- assignees: ''
7
- ---
8
-
9
- **Describe the bug**
10
- A clear and concise description of what the bug is.
11
-
12
- **To Reproduce**
13
- Steps to reproduce the behavior:
14
- 1. Go to '...'
15
- 2. Click on '...'
16
- 3. Scroll down to '....'
17
- 4. See error
18
-
19
- **Expected behavior**
20
- A clear and concise description of what you expected to happen.
21
-
22
- **Screenshots & Logfiles**
23
- If applicable, add screenshots and logfiles to help explain your problem.
24
-
25
- **Versions:**
26
- - Adapter version: <adapter-version>
27
- - JS-Controller version: <js-controller-version> <!-- determine this with `iobroker -v` on the console -->
28
- - Node version: <node-version> <!-- determine this with `node -v` on the console -->
29
- - Operating system: <os-name>
30
-
31
- **Additional context**
32
- Add any other context about the problem here.
1
+ ---
2
+ name: Bug report
3
+ about: Something is not working as it should
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+ ---
8
+
9
+ **Describe the bug**
10
+ A clear and concise description of what the bug is.
11
+
12
+ **To Reproduce**
13
+ Steps to reproduce the behavior:
14
+ 1. Go to '...'
15
+ 2. Click on '...'
16
+ 3. Scroll down to '....'
17
+ 4. See error
18
+
19
+ **Expected behavior**
20
+ A clear and concise description of what you expected to happen.
21
+
22
+ **Screenshots & Logfiles**
23
+ If applicable, add screenshots and logfiles to help explain your problem.
24
+
25
+ **Versions:**
26
+ - Adapter version: <adapter-version>
27
+ - JS-Controller version: <js-controller-version> <!-- determine this with `iobroker -v` on the console -->
28
+ - Node version: <node-version> <!-- determine this with `node -v` on the console -->
29
+ - Operating system: <os-name>
30
+
31
+ **Additional context**
32
+ Add any other context about the problem here.
package/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Changelog for Windows-Installer-NPX
2
+ <!-- **WORK IN PROGRESS** -->
3
+ ## 4.0.3 (2022-05-22)
4
+ * Corrected fixer
5
+
6
+ ## 4.0.2 (2022-05-22)
7
+ * Activate windows as npx installer again
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  const AsyncRequest = require('../Utils/asyncRequest');
4
- const System = require('./System')
4
+ const System = require('./System');
5
+ const Constants = require('./Constants');
5
6
 
6
7
  // Allow to connect to Airzone local API
7
8
 
@@ -11,16 +12,26 @@ class AirzoneLocalApi {
11
12
  constructor(a, local_ip)
12
13
  {
13
14
  adapter = a;
14
- log = a.log;
15
+ log = a.log;
15
16
  this.local_ip = local_ip;
17
+ this.baseUrl = `http://${local_ip}:3000`;
16
18
  }
17
19
 
18
20
  async init(system_id) {
19
-
20
21
  this.system = new System(adapter, this, system_id);
21
22
  await this.system.init();
23
+
24
+ // Try to get version info
25
+ try {
26
+ const version = await this.getVersion();
27
+ if (version) {
28
+ this.logInfo(`Airzone API Version: ${JSON.stringify(version)}`);
29
+ }
30
+ } catch (_e) {
31
+ this.logInfo('Version endpoint not available');
32
+ }
22
33
  }
23
-
34
+
24
35
  async update() {
25
36
  if(this.system == undefined)
26
37
  return;
@@ -36,47 +47,105 @@ class AirzoneLocalApi {
36
47
  log.error(msg);
37
48
  }
38
49
 
50
+ /**
51
+ * Get the API version information
52
+ * @returns {Promise<object|undefined>} Version info or undefined on error
53
+ */
54
+ async getVersion() {
55
+ const url = this.baseUrl + Constants.API_ENDPOINTS.VERSION;
56
+ const response = await AsyncRequest.jsonGetRequest(url);
57
+
58
+ if (response['errors']) {
59
+ return undefined;
60
+ }
61
+
62
+ const body = response['body'];
63
+ return body ? JSON.parse(body) : undefined;
64
+ }
65
+
66
+ /**
67
+ * Get webserver information (if available)
68
+ * @returns {Promise<object|undefined>} Webserver info or undefined on error
69
+ */
70
+ async getWebserverInfo() {
71
+ const url = this.baseUrl + Constants.API_ENDPOINTS.WEBSERVER;
72
+ const data = {};
73
+ const response = await AsyncRequest.jsonPostRequest(url, data);
74
+
75
+ if (response['errors']) {
76
+ return undefined;
77
+ }
78
+
79
+ const body = response['body'];
80
+ return body ? JSON.parse(body) : undefined;
81
+ }
82
+
39
83
  async getZoneState() {
40
84
  if(this.system == undefined)
41
85
  return undefined;
42
-
43
- var url = "http://"+this.local_ip+":3000/api/v1/hvac";
44
- var systemId = this.system.id;
45
- var data = '{\"systemID\":'+systemId+', \"ZoneID\":0}';
46
- var response = await AsyncRequest.jsonPostRequest(url, data);
47
86
 
48
- var errors = response["errors"];
87
+ const url = this.baseUrl + Constants.API_ENDPOINTS.HVAC;
88
+ const systemId = this.system.id;
89
+ const data = { systemID: systemId, ZoneID: 0 };
90
+ const response = await AsyncRequest.jsonPostRequest(url, data);
91
+
92
+ const errors = response['errors'];
49
93
  if(errors)
50
94
  {
51
- this.logError("Failed to get zone state: (statusCode: "+response["statusCode"]+") - "+response["errors"]);
95
+ this.logError('Failed to get zone state: (statusCode: '+response['statusCode']+') - '+response['errors']);
52
96
  return undefined;
53
97
  }
54
- var body = response["body"];
55
- var zones = JSON.parse(body)["data"];
56
-
98
+ const body = response['body'];
99
+ const zones = JSON.parse(body)['data'];
100
+
57
101
  return zones;
58
102
  }
59
103
 
60
- async sendUpdate(zoneid, key, value)
104
+ /**
105
+ * Get IAQ sensor data (if available)
106
+ * @returns {Promise<Array|undefined>} IAQ data array or undefined on error
107
+ */
108
+ async getIAQData() {
109
+ if(this.system == undefined)
110
+ return undefined;
111
+
112
+ const url = this.baseUrl + Constants.API_ENDPOINTS.IAQ;
113
+ const systemId = this.system.id;
114
+ const data = { systemID: systemId };
115
+ const response = await AsyncRequest.jsonPostRequest(url, data);
116
+
117
+ const errors = response['errors'];
118
+ if (errors) {
119
+ // IAQ endpoint may not be available on all devices
120
+ return undefined;
121
+ }
122
+
123
+ const body = response['body'];
124
+ const iaqData = body ? JSON.parse(body)['data'] : undefined;
125
+
126
+ return iaqData;
127
+ }
128
+
129
+ async sendUpdate(zoneid, key, value)
61
130
  {
62
131
  if(this.system == undefined)
63
132
  return false;
64
133
 
65
134
  try
66
135
  {
67
- var url = "http://"+this.local_ip+":3000/api/v1/hvac";
68
- var systemId = this.system.id;
69
- var data = '{\"systemID\":'+systemId+', \"ZoneID\":'+zoneid+', \"'+key+'\":'+value+'}';
70
- var response = await AsyncRequest.jsonPutRequest(url, data);
71
- var errors = response["errors"];
136
+ const url = this.baseUrl + Constants.API_ENDPOINTS.HVAC;
137
+ const systemId = this.system.id;
138
+ const data = { systemID: systemId, zoneID: zoneid, [key]: value };
139
+ const response = await AsyncRequest.jsonPutRequest(url, data);
140
+ const errors = response['errors'];
72
141
  if(errors)
73
142
  {
74
- this.logError("Failed to update '"+key+"' with value '"+value+"': (statusCode: "+response["statusCode"]+") - "+response["errors"]);
75
- return undefined;
143
+ this.logError("Failed to update '"+key+"' with value '"+value+"': (statusCode: "+response['statusCode']+') - '+response['errors']);
144
+ return false;
76
145
  }
77
- var body = response["body"];
78
- var responseData = JSON.parse(body)["data"];
79
- return responseData.hasOwnProperty(key);
146
+ const body = response['body'];
147
+ const responseData = JSON.parse(body)['data'];
148
+ return responseData && responseData.hasOwnProperty(key);
80
149
  }
81
150
  catch (e) {
82
151
  this.logError('error during sendUpdate '+e+'\r\n'+e.stack);
@@ -1,14 +1,58 @@
1
+ 'use strict';
2
+
1
3
  module.exports = Object.freeze({
2
- MODES_CONVERTER : {
3
- "1": {"name": "Stop"},
4
- "2": {"name": "Cooling"},
5
- "3": {"name": "Heating"},
6
- "4": {"name": "Fan"},
7
- "5": {"name": "Dry"}
4
+ // Operating modes - Mode 7 (Auto) added for newer firmware
5
+ MODES_CONVERTER: {
6
+ '1': { 'name': 'Stop' },
7
+ '2': { 'name': 'Cooling' },
8
+ '3': { 'name': 'Heating' },
9
+ '4': { 'name': 'Fan' },
10
+ '5': { 'name': 'Dry' },
11
+ '7': { 'name': 'Auto' }
12
+ },
13
+
14
+ // Temperature units
15
+ UNIT_CONVERTER: {
16
+ '0': { 'name': 'Celsius', 'unit': '°C' },
17
+ '1': { 'name': 'Fahrenheit', 'unit': '°F' }
18
+ },
19
+
20
+ // Fan speed levels (0 = Auto, 1-7 = Manual speeds)
21
+ FAN_SPEED_CONVERTER: {
22
+ '0': { 'name': 'Auto' },
23
+ '1': { 'name': 'Speed 1 (Lowest)' },
24
+ '2': { 'name': 'Speed 2' },
25
+ '3': { 'name': 'Speed 3' },
26
+ '4': { 'name': 'Speed 4' },
27
+ '5': { 'name': 'Speed 5' },
28
+ '6': { 'name': 'Speed 6' },
29
+ '7': { 'name': 'Speed 7 (Highest)' }
30
+ },
31
+
32
+ // Indoor Air Quality score mapping
33
+ IAQ_SCORE_CONVERTER: {
34
+ '1': { 'name': 'Good', 'color': 'green' },
35
+ '2': { 'name': 'Medium', 'color': 'yellow' },
36
+ '3': { 'name': 'Bad', 'color': 'orange' },
37
+ '4': { 'name': 'Very Bad', 'color': 'red' }
8
38
  },
9
39
 
10
- UNIT_CONVERTER : {
11
- "0": {"name": "Celsius", "unit": "°C"},
12
- "1": {"name": "Fahrenheit", "unit": "°F"}
40
+ // API Endpoints
41
+ API_ENDPOINTS: {
42
+ HVAC: '/api/v1/hvac',
43
+ SYSTEMS: '/api/v1/systems',
44
+ ZONES: '/api/v1/zones',
45
+ WEBSERVER: '/api/v1/webserver',
46
+ VERSION: '/api/v1/version',
47
+ IAQ: '/api/v1/iaq'
13
48
  },
49
+
50
+ // Error codes from the API
51
+ ERROR_CODES: {
52
+ '-1': 'System error',
53
+ '1': 'Invalid parameter',
54
+ '2': 'Invalid value',
55
+ '3': 'Out of range',
56
+ '4': 'Permission denied'
57
+ }
14
58
  });
@@ -0,0 +1,123 @@
1
+ 'use strict';
2
+
3
+ const Constants = require('./Constants');
4
+
5
+ /**
6
+ * Indoor Air Quality (IAQ) Sensor class
7
+ * Handles air quality sensor data from the Airzone system
8
+ */
9
+ class IAQSensor {
10
+ constructor(adapter, localApi) {
11
+ this.adapter = adapter;
12
+ this.localApi = localApi;
13
+ }
14
+
15
+ /**
16
+ * Initialize the IAQ sensor with data from the airzone local api
17
+ * @param {string} path - Base path for the sensor objects
18
+ * @param {object} iaqData - IAQ sensor data from the API
19
+ */
20
+ async init(path, iaqData) {
21
+ this.systemId = parseInt(iaqData['system_id'] || iaqData['systemID'] || 1);
22
+ this.zoneId = parseInt(iaqData['zone_id'] || iaqData['zoneID'] || 0);
23
+
24
+ this.path = path + '.IAQ_Zone' + this.zoneId;
25
+ await this.adapter.setObjectNotExistsAsync(this.path, {
26
+ type: 'device',
27
+ common: {
28
+ name: 'IAQ_Sensor_Zone_' + this.zoneId,
29
+ type: 'object',
30
+ read: true,
31
+ write: false,
32
+ },
33
+ native: {},
34
+ });
35
+
36
+ // IAQ Index (overall air quality score 1-4)
37
+ await this.adapter.createProperty(this.path, 'iaq_index', 'number', true, false, 'value');
38
+ await this.adapter.createProperty(this.path, 'iaq_score_name', 'string', true, false, 'text');
39
+
40
+ // CO2 level
41
+ await this.adapter.createUnitProperty(this.path, 'co2', 'number', 0, 5000, 'ppm', true, false, 'value.co2');
42
+
43
+ // Temperature from IAQ sensor
44
+ await this.adapter.createUnitProperty(this.path, 'temperature', 'number', -50, 100, '°C', true, false, 'value.temperature');
45
+
46
+ // Humidity from IAQ sensor
47
+ await this.adapter.createUnitProperty(this.path, 'humidity', 'number', 0, 100, '%', true, false, 'value.humidity');
48
+
49
+ // PM2.5 (Particulate Matter)
50
+ await this.adapter.createUnitProperty(this.path, 'pm2_5', 'number', 0, 1000, 'µg/m³', true, false, 'value');
51
+
52
+ // PM10 (Particulate Matter)
53
+ await this.adapter.createUnitProperty(this.path, 'pm10', 'number', 0, 1000, 'µg/m³', true, false, 'value');
54
+
55
+ // TVOC (Total Volatile Organic Compounds)
56
+ await this.adapter.createUnitProperty(this.path, 'tvoc', 'number', 0, 10000, 'ppb', true, false, 'value');
57
+
58
+ // Atmospheric pressure
59
+ await this.adapter.createUnitProperty(this.path, 'pressure', 'number', 800, 1200, 'hPa', true, false, 'value.pressure');
60
+
61
+ // Ventilation mode
62
+ await this.adapter.createProperty(this.path, 'ventilation_mode', 'number', true, false, 'value');
63
+
64
+ await this.updateData(iaqData);
65
+ }
66
+
67
+ /**
68
+ * Update the IAQ sensor data
69
+ * @param {object} iaqData - IAQ sensor data from the API
70
+ */
71
+ async updateData(iaqData) {
72
+ // IAQ Index
73
+ if (iaqData.hasOwnProperty('iaq_index')) {
74
+ const iaqIndex = iaqData['iaq_index'];
75
+ await this.adapter.updatePropertyValue(this.path, 'iaq_index', iaqIndex);
76
+
77
+ const scoreName = Constants.IAQ_SCORE_CONVERTER[iaqIndex.toString()]?.name || 'Unknown';
78
+ await this.adapter.updatePropertyValue(this.path, 'iaq_score_name', scoreName);
79
+ }
80
+
81
+ // CO2
82
+ if (iaqData.hasOwnProperty('co2_value')) {
83
+ await this.adapter.updatePropertyValue(this.path, 'co2', iaqData['co2_value']);
84
+ }
85
+
86
+ // Temperature
87
+ if (iaqData.hasOwnProperty('iaq_temp_value')) {
88
+ await this.adapter.updatePropertyValue(this.path, 'temperature', iaqData['iaq_temp_value']);
89
+ }
90
+
91
+ // Humidity
92
+ if (iaqData.hasOwnProperty('iaq_humidity_value')) {
93
+ await this.adapter.updatePropertyValue(this.path, 'humidity', iaqData['iaq_humidity_value']);
94
+ }
95
+
96
+ // PM2.5
97
+ if (iaqData.hasOwnProperty('pm2_5_value')) {
98
+ await this.adapter.updatePropertyValue(this.path, 'pm2_5', iaqData['pm2_5_value']);
99
+ }
100
+
101
+ // PM10
102
+ if (iaqData.hasOwnProperty('pm10_value')) {
103
+ await this.adapter.updatePropertyValue(this.path, 'pm10', iaqData['pm10_value']);
104
+ }
105
+
106
+ // TVOC
107
+ if (iaqData.hasOwnProperty('tvoc_value')) {
108
+ await this.adapter.updatePropertyValue(this.path, 'tvoc', iaqData['tvoc_value']);
109
+ }
110
+
111
+ // Pressure
112
+ if (iaqData.hasOwnProperty('pressure_value')) {
113
+ await this.adapter.updatePropertyValue(this.path, 'pressure', iaqData['pressure_value']);
114
+ }
115
+
116
+ // Ventilation mode
117
+ if (iaqData.hasOwnProperty('iaq_mode_vent')) {
118
+ await this.adapter.updatePropertyValue(this.path, 'ventilation_mode', iaqData['iaq_mode_vent']);
119
+ }
120
+ }
121
+ }
122
+
123
+ module.exports = IAQSensor;
@@ -1,12 +1,11 @@
1
- const Zone = require('./Zone')
2
- const AsyncRequest = require('../Utils/asyncRequest');
1
+ const Zone = require('./Zone');
3
2
  const Constants = require('./Constants');
4
3
 
5
4
  class System {
6
5
  constructor(adapter, localApi, id)
7
6
  {
8
7
  this.adapter = adapter;
9
- this.localApi = localApi;
8
+ this.localApi = localApi;
10
9
  this.id = id;
11
10
  }
12
11
 
@@ -14,7 +13,7 @@ class System {
14
13
  * Initialize the system with the data from the airzone local api
15
14
  */
16
15
  async init() {
17
- this.path = "System"+this.id;
16
+ this.path = 'System'+this.id;
18
17
  await this.adapter.setObjectNotExistsAsync(this.path, {
19
18
  type: 'device',
20
19
  common: {
@@ -30,8 +29,8 @@ class System {
30
29
  await this.adapter.createProperty(this.path, 'mode', 'string', true, false, 'text');
31
30
 
32
31
  this.adapter.subscribeState(this.path+'.mode_raw', this, this.reactToModeRawChange);
33
-
34
- var masterZone = await this.load_zones(this.path);
32
+
33
+ const masterZone = await this.load_zones(this.path);
35
34
  if(masterZone == undefined)
36
35
  return false;
37
36
 
@@ -44,24 +43,24 @@ class System {
44
43
  * Synchronized the system data from airzone into the iobroker data points
45
44
  */
46
45
  async updateData(masterZoneData)
47
- {
46
+ {
48
47
  if(masterZoneData == undefined)
49
48
  {
50
- this.localApi.logError("Missing master Zone");
49
+ this.localApi.logError('Missing master Zone');
51
50
  return;
52
- }
51
+ }
53
52
 
54
- this.mode_raw = masterZoneData["mode"];
53
+ this.mode_raw = masterZoneData['mode'];
55
54
  await this.adapter.updatePropertyValue(this.path, 'mode_raw', this.mode_raw);
56
- this.mode = Constants.MODES_CONVERTER[this.mode_raw]["name"];
55
+ this.mode = Constants.MODES_CONVERTER[this.mode_raw]['name'];
57
56
  await this.adapter.updatePropertyValue(this.path, 'mode', this.mode);
58
57
  }
59
-
58
+
60
59
  /**
61
60
  * Synchronized the system data from airzone into the iobroker data points and call update for all sub zones
62
61
  */
63
- async update() {
64
- var masterZoneData = await this.update_zones();
62
+ async update() {
63
+ const masterZoneData = await this.update_zones();
65
64
 
66
65
  await this.updateData(masterZoneData);
67
66
  }
@@ -71,25 +70,25 @@ class System {
71
70
  */
72
71
  async load_zones(path) {
73
72
 
74
- var zones_relations = await this.localApi.getZoneState();
73
+ const zones_relations = await this.localApi.getZoneState();
75
74
  if(zones_relations == undefined)
76
75
  return undefined;
77
76
 
78
- var masterZoneData = undefined;
77
+ let masterZoneData = undefined;
79
78
  this.zones = [];
80
79
  for (let index = 0; index < zones_relations.length; index++) {
81
- var zoneData = zones_relations[index];
82
- var zone = new Zone(this.adapter, this.localApi);
83
- await zone.init(path, zoneData)
84
- this.zones[index] = zone;
85
-
86
- if(zoneData.hasOwnProperty("mode"))
80
+ const zoneData = zones_relations[index];
81
+ const zone = new Zone(this.adapter, this.localApi);
82
+ await zone.init(path, zoneData);
83
+ this.zones[index] = zone;
84
+
85
+ if(zoneData.hasOwnProperty('mode'))
87
86
  {
88
87
  masterZoneData = zoneData;
89
88
  this.masterZoneId = this.zones[index].id;
90
89
  }
91
90
  }
92
-
91
+
93
92
  return masterZoneData;
94
93
  }
95
94
 
@@ -97,21 +96,21 @@ class System {
97
96
  * Update zones with the current zone data from airzone local api
98
97
  */
99
98
  async update_zones() {
100
-
101
- var zones_relations = await this.localApi.getZoneState();
99
+
100
+ const zones_relations = await this.localApi.getZoneState();
102
101
  if(zones_relations == undefined)
103
102
  return undefined;
104
103
 
105
104
  if(this.zones == undefined)
106
105
  return undefined;
107
106
 
108
- var masterZoneData = undefined;
107
+ let masterZoneData = undefined;
109
108
 
110
109
  for (let index = 0; index < zones_relations.length; index++) {
111
- var zoneData = zones_relations[index];
112
- var zId = zoneData["zoneID"];
113
-
114
- if(zoneData.hasOwnProperty("mode"))
110
+ const zoneData = zones_relations[index];
111
+ const zId = zoneData['zoneID'];
112
+
113
+ if(zoneData.hasOwnProperty('mode'))
115
114
  masterZoneData = zoneData;
116
115
 
117
116
  for(let i = 0;i<this.zones.length;i++) {
@@ -119,24 +118,24 @@ class System {
119
118
  await this.zones[i].updateData(zoneData);
120
119
  break;
121
120
  }
122
- }
123
- }
121
+ }
122
+ }
124
123
 
125
124
  return masterZoneData;
126
125
  }
127
-
126
+
128
127
  /**
129
128
  * Is called when the state of mode was changed
130
129
  */
131
130
  async reactToModeRawChange(self, id, state) {
132
-
131
+
133
132
  if(state.val == 0)
134
133
  {
135
134
  self.zones.forEach(zone => {
136
135
  zone.turn_off();
137
136
  });
138
137
  }
139
-
138
+
140
139
  self.sendEvent('mode', state.val);
141
140
  }
142
141
 
@@ -144,7 +143,7 @@ class System {
144
143
  * Send event to the airzone local api
145
144
  */
146
145
  async sendEvent(option, value) {
147
- await this.localApi.sendUpdate(this.masterZoneId, option, value)
148
- }
146
+ await this.localApi.sendUpdate(this.masterZoneId, option, value);
147
+ }
149
148
  }
150
149
  module.exports = System;