iobroker.weathersense 1.0.2 → 2.0.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/main.js CHANGED
@@ -1,30 +1,29 @@
1
- "use strict";
1
+ 'use strict';
2
2
 
3
3
  /*
4
4
  * Created with @iobroker/create-adapter v2.6.2
5
5
  */
6
6
 
7
- const utils = require("@iobroker/adapter-core");
8
- const axios = require("axios");
9
- const mqtt = require("mqtt");
10
- const fs = require("fs");
11
- const crypto = require("crypto");
12
- const https = require("https");
13
- const path = require("path");
7
+ const utils = require('@iobroker/adapter-core');
8
+ const axios = require('axios');
9
+ const mqtt = require('mqtt');
10
+ const fs = require('fs');
11
+ const crypto = require('crypto');
12
+ const https = require('https');
13
+ const path = require('path');
14
14
 
15
- process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
15
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
16
16
 
17
17
  axios.defaults.timeout = 2000;
18
18
 
19
19
  class WeatherSense extends utils.Adapter {
20
-
21
20
  constructor(options) {
22
21
  super({
23
22
  ...options,
24
- name: "weathersense",
23
+ name: 'weathersense',
25
24
  });
26
- this.on("ready", this.onReady.bind(this));
27
- this.on("unload", this.onUnload.bind(this));
25
+ this.on('ready', this.onReady.bind(this));
26
+ this.on('unload', this.onUnload.bind(this));
28
27
  }
29
28
 
30
29
  async onReady() {
@@ -44,42 +43,46 @@ class WeatherSense extends utils.Adapter {
44
43
  if (Number(sensor_in)) {
45
44
  sensor_id = parseInt(sensor_in);
46
45
  if (sensor_id < 1 || sensor_id > 20) {
47
- this.log.error("Sensor ID has no value between 1 and 20");
48
- this.terminate ? this.terminate("Sensor ID has no value between 1 and 20", 0) : process.exit(0);
46
+ this.log.error('Sensor ID has no value between 1 and 20');
47
+ this.terminate ? this.terminate('Sensor ID has no value between 1 and 20', 0) : process.exit(0);
49
48
  return;
50
49
  }
51
50
  } else {
52
- this.log.error("Sensor ID has no valid value");
53
- this.terminate ? this.terminate("Sensor ID has no valid value", 0) : process.exit(0);
51
+ this.log.error('Sensor ID has no valid value');
52
+ this.terminate ? this.terminate('Sensor ID has no valid value', 0) : process.exit(0);
54
53
  return;
55
54
  }
56
- this.log.debug("Sensor ID is " + sensor_id);
55
+ this.log.debug(`Sensor ID is ${sensor_id}`);
57
56
 
58
57
  if (username.trim().length === 0 || passwort.trim().length === 0) {
59
- this.log.error("User email and/or user password empty - please check instance configuration");
60
- this.terminate ? this.terminate("User email and/or user password empty - please check instance configuration", 0) : process.exit(0);
58
+ this.log.error('User email and/or user password empty - please check instance configuration');
59
+ this.terminate
60
+ ? this.terminate('User email and/or user password empty - please check instance configuration', 0)
61
+ : process.exit(0);
61
62
  return;
62
63
  }
63
64
 
64
65
  let client = null;
65
66
  if (mqtt_active) {
66
- if (broker_address.trim().length === 0 || broker_address == "0.0.0.0") {
67
- this.log.error("MQTT IP address is empty - please check instance configuration");
68
- this.terminate ? this.terminate("MQTT IP address is empty - please check instance configuration", 0) : process.exit(0);
67
+ if (broker_address.trim().length === 0 || broker_address == '0.0.0.0') {
68
+ this.log.error('MQTT IP address is empty - please check instance configuration');
69
+ this.terminate
70
+ ? this.terminate('MQTT IP address is empty - please check instance configuration', 0)
71
+ : process.exit(0);
69
72
  return;
70
73
  }
71
74
  client = mqtt.connect(`mqtt://${broker_address}:${mqtt_port}`, {
72
75
  connectTimeout: 4000,
73
76
  username: mqtt_user,
74
- password: mqtt_pass
77
+ password: mqtt_pass,
75
78
  });
76
79
  }
77
80
 
78
81
  try {
79
82
  const instObj = await this.getForeignObjectAsync(`system.adapter.${this.namespace}`);
80
- if (instObj && instObj.common && instObj.common.schedule && instObj.common.schedule === "*/10 * * * *") {
81
- instObj.common.schedule = `*/${(Math.floor(Math.random() * 5) + 3)} * * * *`;
82
- this.log.info(`Default schedule found and adjusted to spread calls better over 3-7 minutes!`);
83
+ if (instObj && instObj.common && instObj.common.schedule && instObj.common.schedule === '*/10 * * * *') {
84
+ instObj.common.schedule = `*/${Math.floor(Math.random() * 3) + 6} * * * *`;
85
+ this.log.info(`Default schedule found and adjusted to spread calls better over 6-9 minutes!`);
83
86
  await this.setForeignObjectAsync(`system.adapter.${this.namespace}`, instObj);
84
87
  this.terminate ? this.terminate() : process.exit(0);
85
88
  return;
@@ -88,162 +91,263 @@ class WeatherSense extends utils.Adapter {
88
91
  this.log.error(`Could not check or adjust the schedule: ${err.message}`);
89
92
  }
90
93
 
91
- this.log.debug("MQTT active: " + mqtt_active);
92
- this.log.debug("MQTT port: " + mqtt_port);
94
+ this.log.debug(`MQTT active: ${mqtt_active}`);
95
+ this.log.debug(`MQTT port: ${mqtt_port}`);
93
96
 
94
- // Forecast Channel anlegen
95
- const forecastChannelId = `${sensor_id}.forecast`;
97
+ const deviceId = `${this.namespace}.${sensor_id}`;
96
98
 
97
99
  try {
98
- const dataReceived = await this.main(client, username, passwort, mqtt_active, sensor_id, storeJson, storeDir, celsius, forecastChannelId);
99
-
100
- const systemStateId = `${sensor_id}.DataReceived`;
100
+ const mainResult = await this.main(
101
+ client,
102
+ username,
103
+ passwort,
104
+ mqtt_active,
105
+ sensor_id,
106
+ storeJson,
107
+ storeDir,
108
+ celsius,
109
+ `${deviceId}.forecast`,
110
+ );
111
+
112
+ const dataReceived = mainResult.dataReceived;
113
+ const devdata = mainResult.devdata;
114
+
115
+ const systemStateId = `${deviceId}.DataReceived`;
101
116
  await this.setObjectNotExistsAsync(systemStateId, {
102
- type: "state",
117
+ type: 'state',
103
118
  common: {
104
- name: "Data successfully received",
105
- type: "boolean",
106
- role: "indicator",
119
+ name: 'Data successfully received',
120
+ type: 'boolean',
121
+ role: 'indicator',
107
122
  read: true,
108
- write: false
123
+ write: false,
109
124
  },
110
125
  native: {},
111
126
  });
112
127
 
113
128
  if (dataReceived === true) {
114
-
115
129
  await this.setStateAsync(systemStateId, { val: true, ack: true });
116
130
 
117
- // DevData Channel anlegen
118
- const devDataChannelId = `${sensor_id}.devdata`;
119
- await this.setObjectNotExistsAsync(devDataChannelId, {
120
- type: "channel",
121
- common: { name: "DevData" },
122
- native: {},
123
- });
131
+ const devDataChannelId = `${deviceId}.devData`;
132
+ const tempUnit = celsius ? '°C' : '°F';
124
133
 
125
- await this.setObjectNotExistsAsync(forecastChannelId, {
126
- type: "channel",
127
- common: { name: "Forecast" },
128
- native: {},
129
- });
134
+ // Alle Werte aus devdata (außer content)
135
+ for (const [key, value] of Object.entries(devdata)) {
136
+ if (key === 'content') {
137
+ continue;
138
+ }
139
+ if (value !== null && value !== undefined) {
140
+ const id = `${devDataChannelId}.${key}`;
141
+ await this.setObjectNotExistsAsync(id, {
142
+ type: typeof value === 'number' ? 'state' : 'state',
143
+ common: {
144
+ name: key,
145
+ type: typeof value,
146
+ role: 'value',
147
+ unit: '',
148
+ read: true,
149
+ write: false,
150
+ },
151
+ native: {},
152
+ });
153
+ await this.setStateAsync(id, { val: value, ack: true });
154
+ }
155
+ }
130
156
 
131
- const tempUnit = celsius ? "°C" : "°F";
132
- this.log.debug(`Unit: ${tempUnit}`);
133
-
134
- // Bekannte Items
135
- const fixedItems = [
136
- { id: "atmospheric_pressure", type: "number", role: "value.pressure", unit: "hPa" },
137
- { id: "indoor_temp", type: "number", role: "value.temperature", unit: tempUnit },
138
- { id: "indoor_humidity", type: "number", role: "value.humidity", unit: "%" },
139
- { id: "outdoor_temp", type: "number", role: "value.temperature", unit: tempUnit },
140
- { id: "outdoor_humidity", type: "number", role: "value.humidity", unit: "%" },
141
- ];
142
-
143
- // Zuerst die festen Items anlegen und setzen
144
- for (const item of fixedItems) {
145
- const id = `${devDataChannelId}.${item.id}`;
146
- await this.setObjectNotExistsAsync(id, {
147
- type: "state",
148
- common: {
149
- name: item.id,
150
- type: item.type,
151
- role: item.role,
152
- unit: item.unit,
153
- read: true,
154
- write: false,
155
- },
156
- native: {},
157
- });
158
-
159
- const val = this.contentDevData ? this.contentDevData[item.id] : null;
160
- if (val != null) {
161
- await this.setStateAsync(id, { val: val, ack: true });
157
+ // Alle Werte aus devdata.content
158
+ const content = devdata.content || {};
159
+ for (const [key, value] of Object.entries(content)) {
160
+ if (value !== null && value !== undefined && key !== 'sensorDatas') {
161
+ const id = `${devDataChannelId}.${key}`;
162
+ await this.setObjectNotExistsAsync(id, {
163
+ type: typeof value === 'number' ? 'state' : 'state',
164
+ common: {
165
+ name: key,
166
+ type: typeof value,
167
+ role: 'value',
168
+ unit: '',
169
+ read: true,
170
+ write: false,
171
+ },
172
+ native: {},
173
+ });
174
+ await this.setStateAsync(id, { val: value, ack: true });
162
175
  }
163
176
  }
164
177
 
165
- // Jetzt alle zusätzlichen dynamischen Keys aus this.contentDevData durchgehen
166
- for (const key of Object.keys(this.contentDevData || {})) {
167
- // Schon behandelt?
168
- if (fixedItems.find(item => item.id === key)) continue;
178
+ // Alle Sensoren aus devdata.content.sensorDatas
179
+ const sensor_data = content.sensorDatas || [];
180
+ for (const s of sensor_data) {
181
+ const type_ = s.type;
182
+ const channel = s.channel;
183
+ const cur_val = s.curVal;
184
+ const high_val = s.hihgVal;
185
+ const low_val = s.lowVal;
169
186
 
170
- const val = this.contentDevData[key];
187
+ let prefix = '';
171
188
 
172
- // Typ und Rolle
173
- const common = {
174
- name: key,
175
- type: typeof val === "number" ? "number" : "string",
176
- role: "value",
177
- unit: "",
178
- read: true,
179
- write: false,
180
- };
181
-
182
- if (key.includes("temp")) {
183
- common.role = "value.temperature";
184
- common.unit = tempUnit;
185
- } else if (key.includes("humidity")) {
186
- common.role = "value.humidity";
187
- common.unit = "%";
188
- } else if (key.includes("pressure")) {
189
- common.role = "value.pressure";
190
- common.unit = "hPa";
189
+ if (type_ === 1) {
190
+ prefix = '-Temp';
191
+ }
192
+ if (type_ === 2) {
193
+ prefix = '-Hum';
191
194
  }
192
195
 
193
- const id = `${devDataChannelId}.${key}`;
194
-
195
- await this.setObjectNotExistsAsync(id, {
196
- type: "state",
197
- common,
198
- native: {},
199
- });
196
+ const key = `Type${type_}-Channel${channel}${prefix}`;
197
+ const base = `${devDataChannelId}.${key}`;
198
+
199
+ // current
200
+ if (cur_val !== null && cur_val !== undefined && cur_val !== 65535 && cur_val !== 255) {
201
+ const id = `${base}.current`;
202
+ await this.setObjectNotExistsAsync(id, {
203
+ type: 'state',
204
+ common: {
205
+ name: 'current',
206
+ type: 'number',
207
+ role:
208
+ prefix === '-Temp'
209
+ ? 'value.temperature'
210
+ : prefix === '-Hum'
211
+ ? 'value.humidity'
212
+ : 'value',
213
+ unit: prefix === '-Temp' ? tempUnit : prefix === '-Hum' ? '%' : '',
214
+ read: true,
215
+ write: false,
216
+ },
217
+ native: {},
218
+ });
219
+ await this.setStateAsync(id, { val: this.c_f_berechnen(cur_val, prefix, celsius), ack: true });
220
+ }
221
+ // high
222
+ if (high_val !== null && high_val !== undefined && high_val !== 65535 && high_val !== 255) {
223
+ const id = `${base}.high`;
224
+ await this.setObjectNotExistsAsync(id, {
225
+ type: 'state',
226
+ common: {
227
+ name: 'high',
228
+ type: 'number',
229
+ role:
230
+ prefix === '-Temp'
231
+ ? 'value.temperature'
232
+ : prefix === '-Hum'
233
+ ? 'value.humidity'
234
+ : 'value',
235
+ unit: prefix === '-Temp' ? tempUnit : prefix === '-Hum' ? '%' : '',
236
+ read: true,
237
+ write: false,
238
+ },
239
+ native: {},
240
+ });
241
+ await this.setStateAsync(id, { val: this.c_f_berechnen(high_val, prefix, celsius), ack: true });
242
+ }
243
+ // low
244
+ if (low_val !== null && low_val !== undefined && low_val !== 65535 && low_val !== 255) {
245
+ const id = `${base}.low`;
246
+ await this.setObjectNotExistsAsync(id, {
247
+ type: 'state',
248
+ common: {
249
+ name: 'low',
250
+ type: 'number',
251
+ role:
252
+ prefix === '-Temp'
253
+ ? 'value.temperature'
254
+ : prefix === '-Hum'
255
+ ? 'value.humidity'
256
+ : 'value',
257
+ unit: prefix === '-Temp' ? tempUnit : prefix === '-Hum' ? '%' : '',
258
+ read: true,
259
+ write: false,
260
+ },
261
+ native: {},
262
+ });
263
+ await this.setStateAsync(id, { val: this.c_f_berechnen(low_val, prefix, celsius), ack: true });
264
+ }
200
265
 
201
- await this.setStateAsync(id, { val: val, ack: true });
266
+ // dev*-Keys im Sensorobjekt
267
+ for (const [k, v] of Object.entries(s)) {
268
+ if (
269
+ k.startsWith('dev') &&
270
+ v !== null &&
271
+ v !== undefined &&
272
+ !(typeof v === 'object' && Object.keys(v).length === 0)
273
+ ) {
274
+ const id = `${base}.${k}`;
275
+ await this.setObjectNotExistsAsync(id, {
276
+ type: 'state',
277
+ common: {
278
+ name: k,
279
+ type: typeof v,
280
+ role: 'value',
281
+ unit: '',
282
+ read: true,
283
+ write: false,
284
+ },
285
+ native: {},
286
+ });
287
+ await this.setStateAsync(id, { val: v, ack: true });
288
+ }
289
+ }
202
290
  }
203
291
  } else {
204
- this.log.error("Error loading data in main()");
292
+ this.log.error('Error loading data in main()');
205
293
  await this.setStateAsync(systemStateId, { val: false, ack: true });
206
294
  }
207
295
  } catch (error) {
208
- this.log.error("Unexpected error in onReady(): " + error.message);
296
+ this.log.error(`Unexpected error in onReady(): ${error.message}`);
209
297
  } finally {
210
298
  if (client) {
211
299
  client.end();
212
300
  }
213
- this.terminate ? this.terminate("Everything done. Going to terminate till next schedule", 0) : process.exit(0);
301
+ this.terminate
302
+ ? this.terminate('Everything done. Going to terminate till next schedule', 0)
303
+ : process.exit(0);
214
304
  }
215
305
  }
216
306
 
307
+ // MQTT senden
217
308
  async sendMqtt(sensor_id, mqtt_active, client, topic, wert) {
218
309
  if (mqtt_active) {
219
- // Wenn wert nicht String ist, in String umwandeln (auch null und undefined abfangen)
220
- if (typeof wert !== "string") {
221
- wert = wert !== null && wert !== undefined ? wert.toString() : "";
310
+ if (typeof wert !== 'string') {
311
+ wert = wert !== null && wert !== undefined ? wert.toString() : '';
312
+ }
313
+ client.publish(`WEATHERSENSE/${sensor_id.toString()}/${topic}`, wert);
314
+ }
315
+ }
316
+
317
+ // °F nach °C umwandeln, falls notwendig
318
+ c_f_berechnen(temp, prefix, celsius) {
319
+ if (celsius && prefix === '-Temp') {
320
+ if (temp !== null && temp !== undefined) {
321
+ temp = ((temp - 32) / 1.8).toFixed(1);
222
322
  }
223
- client.publish("WEATHERSENSE/" + sensor_id.toString() + "/" + topic, wert);
224
323
  }
324
+ return temp;
225
325
  }
226
326
 
327
+ // Forecast Datenpunkte erstellen und schreiben
227
328
  async createOrUpdateForecastDPs(forecastChannelId, forecasts, celsius) {
228
- if (!forecasts || !forecastChannelId) return;
329
+ if (!forecasts || !forecastChannelId) {
330
+ return;
331
+ }
229
332
 
230
- // Struktur der Forecast-Items pro Tag
231
333
  const forecastItems = [
232
- { id: "day", type: "string", role: "value", unit: "" },
233
- { id: "date", type: "string", role: "value", unit: "" },
234
- { id: "high", type: "number", role: "value.temperature", unit: celsius ? "°C" : "°F" },
235
- { id: "low", type: "number", role: "value.temperature", unit: celsius ? "°C" : "°F" },
236
- { id: "text", type: "string", role: "text", unit: "" },
334
+ { id: 'day', type: 'string', role: 'value', unit: '' },
335
+ { id: 'date', type: 'string', role: 'value', unit: '' },
336
+ { id: 'high', type: 'number', role: 'value.temperature', unit: celsius ? '°C' : '°F' },
337
+ { id: 'low', type: 'number', role: 'value.temperature', unit: celsius ? '°C' : '°F' },
338
+ { id: 'text', type: 'string', role: 'text', unit: '' },
237
339
  ];
238
340
 
239
341
  for (let i = 0; i < forecasts.length; i++) {
240
342
  const forecast = forecasts[i];
241
- if (!forecast) continue;
343
+ if (!forecast) {
344
+ continue;
345
+ }
242
346
 
243
347
  for (const item of forecastItems) {
244
- const id = `${forecastChannelId}.${i}.${item.id}`;
348
+ const id = `${forecastChannelId}.day_${i}.${item.id}`;
245
349
  await this.setObjectNotExistsAsync(id, {
246
- type: "state",
350
+ type: 'state',
247
351
  common: {
248
352
  name: item.id,
249
353
  type: item.type,
@@ -256,7 +360,7 @@ class WeatherSense extends utils.Adapter {
256
360
  });
257
361
 
258
362
  let val = forecast[item.id];
259
- if ((item.id === "high" || item.id === "low") && typeof val === "number") {
363
+ if ((item.id === 'high' || item.id === 'low') && typeof val === 'number') {
260
364
  if (celsius) {
261
365
  val = Number(((val - 32) / 1.8).toFixed(1));
262
366
  } else {
@@ -271,21 +375,26 @@ class WeatherSense extends utils.Adapter {
271
375
  }
272
376
  }
273
377
 
378
+ // Forecasts per MQTT senden
274
379
  async sendForecasts(client, forecasts, celsius, sensor_id) {
275
- if (!client || !forecasts) return;
380
+ if (!client || !forecasts) {
381
+ return;
382
+ }
276
383
 
277
384
  for (let i = 0; i < forecasts.length; i++) {
278
385
  const forecast = forecasts[i];
279
- if (!forecast) continue;
386
+ if (!forecast) {
387
+ continue;
388
+ }
280
389
 
281
- await this.sendMqtt(sensor_id, true, client, `forecast/${i}/day`, forecast.day || "");
282
- await this.sendMqtt(sensor_id, true, client, `forecast/${i}/date`, forecast.date || "");
390
+ await this.sendMqtt(sensor_id, true, client, `forecast/day_${i}/day`, forecast.day || '');
391
+ await this.sendMqtt(sensor_id, true, client, `forecast/day_${i}/date`, forecast.date || '');
283
392
 
284
393
  let tempHigh = forecast.high;
285
394
  let tempLow = forecast.low;
286
395
 
287
- if (celsius && typeof tempHigh === "number" && typeof tempLow === "number") {
288
- tempHigh = Number(((tempHigh - 32) / 1.8).toFixed(1)); // Zahl, keine Zeichenkette
396
+ if (celsius && typeof tempHigh === 'number' && typeof tempLow === 'number') {
397
+ tempHigh = Number(((tempHigh - 32) / 1.8).toFixed(1)); // Zahl, keine Zeichenkette
289
398
  tempLow = Number(((tempLow - 32) / 1.8).toFixed(1));
290
399
  } else {
291
400
  // Wenn kein Celsius oder kein Zahlentyp, trotzdem als Zahl (sofern möglich), sonst 0 als Fallback
@@ -293,22 +402,24 @@ class WeatherSense extends utils.Adapter {
293
402
  tempLow = tempLow != null ? Number(tempLow) : 0;
294
403
  }
295
404
 
296
- await this.sendMqtt(sensor_id, true, client, `forecast/${i}/high`, tempHigh);
297
- await this.sendMqtt(sensor_id, true, client, `forecast/${i}/low`, tempLow);
298
- await this.sendMqtt(sensor_id, true, client, `forecast/${i}/text`, forecast.text || "");
405
+ await this.sendMqtt(sensor_id, true, client, `forecast/day_${i}/high`, tempHigh);
406
+ await this.sendMqtt(sensor_id, true, client, `forecast/day_${i}/low`, tempLow);
407
+ await this.sendMqtt(sensor_id, true, client, `forecast/day_${i}/text`, forecast.text || '');
299
408
  }
300
409
  }
301
410
 
411
+ // Alte MQTT Datenpunkte auf Subscriber löschen
302
412
  async clearOldForecasts(sensor_id, client, maxDays = 6) {
303
413
  for (let i = 0; i < maxDays; i++) {
304
- for (const key of ["day", "date", "high", "low", "text"]) {
305
- await this.sendMqtt(sensor_id, true, client, `forecast/${i}/${key}`, "");
414
+ for (const key of ['day', 'date', 'high', 'low', 'text']) {
415
+ await this.sendMqtt(sensor_id, true, client, `forecast/day_${i}/${key}`, '');
306
416
  }
307
417
  }
308
418
  }
309
419
 
310
- printAllKeys(d, prefix = "") {
311
- if (typeof d === "object" && d !== null && !Array.isArray(d)) {
420
+ // Debugausgabe formatieren
421
+ printAllKeys(d, prefix = '') {
422
+ if (typeof d === 'object' && d !== null && !Array.isArray(d)) {
312
423
  for (const [k, v] of Object.entries(d)) {
313
424
  this.printAllKeys(v, `${prefix}${k}/`);
314
425
  }
@@ -321,265 +432,231 @@ class WeatherSense extends utils.Adapter {
321
432
  }
322
433
  }
323
434
 
324
- findValue(sensor_data, type, channel) {
325
- const entry = sensor_data.find(item => item.type === type && item.channel === channel);
326
- return entry ? entry.curVal : null;
327
- }
328
-
329
-
330
- // Funktion zum Erzeugen des MD5-Hashes
435
+ // Funktion zum Erzeugen des PW-Hashes
331
436
  hashPassword(pw) {
332
- const combined = pw + "emax@pwd123";
333
- return crypto.createHash("md5").update(combined, "utf8").digest("hex").toUpperCase();
437
+ const key = Buffer.from('ZW1heEBwd2QxMjM=', 'base64').toString('utf8');
438
+ const combined = pw + key;
439
+ return crypto.createHash('md5').update(combined, 'utf8').digest('hex').toUpperCase();
334
440
  }
335
441
 
336
442
  // Login-Funktion
337
443
  async login(USERNAME, PASSWORD) {
338
- const LOGIN_URL = "https://47.52.149.125/V1.0/account/login";
444
+ const LOGIN_URL = 'https://emaxlife.net/V1.0/account/login';
339
445
  const hashed_pw = this.hashPassword(PASSWORD);
340
446
 
341
447
  const headers = {
342
- "Content-Type": "application/json; charset=utf-8",
343
- "User-Agent": "okhttp/3.14.9"
448
+ 'Content-Type': 'application/json; charset=utf-8',
449
+ 'User-Agent': 'okhttp/3.14.9',
344
450
  };
345
451
 
346
452
  const payload = {
347
453
  email: USERNAME,
348
- pwd: hashed_pw
454
+ pwd: hashed_pw,
349
455
  };
350
456
 
351
457
  try {
352
458
  const response = await axios.post(LOGIN_URL, payload, { headers });
353
459
 
354
- this.log.debug("Status code:", response.status);
355
- this.log.debug("Response:", response.data);
460
+ this.log.debug('Status code:', response.status);
461
+ this.log.debug('Response:', response.data);
356
462
 
357
463
  const data = response.data;
358
464
 
359
465
  if (response.status === 200) {
360
466
  if (data.status === 0 && data.content) {
361
467
  const token = data.content.token;
362
- this.log.debug("Login successful. Token: " + token.substring(0, 20) + "...");
468
+ this.log.debug(`Login successful. Token: ${token.substring(0, 20)}...`);
363
469
  return token;
364
- } else {
365
- this.log.error("Login failed:", data.message);
366
470
  }
471
+ this.log.error('Login failed:', data.message);
367
472
  } else {
368
- this.log.error("Server error");
473
+ this.log.error('Server error');
369
474
  }
370
475
  } catch (error) {
371
- this.log.error("Error during login:", error.message);
476
+ this.log.error('Error during login:', error.message);
372
477
  }
373
478
 
374
479
  return null;
375
480
  }
376
481
 
482
+ // Realtime Daten holen
377
483
  async devData(token) {
378
- // Realtime Daten holen
379
- this.log.debug("getRealtime data...");
484
+ this.log.debug('getRealtime data...');
380
485
 
381
- const url = "https://emaxlife.net/V1.0/weather/devData/getRealtime";
486
+ const url = 'https://emaxlife.net/V1.0/weather/devData/getRealtime';
382
487
  const headers = {
383
- "emaxtoken": token,
384
- "Content-Type": "application/json"
488
+ emaxtoken: token,
489
+ 'Content-Type': 'application/json',
385
490
  };
386
491
 
387
492
  try {
388
493
  const response = await axios.get(url, {
389
494
  headers,
390
495
  timeout: 5000,
391
- httpsAgent: new (require("https").Agent)({ rejectUnauthorized: false })
496
+ httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }),
392
497
  });
393
498
 
394
499
  if (response.status === 200) {
395
- this.log.debug("Data was received");
500
+ this.log.debug('Data was received');
396
501
  return response.data;
397
- } else {
398
- this.log.error(`devData > Status Code: ${response.status}`);
399
- return "error";
400
502
  }
503
+ this.log.error(`devData > Status Code: ${response.status}`);
504
+ return 'error';
401
505
  } catch (error) {
402
506
  if (error.response) {
403
507
  this.log.error(`devData > Status Code: ${error.response.status}`);
404
508
  } else {
405
509
  this.log.error(`Error during request: ${error.message}`);
406
510
  }
407
- return "error";
511
+ return 'error';
408
512
  }
409
513
  }
410
514
 
515
+ // Forecast Daten holen
411
516
  async foreCast(token) {
412
- // Forecast holen
413
- this.log.debug("getForecast data...");
517
+ this.log.debug('getForecast data...');
414
518
 
415
- const url = "https://emaxlife.net/V1.0/weather/netData/getForecast";
519
+ const url = 'https://emaxlife.net/V1.0/weather/netData/getForecast';
416
520
  const headers = {
417
- "emaxtoken": token,
418
- "Content-Type": "application/json"
521
+ emaxtoken: token,
522
+ 'Content-Type': 'application/json',
419
523
  };
420
524
 
421
525
  try {
422
526
  const response = await axios.get(url, {
423
527
  headers,
424
528
  timeout: 5000,
425
- httpsAgent: new https.Agent({ rejectUnauthorized: false }) // entspricht verify=False
529
+ httpsAgent: new https.Agent({ rejectUnauthorized: false }), // entspricht verify=False
426
530
  });
427
531
 
428
532
  if (response.status === 200) {
429
- this.log.debug("Data was received");
533
+ this.log.debug('Data was received');
430
534
  return response.data;
431
- } else {
432
- this.log.error(`foreCast > Status Code: ${response.status}`);
433
- return "error";
434
535
  }
536
+ this.log.error(`foreCast > Status Code: ${response.status}`);
537
+ return 'error';
435
538
  } catch (error) {
436
539
  if (error.response) {
437
540
  this.log.error(`foreCast > Status Code: ${error.response.status}`);
438
541
  } else {
439
542
  this.log.error(`Error during request: ${error.message}`);
440
543
  }
441
- return "error";
544
+ return 'error';
442
545
  }
443
546
  }
444
547
 
445
548
  async main(client, username, passwort, mqtt_active, sensor_id, storeJson, storeDir, celsius, forecastChannelId) {
446
549
  const token = await this.login(username, passwort);
447
550
  if (!token) {
448
- this.log.error("No token received");
551
+ this.log.error('No token received');
449
552
  if (mqtt_active) {
450
- await this.sendMqtt(sensor_id, mqtt_active, client, "dataReceived", "false");
553
+ await this.sendMqtt(sensor_id, mqtt_active, client, 'dataReceived', 'false');
451
554
  client.end();
452
555
  }
453
556
  return false;
454
557
  }
455
558
  const devdata = await this.devData(token);
456
559
  const forecast = await this.foreCast(token);
457
- if (devdata === "error" || forecast === "error") {
458
- this.log.error("No data received");
560
+ if (devdata === 'error' || forecast === 'error') {
561
+ this.log.error('No data received');
459
562
  if (mqtt_active) {
460
- await this.sendMqtt(sensor_id, mqtt_active, client, "dataReceived", "false");
563
+ await this.sendMqtt(sensor_id, mqtt_active, client, 'dataReceived', 'false');
461
564
  client.end();
462
565
  }
463
- return false;
566
+ return { dataReceived: false };
464
567
  }
465
568
 
466
569
  if (storeJson) {
467
- this.log.debug("Save devData.json to " + storeDir);
570
+ this.log.debug(`Save devData.json to ${storeDir}`);
468
571
  const json_object = JSON.stringify(devdata, null, 4);
469
- fs.writeFileSync(path.join(storeDir, "devData.json"), json_object, "utf-8");
572
+ fs.writeFileSync(path.join(storeDir, 'devData.json'), json_object, 'utf-8');
470
573
  }
471
574
 
472
- this.log.debug("devData JSON:");
575
+ this.log.debug('devData JSON:');
473
576
  this.printAllKeys(devdata);
474
577
 
475
- const content = devdata?.content || {};
476
- const sensor_data = content.sensorDatas || [];
477
-
478
- const luftdruck = content.atmos;
479
- let temp_innen = this.findValue(sensor_data, 1, 0);
480
- const feuchte_innen = this.findValue(sensor_data, 2, 0);
481
- let temp_aussen = this.findValue(sensor_data, 1, 2);
482
- const feuchte_aussen = this.findValue(sensor_data, 2, 2);
483
-
484
- const skipCombinations = new Set(["1_0", "1_2", "2_0", "2_2"]);
485
-
486
- if (celsius) {
487
- if (temp_innen != null) temp_innen = ((temp_innen - 32) / 1.8).toFixed(1);
488
- if (temp_aussen != null) temp_aussen = ((temp_aussen - 32) / 1.8).toFixed(1);
489
- }
490
-
491
578
  if (mqtt_active) {
492
- const error_code = devdata.error;
493
-
494
- if (error_code != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/error", error_code);
495
- if (content.devTime) this.sendMqtt(sensor_id, mqtt_active, client, "devData/devtime", content.devTime);
496
- if (content.updateTime) this.sendMqtt(sensor_id, mqtt_active, client, "devData/updateTime", content.updateTime);
497
- if (content.deviceMac) this.sendMqtt(sensor_id, mqtt_active, client, "devData/deviceMac", content.deviceMac);
498
- if (content.devTimezone != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/devTimezone", content.devTimezone);
499
- if (content.wirelessStatus != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/wirelessStatus", content.wirelessStatus);
500
- if (content.powerStatus != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/powerStatus", content.powerStatus);
501
- if (content.weatherStatus != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/weatherStatus", content.weatherStatus);
502
- if (luftdruck != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/atmospheric_pressure", luftdruck);
503
- if (temp_innen != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/indoor_temp", temp_innen);
504
- if (feuchte_innen != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/indoor_humidity", feuchte_innen);
505
- if (temp_aussen != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/outdoor_temp", temp_aussen);
506
- if (feuchte_aussen != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/outdoor_humidity", feuchte_aussen);
507
-
508
- // >>> Zusätzliche dynamische Sensoren ausgeben:
509
- for (const s of sensor_data) {
510
- const { type, channel, curVal, hihgVal, lowVal, ...rest } = s;
511
- const key = `${type}_${channel}`;
512
- if (skipCombinations.has(key)) continue;
513
- const base = `devData/sensor_${type}_${channel}`;
514
-
515
- if (curVal != null && curVal !== 65535) {
516
- await this.sendMqtt(sensor_id, mqtt_active, client, `${base}/current`, curVal);
517
- }
518
- if (hihgVal != null && hihgVal !== 65535) {
519
- await this.sendMqtt(sensor_id, mqtt_active, client, `${base}/high`, hihgVal);
579
+ for (const [key, value] of Object.entries(devdata)) {
580
+ if (key === 'content') {
581
+ continue;
520
582
  }
521
- if (lowVal != null && lowVal !== 65535) {
522
- await this.sendMqtt(sensor_id, mqtt_active, client, `${base}/low`, lowVal);
583
+ if (value !== null && value !== undefined) {
584
+ this.sendMqtt(sensor_id, mqtt_active, client, `devData/${key}`, value);
523
585
  }
586
+ }
524
587
 
525
- for (const [nestedKey, nestedVal] of Object.entries(rest)) {
526
- if (nestedVal && typeof nestedVal === "object") {
527
- const entries = Object.entries(nestedVal);
528
-
529
- if (entries.length === 0) {
530
- // Leeres Objekt → Platzhalter senden
531
- const topic = `${base}/${nestedKey}`;
532
- await this.sendMqtt(sensor_id, mqtt_active, client, topic, "n/a");
533
- this.log.debug(`Send MQTT: ${topic}: n/a (empty object)`);
534
- } else {
535
- // Inhaltliches Objekt → Einzeldaten senden
536
- for (const [k, v] of entries) {
537
- if (v != null) {
538
- const topic = `${base}/${nestedKey}/${k}`;
539
- await this.sendMqtt(sensor_id, mqtt_active, client, topic, v);
540
- this.log.debug(`Send MQTT: ${topic}: ${v}`);
541
- }
542
- }
543
- }
544
- }
588
+ const content = devdata.content || {};
589
+ for (const [key, value] of Object.entries(content)) {
590
+ if (value !== null && value !== undefined) {
591
+ this.sendMqtt(sensor_id, mqtt_active, client, `devData/${key}`, value);
545
592
  }
546
593
  }
547
- }
548
594
 
549
- // Basiswerte setzen
550
- this.contentDevData = {
551
- atmospheric_pressure: luftdruck,
552
- indoor_temp: temp_innen,
553
- indoor_humidity: feuchte_innen,
554
- outdoor_temp: temp_aussen,
555
- outdoor_humidity: feuchte_aussen,
556
- };
557
-
558
- // Zusätzliche Sensoren ergänzen (alle type/channel Kombinationen)
559
- for (const s of sensor_data) {
560
- const { type, channel, curVal, hihgVal, lowVal, ...rest } = s;
561
- const key = `${type}_${channel}`;
562
- if (skipCombinations.has(key)) continue; // Überspringen, wenn schon bekannt
595
+ const sensor_data = content.sensorDatas || [];
563
596
 
564
- const keyBase = `sensor_${type}_${channel}`;
597
+ for (const s of sensor_data) {
598
+ const type_ = s.type;
599
+ const channel = s.channel;
600
+ const cur_val = s.curVal;
601
+ const high_val = s.hihgVal;
602
+ const low_val = s.lowVal;
603
+
604
+ let prefix = '';
605
+ if (type_ === 1) {
606
+ prefix = '-Temp';
607
+ }
608
+ if (type_ === 2) {
609
+ prefix = '-Hum';
610
+ }
565
611
 
566
- if (curVal != null && curVal !== 65535) this.contentDevData[`${keyBase}_current`] = curVal;
567
- if (hihgVal != null && hihgVal !== 65535) this.contentDevData[`${keyBase}_high`] = hihgVal;
568
- if (lowVal != null && lowVal !== 65535) this.contentDevData[`${keyBase}_low`] = lowVal;
612
+ const key = `Type${type_}-Channel${channel}${prefix}`;
613
+ const base = `devData/${key}`;
614
+
615
+ if (cur_val !== null && cur_val !== undefined && cur_val !== 65535 && cur_val !== 255) {
616
+ this.sendMqtt(
617
+ sensor_id,
618
+ mqtt_active,
619
+ client,
620
+ `${base}/current`,
621
+ this.c_f_berechnen(cur_val, prefix, celsius),
622
+ );
623
+ }
624
+ if (high_val !== null && high_val !== undefined && high_val !== 65535 && high_val !== 255) {
625
+ this.sendMqtt(
626
+ sensor_id,
627
+ mqtt_active,
628
+ client,
629
+ `${base}/high`,
630
+ this.c_f_berechnen(high_val, prefix, celsius),
631
+ );
632
+ }
633
+ if (low_val !== null && low_val !== undefined && low_val !== 65535 && low_val !== 255) {
634
+ this.sendMqtt(
635
+ sensor_id,
636
+ mqtt_active,
637
+ client,
638
+ `${base}/low`,
639
+ this.c_f_berechnen(low_val, prefix, celsius),
640
+ );
641
+ }
569
642
 
570
- for (const [k, v] of Object.entries(rest)) {
571
- if (v && typeof v === "object" && Object.keys(v).length === 0) {
572
- this.contentDevData[`${keyBase}_${k}`] = "n/a";
573
- } else if (v != null) {
574
- this.contentDevData[`${keyBase}_${k}`] = v;
643
+ for (const [k, v] of Object.entries(s)) {
644
+ if (
645
+ k.startsWith('dev') &&
646
+ v !== null &&
647
+ v !== undefined &&
648
+ !(typeof v === 'object' && Object.keys(v).length === 0)
649
+ ) {
650
+ this.sendMqtt(sensor_id, mqtt_active, client, `${base}/${k}`, v);
651
+ }
575
652
  }
576
653
  }
577
654
  }
578
655
 
579
656
  if (storeJson) {
580
- this.log.debug("Save forecast.json to " + storeDir);
657
+ this.log.debug(`Save forecast.json to ${storeDir}`);
581
658
  const json_object = JSON.stringify(forecast, null, 4);
582
- fs.writeFileSync(path.join(storeDir, "forecast.json"), json_object, "utf-8");
659
+ fs.writeFileSync(path.join(storeDir, 'forecast.json'), json_object, 'utf-8');
583
660
  }
584
661
 
585
662
  this.printAllKeys(forecast);
@@ -588,7 +665,7 @@ class WeatherSense extends utils.Adapter {
588
665
 
589
666
  if (mqtt_active) {
590
667
  await this.clearOldForecasts(sensor_id, client, 6);
591
- await new Promise(r => setTimeout(r, 2000)); // sleep 2s
668
+ await this.delay(2000); // sleep 2s
592
669
 
593
670
  await this.sendForecasts(client, forecasts, celsius, sensor_id);
594
671
 
@@ -597,20 +674,24 @@ class WeatherSense extends utils.Adapter {
597
674
 
598
675
  await this.createOrUpdateForecastDPs(forecastChannelId, forecasts, celsius);
599
676
 
600
- return true;
677
+ return {
678
+ dataReceived: true,
679
+ devdata,
680
+ forecast,
681
+ };
601
682
  }
602
683
 
603
684
  onUnload(callback) {
604
685
  try {
605
686
  callback();
606
- } catch (e) {
687
+ } catch {
607
688
  callback();
608
689
  }
609
690
  }
610
691
  }
611
692
 
612
693
  if (require.main !== module) {
613
- module.exports = (options) => new WeatherSense(options);
694
+ module.exports = options => new WeatherSense(options);
614
695
  } else {
615
696
  new WeatherSense();
616
697
  }