@evops/lightwaverf 1.0.4 → 1.0.5
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/.dist/LightwaveCommandQueue.d.ts +39 -0
- package/.dist/LightwaveCommandQueue.js +146 -0
- package/.dist/LightwaveRFClient.d.ts +2 -4
- package/.dist/LightwaveRFClient.js +24 -81
- package/.dist/index.d.ts +2 -1
- package/.dist/index.js +18 -37
- package/.dist/index.test.js +15 -9
- package/package.json +1 -1
- package/vitest.config.ts +1 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Debugger } from "debug";
|
|
2
|
+
export interface QueuedTransaction {
|
|
3
|
+
id: number;
|
|
4
|
+
originalMessage: string;
|
|
5
|
+
message: string;
|
|
6
|
+
resolve: (value: TransactionResponse) => void;
|
|
7
|
+
reject: (error: Error) => void;
|
|
8
|
+
timeoutId?: ReturnType<typeof setTimeout>;
|
|
9
|
+
retryTimeoutId?: ReturnType<typeof setTimeout>;
|
|
10
|
+
delay: number;
|
|
11
|
+
completed: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface TransactionResponse {
|
|
14
|
+
id: number;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
export interface LightwaveCommandQueueOptions {
|
|
18
|
+
debug: Debugger;
|
|
19
|
+
delay?: number;
|
|
20
|
+
timeout?: number;
|
|
21
|
+
onExecute: (message: string) => void;
|
|
22
|
+
}
|
|
23
|
+
export declare class LightwaveCommandQueue {
|
|
24
|
+
private queue;
|
|
25
|
+
private transactions;
|
|
26
|
+
private busy;
|
|
27
|
+
private debug;
|
|
28
|
+
private delay;
|
|
29
|
+
private timeout;
|
|
30
|
+
private onExecute;
|
|
31
|
+
constructor(options: LightwaveCommandQueueOptions);
|
|
32
|
+
send(message: string | Buffer): Promise<TransactionResponse>;
|
|
33
|
+
handleResponse(response: TransactionResponse): void;
|
|
34
|
+
handleRetryableError(transactionId: number): void;
|
|
35
|
+
destroy(): void;
|
|
36
|
+
private processQueue;
|
|
37
|
+
private completeTransaction;
|
|
38
|
+
private failTransaction;
|
|
39
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LightwaveCommandQueue = void 0;
|
|
4
|
+
class LightwaveCommandQueue {
|
|
5
|
+
queue = [];
|
|
6
|
+
transactions = new Map();
|
|
7
|
+
busy = false;
|
|
8
|
+
debug;
|
|
9
|
+
delay;
|
|
10
|
+
timeout;
|
|
11
|
+
onExecute;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.debug = options.debug.extend("queue");
|
|
14
|
+
this.delay = options.delay ?? 800;
|
|
15
|
+
this.timeout = options.timeout ?? 10000;
|
|
16
|
+
this.onExecute = options.onExecute;
|
|
17
|
+
}
|
|
18
|
+
send(message) {
|
|
19
|
+
const transactionId = Math.round(Math.random() * Math.pow(10, 8));
|
|
20
|
+
const messageStr = typeof message === "string" ? message : message.toString("utf-8");
|
|
21
|
+
const messageWithTransaction = `${transactionId},${messageStr}`;
|
|
22
|
+
this.debug("[%d] Queueing message: %s", Date.now(), messageWithTransaction);
|
|
23
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
24
|
+
const transaction = {
|
|
25
|
+
id: transactionId,
|
|
26
|
+
originalMessage: messageStr,
|
|
27
|
+
message: messageWithTransaction,
|
|
28
|
+
resolve,
|
|
29
|
+
reject,
|
|
30
|
+
delay: this.delay,
|
|
31
|
+
completed: false,
|
|
32
|
+
};
|
|
33
|
+
this.queue.push(transaction);
|
|
34
|
+
this.transactions.set(transactionId, transaction);
|
|
35
|
+
this.processQueue();
|
|
36
|
+
return promise;
|
|
37
|
+
}
|
|
38
|
+
handleResponse(response) {
|
|
39
|
+
const transaction = this.transactions.get(response.id);
|
|
40
|
+
if (!transaction || transaction.completed) {
|
|
41
|
+
this.debug("No pending transaction found for id %d or already completed", response.id);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.debug("[%d] Transaction %d completed", Date.now(), response.id);
|
|
45
|
+
this.completeTransaction(transaction, response);
|
|
46
|
+
}
|
|
47
|
+
handleRetryableError(transactionId) {
|
|
48
|
+
const transaction = this.transactions.get(transactionId);
|
|
49
|
+
if (!transaction || transaction.completed) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Clear existing timeout
|
|
53
|
+
if (transaction.timeoutId) {
|
|
54
|
+
clearTimeout(transaction.timeoutId);
|
|
55
|
+
transaction.timeoutId = undefined;
|
|
56
|
+
}
|
|
57
|
+
// Exponential backoff, max 10 seconds
|
|
58
|
+
transaction.delay = Math.min(transaction.delay * 2, 10000);
|
|
59
|
+
const retryMessage = `${transactionId},${transaction.originalMessage}`;
|
|
60
|
+
this.debug("Message errored, retrying: %s with delay: %d", retryMessage, transaction.delay);
|
|
61
|
+
// Set timeout for retry
|
|
62
|
+
transaction.timeoutId = setTimeout(() => {
|
|
63
|
+
if (transaction.completed)
|
|
64
|
+
return;
|
|
65
|
+
this.debug("Retry timeout expired for transaction %d", transactionId);
|
|
66
|
+
this.failTransaction(transaction, new Error(`Retry timeout expired ${transactionId}`));
|
|
67
|
+
}, 5000 + transaction.delay * 2);
|
|
68
|
+
// Schedule retry
|
|
69
|
+
transaction.retryTimeoutId = setTimeout(() => {
|
|
70
|
+
if (transaction.completed)
|
|
71
|
+
return;
|
|
72
|
+
this.onExecute(retryMessage);
|
|
73
|
+
}, Math.round(transaction.delay * 2));
|
|
74
|
+
}
|
|
75
|
+
destroy() {
|
|
76
|
+
for (const transaction of this.transactions.values()) {
|
|
77
|
+
if (transaction.timeoutId) {
|
|
78
|
+
clearTimeout(transaction.timeoutId);
|
|
79
|
+
}
|
|
80
|
+
if (transaction.retryTimeoutId) {
|
|
81
|
+
clearTimeout(transaction.retryTimeoutId);
|
|
82
|
+
}
|
|
83
|
+
if (!transaction.completed) {
|
|
84
|
+
transaction.reject(new Error("Queue destroyed"));
|
|
85
|
+
transaction.completed = true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
this.transactions.clear();
|
|
89
|
+
this.queue = [];
|
|
90
|
+
this.busy = false;
|
|
91
|
+
}
|
|
92
|
+
processQueue() {
|
|
93
|
+
if (this.busy) {
|
|
94
|
+
this.debug("Queue is busy, waiting to process");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const transaction = this.queue.shift();
|
|
98
|
+
if (!transaction) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
this.debug("Processing transaction: %d", transaction.id);
|
|
102
|
+
this.busy = true;
|
|
103
|
+
// Set timeout for response
|
|
104
|
+
transaction.timeoutId = setTimeout(() => {
|
|
105
|
+
if (transaction.completed)
|
|
106
|
+
return;
|
|
107
|
+
this.debug("Transaction %d timed out", transaction.id);
|
|
108
|
+
this.failTransaction(transaction, new Error(`Execution expired ${transaction.id}`));
|
|
109
|
+
}, this.timeout);
|
|
110
|
+
this.onExecute(transaction.message);
|
|
111
|
+
}
|
|
112
|
+
completeTransaction(transaction, response) {
|
|
113
|
+
if (transaction.completed)
|
|
114
|
+
return;
|
|
115
|
+
transaction.completed = true;
|
|
116
|
+
if (transaction.timeoutId) {
|
|
117
|
+
clearTimeout(transaction.timeoutId);
|
|
118
|
+
}
|
|
119
|
+
if (transaction.retryTimeoutId) {
|
|
120
|
+
clearTimeout(transaction.retryTimeoutId);
|
|
121
|
+
}
|
|
122
|
+
this.transactions.delete(transaction.id);
|
|
123
|
+
transaction.resolve(response);
|
|
124
|
+
this.debug("Transaction %d completed, scheduling next in %dms", transaction.id, this.delay);
|
|
125
|
+
setTimeout(() => {
|
|
126
|
+
this.busy = false;
|
|
127
|
+
this.processQueue();
|
|
128
|
+
}, this.delay);
|
|
129
|
+
}
|
|
130
|
+
failTransaction(transaction, error) {
|
|
131
|
+
if (transaction.completed)
|
|
132
|
+
return;
|
|
133
|
+
transaction.completed = true;
|
|
134
|
+
if (transaction.timeoutId) {
|
|
135
|
+
clearTimeout(transaction.timeoutId);
|
|
136
|
+
}
|
|
137
|
+
if (transaction.retryTimeoutId) {
|
|
138
|
+
clearTimeout(transaction.retryTimeoutId);
|
|
139
|
+
}
|
|
140
|
+
this.transactions.delete(transaction.id);
|
|
141
|
+
transaction.reject(error);
|
|
142
|
+
this.busy = false;
|
|
143
|
+
this.processQueue();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.LightwaveCommandQueue = LightwaveCommandQueue;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Debugger } from "debug";
|
|
2
2
|
import events from "events";
|
|
3
|
-
import {
|
|
3
|
+
import { TransactionResponse } from "./LightwaveCommandQueue";
|
|
4
4
|
declare interface LightwaveRFClientInterface {
|
|
5
5
|
on(event: "ready", listener: (client: LightwaveRFClient) => void): this;
|
|
6
6
|
on(event: "deviceTurnedOn", listener: (room: number, device: number) => void): this;
|
|
@@ -15,7 +15,6 @@ export declare class LightwaveRFClient extends events.EventEmitter implements Li
|
|
|
15
15
|
private commandQueue;
|
|
16
16
|
private messageProcessors;
|
|
17
17
|
private debug;
|
|
18
|
-
private transactionListeners;
|
|
19
18
|
delay: number;
|
|
20
19
|
serial?: string;
|
|
21
20
|
mac?: string;
|
|
@@ -29,8 +28,7 @@ export declare class LightwaveRFClient extends events.EventEmitter implements Li
|
|
|
29
28
|
connect(): Promise<void>;
|
|
30
29
|
disconnect(): Promise<[void, void]>;
|
|
31
30
|
private checkRegistration;
|
|
32
|
-
send(message: string | Buffer
|
|
33
|
-
private processSendQueue;
|
|
31
|
+
send(message: string | Buffer): Promise<TransactionResponse>;
|
|
34
32
|
private exec;
|
|
35
33
|
private processRawMessage;
|
|
36
34
|
private processLightwaveMessage;
|
|
@@ -6,19 +6,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.LightwaveRFClient = void 0;
|
|
7
7
|
const dgram_1 = __importDefault(require("dgram"));
|
|
8
8
|
const events_1 = __importDefault(require("events"));
|
|
9
|
+
const LightwaveCommandQueue_1 = require("./LightwaveCommandQueue");
|
|
9
10
|
const LightwaveMessageProcessorForJson_1 = require("./LightwaveMessageProcessorForJson");
|
|
10
11
|
const LightwaveMessageProcessorForText_1 = __importDefault(require("./LightwaveMessageProcessorForText"));
|
|
11
|
-
const Queue_1 = require("./Queue");
|
|
12
12
|
const LIGHTWAVE_LINK_SEND_PORT = 9760;
|
|
13
13
|
const LIGHTWAVE_LINK_RECEIVE_PORT = 9761;
|
|
14
14
|
class LightwaveRFClient extends events_1.default.EventEmitter {
|
|
15
15
|
ip;
|
|
16
16
|
senderSocket;
|
|
17
17
|
receiverSocket;
|
|
18
|
-
commandQueue
|
|
18
|
+
commandQueue;
|
|
19
19
|
messageProcessors = [];
|
|
20
20
|
debug;
|
|
21
|
-
transactionListeners = new Map();
|
|
22
21
|
delay = 800;
|
|
23
22
|
serial;
|
|
24
23
|
mac;
|
|
@@ -35,6 +34,11 @@ class LightwaveRFClient extends events_1.default.EventEmitter {
|
|
|
35
34
|
this.senderSocket.unref();
|
|
36
35
|
this.receiverSocket = dgram_1.default.createSocket("udp4");
|
|
37
36
|
this.receiverSocket.unref();
|
|
37
|
+
this.commandQueue = new LightwaveCommandQueue_1.LightwaveCommandQueue({
|
|
38
|
+
debug: this.debug,
|
|
39
|
+
delay: this.delay,
|
|
40
|
+
onExecute: (message) => this.exec(message),
|
|
41
|
+
});
|
|
38
42
|
this.messageProcessors.push(new LightwaveMessageProcessorForJson_1.LightwaveMessageProcessorForJson(this.debug));
|
|
39
43
|
this.messageProcessors.push(new LightwaveMessageProcessorForText_1.default(this.debug));
|
|
40
44
|
}
|
|
@@ -63,74 +67,26 @@ class LightwaveRFClient extends events_1.default.EventEmitter {
|
|
|
63
67
|
async disconnect() {
|
|
64
68
|
this.receiverSocket.removeAllListeners();
|
|
65
69
|
this.senderSocket.removeAllListeners();
|
|
70
|
+
this.commandQueue.destroy();
|
|
66
71
|
const { promise: senderPromise, resolve: senderResolve } = Promise.withResolvers();
|
|
67
72
|
const { promise: receiverPromise, resolve: receiverResolve } = Promise.withResolvers();
|
|
68
73
|
this.senderSocket.close(senderResolve);
|
|
69
74
|
this.receiverSocket.close(receiverResolve);
|
|
70
75
|
return Promise.all([senderPromise, receiverPromise]);
|
|
71
76
|
}
|
|
72
|
-
checkRegistration() {
|
|
77
|
+
async checkRegistration() {
|
|
73
78
|
this.debug("Checking registration");
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
this.debug("Error: %o", error);
|
|
77
|
-
}
|
|
79
|
+
try {
|
|
80
|
+
const transaction = await this.send("@H");
|
|
78
81
|
this.debug(transaction);
|
|
79
|
-
this.emit("ready", this);
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
send(message, callback) {
|
|
83
|
-
const transactionId = Math.round(Math.random() * Math.pow(10, 8));
|
|
84
|
-
const messageWithTransaction = `${transactionId},${message}`;
|
|
85
|
-
const transactionDebug = this.debug.extend("tx:" + transactionId);
|
|
86
|
-
this.transactionListeners.set(transactionId, {
|
|
87
|
-
message: message,
|
|
88
|
-
debug: transactionDebug,
|
|
89
|
-
delay: this.delay,
|
|
90
|
-
callback: callback,
|
|
91
|
-
});
|
|
92
|
-
this.debug("[%d] Queueing message: %s", Date.now(), messageWithTransaction);
|
|
93
|
-
this.commandQueue.add({
|
|
94
|
-
id: transactionId,
|
|
95
|
-
message: messageWithTransaction,
|
|
96
|
-
debug: transactionDebug,
|
|
97
|
-
callback,
|
|
98
|
-
});
|
|
99
|
-
this.processSendQueue();
|
|
100
|
-
}
|
|
101
|
-
async processSendQueue() {
|
|
102
|
-
if (this.commandQueue.busy) {
|
|
103
|
-
this.debug(`The queue is busy, will wait to process`);
|
|
104
|
-
return;
|
|
105
82
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
this.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
transactionListener.callback = (transaction, error) => {
|
|
114
|
-
originalCallback?.(transaction, error);
|
|
115
|
-
this.debug("Transaction %d completed, scheduling next processing in %dms !!!!!!!!!!", transaction.id, this.delay);
|
|
116
|
-
setTimeout(() => {
|
|
117
|
-
this.commandQueue.busy = false;
|
|
118
|
-
this.processSendQueue();
|
|
119
|
-
}, this.delay);
|
|
120
|
-
};
|
|
121
|
-
// If we didn't hear back within the timeout
|
|
122
|
-
setTimeout(() => {
|
|
123
|
-
const listener = this.transactionListeners.get(command.id);
|
|
124
|
-
if (!listener)
|
|
125
|
-
return;
|
|
126
|
-
originalCallback?.(null, new Error("Execution expired " + command.id));
|
|
127
|
-
this.commandQueue.busy = false;
|
|
128
|
-
this.transactionListeners.delete(command.id);
|
|
129
|
-
this.processSendQueue();
|
|
130
|
-
}, 5000);
|
|
131
|
-
const transactionDebug = this.debug.extend(`transaction:${command?.id}`);
|
|
132
|
-
transactionDebug("Starting new transaction");
|
|
133
|
-
this.exec(command.message);
|
|
83
|
+
catch (error) {
|
|
84
|
+
this.debug("Error: %o", error);
|
|
85
|
+
}
|
|
86
|
+
this.emit("ready", this);
|
|
87
|
+
}
|
|
88
|
+
send(message) {
|
|
89
|
+
return this.commandQueue.send(message);
|
|
134
90
|
}
|
|
135
91
|
exec(message) {
|
|
136
92
|
if (message === undefined)
|
|
@@ -174,6 +130,7 @@ class LightwaveRFClient extends events_1.default.EventEmitter {
|
|
|
174
130
|
this.uptime = lightwaveMessage.uptime;
|
|
175
131
|
}
|
|
176
132
|
if (this.discoverLinkIp) {
|
|
133
|
+
this.debug(`Updating link ip to ${remoteInfo.address}`);
|
|
177
134
|
this.ip = remoteInfo.address;
|
|
178
135
|
}
|
|
179
136
|
if (lightwaveMessage.fw) {
|
|
@@ -196,28 +153,14 @@ class LightwaveRFClient extends events_1.default.EventEmitter {
|
|
|
196
153
|
if (lightwaveMessage.fn === "dim") {
|
|
197
154
|
this.emit("deviceDimmed", lightwaveMessage.room, lightwaveMessage.dev, Math.round((lightwaveMessage.param / 32) * 100));
|
|
198
155
|
}
|
|
199
|
-
|
|
200
|
-
if (!listener)
|
|
201
|
-
return;
|
|
156
|
+
// Handle retryable errors (ERR,6 = device busy)
|
|
202
157
|
if (lightwaveMessage.error?.match(/^ERR,6,/)) {
|
|
203
|
-
|
|
204
|
-
// exponential backoff, here the maximum is 10 seconds
|
|
205
|
-
listener.delay = Math.min(listener.delay * 2, 10000);
|
|
206
|
-
const msg = `${lightwaveMessage.id},${listener.message}`;
|
|
207
|
-
this.debug("message errorred, retrying: %o, %o with delay: %o", msg, listener, listener.delay);
|
|
208
|
-
setTimeout(() => {
|
|
209
|
-
this.exec(msg);
|
|
210
|
-
}, Math.round(listener.delay * 2));
|
|
158
|
+
this.commandQueue.handleRetryableError(lightwaveMessage.id);
|
|
211
159
|
return;
|
|
212
160
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
this.transactionListeners.delete(lightwaveMessage.id);
|
|
217
|
-
listener.callback(lightwaveMessage, null);
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
this.debug("Listener not found for message: %o", lightwaveMessage);
|
|
161
|
+
// Pass successful response to the queue
|
|
162
|
+
if (lightwaveMessage.id !== undefined) {
|
|
163
|
+
this.commandQueue.handleResponse(lightwaveMessage);
|
|
221
164
|
}
|
|
222
165
|
}
|
|
223
166
|
}
|
package/.dist/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ declare class LightwaveRFConfiguration {
|
|
|
13
13
|
email?: any;
|
|
14
14
|
pin?: any;
|
|
15
15
|
linkDisplayUpdates?: boolean;
|
|
16
|
+
discoverLinkIp?: boolean;
|
|
16
17
|
}
|
|
17
18
|
declare interface ILightwaveRF {
|
|
18
19
|
on(event: "deviceTurnedOn", listener: (roomId: number, deviceId: number) => void): this;
|
|
@@ -44,7 +45,7 @@ export default class LightwaveRF extends EventEmitter<LightwaveEvents> implement
|
|
|
44
45
|
lwAccount: LightwaveAccount;
|
|
45
46
|
debug: debug.Debugger;
|
|
46
47
|
private linkDisplayUpdates;
|
|
47
|
-
constructor({ email, pin, ip, timeout, linkDisplayUpdates, }: LightwaveRFConfiguration);
|
|
48
|
+
constructor({ email, pin, ip, timeout, linkDisplayUpdates, discoverLinkIp, }: LightwaveRFConfiguration);
|
|
48
49
|
get serial(): string | undefined;
|
|
49
50
|
get mac(): string | undefined;
|
|
50
51
|
get uptime(): number | undefined;
|
package/.dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ class LightwaveRFConfiguration {
|
|
|
18
18
|
email;
|
|
19
19
|
pin;
|
|
20
20
|
linkDisplayUpdates;
|
|
21
|
+
discoverLinkIp;
|
|
21
22
|
}
|
|
22
23
|
/** * LightwaveRF API
|
|
23
24
|
*
|
|
@@ -39,13 +40,15 @@ class LightwaveRF extends events_1.EventEmitter {
|
|
|
39
40
|
lwAccount;
|
|
40
41
|
debug;
|
|
41
42
|
linkDisplayUpdates;
|
|
42
|
-
constructor({ email, pin, ip, timeout, linkDisplayUpdates = true, }) {
|
|
43
|
+
constructor({ email, pin, ip, timeout, linkDisplayUpdates = true, discoverLinkIp = true, }) {
|
|
43
44
|
super();
|
|
44
45
|
this.setMaxListeners(255);
|
|
45
46
|
this.debug = (0, debug_1.default)("lightwave");
|
|
46
47
|
this.linkDisplayUpdates = linkDisplayUpdates;
|
|
47
48
|
this.debug("Initialising LightwaveRF Client");
|
|
48
|
-
this.lwClient = new LightwaveRFClient_1.LightwaveRFClient(this.debug, ip
|
|
49
|
+
this.lwClient = new LightwaveRFClient_1.LightwaveRFClient(this.debug, ip, {
|
|
50
|
+
discoverLinkIp,
|
|
51
|
+
});
|
|
49
52
|
this.lwClient.once("ready", () => {
|
|
50
53
|
this.debug("LightwaveRF ready");
|
|
51
54
|
});
|
|
@@ -86,63 +89,41 @@ class LightwaveRF extends events_1.EventEmitter {
|
|
|
86
89
|
}));
|
|
87
90
|
}
|
|
88
91
|
async turnOn({ roomId, deviceId, roomName, deviceName }) {
|
|
89
|
-
const { promise, resolve, reject } = Promise.withResolvers();
|
|
90
92
|
const linkDisplayUpdate = this.linkDisplayUpdates
|
|
91
93
|
? `|${roomName} ${deviceName}|Turn on|`
|
|
92
94
|
: "";
|
|
93
|
-
this.lwClient.send(`!F1R${roomId}D${deviceId}${linkDisplayUpdate}
|
|
94
|
-
if (error)
|
|
95
|
-
return reject(error);
|
|
96
|
-
resolve();
|
|
97
|
-
});
|
|
98
|
-
return promise;
|
|
95
|
+
await this.lwClient.send(`!F1R${roomId}D${deviceId}${linkDisplayUpdate}`);
|
|
99
96
|
}
|
|
100
97
|
async turnOff({ roomId, deviceId, roomName, deviceName }) {
|
|
101
|
-
const { promise, resolve, reject } = Promise.withResolvers();
|
|
102
98
|
const linkDisplayUpdate = this.linkDisplayUpdates
|
|
103
99
|
? `|${roomName} ${deviceName}|Turn off|`
|
|
104
100
|
: "";
|
|
105
|
-
this.lwClient.send(`!F0R${roomId}D${deviceId}${linkDisplayUpdate}
|
|
106
|
-
if (error)
|
|
107
|
-
return reject(error);
|
|
108
|
-
resolve();
|
|
109
|
-
});
|
|
110
|
-
return promise;
|
|
101
|
+
await this.lwClient.send(`!F0R${roomId}D${deviceId}${linkDisplayUpdate}`);
|
|
111
102
|
}
|
|
112
103
|
async dim({ roomId, deviceId, roomName, deviceName }, percentage) {
|
|
113
|
-
const { promise, resolve, reject } = Promise.withResolvers();
|
|
114
104
|
const lwDim = Math.round(percentage * 0.32);
|
|
115
105
|
const linkDisplayUpdate = this.linkDisplayUpdates
|
|
116
106
|
? `|${roomName} ${deviceName}|Dim to ${percentage}%|`
|
|
117
107
|
: "";
|
|
118
|
-
this.lwClient.send(`!FdP${lwDim}R${roomId}D${deviceId}${linkDisplayUpdate}
|
|
119
|
-
if (error)
|
|
120
|
-
return reject(error);
|
|
121
|
-
resolve();
|
|
122
|
-
});
|
|
123
|
-
return promise;
|
|
108
|
+
await this.lwClient.send(`!FdP${lwDim}R${roomId}D${deviceId}${linkDisplayUpdate}`);
|
|
124
109
|
}
|
|
125
110
|
async connect() {
|
|
126
111
|
return this.lwClient.connect();
|
|
127
112
|
}
|
|
128
113
|
async isRegistered() {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return resolve(!response?.error);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
114
|
+
const user = process.env.USER;
|
|
115
|
+
const response = await this.lwClient.send(`@H|Check registration|user:${user}|`);
|
|
116
|
+
return !response?.error;
|
|
135
117
|
}
|
|
136
118
|
async ensureRegistration() {
|
|
119
|
+
const user = process.env.USER;
|
|
120
|
+
const response = await this.lwClient.send(`@H|Check registration|user:${user}|`);
|
|
121
|
+
if (!response?.error) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
this.debug("We are not registered with the hub");
|
|
125
|
+
await this.lwClient.send("!F*p");
|
|
137
126
|
return new Promise((resolve) => {
|
|
138
|
-
const user = process.env.USER;
|
|
139
|
-
this.lwClient.send(`@H|Check registration|user:${user}|`, (response) => {
|
|
140
|
-
if (!response?.error) {
|
|
141
|
-
return resolve();
|
|
142
|
-
}
|
|
143
|
-
this.debug("We are not registered with the hub");
|
|
144
|
-
this.lwClient.send("!F*p");
|
|
145
|
-
});
|
|
146
127
|
this.lwClient.on("registered", resolve);
|
|
147
128
|
});
|
|
148
129
|
}
|
package/.dist/index.test.js
CHANGED
|
@@ -7,6 +7,7 @@ const fetch_vcr_1 = __importDefault(require("fetch-vcr"));
|
|
|
7
7
|
const vitest_1 = require("vitest");
|
|
8
8
|
const _1 = __importDefault(require("."));
|
|
9
9
|
(0, vitest_1.describe)("LightwaveRF", () => {
|
|
10
|
+
let lw;
|
|
10
11
|
(0, vitest_1.beforeAll)(() => {
|
|
11
12
|
fetch_vcr_1.default.configure({
|
|
12
13
|
fixturePath: __dirname + "/.fixtures",
|
|
@@ -14,8 +15,11 @@ const _1 = __importDefault(require("."));
|
|
|
14
15
|
});
|
|
15
16
|
global.fetch = fetch_vcr_1.default;
|
|
16
17
|
});
|
|
18
|
+
(0, vitest_1.afterEach)(async () => {
|
|
19
|
+
await lw.lwClient.disconnect();
|
|
20
|
+
});
|
|
17
21
|
(0, vitest_1.it)("should allow device linking", async () => {
|
|
18
|
-
|
|
22
|
+
lw = new _1.default({
|
|
19
23
|
email: "some@user.com",
|
|
20
24
|
pin: "1234",
|
|
21
25
|
});
|
|
@@ -25,7 +29,7 @@ const _1 = __importDefault(require("."));
|
|
|
25
29
|
await lw.lwClient.disconnect();
|
|
26
30
|
});
|
|
27
31
|
(0, vitest_1.it)("should turn device on", async () => {
|
|
28
|
-
|
|
32
|
+
lw = new _1.default({
|
|
29
33
|
email: "some@user.com",
|
|
30
34
|
pin: "1234",
|
|
31
35
|
// Disabling link display updates as they cause buffer issues in the link
|
|
@@ -36,17 +40,19 @@ const _1 = __importDefault(require("."));
|
|
|
36
40
|
await lw.connect();
|
|
37
41
|
// await lw.ensureRegistration();
|
|
38
42
|
const devices = await lw.getDevices();
|
|
39
|
-
const
|
|
40
|
-
|
|
43
|
+
const roomToInteractWith = "TV Room";
|
|
44
|
+
const lightToInteractWith = "Lights";
|
|
45
|
+
const light = devices?.find((d) => {
|
|
46
|
+
return (d.roomName === roomToInteractWith &&
|
|
47
|
+
d.deviceName === lightToInteractWith);
|
|
41
48
|
});
|
|
42
|
-
if (!
|
|
43
|
-
throw new Error(
|
|
49
|
+
if (!light) {
|
|
50
|
+
throw new Error(`Could not find ${lightToInteractWith} in the config`);
|
|
44
51
|
}
|
|
45
52
|
for (let i = 0; i < 5; i++) {
|
|
46
53
|
console.debug("Turning device on and off", i);
|
|
47
|
-
await lw.turnOn(
|
|
48
|
-
await lw.turnOff(
|
|
54
|
+
await lw.turnOn(light);
|
|
55
|
+
await lw.turnOff(light);
|
|
49
56
|
}
|
|
50
|
-
await lw.lwClient.disconnect();
|
|
51
57
|
}, 30000);
|
|
52
58
|
});
|
package/package.json
CHANGED