@evops/lightwaverf 1.0.2 → 1.0.4

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.
@@ -22,7 +22,10 @@ export declare class LightwaveRFClient extends events.EventEmitter implements Li
22
22
  model?: string;
23
23
  uptime?: number;
24
24
  version?: string;
25
- constructor(debug: Debugger, ip?: string);
25
+ discoverLinkIp: boolean;
26
+ constructor(debug: Debugger, ip?: string, { discoverLinkIp }?: {
27
+ discoverLinkIp?: boolean;
28
+ });
26
29
  connect(): Promise<void>;
27
30
  disconnect(): Promise<[void, void]>;
28
31
  private checkRegistration;
@@ -19,15 +19,17 @@ class LightwaveRFClient extends events_1.default.EventEmitter {
19
19
  messageProcessors = [];
20
20
  debug;
21
21
  transactionListeners = new Map();
22
- delay = 125;
22
+ delay = 800;
23
23
  serial;
24
24
  mac;
25
25
  model;
26
26
  uptime;
27
27
  version;
28
- constructor(debug, ip = "255.255.255.255") {
28
+ discoverLinkIp;
29
+ constructor(debug, ip = "255.255.255.255", { discoverLinkIp = true } = {}) {
29
30
  super();
30
31
  this.ip = ip;
32
+ this.discoverLinkIp = discoverLinkIp;
31
33
  this.debug = debug.extend("client");
32
34
  this.senderSocket = dgram_1.default.createSocket("udp4");
33
35
  this.senderSocket.unref();
@@ -78,18 +80,18 @@ class LightwaveRFClient extends events_1.default.EventEmitter {
78
80
  });
79
81
  }
80
82
  send(message, callback) {
81
- const transaction = Math.round(Math.random() * Math.pow(10, 8));
82
- const messageWithTransaction = `${transaction},${message}`;
83
- const transactionDebug = this.debug.extend("tx:" + transaction);
84
- this.transactionListeners.set(transaction, {
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, {
85
87
  message: message,
86
88
  debug: transactionDebug,
87
89
  delay: this.delay,
88
90
  callback: callback,
89
91
  });
90
- this.debug("Queueing message: %s", messageWithTransaction);
92
+ this.debug("[%d] Queueing message: %s", Date.now(), messageWithTransaction);
91
93
  this.commandQueue.add({
92
- id: transaction,
94
+ id: transactionId,
93
95
  message: messageWithTransaction,
94
96
  debug: transactionDebug,
95
97
  callback,
@@ -98,26 +100,31 @@ class LightwaveRFClient extends events_1.default.EventEmitter {
98
100
  }
99
101
  async processSendQueue() {
100
102
  if (this.commandQueue.busy) {
101
- setTimeout(this.processSendQueue.bind(this), this.delay);
103
+ this.debug(`The queue is busy, will wait to process`);
102
104
  return;
103
105
  }
104
106
  const command = this.commandQueue.next();
105
107
  if (!command)
106
108
  return;
109
+ this.debug(`Processing command: %o`, { command });
107
110
  this.commandQueue.busy = true;
108
111
  const transactionListener = this.transactionListeners.get(command.id);
109
112
  const originalCallback = transactionListener.callback;
110
113
  transactionListener.callback = (transaction, error) => {
111
- this.commandQueue.busy = false;
112
114
  originalCallback?.(transaction, error);
113
- setTimeout(this.processSendQueue.bind(this), this.delay);
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);
114
120
  };
115
121
  // If we didn't hear back within the timeout
116
122
  setTimeout(() => {
117
123
  const listener = this.transactionListeners.get(command.id);
118
124
  if (!listener)
119
125
  return;
120
- originalCallback?.(null, new Error("Execution expired"));
126
+ originalCallback?.(null, new Error("Execution expired " + command.id));
127
+ this.commandQueue.busy = false;
121
128
  this.transactionListeners.delete(command.id);
122
129
  this.processSendQueue();
123
130
  }, 5000);
@@ -144,15 +151,14 @@ class LightwaveRFClient extends events_1.default.EventEmitter {
144
151
  if (messageProcessor.canProcess(message)) {
145
152
  this.debug("Message can be processed by %s", messageProcessor.constructor.name);
146
153
  const lightwaveMessage = messageProcessor.process(message);
147
- this.processLightwaveMessage(lightwaveMessage);
154
+ this.processLightwaveMessage(lightwaveMessage, remoteInfo);
148
155
  return;
149
156
  }
150
157
  }
151
158
  throw "Message cannot be processed";
152
159
  }
153
- processLightwaveMessage(lightwaveMessage) {
160
+ processLightwaveMessage(lightwaveMessage, remoteInfo) {
154
161
  this.debug("Processing lightwave message: %o", lightwaveMessage);
155
- this.debug("Current listeners: %o", this.transactionListeners);
156
162
  this.debug("Link response fn", lightwaveMessage.fn);
157
163
  // update info from link
158
164
  if (lightwaveMessage.serial) {
@@ -167,8 +173,8 @@ class LightwaveRFClient extends events_1.default.EventEmitter {
167
173
  if (lightwaveMessage.uptime) {
168
174
  this.uptime = lightwaveMessage.uptime;
169
175
  }
170
- if (lightwaveMessage.ip) {
171
- this.ip = lightwaveMessage.ip;
176
+ if (this.discoverLinkIp) {
177
+ this.ip = remoteInfo.address;
172
178
  }
173
179
  if (lightwaveMessage.fw) {
174
180
  this.version ??= lightwaveMessage.fw;
@@ -194,20 +200,21 @@ class LightwaveRFClient extends events_1.default.EventEmitter {
194
200
  if (!listener)
195
201
  return;
196
202
  if (lightwaveMessage.error?.match(/^ERR,6,/)) {
197
- listener.delay = listener.delay * 2;
198
- const msg = `${lightwaveMessage.id},!${listener.message}`;
203
+ // Storing delay on a message level provides the ability to implement
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}`;
199
207
  this.debug("message errorred, retrying: %o, %o with delay: %o", msg, listener, listener.delay);
200
208
  setTimeout(() => {
201
209
  this.exec(msg);
202
- }, Math.round(listener.delay));
210
+ }, Math.round(listener.delay * 2));
203
211
  return;
204
212
  }
205
- this.commandQueue.busy = false;
206
213
  if (listener) {
207
214
  listener.debug("Found transaction listener");
208
- listener.callback(lightwaveMessage, null);
209
- listener.debug("Transaction completed, removing listener");
215
+ listener.debug("[%d] Transaction completed, removing listener", Date.now());
210
216
  this.transactionListeners.delete(lightwaveMessage.id);
217
+ listener.callback(lightwaveMessage, null);
211
218
  }
212
219
  else {
213
220
  this.debug("Listener not found for message: %o", lightwaveMessage);
package/.dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ declare class LightwaveRFConfiguration {
12
12
  host?: any;
13
13
  email?: any;
14
14
  pin?: any;
15
+ linkDisplayUpdates?: boolean;
15
16
  }
16
17
  declare interface ILightwaveRF {
17
18
  on(event: "deviceTurnedOn", listener: (roomId: number, deviceId: number) => void): this;
@@ -42,7 +43,8 @@ export default class LightwaveRF extends EventEmitter<LightwaveEvents> implement
42
43
  lwClient: LightwaveRFClient;
43
44
  lwAccount: LightwaveAccount;
44
45
  debug: debug.Debugger;
45
- constructor({ email, pin, ip, timeout }: LightwaveRFConfiguration);
46
+ private linkDisplayUpdates;
47
+ constructor({ email, pin, ip, timeout, linkDisplayUpdates, }: LightwaveRFConfiguration);
46
48
  get serial(): string | undefined;
47
49
  get mac(): string | undefined;
48
50
  get uptime(): number | undefined;
package/.dist/index.js CHANGED
@@ -17,6 +17,7 @@ class LightwaveRFConfiguration {
17
17
  host;
18
18
  email;
19
19
  pin;
20
+ linkDisplayUpdates;
20
21
  }
21
22
  /** * LightwaveRF API
22
23
  *
@@ -37,10 +38,12 @@ class LightwaveRF extends events_1.EventEmitter {
37
38
  lwClient;
38
39
  lwAccount;
39
40
  debug;
40
- constructor({ email, pin, ip, timeout }) {
41
+ linkDisplayUpdates;
42
+ constructor({ email, pin, ip, timeout, linkDisplayUpdates = true, }) {
41
43
  super();
42
44
  this.setMaxListeners(255);
43
45
  this.debug = (0, debug_1.default)("lightwave");
46
+ this.linkDisplayUpdates = linkDisplayUpdates;
44
47
  this.debug("Initialising LightwaveRF Client");
45
48
  this.lwClient = new LightwaveRFClient_1.LightwaveRFClient(this.debug, ip);
46
49
  this.lwClient.once("ready", () => {
@@ -83,14 +86,41 @@ class LightwaveRF extends events_1.EventEmitter {
83
86
  }));
84
87
  }
85
88
  async turnOn({ roomId, deviceId, roomName, deviceName }) {
86
- this.lwClient.send(`!F1R${roomId}D${deviceId}|${roomName} ${deviceName}|Turn on|`);
89
+ const { promise, resolve, reject } = Promise.withResolvers();
90
+ const linkDisplayUpdate = this.linkDisplayUpdates
91
+ ? `|${roomName} ${deviceName}|Turn on|`
92
+ : "";
93
+ this.lwClient.send(`!F1R${roomId}D${deviceId}${linkDisplayUpdate}`, (_, error) => {
94
+ if (error)
95
+ return reject(error);
96
+ resolve();
97
+ });
98
+ return promise;
87
99
  }
88
100
  async turnOff({ roomId, deviceId, roomName, deviceName }) {
89
- this.lwClient.send(`!F0R${roomId}D${deviceId}|${roomName} ${deviceName}|Turn off|`);
101
+ const { promise, resolve, reject } = Promise.withResolvers();
102
+ const linkDisplayUpdate = this.linkDisplayUpdates
103
+ ? `|${roomName} ${deviceName}|Turn off|`
104
+ : "";
105
+ this.lwClient.send(`!F0R${roomId}D${deviceId}${linkDisplayUpdate}`, (_, error) => {
106
+ if (error)
107
+ return reject(error);
108
+ resolve();
109
+ });
110
+ return promise;
90
111
  }
91
112
  async dim({ roomId, deviceId, roomName, deviceName }, percentage) {
113
+ const { promise, resolve, reject } = Promise.withResolvers();
92
114
  const lwDim = Math.round(percentage * 0.32);
93
- this.lwClient.send(`!FdP${lwDim}R${roomId}D${deviceId}|${roomName} ${deviceName}|Dim to ${percentage}%|`);
115
+ const linkDisplayUpdate = this.linkDisplayUpdates
116
+ ? `|${roomName} ${deviceName}|Dim to ${percentage}%|`
117
+ : "";
118
+ this.lwClient.send(`!FdP${lwDim}R${roomId}D${deviceId}${linkDisplayUpdate}`, (_, error) => {
119
+ if (error)
120
+ return reject(error);
121
+ resolve();
122
+ });
123
+ return promise;
94
124
  }
95
125
  async connect() {
96
126
  return this.lwClient.connect();
@@ -22,22 +22,31 @@ const _1 = __importDefault(require("."));
22
22
  // const devices = await lw.getDevices();
23
23
  await lw.connect();
24
24
  await lw.ensureRegistration();
25
+ await lw.lwClient.disconnect();
25
26
  });
26
27
  (0, vitest_1.it)("should turn device on", async () => {
27
28
  const lw = new _1.default({
28
29
  email: "some@user.com",
29
30
  pin: "1234",
31
+ // Disabling link display updates as they cause buffer issues in the link
32
+ // device
33
+ linkDisplayUpdates: true,
30
34
  });
31
35
  // const devices = await lw.getDevices();
32
36
  await lw.connect();
33
37
  // await lw.ensureRegistration();
34
38
  const devices = await lw.getDevices();
35
39
  const wallLamps = devices?.find((d) => {
36
- return d.deviceName === "Wall lamps";
40
+ return d.deviceName === "Table lamp";
37
41
  });
38
42
  if (!wallLamps) {
39
- throw new Error("Could not find wall lamps in the config");
43
+ throw new Error("Could not find table lamp in the config");
40
44
  }
41
- lw.turnOff(wallLamps);
42
- });
45
+ for (let i = 0; i < 5; i++) {
46
+ console.debug("Turning device on and off", i);
47
+ await lw.turnOn(wallLamps);
48
+ await lw.turnOff(wallLamps);
49
+ }
50
+ await lw.lwClient.disconnect();
51
+ }, 30000);
43
52
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@evops/lightwaverf",
3
3
  "description": "Lightwave RF client library",
4
- "version": "1.0.2",
4
+ "version": "1.0.4",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./.dist/index.d.ts",
package/vitest.config.ts CHANGED
@@ -6,7 +6,7 @@ export default defineConfig({
6
6
  environment: "node",
7
7
  printConsoleTrace: true,
8
8
  env: {
9
- DEBUG: "lightwave*",
9
+ // DEBUG: "lightwave*",
10
10
  },
11
11
  },
12
12
  });