iobroker.weathersense 1.0.3 → 2.0.2
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 +377 -291
- 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,269 @@ 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
|
-
});
|
|
124
|
-
|
|
125
|
-
await this.setObjectNotExistsAsync(forecastChannelId, {
|
|
126
|
-
type: "channel",
|
|
127
|
-
common: { name: "Forecast" },
|
|
128
|
-
native: {},
|
|
129
|
-
});
|
|
131
|
+
const devDataChannelId = `${deviceId}.devData`;
|
|
132
|
+
const tempUnit = celsius ? '°C' : '°F';
|
|
130
133
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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 });
|
|
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 });
|
|
162
154
|
}
|
|
163
155
|
}
|
|
164
156
|
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
if (
|
|
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}`;
|
|
169
162
|
|
|
170
|
-
|
|
163
|
+
let unit = '';
|
|
164
|
+
if (key === 'atmos') {
|
|
165
|
+
unit = 'hPa';
|
|
166
|
+
}
|
|
171
167
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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";
|
|
168
|
+
await this.setObjectNotExistsAsync(id, {
|
|
169
|
+
type: typeof value === 'number' ? 'state' : 'state',
|
|
170
|
+
common: {
|
|
171
|
+
name: key,
|
|
172
|
+
type: typeof value,
|
|
173
|
+
role: 'value',
|
|
174
|
+
unit: unit,
|
|
175
|
+
read: true,
|
|
176
|
+
write: false,
|
|
177
|
+
},
|
|
178
|
+
native: {},
|
|
179
|
+
});
|
|
180
|
+
await this.setStateAsync(id, { val: value, ack: true });
|
|
191
181
|
}
|
|
182
|
+
}
|
|
192
183
|
|
|
193
|
-
|
|
184
|
+
// Alle Sensoren aus devdata.content.sensorDatas
|
|
185
|
+
const sensor_data = content.sensorDatas || [];
|
|
186
|
+
for (const s of sensor_data) {
|
|
187
|
+
const type_ = s.type;
|
|
188
|
+
const channel = s.channel;
|
|
189
|
+
const cur_val = s.curVal;
|
|
190
|
+
const high_val = s.hihgVal;
|
|
191
|
+
const low_val = s.lowVal;
|
|
194
192
|
|
|
195
|
-
|
|
196
|
-
type: "state",
|
|
197
|
-
common,
|
|
198
|
-
native: {},
|
|
199
|
-
});
|
|
193
|
+
let prefix = '';
|
|
200
194
|
|
|
201
|
-
|
|
195
|
+
if (type_ === 1) {
|
|
196
|
+
prefix = '-Temp';
|
|
197
|
+
}
|
|
198
|
+
if (type_ === 2) {
|
|
199
|
+
prefix = '-Hum';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const key = `Type${type_}-Channel${channel}${prefix}`;
|
|
203
|
+
const base = `${devDataChannelId}.${key}`;
|
|
204
|
+
|
|
205
|
+
// current
|
|
206
|
+
if (cur_val !== null && cur_val !== undefined && cur_val !== 65535 && cur_val !== 255) {
|
|
207
|
+
const id = `${base}.current`;
|
|
208
|
+
await this.setObjectNotExistsAsync(id, {
|
|
209
|
+
type: 'state',
|
|
210
|
+
common: {
|
|
211
|
+
name: 'current',
|
|
212
|
+
type: 'number',
|
|
213
|
+
role:
|
|
214
|
+
prefix === '-Temp'
|
|
215
|
+
? 'value.temperature'
|
|
216
|
+
: prefix === '-Hum'
|
|
217
|
+
? 'value.humidity'
|
|
218
|
+
: 'value',
|
|
219
|
+
unit: prefix === '-Temp' ? tempUnit : prefix === '-Hum' ? '%' : '',
|
|
220
|
+
read: true,
|
|
221
|
+
write: false,
|
|
222
|
+
},
|
|
223
|
+
native: {},
|
|
224
|
+
});
|
|
225
|
+
await this.setStateAsync(id, { val: this.c_f_berechnen(cur_val, prefix, celsius), ack: true });
|
|
226
|
+
}
|
|
227
|
+
// high
|
|
228
|
+
if (high_val !== null && high_val !== undefined && high_val !== 65535 && high_val !== 255) {
|
|
229
|
+
const id = `${base}.high`;
|
|
230
|
+
await this.setObjectNotExistsAsync(id, {
|
|
231
|
+
type: 'state',
|
|
232
|
+
common: {
|
|
233
|
+
name: 'high',
|
|
234
|
+
type: 'number',
|
|
235
|
+
role:
|
|
236
|
+
prefix === '-Temp'
|
|
237
|
+
? 'value.temperature'
|
|
238
|
+
: prefix === '-Hum'
|
|
239
|
+
? 'value.humidity'
|
|
240
|
+
: 'value',
|
|
241
|
+
unit: prefix === '-Temp' ? tempUnit : prefix === '-Hum' ? '%' : '',
|
|
242
|
+
read: true,
|
|
243
|
+
write: false,
|
|
244
|
+
},
|
|
245
|
+
native: {},
|
|
246
|
+
});
|
|
247
|
+
await this.setStateAsync(id, { val: this.c_f_berechnen(high_val, prefix, celsius), ack: true });
|
|
248
|
+
}
|
|
249
|
+
// low
|
|
250
|
+
if (low_val !== null && low_val !== undefined && low_val !== 65535 && low_val !== 255) {
|
|
251
|
+
const id = `${base}.low`;
|
|
252
|
+
await this.setObjectNotExistsAsync(id, {
|
|
253
|
+
type: 'state',
|
|
254
|
+
common: {
|
|
255
|
+
name: 'low',
|
|
256
|
+
type: 'number',
|
|
257
|
+
role:
|
|
258
|
+
prefix === '-Temp'
|
|
259
|
+
? 'value.temperature'
|
|
260
|
+
: prefix === '-Hum'
|
|
261
|
+
? 'value.humidity'
|
|
262
|
+
: 'value',
|
|
263
|
+
unit: prefix === '-Temp' ? tempUnit : prefix === '-Hum' ? '%' : '',
|
|
264
|
+
read: true,
|
|
265
|
+
write: false,
|
|
266
|
+
},
|
|
267
|
+
native: {},
|
|
268
|
+
});
|
|
269
|
+
await this.setStateAsync(id, { val: this.c_f_berechnen(low_val, prefix, celsius), ack: true });
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// dev*-Keys im Sensorobjekt
|
|
273
|
+
for (const [k, v] of Object.entries(s)) {
|
|
274
|
+
if (
|
|
275
|
+
k.startsWith('dev') &&
|
|
276
|
+
v !== null &&
|
|
277
|
+
v !== undefined &&
|
|
278
|
+
!(typeof v === 'object' && Object.keys(v).length === 0)
|
|
279
|
+
) {
|
|
280
|
+
const id = `${base}.${k}`;
|
|
281
|
+
await this.setObjectNotExistsAsync(id, {
|
|
282
|
+
type: 'state',
|
|
283
|
+
common: {
|
|
284
|
+
name: k,
|
|
285
|
+
type: typeof v,
|
|
286
|
+
role: 'value',
|
|
287
|
+
unit: '',
|
|
288
|
+
read: true,
|
|
289
|
+
write: false,
|
|
290
|
+
},
|
|
291
|
+
native: {},
|
|
292
|
+
});
|
|
293
|
+
await this.setStateAsync(id, { val: v, ack: true });
|
|
294
|
+
}
|
|
295
|
+
}
|
|
202
296
|
}
|
|
203
297
|
} else {
|
|
204
|
-
this.log.error(
|
|
298
|
+
this.log.error('Error loading data in main()');
|
|
205
299
|
await this.setStateAsync(systemStateId, { val: false, ack: true });
|
|
206
300
|
}
|
|
207
301
|
} catch (error) {
|
|
208
|
-
this.log.error(
|
|
302
|
+
this.log.error(`Unexpected error in onReady(): ${error.message}`);
|
|
209
303
|
} finally {
|
|
210
304
|
if (client) {
|
|
211
305
|
client.end();
|
|
212
306
|
}
|
|
213
|
-
this.terminate
|
|
307
|
+
this.terminate
|
|
308
|
+
? this.terminate('Everything done. Going to terminate till next schedule', 0)
|
|
309
|
+
: process.exit(0);
|
|
214
310
|
}
|
|
215
311
|
}
|
|
216
312
|
|
|
313
|
+
// MQTT senden
|
|
217
314
|
async sendMqtt(sensor_id, mqtt_active, client, topic, wert) {
|
|
218
315
|
if (mqtt_active) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
wert = wert !== null && wert !== undefined ? wert.toString() : "";
|
|
316
|
+
if (typeof wert !== 'string') {
|
|
317
|
+
wert = wert !== null && wert !== undefined ? wert.toString() : '';
|
|
222
318
|
}
|
|
223
|
-
client.publish(
|
|
319
|
+
client.publish(`WEATHERSENSE/${sensor_id.toString()}/${topic}`, wert);
|
|
224
320
|
}
|
|
225
321
|
}
|
|
226
322
|
|
|
323
|
+
// °F nach °C umwandeln, falls notwendig
|
|
324
|
+
c_f_berechnen(temp, prefix, celsius) {
|
|
325
|
+
if (celsius && prefix === '-Temp') {
|
|
326
|
+
if (temp !== null && temp !== undefined) {
|
|
327
|
+
temp = ((temp - 32) / 1.8).toFixed(1);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return temp;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Forecast Datenpunkte erstellen und schreiben
|
|
227
334
|
async createOrUpdateForecastDPs(forecastChannelId, forecasts, celsius) {
|
|
228
|
-
if (!forecasts || !forecastChannelId)
|
|
335
|
+
if (!forecasts || !forecastChannelId) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
229
338
|
|
|
230
|
-
// Struktur der Forecast-Items pro Tag
|
|
231
339
|
const forecastItems = [
|
|
232
|
-
{ id:
|
|
233
|
-
{ id:
|
|
234
|
-
{ id:
|
|
235
|
-
{ id:
|
|
236
|
-
{ id:
|
|
340
|
+
{ id: 'day', type: 'string', role: 'value', unit: '' },
|
|
341
|
+
{ id: 'date', type: 'string', role: 'value', unit: '' },
|
|
342
|
+
{ id: 'high', type: 'number', role: 'value.temperature', unit: celsius ? '°C' : '°F' },
|
|
343
|
+
{ id: 'low', type: 'number', role: 'value.temperature', unit: celsius ? '°C' : '°F' },
|
|
344
|
+
{ id: 'text', type: 'string', role: 'text', unit: '' },
|
|
237
345
|
];
|
|
238
346
|
|
|
239
347
|
for (let i = 0; i < forecasts.length; i++) {
|
|
240
348
|
const forecast = forecasts[i];
|
|
241
|
-
if (!forecast)
|
|
349
|
+
if (!forecast) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
242
352
|
|
|
243
353
|
for (const item of forecastItems) {
|
|
244
|
-
const id = `${forecastChannelId}
|
|
354
|
+
const id = `${forecastChannelId}.day_${i}.${item.id}`;
|
|
245
355
|
await this.setObjectNotExistsAsync(id, {
|
|
246
|
-
type:
|
|
356
|
+
type: 'state',
|
|
247
357
|
common: {
|
|
248
358
|
name: item.id,
|
|
249
359
|
type: item.type,
|
|
@@ -256,7 +366,7 @@ class WeatherSense extends utils.Adapter {
|
|
|
256
366
|
});
|
|
257
367
|
|
|
258
368
|
let val = forecast[item.id];
|
|
259
|
-
if ((item.id ===
|
|
369
|
+
if ((item.id === 'high' || item.id === 'low') && typeof val === 'number') {
|
|
260
370
|
if (celsius) {
|
|
261
371
|
val = Number(((val - 32) / 1.8).toFixed(1));
|
|
262
372
|
} else {
|
|
@@ -271,21 +381,26 @@ class WeatherSense extends utils.Adapter {
|
|
|
271
381
|
}
|
|
272
382
|
}
|
|
273
383
|
|
|
384
|
+
// Forecasts per MQTT senden
|
|
274
385
|
async sendForecasts(client, forecasts, celsius, sensor_id) {
|
|
275
|
-
if (!client || !forecasts)
|
|
386
|
+
if (!client || !forecasts) {
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
276
389
|
|
|
277
390
|
for (let i = 0; i < forecasts.length; i++) {
|
|
278
391
|
const forecast = forecasts[i];
|
|
279
|
-
if (!forecast)
|
|
392
|
+
if (!forecast) {
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
280
395
|
|
|
281
|
-
await this.sendMqtt(sensor_id, true, client, `forecast
|
|
282
|
-
await this.sendMqtt(sensor_id, true, client, `forecast
|
|
396
|
+
await this.sendMqtt(sensor_id, true, client, `forecast/day_${i}/day`, forecast.day || '');
|
|
397
|
+
await this.sendMqtt(sensor_id, true, client, `forecast/day_${i}/date`, forecast.date || '');
|
|
283
398
|
|
|
284
399
|
let tempHigh = forecast.high;
|
|
285
400
|
let tempLow = forecast.low;
|
|
286
401
|
|
|
287
|
-
if (celsius && typeof tempHigh ===
|
|
288
|
-
tempHigh = Number(((tempHigh - 32) / 1.8).toFixed(1));
|
|
402
|
+
if (celsius && typeof tempHigh === 'number' && typeof tempLow === 'number') {
|
|
403
|
+
tempHigh = Number(((tempHigh - 32) / 1.8).toFixed(1)); // Zahl, keine Zeichenkette
|
|
289
404
|
tempLow = Number(((tempLow - 32) / 1.8).toFixed(1));
|
|
290
405
|
} else {
|
|
291
406
|
// Wenn kein Celsius oder kein Zahlentyp, trotzdem als Zahl (sofern möglich), sonst 0 als Fallback
|
|
@@ -293,22 +408,24 @@ class WeatherSense extends utils.Adapter {
|
|
|
293
408
|
tempLow = tempLow != null ? Number(tempLow) : 0;
|
|
294
409
|
}
|
|
295
410
|
|
|
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
|
|
411
|
+
await this.sendMqtt(sensor_id, true, client, `forecast/day_${i}/high`, tempHigh);
|
|
412
|
+
await this.sendMqtt(sensor_id, true, client, `forecast/day_${i}/low`, tempLow);
|
|
413
|
+
await this.sendMqtt(sensor_id, true, client, `forecast/day_${i}/text`, forecast.text || '');
|
|
299
414
|
}
|
|
300
415
|
}
|
|
301
416
|
|
|
417
|
+
// Alte MQTT Datenpunkte auf Subscriber löschen
|
|
302
418
|
async clearOldForecasts(sensor_id, client, maxDays = 6) {
|
|
303
419
|
for (let i = 0; i < maxDays; i++) {
|
|
304
|
-
for (const key of [
|
|
305
|
-
await this.sendMqtt(sensor_id, true, client, `forecast
|
|
420
|
+
for (const key of ['day', 'date', 'high', 'low', 'text']) {
|
|
421
|
+
await this.sendMqtt(sensor_id, true, client, `forecast/day_${i}/${key}`, '');
|
|
306
422
|
}
|
|
307
423
|
}
|
|
308
424
|
}
|
|
309
425
|
|
|
310
|
-
|
|
311
|
-
|
|
426
|
+
// Debugausgabe formatieren
|
|
427
|
+
printAllKeys(d, prefix = '') {
|
|
428
|
+
if (typeof d === 'object' && d !== null && !Array.isArray(d)) {
|
|
312
429
|
for (const [k, v] of Object.entries(d)) {
|
|
313
430
|
this.printAllKeys(v, `${prefix}${k}/`);
|
|
314
431
|
}
|
|
@@ -321,266 +438,231 @@ class WeatherSense extends utils.Adapter {
|
|
|
321
438
|
}
|
|
322
439
|
}
|
|
323
440
|
|
|
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
441
|
// Funktion zum Erzeugen des PW-Hashes
|
|
331
442
|
hashPassword(pw) {
|
|
332
|
-
const key = Buffer.from(
|
|
443
|
+
const key = Buffer.from('ZW1heEBwd2QxMjM=', 'base64').toString('utf8');
|
|
333
444
|
const combined = pw + key;
|
|
334
|
-
return crypto.createHash(
|
|
445
|
+
return crypto.createHash('md5').update(combined, 'utf8').digest('hex').toUpperCase();
|
|
335
446
|
}
|
|
336
447
|
|
|
337
448
|
// Login-Funktion
|
|
338
449
|
async login(USERNAME, PASSWORD) {
|
|
339
|
-
const LOGIN_URL =
|
|
450
|
+
const LOGIN_URL = 'https://emaxlife.net/V1.0/account/login';
|
|
340
451
|
const hashed_pw = this.hashPassword(PASSWORD);
|
|
341
452
|
|
|
342
453
|
const headers = {
|
|
343
|
-
|
|
344
|
-
|
|
454
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
455
|
+
'User-Agent': 'okhttp/3.14.9',
|
|
345
456
|
};
|
|
346
457
|
|
|
347
458
|
const payload = {
|
|
348
459
|
email: USERNAME,
|
|
349
|
-
pwd: hashed_pw
|
|
460
|
+
pwd: hashed_pw,
|
|
350
461
|
};
|
|
351
462
|
|
|
352
463
|
try {
|
|
353
464
|
const response = await axios.post(LOGIN_URL, payload, { headers });
|
|
354
465
|
|
|
355
|
-
this.log.debug(
|
|
356
|
-
this.log.debug(
|
|
466
|
+
this.log.debug('Status code:', response.status);
|
|
467
|
+
this.log.debug('Response:', response.data);
|
|
357
468
|
|
|
358
469
|
const data = response.data;
|
|
359
470
|
|
|
360
471
|
if (response.status === 200) {
|
|
361
472
|
if (data.status === 0 && data.content) {
|
|
362
473
|
const token = data.content.token;
|
|
363
|
-
this.log.debug(
|
|
474
|
+
this.log.debug(`Login successful. Token: ${token.substring(0, 20)}...`);
|
|
364
475
|
return token;
|
|
365
|
-
} else {
|
|
366
|
-
this.log.error("Login failed:", data.message);
|
|
367
476
|
}
|
|
477
|
+
this.log.error('Login failed:', data.message);
|
|
368
478
|
} else {
|
|
369
|
-
this.log.error(
|
|
479
|
+
this.log.error('Server error');
|
|
370
480
|
}
|
|
371
481
|
} catch (error) {
|
|
372
|
-
this.log.error(
|
|
482
|
+
this.log.error('Error during login:', error.message);
|
|
373
483
|
}
|
|
374
484
|
|
|
375
485
|
return null;
|
|
376
486
|
}
|
|
377
487
|
|
|
488
|
+
// Realtime Daten holen
|
|
378
489
|
async devData(token) {
|
|
379
|
-
|
|
380
|
-
this.log.debug("getRealtime data...");
|
|
490
|
+
this.log.debug('getRealtime data...');
|
|
381
491
|
|
|
382
|
-
const url =
|
|
492
|
+
const url = 'https://emaxlife.net/V1.0/weather/devData/getRealtime';
|
|
383
493
|
const headers = {
|
|
384
|
-
|
|
385
|
-
|
|
494
|
+
emaxtoken: token,
|
|
495
|
+
'Content-Type': 'application/json',
|
|
386
496
|
};
|
|
387
497
|
|
|
388
498
|
try {
|
|
389
499
|
const response = await axios.get(url, {
|
|
390
500
|
headers,
|
|
391
501
|
timeout: 5000,
|
|
392
|
-
httpsAgent: new (require(
|
|
502
|
+
httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }),
|
|
393
503
|
});
|
|
394
504
|
|
|
395
505
|
if (response.status === 200) {
|
|
396
|
-
this.log.debug(
|
|
506
|
+
this.log.debug('Data was received');
|
|
397
507
|
return response.data;
|
|
398
|
-
} else {
|
|
399
|
-
this.log.error(`devData > Status Code: ${response.status}`);
|
|
400
|
-
return "error";
|
|
401
508
|
}
|
|
509
|
+
this.log.error(`devData > Status Code: ${response.status}`);
|
|
510
|
+
return 'error';
|
|
402
511
|
} catch (error) {
|
|
403
512
|
if (error.response) {
|
|
404
513
|
this.log.error(`devData > Status Code: ${error.response.status}`);
|
|
405
514
|
} else {
|
|
406
515
|
this.log.error(`Error during request: ${error.message}`);
|
|
407
516
|
}
|
|
408
|
-
return
|
|
517
|
+
return 'error';
|
|
409
518
|
}
|
|
410
519
|
}
|
|
411
520
|
|
|
521
|
+
// Forecast Daten holen
|
|
412
522
|
async foreCast(token) {
|
|
413
|
-
|
|
414
|
-
this.log.debug("getForecast data...");
|
|
523
|
+
this.log.debug('getForecast data...');
|
|
415
524
|
|
|
416
|
-
const url =
|
|
525
|
+
const url = 'https://emaxlife.net/V1.0/weather/netData/getForecast';
|
|
417
526
|
const headers = {
|
|
418
|
-
|
|
419
|
-
|
|
527
|
+
emaxtoken: token,
|
|
528
|
+
'Content-Type': 'application/json',
|
|
420
529
|
};
|
|
421
530
|
|
|
422
531
|
try {
|
|
423
532
|
const response = await axios.get(url, {
|
|
424
533
|
headers,
|
|
425
534
|
timeout: 5000,
|
|
426
|
-
httpsAgent: new https.Agent({ rejectUnauthorized: false }) // entspricht verify=False
|
|
535
|
+
httpsAgent: new https.Agent({ rejectUnauthorized: false }), // entspricht verify=False
|
|
427
536
|
});
|
|
428
537
|
|
|
429
538
|
if (response.status === 200) {
|
|
430
|
-
this.log.debug(
|
|
539
|
+
this.log.debug('Data was received');
|
|
431
540
|
return response.data;
|
|
432
|
-
} else {
|
|
433
|
-
this.log.error(`foreCast > Status Code: ${response.status}`);
|
|
434
|
-
return "error";
|
|
435
541
|
}
|
|
542
|
+
this.log.error(`foreCast > Status Code: ${response.status}`);
|
|
543
|
+
return 'error';
|
|
436
544
|
} catch (error) {
|
|
437
545
|
if (error.response) {
|
|
438
546
|
this.log.error(`foreCast > Status Code: ${error.response.status}`);
|
|
439
547
|
} else {
|
|
440
548
|
this.log.error(`Error during request: ${error.message}`);
|
|
441
549
|
}
|
|
442
|
-
return
|
|
550
|
+
return 'error';
|
|
443
551
|
}
|
|
444
552
|
}
|
|
445
553
|
|
|
446
554
|
async main(client, username, passwort, mqtt_active, sensor_id, storeJson, storeDir, celsius, forecastChannelId) {
|
|
447
555
|
const token = await this.login(username, passwort);
|
|
448
556
|
if (!token) {
|
|
449
|
-
this.log.error(
|
|
557
|
+
this.log.error('No token received');
|
|
450
558
|
if (mqtt_active) {
|
|
451
|
-
await this.sendMqtt(sensor_id, mqtt_active, client,
|
|
559
|
+
await this.sendMqtt(sensor_id, mqtt_active, client, 'dataReceived', 'false');
|
|
452
560
|
client.end();
|
|
453
561
|
}
|
|
454
562
|
return false;
|
|
455
563
|
}
|
|
456
564
|
const devdata = await this.devData(token);
|
|
457
565
|
const forecast = await this.foreCast(token);
|
|
458
|
-
if (devdata ===
|
|
459
|
-
this.log.error(
|
|
566
|
+
if (devdata === 'error' || forecast === 'error') {
|
|
567
|
+
this.log.error('No data received');
|
|
460
568
|
if (mqtt_active) {
|
|
461
|
-
await this.sendMqtt(sensor_id, mqtt_active, client,
|
|
569
|
+
await this.sendMqtt(sensor_id, mqtt_active, client, 'dataReceived', 'false');
|
|
462
570
|
client.end();
|
|
463
571
|
}
|
|
464
|
-
return false;
|
|
572
|
+
return { dataReceived: false };
|
|
465
573
|
}
|
|
466
574
|
|
|
467
575
|
if (storeJson) {
|
|
468
|
-
this.log.debug(
|
|
576
|
+
this.log.debug(`Save devData.json to ${storeDir}`);
|
|
469
577
|
const json_object = JSON.stringify(devdata, null, 4);
|
|
470
|
-
fs.writeFileSync(path.join(storeDir,
|
|
578
|
+
fs.writeFileSync(path.join(storeDir, 'devData.json'), json_object, 'utf-8');
|
|
471
579
|
}
|
|
472
580
|
|
|
473
|
-
this.log.debug(
|
|
581
|
+
this.log.debug('devData JSON:');
|
|
474
582
|
this.printAllKeys(devdata);
|
|
475
583
|
|
|
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
584
|
if (mqtt_active) {
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
|
|
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);
|
|
585
|
+
for (const [key, value] of Object.entries(devdata)) {
|
|
586
|
+
if (key === 'content') {
|
|
587
|
+
continue;
|
|
518
588
|
}
|
|
519
|
-
if (
|
|
520
|
-
|
|
521
|
-
}
|
|
522
|
-
if (lowVal != null && lowVal !== 65535) {
|
|
523
|
-
await this.sendMqtt(sensor_id, mqtt_active, client, `${base}/low`, lowVal);
|
|
589
|
+
if (value !== null && value !== undefined) {
|
|
590
|
+
this.sendMqtt(sensor_id, mqtt_active, client, `devData/${key}`, value);
|
|
524
591
|
}
|
|
592
|
+
}
|
|
525
593
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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
|
-
}
|
|
594
|
+
const content = devdata.content || {};
|
|
595
|
+
for (const [key, value] of Object.entries(content)) {
|
|
596
|
+
if (value !== null && value !== undefined) {
|
|
597
|
+
this.sendMqtt(sensor_id, mqtt_active, client, `devData/${key}`, value);
|
|
546
598
|
}
|
|
547
599
|
}
|
|
548
|
-
}
|
|
549
|
-
|
|
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
600
|
|
|
559
|
-
|
|
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
|
|
601
|
+
const sensor_data = content.sensorDatas || [];
|
|
564
602
|
|
|
565
|
-
const
|
|
603
|
+
for (const s of sensor_data) {
|
|
604
|
+
const type_ = s.type;
|
|
605
|
+
const channel = s.channel;
|
|
606
|
+
const cur_val = s.curVal;
|
|
607
|
+
const high_val = s.hihgVal;
|
|
608
|
+
const low_val = s.lowVal;
|
|
609
|
+
|
|
610
|
+
let prefix = '';
|
|
611
|
+
if (type_ === 1) {
|
|
612
|
+
prefix = '-Temp';
|
|
613
|
+
}
|
|
614
|
+
if (type_ === 2) {
|
|
615
|
+
prefix = '-Hum';
|
|
616
|
+
}
|
|
566
617
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
618
|
+
const key = `Type${type_}-Channel${channel}${prefix}`;
|
|
619
|
+
const base = `devData/${key}`;
|
|
620
|
+
|
|
621
|
+
if (cur_val !== null && cur_val !== undefined && cur_val !== 65535 && cur_val !== 255) {
|
|
622
|
+
this.sendMqtt(
|
|
623
|
+
sensor_id,
|
|
624
|
+
mqtt_active,
|
|
625
|
+
client,
|
|
626
|
+
`${base}/current`,
|
|
627
|
+
this.c_f_berechnen(cur_val, prefix, celsius),
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
if (high_val !== null && high_val !== undefined && high_val !== 65535 && high_val !== 255) {
|
|
631
|
+
this.sendMqtt(
|
|
632
|
+
sensor_id,
|
|
633
|
+
mqtt_active,
|
|
634
|
+
client,
|
|
635
|
+
`${base}/high`,
|
|
636
|
+
this.c_f_berechnen(high_val, prefix, celsius),
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
if (low_val !== null && low_val !== undefined && low_val !== 65535 && low_val !== 255) {
|
|
640
|
+
this.sendMqtt(
|
|
641
|
+
sensor_id,
|
|
642
|
+
mqtt_active,
|
|
643
|
+
client,
|
|
644
|
+
`${base}/low`,
|
|
645
|
+
this.c_f_berechnen(low_val, prefix, celsius),
|
|
646
|
+
);
|
|
647
|
+
}
|
|
570
648
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
649
|
+
for (const [k, v] of Object.entries(s)) {
|
|
650
|
+
if (
|
|
651
|
+
k.startsWith('dev') &&
|
|
652
|
+
v !== null &&
|
|
653
|
+
v !== undefined &&
|
|
654
|
+
!(typeof v === 'object' && Object.keys(v).length === 0)
|
|
655
|
+
) {
|
|
656
|
+
this.sendMqtt(sensor_id, mqtt_active, client, `${base}/${k}`, v);
|
|
657
|
+
}
|
|
576
658
|
}
|
|
577
659
|
}
|
|
578
660
|
}
|
|
579
661
|
|
|
580
662
|
if (storeJson) {
|
|
581
|
-
this.log.debug(
|
|
663
|
+
this.log.debug(`Save forecast.json to ${storeDir}`);
|
|
582
664
|
const json_object = JSON.stringify(forecast, null, 4);
|
|
583
|
-
fs.writeFileSync(path.join(storeDir,
|
|
665
|
+
fs.writeFileSync(path.join(storeDir, 'forecast.json'), json_object, 'utf-8');
|
|
584
666
|
}
|
|
585
667
|
|
|
586
668
|
this.printAllKeys(forecast);
|
|
@@ -598,20 +680,24 @@ class WeatherSense extends utils.Adapter {
|
|
|
598
680
|
|
|
599
681
|
await this.createOrUpdateForecastDPs(forecastChannelId, forecasts, celsius);
|
|
600
682
|
|
|
601
|
-
return
|
|
683
|
+
return {
|
|
684
|
+
dataReceived: true,
|
|
685
|
+
devdata,
|
|
686
|
+
forecast,
|
|
687
|
+
};
|
|
602
688
|
}
|
|
603
689
|
|
|
604
690
|
onUnload(callback) {
|
|
605
691
|
try {
|
|
606
692
|
callback();
|
|
607
|
-
} catch
|
|
693
|
+
} catch {
|
|
608
694
|
callback();
|
|
609
695
|
}
|
|
610
696
|
}
|
|
611
697
|
}
|
|
612
698
|
|
|
613
699
|
if (require.main !== module) {
|
|
614
|
-
module.exports =
|
|
700
|
+
module.exports = options => new WeatherSense(options);
|
|
615
701
|
} else {
|
|
616
702
|
new WeatherSense();
|
|
617
703
|
}
|