elero-usb-transmitter-client 1.0.6 → 1.1.2
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/.github/workflows/nodejs.yml +27 -0
- package/README.md +62 -1
- package/dist/UsbTransmitterClient.d.ts +18 -0
- package/dist/UsbTransmitterClient.js +284 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +277 -0
- package/dist/domain/constants.d.ts +32 -0
- package/dist/domain/constants.js +44 -0
- package/dist/domain/enums.d.ts +34 -0
- package/dist/domain/enums.js +40 -0
- package/dist/domain/types.d.ts +3 -0
- package/dist/domain/types.js +2 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +18 -0
- package/dist/model/Response.d.ts +10 -0
- package/dist/model/Response.js +2 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +277 -0
- package/jest.json +1 -1
- package/package.json +9 -2
- package/src/UsbTransmitterClient.ts +46 -39
- package/src/cli.ts +167 -0
- package/test/UsbTransmitterClient.test.ts +39 -0
- package/test/UsbTransmitterClientMock.test.ts +182 -0
- package/__test__/UsbTransmitterClient.test.ts +0 -31
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Node.js CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, develop ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
node-version: [18.x, 20.x, 22.x]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v3
|
|
20
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
21
|
+
uses: actions/setup-node@v3
|
|
22
|
+
with:
|
|
23
|
+
node-version: ${{ matrix.node-version }}
|
|
24
|
+
cache: 'npm'
|
|
25
|
+
- run: npm ci
|
|
26
|
+
- run: npm run build
|
|
27
|
+
- run: npm test
|
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# elero-usb-transmitter-client
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/elero-usb-transmitter-client)
|
|
4
|
-
[](https://github.com/marc2016/elero-usb-transmitter-client/actions/workflows/nodejs.yml)
|
|
5
|
+
|
|
5
6
|
|
|
6
7
|
Elero USB Transmitter Client for node.js to send commands to Elero USB Stick and receive information. This libaray needs Elero Transmitter Stick (https://www.der-sonnenschutz-shop.de/elero-221250001-centero-transmitter-stick.html).
|
|
7
8
|
|
|
@@ -18,6 +19,30 @@ Using npm:
|
|
|
18
19
|
$ npm install elero-usb-transmitter-client
|
|
19
20
|
```
|
|
20
21
|
|
|
22
|
+
## CLI Usage
|
|
23
|
+
|
|
24
|
+
You can use the interactive CLI to control the transmitter directly.
|
|
25
|
+
|
|
26
|
+
### Installation
|
|
27
|
+
|
|
28
|
+
**Globally (if published to npm):**
|
|
29
|
+
```bash
|
|
30
|
+
npm install -g elero-usb-transmitter-client
|
|
31
|
+
elero-cli
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Via npx (if published to npm):**
|
|
35
|
+
```bash
|
|
36
|
+
npx elero-usb-transmitter-client
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**From Source (Development):**
|
|
40
|
+
1. Clone the repository
|
|
41
|
+
2. Install dependencies: `npm install`
|
|
42
|
+
3. Build the project: `npm run build`
|
|
43
|
+
4. Link command: `npm link`
|
|
44
|
+
5. Run: `elero-cli`
|
|
45
|
+
|
|
21
46
|
## Example
|
|
22
47
|
|
|
23
48
|
### Initialize
|
|
@@ -57,6 +82,42 @@ console.log(response)
|
|
|
57
82
|
await client.close()
|
|
58
83
|
```
|
|
59
84
|
|
|
85
|
+
## Changelog
|
|
86
|
+
|
|
87
|
+
### 1.1.2
|
|
88
|
+
|
|
89
|
+
- Fix CLI entry point name in `package.json`
|
|
90
|
+
|
|
91
|
+
### 1.1.1
|
|
92
|
+
|
|
93
|
+
- Fixed `responseBytes are null` error by handling fragmented serial packets
|
|
94
|
+
- Added detailed unit tests with mocks
|
|
95
|
+
|
|
96
|
+
### 1.1.0
|
|
97
|
+
|
|
98
|
+
- Added interactive CLI (`elero-cli`)
|
|
99
|
+
- Added `inquirer` and `commander` dependencies
|
|
100
|
+
|
|
101
|
+
### 1.0.6
|
|
102
|
+
|
|
103
|
+
- Updated `serialport` dependency
|
|
104
|
+
- Fixed errors in response handling
|
|
105
|
+
|
|
106
|
+
### 1.0.5
|
|
107
|
+
|
|
108
|
+
- Improved mutex handling (release added)
|
|
109
|
+
|
|
110
|
+
### 1.0.4
|
|
111
|
+
|
|
112
|
+
- Fixed promise rejection logic
|
|
113
|
+
- Added null checks for response bytes
|
|
114
|
+
|
|
115
|
+
### 1.0.0
|
|
116
|
+
|
|
117
|
+
- Initial release with `getInfo`, `sendControlCommand`, and `checkChannels`
|
|
118
|
+
- Implemented `UsbTransmitterClient`
|
|
119
|
+
|
|
120
|
+
|
|
60
121
|
## License
|
|
61
122
|
|
|
62
123
|
[MIT](LICENSE)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SerialPort } from 'serialport';
|
|
2
|
+
import { Response } from './model/Response';
|
|
3
|
+
import { ControlCommand } from './domain/enums';
|
|
4
|
+
export declare class UsbTransmitterClient {
|
|
5
|
+
serialPort: SerialPort<any>;
|
|
6
|
+
constructor(devPath: string);
|
|
7
|
+
open(): Promise<void>;
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
checkChannels(): Promise<number[]>;
|
|
10
|
+
getInfo(channel: number): Promise<Response>;
|
|
11
|
+
sendControlCommand(channel: number, controlCommand: ControlCommand): Promise<Response>;
|
|
12
|
+
private waitForResponse;
|
|
13
|
+
private sendCommand;
|
|
14
|
+
private readResponseBytes;
|
|
15
|
+
private calculateChecksum;
|
|
16
|
+
private getActiveChannels;
|
|
17
|
+
private parseResponse;
|
|
18
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (_) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.UsbTransmitterClient = void 0;
|
|
40
|
+
var serialport_1 = require("serialport");
|
|
41
|
+
var _ = require("lodash");
|
|
42
|
+
var constants_1 = require("./domain/constants");
|
|
43
|
+
var enums_1 = require("./domain/enums");
|
|
44
|
+
var async_mutex_1 = require("async-mutex");
|
|
45
|
+
var DEFAULT_BAUDRATE = 38400;
|
|
46
|
+
var DEFAULT_BYTESIZE = 8;
|
|
47
|
+
var DEFAULT_PARITY = 'none';
|
|
48
|
+
var DEFAULT_STOPBITS = 1;
|
|
49
|
+
var mutex = new async_mutex_1.Mutex();
|
|
50
|
+
var UsbTransmitterClient = /** @class */ (function () {
|
|
51
|
+
function UsbTransmitterClient(devPath) {
|
|
52
|
+
this.serialPort = new serialport_1.SerialPort({
|
|
53
|
+
path: devPath,
|
|
54
|
+
baudRate: DEFAULT_BAUDRATE,
|
|
55
|
+
dataBits: DEFAULT_BYTESIZE,
|
|
56
|
+
parity: DEFAULT_PARITY,
|
|
57
|
+
stopBits: DEFAULT_STOPBITS,
|
|
58
|
+
autoOpen: false,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
UsbTransmitterClient.prototype.open = function () {
|
|
62
|
+
var _this = this;
|
|
63
|
+
return new Promise(function (resolve, reject) {
|
|
64
|
+
if (!_this.serialPort.isOpen) {
|
|
65
|
+
_this.serialPort.open(function (error) {
|
|
66
|
+
if (error)
|
|
67
|
+
reject(error);
|
|
68
|
+
_this.serialPort.flush(function (error) {
|
|
69
|
+
if (error)
|
|
70
|
+
reject(error);
|
|
71
|
+
resolve();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
UsbTransmitterClient.prototype.close = function () {
|
|
78
|
+
var _this = this;
|
|
79
|
+
return new Promise(function (resolve, reject) {
|
|
80
|
+
_this.serialPort.close(function (error) {
|
|
81
|
+
if (error)
|
|
82
|
+
reject(error);
|
|
83
|
+
resolve();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
UsbTransmitterClient.prototype.checkChannels = function () {
|
|
88
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
89
|
+
var data, release, responseBytes, response;
|
|
90
|
+
return __generator(this, function (_a) {
|
|
91
|
+
switch (_a.label) {
|
|
92
|
+
case 0:
|
|
93
|
+
data = [constants_1.BYTE_HEADER, constants_1.BYTE_LENGTH_2, enums_1.EasyCommand.EASY_CHECK];
|
|
94
|
+
return [4 /*yield*/, mutex.acquire()];
|
|
95
|
+
case 1:
|
|
96
|
+
release = _a.sent();
|
|
97
|
+
_a.label = 2;
|
|
98
|
+
case 2:
|
|
99
|
+
_a.trys.push([2, , 5, 6]);
|
|
100
|
+
return [4 /*yield*/, this.sendCommand(data)];
|
|
101
|
+
case 3:
|
|
102
|
+
_a.sent();
|
|
103
|
+
return [4 /*yield*/, this.waitForResponse(constants_1.RESPONSE_LENGTH_CHECK)];
|
|
104
|
+
case 4:
|
|
105
|
+
responseBytes = _a.sent();
|
|
106
|
+
response = this.parseResponse(responseBytes);
|
|
107
|
+
return [2 /*return*/, response.activeChannels];
|
|
108
|
+
case 5:
|
|
109
|
+
release();
|
|
110
|
+
return [7 /*endfinally*/];
|
|
111
|
+
case 6: return [2 /*return*/];
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
UsbTransmitterClient.prototype.getInfo = function (channel) {
|
|
117
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
118
|
+
var lowChannels, highChannels, data, release, responseBytes, response;
|
|
119
|
+
return __generator(this, function (_a) {
|
|
120
|
+
switch (_a.label) {
|
|
121
|
+
case 0:
|
|
122
|
+
lowChannels = (1 << (channel - 1)) & 0xff;
|
|
123
|
+
highChannels = (1 << (channel - 1)) >> 8;
|
|
124
|
+
data = [
|
|
125
|
+
constants_1.BYTE_HEADER,
|
|
126
|
+
constants_1.BYTE_LENGTH_4,
|
|
127
|
+
enums_1.EasyCommand.EASY_INFO,
|
|
128
|
+
highChannels,
|
|
129
|
+
lowChannels,
|
|
130
|
+
];
|
|
131
|
+
return [4 /*yield*/, mutex.acquire()];
|
|
132
|
+
case 1:
|
|
133
|
+
release = _a.sent();
|
|
134
|
+
_a.label = 2;
|
|
135
|
+
case 2:
|
|
136
|
+
_a.trys.push([2, , 5, 6]);
|
|
137
|
+
return [4 /*yield*/, this.sendCommand(data)];
|
|
138
|
+
case 3:
|
|
139
|
+
_a.sent();
|
|
140
|
+
return [4 /*yield*/, this.waitForResponse(constants_1.RESPONSE_LENGTH_INFO)];
|
|
141
|
+
case 4:
|
|
142
|
+
responseBytes = _a.sent();
|
|
143
|
+
response = this.parseResponse(responseBytes);
|
|
144
|
+
return [2 /*return*/, response];
|
|
145
|
+
case 5:
|
|
146
|
+
release();
|
|
147
|
+
return [7 /*endfinally*/];
|
|
148
|
+
case 6: return [2 /*return*/];
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
UsbTransmitterClient.prototype.sendControlCommand = function (channel, controlCommand) {
|
|
154
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
155
|
+
var lowChannels, highChannels, data, release, responseBytes, response;
|
|
156
|
+
return __generator(this, function (_a) {
|
|
157
|
+
switch (_a.label) {
|
|
158
|
+
case 0:
|
|
159
|
+
lowChannels = (1 << (channel - 1)) & 0xff;
|
|
160
|
+
highChannels = (1 << (channel - 1)) >> 8;
|
|
161
|
+
data = [
|
|
162
|
+
constants_1.BYTE_HEADER,
|
|
163
|
+
constants_1.BYTE_LENGTH_5,
|
|
164
|
+
enums_1.EasyCommand.EASY_SEND,
|
|
165
|
+
highChannels,
|
|
166
|
+
lowChannels,
|
|
167
|
+
controlCommand,
|
|
168
|
+
];
|
|
169
|
+
return [4 /*yield*/, mutex.acquire()];
|
|
170
|
+
case 1:
|
|
171
|
+
release = _a.sent();
|
|
172
|
+
_a.label = 2;
|
|
173
|
+
case 2:
|
|
174
|
+
_a.trys.push([2, , 5, 6]);
|
|
175
|
+
return [4 /*yield*/, this.sendCommand(data)];
|
|
176
|
+
case 3:
|
|
177
|
+
_a.sent();
|
|
178
|
+
return [4 /*yield*/, this.waitForResponse(constants_1.RESPONSE_LENGTH_INFO)];
|
|
179
|
+
case 4:
|
|
180
|
+
responseBytes = _a.sent();
|
|
181
|
+
response = this.parseResponse(responseBytes);
|
|
182
|
+
return [2 /*return*/, response];
|
|
183
|
+
case 5:
|
|
184
|
+
release();
|
|
185
|
+
return [7 /*endfinally*/];
|
|
186
|
+
case 6: return [2 /*return*/];
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
UsbTransmitterClient.prototype.waitForResponse = function (length) {
|
|
192
|
+
var _this = this;
|
|
193
|
+
return new Promise(function (resolve, reject) {
|
|
194
|
+
var timeout = setTimeout(function () {
|
|
195
|
+
cleanup();
|
|
196
|
+
reject(new Error('Timeout waiting for response'));
|
|
197
|
+
}, 2000);
|
|
198
|
+
var tryRead = function () {
|
|
199
|
+
var buffer = _this.serialPort.read(length);
|
|
200
|
+
if (buffer) {
|
|
201
|
+
cleanup();
|
|
202
|
+
resolve(buffer);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
var cleanup = function () {
|
|
206
|
+
clearTimeout(timeout);
|
|
207
|
+
_this.serialPort.removeListener('readable', tryRead);
|
|
208
|
+
};
|
|
209
|
+
_this.serialPort.on('readable', tryRead);
|
|
210
|
+
tryRead();
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
UsbTransmitterClient.prototype.sendCommand = function (data) {
|
|
214
|
+
var _this = this;
|
|
215
|
+
var checksum = this.calculateChecksum(data);
|
|
216
|
+
data.push(checksum);
|
|
217
|
+
return new Promise(function (resolve, reject) {
|
|
218
|
+
_this.serialPort.flush(function (error) {
|
|
219
|
+
if (error)
|
|
220
|
+
reject(error);
|
|
221
|
+
_this.serialPort.write(data, function (error) {
|
|
222
|
+
if (error)
|
|
223
|
+
reject(error);
|
|
224
|
+
resolve(data.length);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
};
|
|
229
|
+
UsbTransmitterClient.prototype.readResponseBytes = function (length) {
|
|
230
|
+
//Get the serial data from the serial port.
|
|
231
|
+
var response = this.serialPort.read(length);
|
|
232
|
+
return response;
|
|
233
|
+
};
|
|
234
|
+
UsbTransmitterClient.prototype.calculateChecksum = function (data) {
|
|
235
|
+
//Calculate checksum.
|
|
236
|
+
//All the sum of all bytes (Header to CS) must be 0x00.
|
|
237
|
+
var sum = _.sum(data);
|
|
238
|
+
var result = (256 - sum) % 256;
|
|
239
|
+
return result;
|
|
240
|
+
};
|
|
241
|
+
UsbTransmitterClient.prototype.getActiveChannels = function (byte, start) {
|
|
242
|
+
var channels = new Array();
|
|
243
|
+
for (var i = 0; i < 9; i++) {
|
|
244
|
+
if (((byte >> i) & 1) == 1) {
|
|
245
|
+
var channel = i + start;
|
|
246
|
+
channels.push(channel);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return channels;
|
|
250
|
+
};
|
|
251
|
+
UsbTransmitterClient.prototype.parseResponse = function (bytes) {
|
|
252
|
+
var activeHighChannels = this.getActiveChannels(bytes[3], 9);
|
|
253
|
+
var activeLowChannels = this.getActiveChannels(bytes[4], 1);
|
|
254
|
+
var activeChannels = _.concat(activeLowChannels, activeHighChannels);
|
|
255
|
+
var response = {
|
|
256
|
+
header: bytes[0],
|
|
257
|
+
length: bytes[1],
|
|
258
|
+
command: bytes[2],
|
|
259
|
+
activeChannels: activeChannels,
|
|
260
|
+
checksum: -1,
|
|
261
|
+
status: null,
|
|
262
|
+
statusCode: -1,
|
|
263
|
+
};
|
|
264
|
+
if (bytes.length == constants_1.RESPONSE_LENGTH_CHECK) {
|
|
265
|
+
response.checksum = bytes[5];
|
|
266
|
+
//Easy Ack (the answer on Easy Info)
|
|
267
|
+
}
|
|
268
|
+
else if (bytes.length == constants_1.RESPONSE_LENGTH_SEND) {
|
|
269
|
+
if (bytes[5] in enums_1.InfoData) {
|
|
270
|
+
response.status = bytes[5];
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
response.status = enums_1.InfoData.INFO_UNKNOWN;
|
|
274
|
+
}
|
|
275
|
+
response.checksum = bytes[6];
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
response.status = enums_1.InfoData.INFO_UNKNOWN;
|
|
279
|
+
}
|
|
280
|
+
return response;
|
|
281
|
+
};
|
|
282
|
+
return UsbTransmitterClient;
|
|
283
|
+
}());
|
|
284
|
+
exports.UsbTransmitterClient = UsbTransmitterClient;
|
package/dist/cli.d.ts
ADDED