@evops/lightwaverf 0.0.8 → 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.
- package/.dist/LightwaveAccount.d.ts +11 -0
- package/.dist/LightwaveAccount.js +99 -0
- package/.dist/LightwaveAccount.test.d.ts +1 -0
- package/.dist/LightwaveAccount.test.js +26 -0
- package/.dist/LightwaveDevice.d.ts +21 -0
- package/.dist/LightwaveDevice.js +25 -0
- package/{dist → .dist}/LightwaveMessageProcessor.d.ts +0 -1
- package/{dist/LightwaveTextMessageProcessor.d.ts → .dist/LightwaveMessageProcessorForJson.d.ts} +2 -3
- package/.dist/LightwaveMessageProcessorForJson.js +24 -0
- package/{dist/LightwaveJsonMessageProcessor.d.ts → .dist/LightwaveMessageProcessorForText.d.ts} +2 -3
- package/{dist/LightwaveTextMessageProcessor.js → .dist/LightwaveMessageProcessorForText.js} +7 -6
- package/.dist/LightwaveRFClient.d.ts +35 -0
- package/.dist/LightwaveRFClient.js +217 -0
- package/.dist/LightwaveRFClient.test.d.ts +1 -0
- package/.dist/LightwaveRFClient.test.js +30 -0
- package/{dist → .dist}/Queue.d.ts +1 -1
- package/{dist → .dist}/Queue.js +3 -5
- package/.dist/index.d.ts +58 -0
- package/.dist/index.js +120 -0
- package/.dist/index.test.d.ts +1 -0
- package/.dist/index.test.js +43 -0
- package/CHANGELOG.md +69 -0
- package/README.md +453 -2
- package/package.json +13 -8
- package/vitest.config.ts +12 -0
- package/dist/LightwaveAccount.d.ts +0 -13
- package/dist/LightwaveAccount.js +0 -81
- package/dist/LightwaveDevice.d.ts +0 -26
- package/dist/LightwaveDevice.js +0 -64
- package/dist/LightwaveJsonMessageProcessor.js +0 -24
- package/dist/LightwaveMessage.d.ts +0 -9
- package/dist/LightwaveMessage.js +0 -2
- package/dist/LightwaveRFClient.d.ts +0 -32
- package/dist/LightwaveRFClient.js +0 -202
- package/dist/index.d.ts +0 -98
- package/dist/index.js +0 -278
- package/src/LightwaveAccount.ts +0 -107
- package/src/LightwaveDevice.ts +0 -66
- package/src/LightwaveJsonMessageProcessor.ts +0 -29
- package/src/LightwaveMessageProcessor.ts +0 -8
- package/src/LightwaveRFClient.ts +0 -236
- package/src/LightwaveTextMessageProcessor.ts +0 -30
- package/src/LightwaveTransaction.ts +0 -9
- package/src/Queue.ts +0 -17
- package/src/index.ts +0 -332
- /package/{dist → .dist}/LightwaveMessageProcessor.js +0 -0
- /package/{dist → .dist}/LightwaveTransaction.d.ts +0 -0
- /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
|
-
#
|
|
1
|
+
# @evops/lightwaverf
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> A modern TypeScript client library for controlling LightwaveRF home automation devices
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@evops/lightwaverf)
|
|
6
|
+
[](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**
|