-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathWH2600.js
312 lines (275 loc) · 9.52 KB
/
WH2600.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
const dgram = require("dgram");
const net = require("net");
const WH2600Utils = require("./WH2600Utils");
const Commands = {
CMD_WRITE_SSID: 0x11, // send router SSID and Password to wifi module
CMD_BROADCAST: 0x12, //looking for device inside network. Returned data size is 2 Byte
CMD_READ_ECOWITT: 0x1e, // read setting for Ecowitt.net
CMD_WRITE_ECOWITT: 0x1f, // write back setting for Ecowitt.net
CMD_READ_WUNDERGROUND: 0x20, // read back setting for Wunderground
CMD_WRITE_WUNDERGROUND: 0x21, // write back setting for Wunderground
CMD_READ_WOW: 0x22, // read setting for WeatherObservationsWebsite
CMD_WRITE_WOW: 0x23, // write back setting for WeatherObservationsWebsite
CMD_READ_WEATHERCLOUD: 0x24, // read setting for Weathercloud
CMD_WRITE_WEATHERCLOUD: 0x25, // write back setting for Weathercloud
CMD_READ_SATION_MAC: 0x26, // read module MAC
CMD_WH2600_LIVEDATA: 0x27, // read current,return size is 2 Byte (only valid for WH2600 and WH2650)
CMD_GET_SOILHUMIAD: 0x28, // read Soilmoisture Sensor calibration parameter
CMD_SET_SOILHUMIAD: 0x29, // write back Soilmoisture Sensor calibration parameter
CMD_READ_CUSTOMIZED: 0x2a, // read setting for Customized sever
CMD_WRITE_CUSTOMIZED: 0x2b, // write back customized sever setting
CMD_GET_MUlCH_OFFSET: 0x2c, // read multi channel sensor OFFSET value
CMD_SET_MUlCH_OFFSET: 0x2d, // write back multi sensor OFFSET value
CMD_GET_PM25_OFFSET: 0x2e, // read PM2.5OFFSET value
CMD_SET_PM25_OFFSET: 0x2f, // write back PM2.5OFFSET value
CMD_READ_SSSS: 0x30, // read sensor setup ( sensor frequency, wh24/wh65 sensor)
CMD_WRITE_SSSS: 0x31, // write back sensor setup
CMD_READ_RAINDATA: 0x34, // read rain data
CMD_WRITE_RAINDATA: 0x35, // write back rain data
CMD_READ_GAIN: 0x36, // read rain gain
CMD_WRITE_GAIN: 0x37, // write back rain gain
CMD_READ_CALIBRATION: 0x38, // read multiple parameter offset( refer to command description below in detail)
CMD_WRITE_CALIBRATION: 0x39, // write back multiple parameter offset
CMD_READ_SENSOR_ID: 0x3a, // read Sensors ID
CMD_WRITE_SENSOR_ID: 0x3b, // write back Sensors ID
CMD_WRITE_SENSOR_ID_NEW: 0x3c, // write back Sensors ID New
CMD_WRITE_REBOOT: 0x40, // system reset
CMD_WRITE_RESET: 0x41, // system default setting reset
CMD_WRITE_UPDATE: 0x43, // update firmware
CMD_READ_FIRMWARE: 0x50, // read back firmware version
CMD_READ_USR_PATH: 0x51, // read path for custom Server
CMD_WRITE_USR_PATH: 0x52 // write path for custom Server
};
const SensorIDs = [
"WH65",
"WH68",
"WH80",
"WH40",
"WH25",
"WH26",
"WH31_CH1",
"WH31_CH2",
"WH31_CH3",
"WH31_CH4",
"WH31_CH5",
"WH31_CH6",
"WH31_CH7",
"WH31_CH8",
"WH51_CH1",
"WH51_CH2",
"WH51_CH3",
"WH51_CH4",
"WH51_CH5",
"WH51_CH6",
"WH51_CH7",
"WH51_CH8",
"WH41_CH1",
"WH41_CH2",
"WH41_CH3",
"WH41_CH4",
"WH57",
"WH55_CH1",
"WH55_CH2",
"WH55_CH3",
"WH55_CH4"
];
class WH2600 {
constructor(ipAddr) {
this.utils = new WH2600Utils();
const buildPacket = (command, data) => {
var size = (data !== null ? data.length : 0) + 3;
var body = [command, size].concat(data !== null ? data : []);
return new Uint8Array(
[255, 255].concat(body, [WH2600Utils.calcChecksum(body)])
);
};
const checkResponse = (resp, cmd, callback) => {
if (resp == null) {
callback(resp, "No Response");
} else if (resp.length < 3) {
callback(resp, "Invalid Response");
} else if (resp[2] !== cmd) {
callback(resp, "Invalid Command Code Response");
} else if (
resp[resp.length - 1] !==
WH2600Utils.calcChecksum(resp.slice(2, resp.length - 1))
) {
callback(resp, "Invalid Checksum");
} else {
callback(resp, null);
}
};
this.runCommand = (command, data = null) => {
return new Promise((res, rej) => {
const client = new net.Socket();
client.connect(45000, ipAddr, function () {
console.debug("Connected");
client.write(buildPacket(command, data));
});
client.on("data", function (buffer) {
console.debug(
`Received Data: ${buffer != null ? buffer.length : 0} bytes`
);
client.destroy(); // kill client after server's response as to not mix up commands
checkResponse(buffer, command, (resData, err) => {
err ? rej(err) : res(resData);
});
});
client.on("close", function () {
console.debug("Connection closed");
});
});
};
}
getSensors(filter = null) {
const statusFilter =
filter && filter.status
? typeof filter.status === "string"
? (status) => status.includes(filter.status.toLowerCase())
: Array.isArray(filter.status)
? (status) => filter.status.includes(status.toLowerCase())
: () => true
: () => true;
const typeFilter =
filter && filter.type
? typeof filter.type === "string"
? (type) => type.includes(filter.type.toUpperCase())
: Array.isArray(filter.type)
? (type) => filter.type.includes(type.toUpperCase())
: () => true
: () => true;
return new Promise((res, rej) => {
this.runCommand(Commands.CMD_READ_SENSOR_ID).then((buffer) => {
if (buffer.length > 200) {
var sensors = [];
for (var i = 4; i < buffer[3]; i += 7) {
var id = buffer.toString("hex", i + 1, i + 5).toUpperCase();
var typeID = buffer[i];
var type =
typeID < SensorIDs.length && typeID >= 0
? SensorIDs[typeID]
: `Unknown Type (${id})`;
var status =
id === "FFFFFFFE"
? "disabled"
: id === "FFFFFFFF"
? "registering"
: "active";
if (statusFilter(status) && typeFilter(type)) {
sensors.push({
type: type,
status: status,
id:
status === "active"
? parseInt(id, 16).toString(16).toUpperCase()
: null, //remove leading 0's
signal: status === "active" ? buffer[i + 5] : null,
battery: status === "active" ? buffer[i + 6] : null
});
}
}
if (filter === null) {
this.sensors = sensors;
}
res(sensors);
} else {
rej("Invalid Data Length");
}
});
});
}
getLiveData(filterActiveSensors = true) {
return new Promise((res) => {
this.runCommand(Commands.CMD_WH2600_LIVEDATA).then((buffer) => {
var data = this.utils.parseLiveData(buffer);
if (filterActiveSensors) {
const filterSensors = (resData, sensors) => {
if (resData.lowbatt) {
Object.keys(resData.lowbatt).forEach((key) => {
var ukey = key.toUpperCase();
if (typeof resData.lowbatt[key] === "object") {
Object.keys(resData.lowbatt[key]).forEach((chn) => {
const uchn = chn.toUpperCase();
const usen = key.toUpperCase();
if (
sensors.filter(
(s) =>
s.type === `${usen}_${uchn}` && s.status === "active"
).length < 1
) {
delete resData.lowbatt[key][chn];
}
});
if (Object.keys(resData.lowbatt[key]).length < 1) {
delete resData.lowbatt[key];
}
} else {
if (
sensors.filter(
(s) => s.type === ukey && s.status === "active"
).length < 1
) {
delete resData.lowbatt[key];
}
}
});
}
res(resData);
};
if (this.sensors === undefined) {
this.getSensors().then((sensors) => {
filterSensors(data, sensors);
});
} else {
filterSensors(data, this.sensors);
}
} else {
res(data);
}
});
});
}
getRainData() {
return new Promise((res) => {
this.runCommand(Commands.CMD_READ_RAINDATA).then((buffer) => {
res(this.utils.parseRainData(buffer));
});
});
}
getFirmwareVersion() {
return new Promise((res) => {
this.runCommand(Commands.CMD_READ_FIRMWARE).then((buffer) => {
res(buffer.slice(5, buffer.length - 1).toString("ascii"));
});
});
}
getCustomServerInfo() {
return new Promise((res) => {
this.runCommand(Commands.CMD_READ_CUSTOMIZED).then((rcbuffer) => {
var info = this.utils.parseCustomServerInfo(rcbuffer);
this.runCommand(Commands.CMD_READ_USR_PATH).then((upbuffer) => {
Object.assign(info, this.utils.parseUserPathInfo(upbuffer));
res(info);
});
});
});
}
static discover(timeout = 5000) {
return new Promise((res) => {
const server = dgram.createSocket("udp4");
var ips = [];
server.on("message", (msg, rinfo) => {
if (!ips.includes(rinfo.address)) {
ips.push(rinfo.address);
}
});
server.bind(59387);
setTimeout(() => {
server.close(() => {
res(ips);
});
}, timeout);
});
}
}
module.exports = WH2600;