@evops/lightwaverf 0.0.9 → 1.0.0

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.
Files changed (48) hide show
  1. package/.dist/LightwaveAccount.d.ts +11 -0
  2. package/.dist/LightwaveAccount.js +99 -0
  3. package/.dist/LightwaveAccount.test.d.ts +1 -0
  4. package/.dist/LightwaveAccount.test.js +26 -0
  5. package/.dist/LightwaveDevice.d.ts +21 -0
  6. package/.dist/LightwaveDevice.js +25 -0
  7. package/{dist → .dist}/LightwaveMessageProcessor.d.ts +0 -1
  8. package/{dist/LightwaveTextMessageProcessor.d.ts → .dist/LightwaveMessageProcessorForJson.d.ts} +2 -3
  9. package/.dist/LightwaveMessageProcessorForJson.js +24 -0
  10. package/{dist/LightwaveJsonMessageProcessor.d.ts → .dist/LightwaveMessageProcessorForText.d.ts} +2 -3
  11. package/{dist/LightwaveTextMessageProcessor.js → .dist/LightwaveMessageProcessorForText.js} +7 -6
  12. package/.dist/LightwaveRFClient.d.ts +35 -0
  13. package/.dist/LightwaveRFClient.js +217 -0
  14. package/.dist/LightwaveRFClient.test.d.ts +1 -0
  15. package/.dist/LightwaveRFClient.test.js +30 -0
  16. package/{dist → .dist}/Queue.d.ts +1 -1
  17. package/{dist → .dist}/Queue.js +3 -5
  18. package/.dist/index.d.ts +58 -0
  19. package/.dist/index.js +120 -0
  20. package/.dist/index.test.d.ts +1 -0
  21. package/.dist/index.test.js +43 -0
  22. package/CHANGELOG.md +69 -0
  23. package/README.md +453 -2
  24. package/package.json +13 -7
  25. package/vitest.config.ts +12 -0
  26. package/dist/LightwaveAccount.d.ts +0 -13
  27. package/dist/LightwaveAccount.js +0 -81
  28. package/dist/LightwaveDevice.d.ts +0 -26
  29. package/dist/LightwaveDevice.js +0 -64
  30. package/dist/LightwaveJsonMessageProcessor.js +0 -24
  31. package/dist/LightwaveMessage.d.ts +0 -9
  32. package/dist/LightwaveMessage.js +0 -2
  33. package/dist/LightwaveRFClient.d.ts +0 -32
  34. package/dist/LightwaveRFClient.js +0 -202
  35. package/dist/index.d.ts +0 -98
  36. package/dist/index.js +0 -278
  37. package/src/LightwaveAccount.ts +0 -107
  38. package/src/LightwaveDevice.ts +0 -66
  39. package/src/LightwaveJsonMessageProcessor.ts +0 -29
  40. package/src/LightwaveMessageProcessor.ts +0 -8
  41. package/src/LightwaveRFClient.ts +0 -236
  42. package/src/LightwaveTextMessageProcessor.ts +0 -30
  43. package/src/LightwaveTransaction.ts +0 -9
  44. package/src/Queue.ts +0 -17
  45. package/src/index.ts +0 -332
  46. /package/{dist → .dist}/LightwaveMessageProcessor.js +0 -0
  47. /package/{dist → .dist}/LightwaveTransaction.d.ts +0 -0
  48. /package/{dist → .dist}/LightwaveTransaction.js +0 -0
package/.dist/index.js ADDED
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LightwaveDeviceType = void 0;
7
+ const debug_1 = __importDefault(require("debug"));
8
+ const events_1 = require("events");
9
+ const LightwaveAccount_1 = require("./LightwaveAccount");
10
+ const LightwaveRFClient_1 = require("./LightwaveRFClient");
11
+ var LightwaveDevice_1 = require("./LightwaveDevice");
12
+ Object.defineProperty(exports, "LightwaveDeviceType", { enumerable: true, get: function () { return LightwaveDevice_1.LightwaveDeviceType; } });
13
+ class LightwaveRFConfiguration {
14
+ timeout = 1000;
15
+ ip;
16
+ file;
17
+ host;
18
+ email;
19
+ pin;
20
+ }
21
+ /** * LightwaveRF API
22
+ *
23
+ * @param object config The config
24
+ *
25
+ * An instance of the LightwaveRF API
26
+ */
27
+ class LightwaveRF extends events_1.EventEmitter {
28
+ timeout = 1000;
29
+ queue = [];
30
+ ready = true;
31
+ awaitRegistrration = false;
32
+ currentTransactionNumber = 4674773;
33
+ devices = [];
34
+ messageCounter = 0;
35
+ config = {};
36
+ responseListeners = new Map();
37
+ lwClient;
38
+ lwAccount;
39
+ debug;
40
+ constructor({ email, pin, ip, timeout }) {
41
+ super();
42
+ this.setMaxListeners(255);
43
+ this.debug = (0, debug_1.default)("lightwave");
44
+ this.debug("Initialising LightwaveRF Client");
45
+ this.lwClient = new LightwaveRFClient_1.LightwaveRFClient(this.debug, ip);
46
+ this.lwClient.once("ready", () => {
47
+ this.debug("LightwaveRF ready");
48
+ });
49
+ this.lwAccount = new LightwaveAccount_1.LightwaveAccount(this.debug, email, pin);
50
+ this.timeout = timeout || 1000;
51
+ this.devices = [];
52
+ const self = this;
53
+ this.lwClient.on("deviceTurnedOn", function (roomId, deviceId) {
54
+ self.emit("deviceTurnedOn", roomId, deviceId);
55
+ });
56
+ this.lwClient.on("deviceTurnedOff", function (roomId, deviceId) {
57
+ self.emit("deviceTurnedOff", roomId, deviceId);
58
+ });
59
+ this.lwClient.on("deviceDimmed", function (roomId, deviceId, percentage) {
60
+ self.emit("deviceDimmed", roomId, deviceId, percentage);
61
+ });
62
+ }
63
+ get serial() {
64
+ return this.lwClient.serial;
65
+ }
66
+ get mac() {
67
+ return this.lwClient.mac;
68
+ }
69
+ get uptime() {
70
+ return this.lwClient.uptime;
71
+ }
72
+ get model() {
73
+ return this.lwClient.model;
74
+ }
75
+ get version() {
76
+ return this.lwClient.version;
77
+ }
78
+ async getDevices() {
79
+ return new Promise((resolve, reject) => this.lwAccount.getConfiguration((devices, error) => {
80
+ if (error)
81
+ return reject(error);
82
+ resolve(devices);
83
+ }));
84
+ }
85
+ async turnOn({ roomId, deviceId, roomName, deviceName }) {
86
+ this.lwClient.send(`!F1R${roomId}D${deviceId}|${roomName} ${deviceName}|Turn on|`);
87
+ }
88
+ async turnOff({ roomId, deviceId, roomName, deviceName }) {
89
+ this.lwClient.send(`!F0R${roomId}D${deviceId}|${roomName} ${deviceName}|Turn off|`);
90
+ }
91
+ async dim({ roomId, deviceId, roomName, deviceName }, percentage) {
92
+ const lwDim = Math.round(percentage * 0.32);
93
+ this.lwClient.send(`!FdP${lwDim}R${roomId}D${deviceId}|${roomName} ${deviceName}|Dim to ${percentage}%|`);
94
+ }
95
+ async connect() {
96
+ return this.lwClient.connect();
97
+ }
98
+ async isRegistered() {
99
+ return new Promise((resolve) => {
100
+ const user = process.env.USER;
101
+ this.lwClient.send(`@H|Check registration|user:${user}|`, (response) => {
102
+ return resolve(!response?.error);
103
+ });
104
+ });
105
+ }
106
+ async ensureRegistration() {
107
+ return new Promise((resolve) => {
108
+ const user = process.env.USER;
109
+ this.lwClient.send(`@H|Check registration|user:${user}|`, (response) => {
110
+ if (!response?.error) {
111
+ return resolve();
112
+ }
113
+ this.debug("We are not registered with the hub");
114
+ this.lwClient.send("!F*p");
115
+ });
116
+ this.lwClient.on("registered", resolve);
117
+ });
118
+ }
119
+ }
120
+ exports.default = LightwaveRF;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fetch_vcr_1 = __importDefault(require("fetch-vcr"));
7
+ const vitest_1 = require("vitest");
8
+ const _1 = __importDefault(require("."));
9
+ (0, vitest_1.describe)("LightwaveRF", () => {
10
+ (0, vitest_1.beforeAll)(() => {
11
+ fetch_vcr_1.default.configure({
12
+ fixturePath: __dirname + "/.fixtures",
13
+ mode: "playback",
14
+ });
15
+ global.fetch = fetch_vcr_1.default;
16
+ });
17
+ (0, vitest_1.it)("should allow device linking", async () => {
18
+ const lw = new _1.default({
19
+ email: "some@user.com",
20
+ pin: "1234",
21
+ });
22
+ // const devices = await lw.getDevices();
23
+ await lw.connect();
24
+ await lw.ensureRegistration();
25
+ });
26
+ (0, vitest_1.it)("should turn device on", async () => {
27
+ const lw = new _1.default({
28
+ email: "some@user.com",
29
+ pin: "1234",
30
+ });
31
+ // const devices = await lw.getDevices();
32
+ await lw.connect();
33
+ // await lw.ensureRegistration();
34
+ const devices = await lw.getDevices();
35
+ const wallLamps = devices?.find((d) => {
36
+ return d.deviceName === "Wall lamps";
37
+ });
38
+ if (!wallLamps) {
39
+ throw new Error("Could not find wall lamps in the config");
40
+ }
41
+ lw.turnOff(wallLamps);
42
+ });
43
+ });
package/CHANGELOG.md ADDED
@@ -0,0 +1,69 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2025-11-09
9
+
10
+ ### BREAKING CHANGES
11
+
12
+ #### Package Structure
13
+ - Changed output directory from `dist` to `.dist`
14
+ - Updated module exports to use new ES module format with explicit `types` and `default` fields
15
+ - Removed CommonJS `main` and `types` fields in favor of `exports` map
16
+ - Updated TypeScript target from older ECMAScript to ES2024
17
+
18
+ #### File Renames (Internal API)
19
+ - Renamed `LightwaveJsonMessageProcessor.ts` to `LightwaveMessageProcessorForJson.ts`
20
+ - Renamed `LightwaveTextMessageProcessor.ts` to `LightwaveMessageProcessorForText.ts`
21
+
22
+ #### Removed Features
23
+ - Removed `bin` directory and CLI executable
24
+
25
+ ### Added
26
+ - Comprehensive test suite using Vitest
27
+ - Added test files: `LightwaveAccount.test.ts`, `LightwaveRFClient.test.ts`, `index.test.ts`
28
+ - Added test fixtures in `src/.fixtures/` for VCR-based HTTP testing
29
+ - Integration with `fetch-vcr` for HTTP request recording/replaying
30
+ - TypeScript strict mode enabled for better type safety
31
+ - Modern build tooling with Vitest for testing
32
+
33
+ ### Changed
34
+ - Complete TypeScript rewrite with improved type definitions
35
+ - Updated dependencies:
36
+ - Upgraded to modern lockfile format (lockfileVersion 3)
37
+ - Added `vitest` v4.0.7 as dev dependency
38
+ - Added `fetch-vcr` v3.2.0 for HTTP testing
39
+ - Updated `debug` to 4.3.3
40
+ - Updated all TypeScript type definitions to latest versions
41
+ - Modernized TypeScript configuration:
42
+ - Target set to ES2024
43
+ - Strict mode enabled
44
+ - Output directory changed to `.dist`
45
+ - Enabled declaration file generation
46
+ - Improved `.gitignore` to include `.dist` directory
47
+
48
+ ### Improved
49
+ - Better code organization with renamed message processor files
50
+ - Enhanced type safety throughout the codebase
51
+ - More maintainable project structure
52
+ - Better testing infrastructure
53
+
54
+ ### Migration Guide
55
+
56
+ For users upgrading from 0.0.x to 1.0.0:
57
+
58
+ 1. **Import paths remain unchanged** - The public API exports are the same
59
+ 2. **If you were directly importing internal files**, update:
60
+ - `LightwaveJsonMessageProcessor` → `LightwaveMessageProcessorForJson`
61
+ - `LightwaveTextMessageProcessor` → `LightwaveMessageProcessorForText`
62
+ 3. **Build output** - The compiled files are now in `.dist/` instead of `dist/` (but this shouldn't affect consumers of the package)
63
+ 4. **CLI removed** - If you were using the CLI, you'll need to use the library API directly
64
+
65
+ ---
66
+
67
+ ## [0.0.9] - Earlier
68
+
69
+ Previous releases. See git history for details.
package/README.md CHANGED
@@ -1,3 +1,454 @@
1
- # LightwaveRF client library
1
+ # @evops/lightwaverf
2
2
 
3
- Originally based on https://github.com/ollieparsley/node-lightwaverf
3
+ > A modern TypeScript client library for controlling LightwaveRF home automation devices
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@evops/lightwaverf.svg)](https://www.npmjs.com/package/@evops/lightwaverf)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ Control your LightwaveRF smart lights, switches, and dimmers from Node.js with a clean, type-safe API. This library communicates with LightwaveRF Link Plus devices over your local network and integrates with the LightwaveRF cloud API for device configuration.
9
+
10
+ ## Features
11
+
12
+ - 🎯 **Type-Safe** - Full TypeScript support with strict type checking
13
+ - 🔌 **Event-Driven** - Real-time device state change notifications
14
+ - 🔄 **Automatic Device Discovery** - Finds LightwaveRF Link devices on your network
15
+ - 🔐 **Registration Management** - Handles device pairing automatically
16
+ - ⚡ **Command Queueing** - Built-in queue prevents command collisions
17
+ - 🔁 **Retry Logic** - Automatic retry with exponential backoff on errors
18
+ - 🐛 **Debug Logging** - Comprehensive debug output via `debug` module
19
+ - ☁️ **Cloud Integration** - Fetches device configuration from LightwaveRF cloud
20
+ - ✅ **Well Tested** - Comprehensive test suite with Vitest
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install @evops/lightwaverf
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ```typescript
31
+ import LightwaveRF from '@evops/lightwaverf';
32
+
33
+ // Initialize the client
34
+ const lw = new LightwaveRF({
35
+ email: 'your-email@example.com',
36
+ pin: '1234',
37
+ ip: '192.168.1.100' // Optional: Link IP (auto-discovered if omitted)
38
+ });
39
+
40
+ // Connect and ensure registration
41
+ await lw.connect();
42
+ await lw.ensureRegistration();
43
+
44
+ // Get all your devices
45
+ const devices = await lw.getDevices();
46
+ console.log('Found devices:', devices);
47
+
48
+ // Control a device
49
+ const myLight = devices.find(d => d.deviceName === 'Living Room Light');
50
+ await lw.turnOn(myLight);
51
+ await lw.dim(myLight, 50); // Set to 50% brightness
52
+ await lw.turnOff(myLight);
53
+ ```
54
+
55
+ ## API Reference
56
+
57
+ ### Constructor
58
+
59
+ ```typescript
60
+ new LightwaveRF(config: LightwaveRFConfiguration)
61
+ ```
62
+
63
+ #### Configuration Options
64
+
65
+ | Option | Type | Default | Description |
66
+ |--------|------|---------|-------------|
67
+ | `email` | `string` | - | Your LightwaveRF account email |
68
+ | `pin` | `string` | - | Your LightwaveRF account PIN |
69
+ | `ip` | `string` | `255.255.255.255` | Link device IP (defaults to broadcast for auto-discovery) |
70
+ | `timeout` | `number` | `1000` | Command timeout in milliseconds |
71
+
72
+ ### Methods
73
+
74
+ #### Device Control
75
+
76
+ ##### `turnOn(device: ILightwaveDevice): Promise<void>`
77
+
78
+ Turns a device on.
79
+
80
+ ```typescript
81
+ await lw.turnOn({ roomId: 1, deviceId: 1, roomName: 'Living Room', deviceName: 'Light' });
82
+ ```
83
+
84
+ ##### `turnOff(device: ILightwaveDevice): Promise<void>`
85
+
86
+ Turns a device off.
87
+
88
+ ```typescript
89
+ await lw.turnOff(myLight);
90
+ ```
91
+
92
+ ##### `dim(device: ILightwaveDevice, percentage: number): Promise<void>`
93
+
94
+ Dims a device to the specified percentage (0-100).
95
+
96
+ ```typescript
97
+ await lw.dim(myLight, 75); // Set to 75% brightness
98
+ ```
99
+
100
+ #### Device Management
101
+
102
+ ##### `getDevices(): Promise<LightwaveDevice[]>`
103
+
104
+ Retrieves all devices from your LightwaveRF cloud account.
105
+
106
+ ```typescript
107
+ const devices = await lw.getDevices();
108
+ devices.forEach(device => {
109
+ console.log(`${device.roomName} - ${device.deviceName} (${device.deviceType})`);
110
+ });
111
+ ```
112
+
113
+ #### Connection & Registration
114
+
115
+ ##### `connect(): Promise<void>`
116
+
117
+ Connects to the LightwaveRF Link device on your local network.
118
+
119
+ ```typescript
120
+ await lw.connect();
121
+ ```
122
+
123
+ ##### `isRegistered(): Promise<boolean>`
124
+
125
+ Checks if the client is registered with the Link device.
126
+
127
+ ```typescript
128
+ const registered = await lw.isRegistered();
129
+ if (!registered) {
130
+ console.log('Please press the button on your Link device to register');
131
+ }
132
+ ```
133
+
134
+ ##### `ensureRegistration(): Promise<void>`
135
+
136
+ Ensures the client is registered with the Link. If not registered, enters pairing mode. You'll need to press the pairing button on your Link device.
137
+
138
+ ```typescript
139
+ await lw.ensureRegistration();
140
+ ```
141
+
142
+ #### Device Information (Read-only Properties)
143
+
144
+ ```typescript
145
+ console.log('Serial:', lw.serial);
146
+ console.log('MAC Address:', lw.mac);
147
+ console.log('Uptime:', lw.uptime);
148
+ console.log('Model:', lw.model);
149
+ console.log('Firmware Version:', lw.version);
150
+ ```
151
+
152
+ ### Events
153
+
154
+ The client extends `EventEmitter` and emits the following events:
155
+
156
+ #### `deviceTurnedOn`
157
+
158
+ Emitted when a device is turned on (including via physical switches or other apps).
159
+
160
+ ```typescript
161
+ lw.on('deviceTurnedOn', (roomId: number, deviceId: number) => {
162
+ console.log(`Device ${deviceId} in room ${roomId} was turned on`);
163
+ });
164
+ ```
165
+
166
+ #### `deviceTurnedOff`
167
+
168
+ Emitted when a device is turned off.
169
+
170
+ ```typescript
171
+ lw.on('deviceTurnedOff', (roomId: number, deviceId: number) => {
172
+ console.log(`Device ${deviceId} in room ${roomId} was turned off`);
173
+ });
174
+ ```
175
+
176
+ #### `deviceDimmed`
177
+
178
+ Emitted when a device is dimmed to a specific level.
179
+
180
+ ```typescript
181
+ lw.on('deviceDimmed', (roomId: number, deviceId: number, percentage: number) => {
182
+ console.log(`Device ${deviceId} in room ${roomId} dimmed to ${percentage}%`);
183
+ });
184
+ ```
185
+
186
+ ### Types
187
+
188
+ #### `ILightwaveDevice`
189
+
190
+ ```typescript
191
+ interface ILightwaveDevice {
192
+ roomId: number;
193
+ deviceId: number;
194
+ roomName: string;
195
+ deviceName: string;
196
+ deviceType: string;
197
+ }
198
+ ```
199
+
200
+ #### `LightwaveDeviceType`
201
+
202
+ ```typescript
203
+ enum LightwaveDeviceType {
204
+ Dimmer = "D",
205
+ OnOff = "O"
206
+ }
207
+ ```
208
+
209
+ #### `LightwaveDevice`
210
+
211
+ A class representing a physical LightwaveRF device. Returned by `getDevices()`.
212
+
213
+ ```typescript
214
+ class LightwaveDevice {
215
+ roomId: number;
216
+ deviceId: number;
217
+ roomName: string;
218
+ deviceName: string;
219
+ deviceType: LightwaveDeviceType;
220
+ }
221
+ ```
222
+
223
+ ## Examples
224
+
225
+ ### Basic Device Control
226
+
227
+ ```typescript
228
+ import LightwaveRF from '@evops/lightwaverf';
229
+
230
+ const lw = new LightwaveRF({
231
+ email: 'your-email@example.com',
232
+ pin: '1234'
233
+ });
234
+
235
+ await lw.connect();
236
+ await lw.ensureRegistration();
237
+
238
+ // Turn on all devices
239
+ const devices = await lw.getDevices();
240
+ for (const device of devices) {
241
+ await lw.turnOn(device);
242
+ }
243
+ ```
244
+
245
+ ### Scene Control with Dimming
246
+
247
+ ```typescript
248
+ // Create a movie night scene
249
+ async function movieNightScene(lw, devices) {
250
+ const ceilingLight = devices.find(d => d.deviceName === 'Ceiling Light');
251
+ const lampLeft = devices.find(d => d.deviceName === 'Lamp Left');
252
+ const lampRight = devices.find(d => d.deviceName === 'Lamp Right');
253
+
254
+ await lw.turnOff(ceilingLight);
255
+ await lw.dim(lampLeft, 30);
256
+ await lw.dim(lampRight, 30);
257
+ }
258
+
259
+ await movieNightScene(lw, await lw.getDevices());
260
+ ```
261
+
262
+ ### Event Monitoring
263
+
264
+ ```typescript
265
+ // Monitor all device state changes
266
+ lw.on('deviceTurnedOn', (roomId, deviceId) => {
267
+ console.log(`[ON] Room ${roomId}, Device ${deviceId}`);
268
+ });
269
+
270
+ lw.on('deviceTurnedOff', (roomId, deviceId) => {
271
+ console.log(`[OFF] Room ${roomId}, Device ${deviceId}`);
272
+ });
273
+
274
+ lw.on('deviceDimmed', (roomId, deviceId, percentage) => {
275
+ console.log(`[DIM] Room ${roomId}, Device ${deviceId} -> ${percentage}%`);
276
+ });
277
+ ```
278
+
279
+ ### Control by Room and Device ID
280
+
281
+ If you already know your room and device IDs, you can control devices directly without fetching from the cloud:
282
+
283
+ ```typescript
284
+ await lw.turnOn({
285
+ roomId: 1,
286
+ deviceId: 2,
287
+ roomName: 'Living Room',
288
+ deviceName: 'Light'
289
+ });
290
+ ```
291
+
292
+ ### Error Handling
293
+
294
+ ```typescript
295
+ try {
296
+ await lw.connect();
297
+ await lw.ensureRegistration();
298
+
299
+ const devices = await lw.getDevices();
300
+ await lw.turnOn(devices[0]);
301
+ } catch (error) {
302
+ console.error('Error controlling devices:', error);
303
+ }
304
+ ```
305
+
306
+ ### Enable Debug Logging
307
+
308
+ Use the `debug` module to see detailed logging:
309
+
310
+ ```bash
311
+ DEBUG=lightwave* node your-script.js
312
+ ```
313
+
314
+ This will show all UDP communication, command queue operations, and API calls.
315
+
316
+ ## How It Works
317
+
318
+ ### Architecture
319
+
320
+ 1. **LightwaveRF Client (Main API)** - High-level interface for device control and management
321
+ 2. **LightwaveRFClient** - Low-level UDP client handling communication with Link devices
322
+ 3. **LightwaveAccount** - Cloud API integration for device configuration
323
+ 4. **Message Processors** - Parse JSON and text protocol responses
324
+ 5. **Queue System** - Manages command transactions and prevents conflicts
325
+
326
+ ### Communication Protocol
327
+
328
+ The library communicates with LightwaveRF Link devices using:
329
+
330
+ - **Protocol**: UDP
331
+ - **Ports**: Sends on 9760, receives on 9761
332
+ - **Rate Limiting**: 125ms delay between commands
333
+ - **Transaction-Based**: Each command gets a unique transaction ID
334
+ - **Response Timeout**: 5 seconds (configurable)
335
+ - **Format**: Supports both JSON (`*!{...}`) and text responses
336
+
337
+ ### Device Discovery
338
+
339
+ When no IP is specified (or `255.255.255.255` is used), the library broadcasts on the local network to automatically discover LightwaveRF Link devices.
340
+
341
+ ## Requirements
342
+
343
+ - Node.js 14 or higher
344
+ - LightwaveRF Link or Link Plus device
345
+ - LightwaveRF account (for device configuration)
346
+ - Local network access to the Link device
347
+
348
+ ## Troubleshooting
349
+
350
+ ### Connection Issues
351
+
352
+ **Problem**: Cannot connect to Link device
353
+
354
+ **Solution**:
355
+ - Ensure your Link device is powered on and connected to the same network
356
+ - Try specifying the Link IP address explicitly in the config
357
+ - Check your firewall allows UDP traffic on ports 9760/9761
358
+
359
+ ### Registration Issues
360
+
361
+ **Problem**: `ensureRegistration()` hangs or fails
362
+
363
+ **Solution**:
364
+ - Press and release the pairing button on your Link device when prompted
365
+ - The Link button should flash during pairing mode
366
+ - Registration is usually required only once per client
367
+
368
+ ### Device Control Not Working
369
+
370
+ **Problem**: Commands don't affect devices
371
+
372
+ **Solution**:
373
+ - Verify you're registered with `await lw.isRegistered()`
374
+ - Check device IDs match your actual device configuration
375
+ - Enable debug logging to see command/response details
376
+ - Ensure devices are paired with your Link in the LightwaveRF app
377
+
378
+ ### Cloud API Issues
379
+
380
+ **Problem**: `getDevices()` fails
381
+
382
+ **Solution**:
383
+ - Verify your email and PIN are correct
384
+ - Check your internet connection (cloud API requires internet)
385
+ - Ensure you have devices configured in your LightwaveRF account
386
+
387
+ ## Migration from 0.x to 1.0
388
+
389
+ Version 1.0 is a complete TypeScript rewrite with breaking changes. See [CHANGELOG.md](CHANGELOG.md) for details.
390
+
391
+ ### Key Changes
392
+
393
+ - ✅ Public API remains compatible
394
+ - ⚠️ Output directory changed from `dist` to `.dist`
395
+ - ⚠️ Internal files renamed (only affects direct imports)
396
+ - ⚠️ CLI removed (use library API directly)
397
+ - ⚠️ Modern ES2024 output with strict TypeScript
398
+
399
+ ## Development
400
+
401
+ ```bash
402
+ # Install dependencies
403
+ npm install
404
+
405
+ # Run tests
406
+ npm test
407
+
408
+ # Build TypeScript
409
+ npm run prepublish
410
+ ```
411
+
412
+ ## Testing
413
+
414
+ The library includes comprehensive tests using Vitest with HTTP request recording/replay:
415
+
416
+ ```bash
417
+ npm test
418
+ ```
419
+
420
+ Tests use `fetch-vcr` to record API interactions, making them fast and reliable.
421
+
422
+ ## Contributing
423
+
424
+ Contributions are welcome! Please feel free to submit a Pull Request.
425
+
426
+ 1. Fork the repository
427
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
428
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
429
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
430
+ 5. Open a Pull Request
431
+
432
+ ## License
433
+
434
+ MIT License - see [LICENSE](LICENSE) file for details
435
+
436
+ ## Credits
437
+
438
+ Originally based on [node-lightwaverf](https://github.com/ollieparsley/node-lightwaverf) by Ollie Parsley.
439
+
440
+ Rewritten and maintained by [Stanislaw Wozniak](https://github.com/eu-evos).
441
+
442
+ ## Related Projects
443
+
444
+ - [LightwaveRF Official Website](https://lightwaverf.com/)
445
+ - [LightwaveRF API Documentation](https://api.lightwaverf.com/)
446
+
447
+ ## Support
448
+
449
+ - Issues: [GitHub Issues](https://github.com/eu-evos/node-lightwaverf/issues)
450
+ - Email: Contact via GitHub
451
+
452
+ ---
453
+
454
+ **Made with ⚡ by the community**