node-libcec 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/LICENSE +21 -0
- package/README.md +181 -0
- package/binding.gyp +62 -0
- package/lib/constants.js +275 -0
- package/lib/index.js +433 -0
- package/package.json +46 -0
- package/src/native/cec_adapter.cc +909 -0
- package/src/native/cec_adapter.h +96 -0
- package/src/native/cec_addon.cc +9 -0
- package/src/native/cec_callbacks.cc +313 -0
- package/src/native/cec_callbacks.h +48 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
const EventEmitter = require('events');
|
|
2
|
+
|
|
3
|
+
// Load native addon
|
|
4
|
+
let native;
|
|
5
|
+
try {
|
|
6
|
+
native = require('../build/Release/cec.node');
|
|
7
|
+
} catch (e) {
|
|
8
|
+
try {
|
|
9
|
+
native = require('../build/Debug/cec.node');
|
|
10
|
+
} catch (e2) {
|
|
11
|
+
throw new Error('Failed to load native cec module. Did you run npm run build?');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const constants = require('./constants');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* CEC Adapter class - provides high-level interface to libcec
|
|
19
|
+
*/
|
|
20
|
+
class CECAdapter extends EventEmitter {
|
|
21
|
+
/**
|
|
22
|
+
* Create a new CEC adapter instance
|
|
23
|
+
* @param {Object} options - Configuration options
|
|
24
|
+
* @param {string} [options.deviceName='node-libcec'] - OSD name for this device
|
|
25
|
+
* @param {number[]} [options.deviceTypes] - Array of device types (default: [DeviceType.RECORDING_DEVICE])
|
|
26
|
+
* @param {number} [options.physicalAddress] - Physical address (auto-detected if not specified)
|
|
27
|
+
* @param {number} [options.baseDevice] - Base device logical address
|
|
28
|
+
* @param {number} [options.hdmiPort] - HDMI port number
|
|
29
|
+
* @param {boolean} [options.activateSource=false] - Whether to activate as source on connect
|
|
30
|
+
*/
|
|
31
|
+
constructor(options = {}) {
|
|
32
|
+
super();
|
|
33
|
+
this._adapter = new native.CECAdapter(options);
|
|
34
|
+
this._setupCallbacks();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Set up internal callbacks that emit events
|
|
39
|
+
*/
|
|
40
|
+
_setupCallbacks() {
|
|
41
|
+
this._adapter.setCallbacks({
|
|
42
|
+
onLogMessage: (msg) => this.emit('log', msg),
|
|
43
|
+
onKeyPress: (key) => this.emit('keypress', key),
|
|
44
|
+
onCommand: (cmd) => this.emit('command', cmd),
|
|
45
|
+
onConfigurationChanged: (config) => this.emit('configurationChanged', config),
|
|
46
|
+
onAlert: (alert) => this.emit('alert', alert),
|
|
47
|
+
onSourceActivated: (source) => this.emit('sourceActivated', source)
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Detect available CEC adapters
|
|
53
|
+
* @returns {Array} Array of adapter descriptors
|
|
54
|
+
*/
|
|
55
|
+
static detectAdapters() {
|
|
56
|
+
return native.CECAdapter.detectAdapters();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Open connection to a CEC adapter
|
|
61
|
+
* @param {string} [port] - COM port path (auto-detected if not specified)
|
|
62
|
+
* @param {number} [timeout=10000] - Connection timeout in milliseconds
|
|
63
|
+
* @returns {boolean} True if connection successful
|
|
64
|
+
*/
|
|
65
|
+
open(port, timeout = 10000) {
|
|
66
|
+
return this._adapter.open(port, timeout);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Close the adapter connection
|
|
71
|
+
*/
|
|
72
|
+
close() {
|
|
73
|
+
this._adapter.close();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Check if the adapter is responding
|
|
78
|
+
* @returns {boolean} True if adapter responds to ping
|
|
79
|
+
*/
|
|
80
|
+
pingAdapter() {
|
|
81
|
+
return this._adapter.pingAdapter();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get the adapter's USB vendor ID
|
|
86
|
+
* @returns {number} Vendor ID
|
|
87
|
+
*/
|
|
88
|
+
getAdapterVendorId() {
|
|
89
|
+
return this._adapter.getAdapterVendorId();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get the adapter's USB product ID
|
|
94
|
+
* @returns {number} Product ID
|
|
95
|
+
*/
|
|
96
|
+
getAdapterProductId() {
|
|
97
|
+
return this._adapter.getAdapterProductId();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get libcec library information
|
|
102
|
+
* @returns {string} Library info string
|
|
103
|
+
*/
|
|
104
|
+
getLibInfo() {
|
|
105
|
+
return this._adapter.getLibInfo();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// === Device Control ===
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Power on devices
|
|
112
|
+
* @param {number} [address=LogicalAddress.TV] - Target device address
|
|
113
|
+
* @returns {boolean} True if command sent successfully
|
|
114
|
+
*/
|
|
115
|
+
powerOnDevices(address = constants.LogicalAddress.TV) {
|
|
116
|
+
return this._adapter.powerOnDevices(address);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Put devices into standby
|
|
121
|
+
* @param {number} [address=LogicalAddress.BROADCAST] - Target device address
|
|
122
|
+
* @returns {boolean} True if command sent successfully
|
|
123
|
+
*/
|
|
124
|
+
standbyDevices(address = constants.LogicalAddress.BROADCAST) {
|
|
125
|
+
return this._adapter.standbyDevices(address);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Set this device as the active source
|
|
130
|
+
* @param {number} [deviceType] - Device type
|
|
131
|
+
* @returns {boolean} True if command sent successfully
|
|
132
|
+
*/
|
|
133
|
+
setActiveSource(deviceType) {
|
|
134
|
+
return this._adapter.setActiveSource(deviceType);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Mark this device as inactive source
|
|
139
|
+
* @returns {boolean} True if command sent successfully
|
|
140
|
+
*/
|
|
141
|
+
setInactiveView() {
|
|
142
|
+
return this._adapter.setInactiveView();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// === Command Transmission ===
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Transmit a raw CEC command
|
|
149
|
+
* @param {Object} command - Command object
|
|
150
|
+
* @param {number} command.initiator - Source logical address
|
|
151
|
+
* @param {number} command.destination - Target logical address
|
|
152
|
+
* @param {number} command.opcode - CEC opcode
|
|
153
|
+
* @param {number[]} [command.parameters] - Command parameters
|
|
154
|
+
* @returns {boolean} True if command sent successfully
|
|
155
|
+
*/
|
|
156
|
+
transmit(command) {
|
|
157
|
+
return this._adapter.transmit(command);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Send a keypress to a device
|
|
162
|
+
* @param {number} destination - Target device address
|
|
163
|
+
* @param {number} keycode - User control code
|
|
164
|
+
* @param {boolean} [wait=false] - Wait for ACK
|
|
165
|
+
* @returns {boolean} True if command sent successfully
|
|
166
|
+
*/
|
|
167
|
+
sendKeypress(destination, keycode, wait = false) {
|
|
168
|
+
return this._adapter.sendKeypress(destination, keycode, wait);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Send a key release to a device
|
|
173
|
+
* @param {number} destination - Target device address
|
|
174
|
+
* @param {boolean} [wait=false] - Wait for ACK
|
|
175
|
+
* @returns {boolean} True if command sent successfully
|
|
176
|
+
*/
|
|
177
|
+
sendKeyRelease(destination, wait = false) {
|
|
178
|
+
return this._adapter.sendKeyRelease(destination, wait);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// === Device Information ===
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Get the CEC version of a device
|
|
185
|
+
* @param {number} address - Device logical address
|
|
186
|
+
* @returns {number} CEC version
|
|
187
|
+
*/
|
|
188
|
+
getDeviceCecVersion(address) {
|
|
189
|
+
return this._adapter.getDeviceCecVersion(address);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get the menu language of a device
|
|
194
|
+
* @param {number} address - Device logical address
|
|
195
|
+
* @returns {string} Menu language (3-letter code)
|
|
196
|
+
*/
|
|
197
|
+
getDeviceMenuLanguage(address) {
|
|
198
|
+
return this._adapter.getDeviceMenuLanguage(address);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get the vendor ID of a device
|
|
203
|
+
* @param {number} address - Device logical address
|
|
204
|
+
* @returns {number} Vendor ID
|
|
205
|
+
*/
|
|
206
|
+
getDeviceVendorId(address) {
|
|
207
|
+
return this._adapter.getDeviceVendorId(address);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get the power status of a device
|
|
212
|
+
* @param {number} address - Device logical address
|
|
213
|
+
* @returns {number} Power status (see PowerStatus)
|
|
214
|
+
*/
|
|
215
|
+
getDevicePowerStatus(address) {
|
|
216
|
+
return this._adapter.getDevicePowerStatus(address);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get the OSD name of a device
|
|
221
|
+
* @param {number} address - Device logical address
|
|
222
|
+
* @returns {string} OSD name
|
|
223
|
+
*/
|
|
224
|
+
getDeviceOSDName(address) {
|
|
225
|
+
return this._adapter.getDeviceOSDName(address);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get the physical address of a device
|
|
230
|
+
* @param {number} address - Device logical address
|
|
231
|
+
* @returns {number} Physical address
|
|
232
|
+
*/
|
|
233
|
+
getDevicePhysicalAddress(address) {
|
|
234
|
+
return this._adapter.getDevicePhysicalAddress(address);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// === Device Status ===
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Poll a device to check if it's present
|
|
241
|
+
* @param {number} address - Device logical address
|
|
242
|
+
* @returns {boolean} True if device responds
|
|
243
|
+
*/
|
|
244
|
+
pollDevice(address) {
|
|
245
|
+
return this._adapter.pollDevice(address);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Get all active devices on the bus
|
|
250
|
+
* @returns {number[]} Array of active logical addresses
|
|
251
|
+
*/
|
|
252
|
+
getActiveDevices() {
|
|
253
|
+
return this._adapter.getActiveDevices();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Check if a device is active
|
|
258
|
+
* @param {number} address - Device logical address
|
|
259
|
+
* @returns {boolean} True if device is active
|
|
260
|
+
*/
|
|
261
|
+
isActiveDevice(address) {
|
|
262
|
+
return this._adapter.isActiveDevice(address);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Check if a device type is active
|
|
267
|
+
* @param {number} type - Device type
|
|
268
|
+
* @returns {boolean} True if device type is active
|
|
269
|
+
*/
|
|
270
|
+
isActiveDeviceType(type) {
|
|
271
|
+
return this._adapter.isActiveDeviceType(type);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Get the current active source
|
|
276
|
+
* @returns {number} Logical address of active source
|
|
277
|
+
*/
|
|
278
|
+
getActiveSource() {
|
|
279
|
+
return this._adapter.getActiveSource();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Check if a device is the active source
|
|
284
|
+
* @param {number} address - Device logical address
|
|
285
|
+
* @returns {boolean} True if device is active source
|
|
286
|
+
*/
|
|
287
|
+
isActiveSource(address) {
|
|
288
|
+
return this._adapter.isActiveSource(address);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Check if libcec is the active source
|
|
293
|
+
* @returns {boolean} True if libcec controls active source
|
|
294
|
+
*/
|
|
295
|
+
isLibCECActiveSource() {
|
|
296
|
+
return this._adapter.isLibCECActiveSource();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// === Audio Control ===
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Increase volume
|
|
303
|
+
* @param {boolean} [sendRelease=true] - Send key release after
|
|
304
|
+
* @returns {number} New audio status
|
|
305
|
+
*/
|
|
306
|
+
volumeUp(sendRelease = true) {
|
|
307
|
+
return this._adapter.volumeUp(sendRelease);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Decrease volume
|
|
312
|
+
* @param {boolean} [sendRelease=true] - Send key release after
|
|
313
|
+
* @returns {number} New audio status
|
|
314
|
+
*/
|
|
315
|
+
volumeDown(sendRelease = true) {
|
|
316
|
+
return this._adapter.volumeDown(sendRelease);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Mute audio
|
|
321
|
+
* @returns {number} Audio status
|
|
322
|
+
*/
|
|
323
|
+
muteAudio() {
|
|
324
|
+
return this._adapter.muteAudio();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Toggle mute
|
|
329
|
+
* @returns {number} Audio status
|
|
330
|
+
*/
|
|
331
|
+
audioToggleMute() {
|
|
332
|
+
return this._adapter.audioToggleMute();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Get current audio status
|
|
337
|
+
* @returns {number} Audio status
|
|
338
|
+
*/
|
|
339
|
+
audioStatus() {
|
|
340
|
+
return this._adapter.audioStatus();
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// === Display Control ===
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Display a message on a device's OSD
|
|
347
|
+
* @param {number} address - Target device address
|
|
348
|
+
* @param {number} duration - Display control (see DisplayControl)
|
|
349
|
+
* @param {string} message - Message to display
|
|
350
|
+
* @returns {boolean} True if command sent successfully
|
|
351
|
+
*/
|
|
352
|
+
setOSDString(address, duration, message) {
|
|
353
|
+
return this._adapter.setOSDString(address, duration, message);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// === Address Configuration ===
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Set the logical address
|
|
360
|
+
* @param {number} [address=LogicalAddress.PLAYBACK_DEVICE_1] - Logical address
|
|
361
|
+
* @returns {boolean} True if successful
|
|
362
|
+
*/
|
|
363
|
+
setLogicalAddress(address = constants.LogicalAddress.PLAYBACK_DEVICE_1) {
|
|
364
|
+
return this._adapter.setLogicalAddress(address);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Set the physical address
|
|
369
|
+
* @param {number} address - Physical address (e.g., 0x1000 for HDMI 1)
|
|
370
|
+
* @returns {boolean} True if successful
|
|
371
|
+
*/
|
|
372
|
+
setPhysicalAddress(address) {
|
|
373
|
+
return this._adapter.setPhysicalAddress(address);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Set the HDMI port
|
|
378
|
+
* @param {number} baseDevice - Base device logical address (usually TV)
|
|
379
|
+
* @param {number} port - HDMI port number (1-15)
|
|
380
|
+
* @returns {boolean} True if successful
|
|
381
|
+
*/
|
|
382
|
+
setHDMIPort(baseDevice, port) {
|
|
383
|
+
return this._adapter.setHDMIPort(baseDevice, port);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Get the logical addresses controlled by this adapter
|
|
388
|
+
* @returns {Object} Object with primary address and array of all addresses
|
|
389
|
+
*/
|
|
390
|
+
getLogicalAddresses() {
|
|
391
|
+
return this._adapter.getLogicalAddresses();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// === Configuration ===
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Get the current configuration
|
|
398
|
+
* @returns {Object} Configuration object
|
|
399
|
+
*/
|
|
400
|
+
getCurrentConfiguration() {
|
|
401
|
+
return this._adapter.getCurrentConfiguration();
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Set the configuration
|
|
406
|
+
* @param {Object} config - Configuration object
|
|
407
|
+
* @returns {boolean} True if successful
|
|
408
|
+
*/
|
|
409
|
+
setConfiguration(config) {
|
|
410
|
+
return this._adapter.setConfiguration(config);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Rescan for active devices
|
|
415
|
+
*/
|
|
416
|
+
rescanActiveDevices() {
|
|
417
|
+
this._adapter.rescanActiveDevices();
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Disable all callbacks
|
|
422
|
+
* @returns {boolean} True if successful
|
|
423
|
+
*/
|
|
424
|
+
disableCallbacks() {
|
|
425
|
+
return this._adapter.disableCallbacks();
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Export everything
|
|
430
|
+
module.exports = {
|
|
431
|
+
CECAdapter,
|
|
432
|
+
...constants
|
|
433
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-libcec",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Node.js bindings for libcec - control CEC devices over HDMI",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "node-gyp rebuild",
|
|
8
|
+
"clean": "node-gyp clean",
|
|
9
|
+
"prepare": "npm run build",
|
|
10
|
+
"test": "node --test test/"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"cec",
|
|
14
|
+
"hdmi",
|
|
15
|
+
"libcec",
|
|
16
|
+
"pulse-eight",
|
|
17
|
+
"consumer-electronics-control",
|
|
18
|
+
"tv",
|
|
19
|
+
"remote",
|
|
20
|
+
"home-automation",
|
|
21
|
+
"smart-home",
|
|
22
|
+
"native-addon"
|
|
23
|
+
],
|
|
24
|
+
"author": "fajitacat",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18.0.0"
|
|
28
|
+
},
|
|
29
|
+
"os": [
|
|
30
|
+
"linux",
|
|
31
|
+
"darwin",
|
|
32
|
+
"win32"
|
|
33
|
+
],
|
|
34
|
+
"files": [
|
|
35
|
+
"lib/",
|
|
36
|
+
"src/",
|
|
37
|
+
"binding.gyp"
|
|
38
|
+
],
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"node-addon-api": "^8.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"node-gyp": "^10.0.0"
|
|
44
|
+
},
|
|
45
|
+
"gypfile": true
|
|
46
|
+
}
|