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/README.md +12 -1
- package/admin/i18n/de/translations.json +1 -1
- package/admin/i18n/en/translations.json +1 -1
- package/admin/i18n/es/translations.json +1 -1
- package/admin/i18n/fr/translations.json +1 -1
- package/admin/i18n/it/translations.json +1 -1
- package/admin/i18n/nl/translations.json +1 -1
- package/admin/i18n/pl/translations.json +1 -1
- package/admin/i18n/pt/translations.json +1 -1
- package/admin/i18n/ru/translations.json +1 -1
- package/admin/i18n/uk/translations.json +1 -1
- package/admin/i18n/zh-cn/translations.json +1 -1
- package/admin/jsonConfig.json +115 -115
- package/io-package.json +43 -4
- package/main.js +374 -293
- package/package.json +9 -18
- package/lib/adapter-config.d.ts +0 -19
package/main.js
CHANGED
|
@@ -1,30 +1,29 @@
|
|
|
1
|
-
|
|
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(
|
|
8
|
-
const axios = require(
|
|
9
|
-
const mqtt = require(
|
|
10
|
-
const fs = require(
|
|
11
|
-
const crypto = require(
|
|
12
|
-
const https = require(
|
|
13
|
-
const path = require(
|
|
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 =
|
|
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:
|
|
23
|
+
name: 'weathersense',
|
|
25
24
|
});
|
|
26
|
-
this.on(
|
|
27
|
-
this.on(
|
|
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(
|
|
48
|
-
this.terminate ? this.terminate(
|
|
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(
|
|
53
|
-
this.terminate ? this.terminate(
|
|
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(
|
|
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(
|
|
60
|
-
this.terminate
|
|
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 ==
|
|
67
|
-
this.log.error(
|
|
68
|
-
this.terminate
|
|
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 ===
|
|
81
|
-
instObj.common.schedule = `*/${
|
|
82
|
-
this.log.info(`Default schedule found and adjusted to spread calls better over
|
|
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(
|
|
92
|
-
this.log.debug(
|
|
94
|
+
this.log.debug(`MQTT active: ${mqtt_active}`);
|
|
95
|
+
this.log.debug(`MQTT port: ${mqtt_port}`);
|
|
93
96
|
|
|
94
|
-
|
|
95
|
-
const forecastChannelId = `${sensor_id}.forecast`;
|
|
97
|
+
const deviceId = `${this.namespace}.${sensor_id}`;
|
|
96
98
|
|
|
97
99
|
try {
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
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:
|
|
117
|
+
type: 'state',
|
|
103
118
|
common: {
|
|
104
|
-
name:
|
|
105
|
-
type:
|
|
106
|
-
role:
|
|
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
|
-
|
|
118
|
-
const
|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
187
|
+
let prefix = '';
|
|
171
188
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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)
|
|
329
|
+
if (!forecasts || !forecastChannelId) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
229
332
|
|
|
230
|
-
// Struktur der Forecast-Items pro Tag
|
|
231
333
|
const forecastItems = [
|
|
232
|
-
{ id:
|
|
233
|
-
{ id:
|
|
234
|
-
{ id:
|
|
235
|
-
{ id:
|
|
236
|
-
{ id:
|
|
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)
|
|
343
|
+
if (!forecast) {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
242
346
|
|
|
243
347
|
for (const item of forecastItems) {
|
|
244
|
-
const id = `${forecastChannelId}
|
|
348
|
+
const id = `${forecastChannelId}.day_${i}.${item.id}`;
|
|
245
349
|
await this.setObjectNotExistsAsync(id, {
|
|
246
|
-
type:
|
|
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 ===
|
|
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)
|
|
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)
|
|
386
|
+
if (!forecast) {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
280
389
|
|
|
281
|
-
await this.sendMqtt(sensor_id, true, client, `forecast
|
|
282
|
-
await this.sendMqtt(sensor_id, true, client, `forecast
|
|
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 ===
|
|
288
|
-
tempHigh = Number(((tempHigh - 32) / 1.8).toFixed(1));
|
|
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
|
|
297
|
-
await this.sendMqtt(sensor_id, true, client, `forecast
|
|
298
|
-
await this.sendMqtt(sensor_id, true, client, `forecast
|
|
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 [
|
|
305
|
-
await this.sendMqtt(sensor_id, true, client, `forecast
|
|
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
|
-
|
|
311
|
-
|
|
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
|
-
|
|
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
|
|
333
|
-
|
|
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 =
|
|
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
|
-
|
|
343
|
-
|
|
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(
|
|
355
|
-
this.log.debug(
|
|
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(
|
|
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(
|
|
473
|
+
this.log.error('Server error');
|
|
369
474
|
}
|
|
370
475
|
} catch (error) {
|
|
371
|
-
this.log.error(
|
|
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
|
-
|
|
379
|
-
this.log.debug("getRealtime data...");
|
|
484
|
+
this.log.debug('getRealtime data...');
|
|
380
485
|
|
|
381
|
-
const url =
|
|
486
|
+
const url = 'https://emaxlife.net/V1.0/weather/devData/getRealtime';
|
|
382
487
|
const headers = {
|
|
383
|
-
|
|
384
|
-
|
|
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(
|
|
496
|
+
httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }),
|
|
392
497
|
});
|
|
393
498
|
|
|
394
499
|
if (response.status === 200) {
|
|
395
|
-
this.log.debug(
|
|
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
|
|
511
|
+
return 'error';
|
|
408
512
|
}
|
|
409
513
|
}
|
|
410
514
|
|
|
515
|
+
// Forecast Daten holen
|
|
411
516
|
async foreCast(token) {
|
|
412
|
-
|
|
413
|
-
this.log.debug("getForecast data...");
|
|
517
|
+
this.log.debug('getForecast data...');
|
|
414
518
|
|
|
415
|
-
const url =
|
|
519
|
+
const url = 'https://emaxlife.net/V1.0/weather/netData/getForecast';
|
|
416
520
|
const headers = {
|
|
417
|
-
|
|
418
|
-
|
|
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(
|
|
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
|
|
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(
|
|
551
|
+
this.log.error('No token received');
|
|
449
552
|
if (mqtt_active) {
|
|
450
|
-
await this.sendMqtt(sensor_id, mqtt_active, client,
|
|
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 ===
|
|
458
|
-
this.log.error(
|
|
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,
|
|
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(
|
|
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,
|
|
572
|
+
fs.writeFileSync(path.join(storeDir, 'devData.json'), json_object, 'utf-8');
|
|
470
573
|
}
|
|
471
574
|
|
|
472
|
-
this.log.debug(
|
|
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
|
|
493
|
-
|
|
494
|
-
|
|
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 (
|
|
522
|
-
|
|
583
|
+
if (value !== null && value !== undefined) {
|
|
584
|
+
this.sendMqtt(sensor_id, mqtt_active, client, `devData/${key}`, value);
|
|
523
585
|
}
|
|
586
|
+
}
|
|
524
587
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
567
|
-
|
|
568
|
-
|
|
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
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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(
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
687
|
+
} catch {
|
|
607
688
|
callback();
|
|
608
689
|
}
|
|
609
690
|
}
|
|
610
691
|
}
|
|
611
692
|
|
|
612
693
|
if (require.main !== module) {
|
|
613
|
-
module.exports =
|
|
694
|
+
module.exports = options => new WeatherSense(options);
|
|
614
695
|
} else {
|
|
615
696
|
new WeatherSense();
|
|
616
697
|
}
|