iobroker.weathersense 1.0.3 → 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,266 +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
435
  // Funktion zum Erzeugen des PW-Hashes
331
436
  hashPassword(pw) {
332
- const key = Buffer.from("ZW1heEBwd2QxMjM=", "base64").toString("utf8");
437
+ const key = Buffer.from('ZW1heEBwd2QxMjM=', 'base64').toString('utf8');
333
438
  const combined = pw + key;
334
- return crypto.createHash("md5").update(combined, "utf8").digest("hex").toUpperCase();
439
+ return crypto.createHash('md5').update(combined, 'utf8').digest('hex').toUpperCase();
335
440
  }
336
441
 
337
442
  // Login-Funktion
338
443
  async login(USERNAME, PASSWORD) {
339
- const LOGIN_URL = "https://47.52.149.125/V1.0/account/login";
444
+ const LOGIN_URL = 'https://emaxlife.net/V1.0/account/login';
340
445
  const hashed_pw = this.hashPassword(PASSWORD);
341
446
 
342
447
  const headers = {
343
- "Content-Type": "application/json; charset=utf-8",
344
- "User-Agent": "okhttp/3.14.9"
448
+ 'Content-Type': 'application/json; charset=utf-8',
449
+ 'User-Agent': 'okhttp/3.14.9',
345
450
  };
346
451
 
347
452
  const payload = {
348
453
  email: USERNAME,
349
- pwd: hashed_pw
454
+ pwd: hashed_pw,
350
455
  };
351
456
 
352
457
  try {
353
458
  const response = await axios.post(LOGIN_URL, payload, { headers });
354
459
 
355
- this.log.debug("Status code:", response.status);
356
- this.log.debug("Response:", response.data);
460
+ this.log.debug('Status code:', response.status);
461
+ this.log.debug('Response:', response.data);
357
462
 
358
463
  const data = response.data;
359
464
 
360
465
  if (response.status === 200) {
361
466
  if (data.status === 0 && data.content) {
362
467
  const token = data.content.token;
363
- this.log.debug("Login successful. Token: " + token.substring(0, 20) + "...");
468
+ this.log.debug(`Login successful. Token: ${token.substring(0, 20)}...`);
364
469
  return token;
365
- } else {
366
- this.log.error("Login failed:", data.message);
367
470
  }
471
+ this.log.error('Login failed:', data.message);
368
472
  } else {
369
- this.log.error("Server error");
473
+ this.log.error('Server error');
370
474
  }
371
475
  } catch (error) {
372
- this.log.error("Error during login:", error.message);
476
+ this.log.error('Error during login:', error.message);
373
477
  }
374
478
 
375
479
  return null;
376
480
  }
377
481
 
482
+ // Realtime Daten holen
378
483
  async devData(token) {
379
- // Realtime Daten holen
380
- this.log.debug("getRealtime data...");
484
+ this.log.debug('getRealtime data...');
381
485
 
382
- const url = "https://emaxlife.net/V1.0/weather/devData/getRealtime";
486
+ const url = 'https://emaxlife.net/V1.0/weather/devData/getRealtime';
383
487
  const headers = {
384
- "emaxtoken": token,
385
- "Content-Type": "application/json"
488
+ emaxtoken: token,
489
+ 'Content-Type': 'application/json',
386
490
  };
387
491
 
388
492
  try {
389
493
  const response = await axios.get(url, {
390
494
  headers,
391
495
  timeout: 5000,
392
- httpsAgent: new (require("https").Agent)({ rejectUnauthorized: false })
496
+ httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }),
393
497
  });
394
498
 
395
499
  if (response.status === 200) {
396
- this.log.debug("Data was received");
500
+ this.log.debug('Data was received');
397
501
  return response.data;
398
- } else {
399
- this.log.error(`devData > Status Code: ${response.status}`);
400
- return "error";
401
502
  }
503
+ this.log.error(`devData > Status Code: ${response.status}`);
504
+ return 'error';
402
505
  } catch (error) {
403
506
  if (error.response) {
404
507
  this.log.error(`devData > Status Code: ${error.response.status}`);
405
508
  } else {
406
509
  this.log.error(`Error during request: ${error.message}`);
407
510
  }
408
- return "error";
511
+ return 'error';
409
512
  }
410
513
  }
411
514
 
515
+ // Forecast Daten holen
412
516
  async foreCast(token) {
413
- // Forecast holen
414
- this.log.debug("getForecast data...");
517
+ this.log.debug('getForecast data...');
415
518
 
416
- const url = "https://emaxlife.net/V1.0/weather/netData/getForecast";
519
+ const url = 'https://emaxlife.net/V1.0/weather/netData/getForecast';
417
520
  const headers = {
418
- "emaxtoken": token,
419
- "Content-Type": "application/json"
521
+ emaxtoken: token,
522
+ 'Content-Type': 'application/json',
420
523
  };
421
524
 
422
525
  try {
423
526
  const response = await axios.get(url, {
424
527
  headers,
425
528
  timeout: 5000,
426
- httpsAgent: new https.Agent({ rejectUnauthorized: false }) // entspricht verify=False
529
+ httpsAgent: new https.Agent({ rejectUnauthorized: false }), // entspricht verify=False
427
530
  });
428
531
 
429
532
  if (response.status === 200) {
430
- this.log.debug("Data was received");
533
+ this.log.debug('Data was received');
431
534
  return response.data;
432
- } else {
433
- this.log.error(`foreCast > Status Code: ${response.status}`);
434
- return "error";
435
535
  }
536
+ this.log.error(`foreCast > Status Code: ${response.status}`);
537
+ return 'error';
436
538
  } catch (error) {
437
539
  if (error.response) {
438
540
  this.log.error(`foreCast > Status Code: ${error.response.status}`);
439
541
  } else {
440
542
  this.log.error(`Error during request: ${error.message}`);
441
543
  }
442
- return "error";
544
+ return 'error';
443
545
  }
444
546
  }
445
547
 
446
548
  async main(client, username, passwort, mqtt_active, sensor_id, storeJson, storeDir, celsius, forecastChannelId) {
447
549
  const token = await this.login(username, passwort);
448
550
  if (!token) {
449
- this.log.error("No token received");
551
+ this.log.error('No token received');
450
552
  if (mqtt_active) {
451
- await this.sendMqtt(sensor_id, mqtt_active, client, "dataReceived", "false");
553
+ await this.sendMqtt(sensor_id, mqtt_active, client, 'dataReceived', 'false');
452
554
  client.end();
453
555
  }
454
556
  return false;
455
557
  }
456
558
  const devdata = await this.devData(token);
457
559
  const forecast = await this.foreCast(token);
458
- if (devdata === "error" || forecast === "error") {
459
- this.log.error("No data received");
560
+ if (devdata === 'error' || forecast === 'error') {
561
+ this.log.error('No data received');
460
562
  if (mqtt_active) {
461
- await this.sendMqtt(sensor_id, mqtt_active, client, "dataReceived", "false");
563
+ await this.sendMqtt(sensor_id, mqtt_active, client, 'dataReceived', 'false');
462
564
  client.end();
463
565
  }
464
- return false;
566
+ return { dataReceived: false };
465
567
  }
466
568
 
467
569
  if (storeJson) {
468
- this.log.debug("Save devData.json to " + storeDir);
570
+ this.log.debug(`Save devData.json to ${storeDir}`);
469
571
  const json_object = JSON.stringify(devdata, null, 4);
470
- fs.writeFileSync(path.join(storeDir, "devData.json"), json_object, "utf-8");
572
+ fs.writeFileSync(path.join(storeDir, 'devData.json'), json_object, 'utf-8');
471
573
  }
472
574
 
473
- this.log.debug("devData JSON:");
575
+ this.log.debug('devData JSON:');
474
576
  this.printAllKeys(devdata);
475
577
 
476
- const content = devdata?.content || {};
477
- const sensor_data = content.sensorDatas || [];
478
-
479
- const luftdruck = content.atmos;
480
- let temp_innen = this.findValue(sensor_data, 1, 0);
481
- const feuchte_innen = this.findValue(sensor_data, 2, 0);
482
- let temp_aussen = this.findValue(sensor_data, 1, 2);
483
- const feuchte_aussen = this.findValue(sensor_data, 2, 2);
484
-
485
- const skipCombinations = new Set(["1_0", "1_2", "2_0", "2_2"]);
486
-
487
- if (celsius) {
488
- if (temp_innen != null) temp_innen = ((temp_innen - 32) / 1.8).toFixed(1);
489
- if (temp_aussen != null) temp_aussen = ((temp_aussen - 32) / 1.8).toFixed(1);
490
- }
491
-
492
578
  if (mqtt_active) {
493
- const error_code = devdata.error;
494
-
495
- if (error_code != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/error", error_code);
496
- if (content.devTime) this.sendMqtt(sensor_id, mqtt_active, client, "devData/devtime", content.devTime);
497
- if (content.updateTime) this.sendMqtt(sensor_id, mqtt_active, client, "devData/updateTime", content.updateTime);
498
- if (content.deviceMac) this.sendMqtt(sensor_id, mqtt_active, client, "devData/deviceMac", content.deviceMac);
499
- if (content.devTimezone != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/devTimezone", content.devTimezone);
500
- if (content.wirelessStatus != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/wirelessStatus", content.wirelessStatus);
501
- if (content.powerStatus != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/powerStatus", content.powerStatus);
502
- if (content.weatherStatus != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/weatherStatus", content.weatherStatus);
503
- if (luftdruck != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/atmospheric_pressure", luftdruck);
504
- if (temp_innen != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/indoor_temp", temp_innen);
505
- if (feuchte_innen != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/indoor_humidity", feuchte_innen);
506
- if (temp_aussen != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/outdoor_temp", temp_aussen);
507
- if (feuchte_aussen != null) this.sendMqtt(sensor_id, mqtt_active, client, "devData/outdoor_humidity", feuchte_aussen);
508
-
509
- // >>> Zusätzliche dynamische Sensoren ausgeben:
510
- for (const s of sensor_data) {
511
- const { type, channel, curVal, hihgVal, lowVal, ...rest } = s;
512
- const key = `${type}_${channel}`;
513
- if (skipCombinations.has(key)) continue;
514
- const base = `devData/sensor_${type}_${channel}`;
515
-
516
- if (curVal != null && curVal !== 65535) {
517
- await this.sendMqtt(sensor_id, mqtt_active, client, `${base}/current`, curVal);
518
- }
519
- if (hihgVal != null && hihgVal !== 65535) {
520
- 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;
521
582
  }
522
- if (lowVal != null && lowVal !== 65535) {
523
- 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);
524
585
  }
586
+ }
525
587
 
526
- for (const [nestedKey, nestedVal] of Object.entries(rest)) {
527
- if (nestedVal && typeof nestedVal === "object") {
528
- const entries = Object.entries(nestedVal);
529
-
530
- if (entries.length === 0) {
531
- // Leeres Objekt → Platzhalter senden
532
- const topic = `${base}/${nestedKey}`;
533
- await this.sendMqtt(sensor_id, mqtt_active, client, topic, "n/a");
534
- this.log.debug(`Send MQTT: ${topic}: n/a (empty object)`);
535
- } else {
536
- // Inhaltliches Objekt → Einzeldaten senden
537
- for (const [k, v] of entries) {
538
- if (v != null) {
539
- const topic = `${base}/${nestedKey}/${k}`;
540
- await this.sendMqtt(sensor_id, mqtt_active, client, topic, v);
541
- this.log.debug(`Send MQTT: ${topic}: ${v}`);
542
- }
543
- }
544
- }
545
- }
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);
546
592
  }
547
593
  }
548
- }
549
594
 
550
- // Basiswerte setzen
551
- this.contentDevData = {
552
- atmospheric_pressure: luftdruck,
553
- indoor_temp: temp_innen,
554
- indoor_humidity: feuchte_innen,
555
- outdoor_temp: temp_aussen,
556
- outdoor_humidity: feuchte_aussen,
557
- };
558
-
559
- // Zusätzliche Sensoren ergänzen (alle type/channel Kombinationen)
560
- for (const s of sensor_data) {
561
- const { type, channel, curVal, hihgVal, lowVal, ...rest } = s;
562
- const key = `${type}_${channel}`;
563
- if (skipCombinations.has(key)) continue; // Überspringen, wenn schon bekannt
595
+ const sensor_data = content.sensorDatas || [];
564
596
 
565
- 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
+ }
566
611
 
567
- if (curVal != null && curVal !== 65535) this.contentDevData[`${keyBase}_current`] = curVal;
568
- if (hihgVal != null && hihgVal !== 65535) this.contentDevData[`${keyBase}_high`] = hihgVal;
569
- 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
+ }
570
642
 
571
- for (const [k, v] of Object.entries(rest)) {
572
- if (v && typeof v === "object" && Object.keys(v).length === 0) {
573
- this.contentDevData[`${keyBase}_${k}`] = "n/a";
574
- } else if (v != null) {
575
- 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
+ }
576
652
  }
577
653
  }
578
654
  }
579
655
 
580
656
  if (storeJson) {
581
- this.log.debug("Save forecast.json to " + storeDir);
657
+ this.log.debug(`Save forecast.json to ${storeDir}`);
582
658
  const json_object = JSON.stringify(forecast, null, 4);
583
- fs.writeFileSync(path.join(storeDir, "forecast.json"), json_object, "utf-8");
659
+ fs.writeFileSync(path.join(storeDir, 'forecast.json'), json_object, 'utf-8');
584
660
  }
585
661
 
586
662
  this.printAllKeys(forecast);
@@ -598,20 +674,24 @@ class WeatherSense extends utils.Adapter {
598
674
 
599
675
  await this.createOrUpdateForecastDPs(forecastChannelId, forecasts, celsius);
600
676
 
601
- return true;
677
+ return {
678
+ dataReceived: true,
679
+ devdata,
680
+ forecast,
681
+ };
602
682
  }
603
683
 
604
684
  onUnload(callback) {
605
685
  try {
606
686
  callback();
607
- } catch (e) {
687
+ } catch {
608
688
  callback();
609
689
  }
610
690
  }
611
691
  }
612
692
 
613
693
  if (require.main !== module) {
614
- module.exports = (options) => new WeatherSense(options);
694
+ module.exports = options => new WeatherSense(options);
615
695
  } else {
616
696
  new WeatherSense();
617
697
  }