@evops/lightwaverf 0.0.7 → 0.0.8

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/index.ts DELETED
@@ -1,586 +0,0 @@
1
- import util from 'util'
2
- import events, { EventEmitter } from 'events';
3
- import dgram from 'dgram';
4
- import fs from 'fs';
5
- import yaml from 'js-yaml';
6
- import rp from 'request-promise';
7
- import Debug from 'debug';
8
- import { RequestAPI, RequiredUriUrl } from 'request';
9
-
10
- const debug = Debug('lightwaverf');
11
-
12
- class LightwaveRFConfiguration {
13
- timeout?: number = 1000;
14
- ip?: string;
15
- file?: any;
16
- host?: any;
17
- email?: any;
18
- pin?: any;
19
- }
20
-
21
- interface LightwaveRFDevice {
22
- roomId: number
23
- deviceId: number
24
- roomName: string
25
- deviceName: string
26
- deviceType: string
27
- }
28
-
29
- declare interface LightwaveRF {
30
- on(event: 'deviceTurnedOn', listener: (roomId: number, deviceId: number) => void): this;
31
- on(event: 'deviceTurnedOff', listener: (roomId: number, deviceId: number) => void): this;
32
- }
33
-
34
- /**
35
- * LightwaveRF API
36
- *
37
- * @param object config The config
38
- *
39
- * An instance of the LightwaveRF API
40
- */
41
- class LightwaveRF extends EventEmitter {
42
- timeout: number = 1000;
43
- queue: any = [];
44
- ready = true;
45
- awaitRegistrration = false;
46
- currentTransactionNumber: number = 4674773;
47
- devices: Array<LightwaveRFDevice> = [];
48
- messageCounter = 0;
49
- config: LightwaveRFConfiguration = {};
50
- sendSocket: dgram.Socket;
51
- receiveSocket: dgram.Socket;
52
- responseListeners = new Map<number, any>();
53
-
54
- constructor(config: LightwaveRFConfiguration, callback: (config: any, error: any) => void) {
55
- super();
56
-
57
- const self: LightwaveRF = this;
58
- this.timeout = config.timeout || 1000;
59
-
60
- this.devices = [];//[{roomId:0,roomName:'',
61
- //deviceId:0,deviceName:'',
62
- //deviceType:''}];
63
-
64
- this.setMaxListeners(255);
65
-
66
- //Counter
67
- this.messageCounter = 0;
68
-
69
- //Config
70
- this.config = config;
71
-
72
- // Use broadcast to discover the IP
73
- this.config.ip = this.config.ip || '255.255.255.255';
74
-
75
-
76
- //Send Socket
77
- this.sendSocket = dgram.createSocket("udp4");
78
-
79
- //Receive socket
80
- this.receiveSocket = dgram.createSocket("udp4");
81
-
82
- //Receive message
83
- this.receiveSocket.on("message", function (message: Buffer, rinfo: dgram.RemoteInfo) {
84
- // If we were using broadcast IP, we have now
85
- // discovered Link device IP and can switch off
86
- // broadcast
87
- if (self.config.ip == '255.255.255.255') {
88
- console.log("We have now discovered Link IP address: %s", rinfo.address);
89
- self.config.ip = rinfo.address
90
- self.sendSocket.setBroadcast(false)
91
- }
92
-
93
- //Check this came from the lightwave unit
94
- if (rinfo.address !== self.config.ip) {
95
- //Came from wrong ip]
96
- console.warn("Response came from a different IP than our configured", rinfo.address, self.config.ip)
97
- return false;
98
- }
99
-
100
- const parseResponse = (buffer: Buffer) => {
101
- const response: any = new Object();
102
- const message = buffer.toString('utf-8');
103
- if (message.match(/^\*!/)) {
104
- const jsonResponse = JSON.parse(message.replace(/^\*!/, ''))
105
- self.currentTransactionNumber = jsonResponse.trans + 1;
106
-
107
- Object.assign(response, jsonResponse)
108
- } else {
109
- //Split off the code for the message
110
- var parts = message.split(",");
111
- var trans = parts.splice(0, 1);
112
- var content = parts.join(",").replace(/(\r\n|\n|\r)/gm, "");
113
- response.trans = parseInt(trans[0]);
114
- response.message = content;
115
- }
116
-
117
- response.trans = response.trans !== null ? parseInt(response.trans) : null;
118
- response.error = response.pkt === "error" ? response.fn : null;
119
-
120
- return response;
121
- }
122
-
123
- let linkResponse = parseResponse(message)
124
- debug(">>>>>>>> Received response msg: %s, response: %s, rinfo: %s", message, linkResponse, rinfo);
125
- if (linkResponse.error === "nonRegistered" && !self.awaitRegistrration) {
126
- console.warn("Your device is not registered, please accept registration on the Link devices")
127
- self.register(() => { });
128
- }
129
-
130
- if (linkResponse.msg === "success" && linkResponse.pairType) {
131
- self.awaitRegistrration = false;
132
- }
133
-
134
- debug("Link response fn", linkResponse.fn)
135
- if (linkResponse.fn === "on") {
136
- self.emit("deviceTurnedOn", linkResponse.room, linkResponse.dev);
137
- }
138
-
139
- if (linkResponse.fn === "off") {
140
- self.emit("deviceTurnedOff", linkResponse.room, linkResponse.dev);
141
- }
142
-
143
-
144
- debug(self.responseListeners);
145
- var responseListenerData = self.responseListeners.get(linkResponse.trans);
146
- if (!responseListenerData) {
147
- debug("We haven't got anyone to respond to, ignoring the message")
148
- return;
149
- }
150
-
151
-
152
- responseListenerData.listener(
153
- linkResponse.error ? linkResponse.error : null,
154
- linkResponse.fn,
155
- );
156
-
157
- self.responseListeners.delete(linkResponse.trans);
158
- });
159
-
160
- this.receiveSocket.on("listening", function () {
161
- var address = self.receiveSocket.address();
162
- debug("Receiver socket listening " + address.address + ":" + address.port);
163
-
164
- self.send('@H', (code, err) => {
165
- if (err) {
166
- console.log('code', code, 'error', err)
167
- return
168
- }
169
-
170
- self.initialiseConfiguration(callback);
171
- })
172
- });
173
-
174
- this.sendSocket.bind();
175
- this.sendSocket.on('listening', () => {
176
- debug("Send socket is ready")
177
- debug("Setting up receiver socket")
178
- //Bind to the receive port
179
- self.receiveSocket.bind(9761);
180
- })
181
-
182
-
183
- process.on('SIGINT', () => {
184
- self.stop();
185
- })
186
- }
187
-
188
- stop() {
189
- debug("Stopping server sockets")
190
- this.sendSocket.close();
191
- this.receiveSocket.close();
192
- }
193
-
194
- initialiseConfiguration(callback: (config: any, error: string) => void) {
195
- //Check config
196
- if (!this.config.host) {
197
- this.config.host = "web.trustsmartcloud.com"
198
- }
199
-
200
- if (!this.config.email || !this.config.pin) {
201
- callback(null, "No email or pin specified. The server configuration (rooms, devices, etc.) cannot be obtained");
202
- this.stop();
203
- }
204
- else {
205
- this.getConfiguration(this.config.email, this.config.pin, this.config.host, callback)
206
- }
207
- }
208
-
209
-
210
- /**
211
- * Register this device with the Wi-Fi Link
212
- *
213
- * @param Function callback The callback function
214
- *
215
- * @return void
216
- */
217
- register(callback: any) {
218
- this.awaitRegistrration = true;
219
- this.sendUdp("!F*p", callback);
220
- }
221
-
222
- /**
223
- * Request energy
224
- *
225
- * @param Function callback The callback function
226
- *
227
- * @return void
228
- */
229
- requestEnergy(callback: any) {
230
- this.sendUdp("@?\0", function (error: any, content: any) {
231
- if (error) {
232
- //Send error back
233
- callback(error);
234
- } else {
235
- //Determine if this is the energy monitor
236
- //ID,?W=current,max,today,yesterday (all kwh)
237
- var values = content.substring(3).split(",");
238
- callback(undefined, {
239
- current: parseInt(values[0], 10),
240
- max: parseInt(values[1], 10),
241
- today: parseInt(values[2], 10),
242
- yesterday: parseInt(values[3], 10)
243
- });
244
- }
245
- });
246
- }
247
-
248
- /**
249
- * Turn a device off
250
- *
251
- * @param integer roomId The room ID
252
- * @param integer deviceId The device ID
253
- * @param Function callback The callback for if there are any errors
254
- *
255
- * @return void
256
- */
257
- turnDeviceOff(roomId: string, deviceId: string, callback?: any) {
258
- var state = "0";
259
- this.exec("!R" + roomId + "D" + deviceId + "F" + state + "|\0", callback);
260
- }
261
-
262
- /**
263
- * Turn a device on
264
- *
265
- * @param integer roomId The room ID
266
- * @param integer deviceId The device ID
267
- * @param Function callback The callback for if there are any errors
268
- *
269
- * @return void
270
- */
271
- turnDeviceOn(roomId: string, deviceId: string, callback?: any) {
272
- var state = "1";
273
- this.exec("!R" + roomId + "D" + deviceId + "F" + state + "|\0", callback);
274
- }
275
-
276
- /**
277
- * Open a device
278
- *
279
- * @param integer roomId The room ID
280
- * @param integer deviceId The device ID
281
- * @param Function callback The callback for if there are any errors
282
- *
283
- * @return void
284
- */
285
- openDevice(roomId: string, deviceId: string, callback: any) {
286
- var state = ">";
287
- this.exec("!R" + roomId + "D" + deviceId + "F" + state + "|\0", callback);
288
- }
289
-
290
- /**
291
- * Close a device
292
- *
293
- * @param integer roomId The room ID
294
- * @param integer deviceId The device ID
295
- * @param Function callback The callback for if there are any errors
296
- *
297
- * @return void
298
- */
299
- closeDevice(roomId: string, deviceId: string, callback: any) {
300
- var state = "<";
301
- this.exec("!R" + roomId + "D" + deviceId + "F" + state + "|\0", callback);
302
- }
303
-
304
- /**
305
- * Stop a device
306
- *
307
- * @param integer roomId The room ID
308
- * @param integer deviceId The device ID
309
- * @param Function callback The callback for if there are any errors
310
- *
311
- * @return void
312
- */
313
- stopDevice(roomId: string, deviceId: string, callback: any) {
314
- var state = "^";
315
- this.exec("!R" + roomId + "D" + deviceId + "F" + state + "|\0", callback);
316
- }
317
-
318
- /**
319
- * Turn all devices in a room off
320
- *
321
- * @param integer roomId The room ID
322
- * @param Function callback The callback for if there are any errors
323
- *
324
- * @return void
325
- */
326
- turnRoomOff(roomId: string, callback: any) {
327
- this.exec("!R" + roomId + "Fa\0", callback);
328
- }
329
-
330
- /**
331
- * Set the dim percentage of a device
332
- *
333
- * @param integer roomId The room ID
334
- * @param integer deviceId The device ID
335
- * @param integer dimPercentage The percentage to set the device dim
336
- * @param Function callback The callback for if there are any errors
337
- *
338
- * @return void
339
- */
340
- setDeviceDim(roomId: string, deviceId: string, dimPercentage: number, callback: any) {
341
- var dimAmount = dimPercentage * 0.32; //Dim is on a scale from 0 to 32
342
-
343
- if (dimAmount === 0) {
344
- this.turnDeviceOff(roomId, deviceId, callback);
345
- } else {
346
- this.exec("!R" + roomId + "D" + deviceId + "FdP" + dimAmount + "|\0", callback);
347
- }
348
- }
349
-
350
- /**
351
- * Get message code
352
- *
353
- * @return string
354
- */
355
- private getTransactionNumber(): number {
356
- return this.currentTransactionNumber;
357
- }
358
-
359
-
360
- private exec(...args: any[]) {
361
- // Check if the queue has a reasonable size
362
- if (this.queue.length > 100) {
363
- this.queue.pop();
364
- }
365
-
366
- debug("Ading to queue: " + args.join(" "));
367
- this.queue.push(args);
368
- this.process();
369
- };
370
-
371
- private send(cmd: string, callback: (code: any, err: any) => void) {
372
- this.sendUdp(cmd, callback);
373
- //if (callback) callback();
374
- };
375
- /**
376
- * Send a message over udp
377
- *
378
- * @param string message The message to send
379
- * @param Function callback The callback for if there are any errors
380
- *
381
- * @return void
382
- */
383
- private sendUdp(message: string, callback: any) {
384
- //Add to message
385
- const transactionNumber = this.getTransactionNumber();
386
- //Prepend code to message
387
- message = `${transactionNumber},${message}`;
388
-
389
- debug(`[${this.config.ip}][trans: ${transactionNumber}] Sending message: ${message}`);
390
-
391
- //Create buffer from message
392
- const messageBuffer = Buffer.from(message, 'utf-8');
393
-
394
- this.sendSocket.setBroadcast(true);
395
-
396
- debug("Callback for message: " + message, callback);
397
- //Add listener
398
- if (callback) {
399
- debug("Registering call back with transaction number: " + transactionNumber);
400
- this.responseListeners.set(transactionNumber, {
401
- time: new Date().getTime(),
402
- listener: callback
403
- });
404
-
405
- debug(this.responseListeners)
406
-
407
- // Expire request, trigger retry
408
- setTimeout(() => {
409
- const listener = this.responseListeners.get(transactionNumber);
410
- if (listener) {
411
- debug("The listener is still there, triggering error");
412
- this.responseListeners.delete(transactionNumber);
413
- callback("ERR:EXPIRED", undefined);
414
- }
415
- }, 1000);
416
- }
417
-
418
- //Broadcast the message
419
- this.sendSocket.send(messageBuffer, 0, messageBuffer.length, 9760, this.config.ip);
420
- }
421
-
422
- private process() {
423
- debug("Checking queue")
424
- if (this.queue.length === 0) return;
425
- if (!this.ready) return;
426
- var self = this;
427
- this.ready = false;
428
- debug("Processing queue...");
429
- debug("Items in the queue", this.queue.length);
430
- this.send.apply(this, this.queue.shift());
431
- setTimeout(function () {
432
- self.ready = true;
433
- self.process();
434
- }, this.timeout);
435
- };
436
-
437
-
438
- /**
439
- * Parser to get de devices from https POST
440
- */
441
- getDevices(roomsString: string, devicesString: string, typesString: string, callback: (object: any, lw: this) => void) {
442
-
443
- var nrRooms = 8;
444
- var nrDevicesPerRoom = 10;
445
-
446
- var tempRS = roomsString;
447
- var tempDS = devicesString;
448
- var tempTS = typesString;
449
- var deviceCounter = 0;
450
- for (var i = 0; i < nrRooms; i++) {
451
- var rId = i + 1;
452
-
453
- tempRS = tempRS.substring(tempRS.indexOf('\"') + 1);
454
- var rName = tempRS.substring(0, tempRS.indexOf('\"'));
455
- tempRS = tempRS.substring(tempRS.indexOf('\"') + 1);
456
- //console.log("room=" + rName);
457
-
458
- for (var j = 0; j < nrDevicesPerRoom; j++) {
459
- var dId = j + 1;
460
-
461
- tempDS = tempDS.substring(tempDS.indexOf('\"') + 1);
462
- var dName = tempDS.substring(0, tempDS.indexOf('\"'));
463
- tempDS = tempDS.substring(tempDS.indexOf('\"') + 1);
464
- //console.log("devices=" + dName);
465
-
466
- tempTS = tempTS.substring(tempTS.indexOf('\"') + 1);
467
- var dType = tempTS.substring(0, tempTS.indexOf('\"'));
468
- tempTS = tempTS.substring(tempTS.indexOf('\"') + 1);
469
- //console.log("devices=" + deviceName + " type=" + dType);
470
-
471
- // Get device types
472
- // O: On/Off Switch
473
- // D: Dimmer
474
- // R: Radiator(s)
475
- // P: Open/Close
476
- // I: Inactive (i.e. not configured)
477
- // m: Mood (inactive)
478
- // M: Mood (active)
479
- // o: All Off
480
- if (dType == "O" || dType == "D") {
481
- this.devices.push({
482
- roomId: rId, roomName: rName,
483
- deviceId: dId, deviceName: dName,
484
- deviceType: dType
485
- });
486
- //console.log("devices=" + deviceName + " type=" + deviceType);
487
- deviceCounter += 1;
488
- }
489
- }
490
- }
491
-
492
- if (callback) callback(this.devices, this);
493
-
494
- //console.log(this.devices);
495
- }
496
-
497
- parseRooms(lightwaveResponse: any, callback: any) {
498
- debug('Parsing lightwaveResponse: ',
499
- lightwaveResponse.content.estates[0].locations[0].zones[0].rooms[0].devices);
500
-
501
- var home = lightwaveResponse.content.estates[0].locations[0].zones[0];
502
-
503
- for (var i = 0; i < home.rooms.length; i++) {
504
- var r = home.rooms[i];
505
-
506
- debug("Room " + r.name + " with " + r.devices.length + " devices");
507
-
508
- // Get device types
509
- // O: On/Off Switch
510
- // D: Dimmer
511
- // R: Radiator(s)
512
- // P: Open/Close
513
- // I: Inactive (i.e. not configured)
514
- // m: Mood (inactive)
515
- // M: Mood (active)
516
- // o: All Off
517
- var deviceTypeMapping = {
518
- 1: 'L',
519
- 2: 'D',
520
- 3: 'P'
521
- }
522
-
523
- for (var j = 0; j < r.devices.length; j++) {
524
- var d = r.devices[j];
525
-
526
- const device = <LightwaveRFDevice>{
527
- roomId: r.room_number,
528
- roomName: r.name,
529
- deviceId: d.device_number,
530
- deviceName: d.name,
531
- deviceType: d.device_type_id
532
- }
533
-
534
- this.devices.push(device);
535
- }
536
- }
537
-
538
- debug('Devices:', this.devices)
539
-
540
- callback(this.devices, null);
541
- };
542
-
543
- /**
544
- * Connect to the server and obtain the configuration
545
- */
546
- getConfiguration(email: string, pin: string, manager_host: string, callback: any) {
547
- // An object of options to indicate where to post to
548
-
549
- debug('Getting rooms from LightWave');
550
- var self = this;
551
- var host = 'https://control-api.lightwaverf.com';
552
- var json = rp.defaults({
553
- json: true
554
- });
555
-
556
- var auth: RequestAPI<rp.RequestPromise<any>, rp.RequestPromiseOptions, RequiredUriUrl>, token;
557
- json.get(host + '/v1/user?password=' + pin + '&username=' + email)
558
- .then(function (res) {
559
- return json.get(host + '/v1/auth?application_key=' + res.application_key)
560
- })
561
- .then(function (res) {
562
- token = res.token;
563
- auth = json.defaults({
564
- headers: {
565
- 'X-LWRF-token': token,
566
- 'X-LWRF-platform': 'ios',
567
- 'X-LWRF-skin': 'lightwaverf'
568
- }
569
- });
570
-
571
- return auth.get(host + '/v1/device_type?nested=1');
572
- })
573
- .then(function (res) {
574
- return auth.get(host + '/v1/user_profile?nested=1')
575
- })
576
- .then(function (res) {
577
- self.parseRooms(res, callback);
578
- });
579
- }
580
-
581
- }
582
-
583
- util.inherits(LightwaveRF, events.EventEmitter);
584
-
585
-
586
- export default LightwaveRF;