node-libcec 1.0.3 → 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.
- package/examples/advanced.js +422 -0
- package/examples/basic.js +98 -0
- package/examples/diagnose.js +148 -0
- package/package.json +2 -1
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CECAdapter,
|
|
3
|
+
LogicalAddress,
|
|
4
|
+
DeviceType,
|
|
5
|
+
PowerStatus,
|
|
6
|
+
UserControlCode,
|
|
7
|
+
DisplayControl,
|
|
8
|
+
Opcode,
|
|
9
|
+
Version
|
|
10
|
+
} from '../lib/index.js';
|
|
11
|
+
import readline from 'readline';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Advanced example demonstrating more libcec features
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
class CECController {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.cec = new CECAdapter({
|
|
20
|
+
deviceName: 'AdvancedCEC',
|
|
21
|
+
deviceTypes: [DeviceType.PLAYBACK_DEVICE],
|
|
22
|
+
hdmiPort: 1,
|
|
23
|
+
activateSource: false
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
this.devices = new Map();
|
|
27
|
+
this.setupEventHandlers();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setupEventHandlers() {
|
|
31
|
+
// Log messages with filtering
|
|
32
|
+
this.cec.on('log', (msg) => {
|
|
33
|
+
// Only show warnings and errors
|
|
34
|
+
if (msg.level <= 2) {
|
|
35
|
+
const level = msg.level === 1 ? 'ERROR' : 'WARN';
|
|
36
|
+
console.log(`[${level}] ${msg.message}`);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Handle remote control keypresses
|
|
41
|
+
this.cec.on('keypress', (key) => {
|
|
42
|
+
if (key.duration === 0) {
|
|
43
|
+
// Key pressed
|
|
44
|
+
this.handleKeyPress(key.keycode);
|
|
45
|
+
} else {
|
|
46
|
+
// Key released (duration > 0 indicates release)
|
|
47
|
+
this.handleKeyRelease(key.keycode, key.duration);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Handle incoming CEC commands
|
|
52
|
+
this.cec.on('command', (cmd) => {
|
|
53
|
+
this.handleCommand(cmd);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Handle source activation changes
|
|
57
|
+
this.cec.on('sourceActivated', (source) => {
|
|
58
|
+
if (source.activated) {
|
|
59
|
+
console.log(`Source activated: device ${source.logicalAddress}`);
|
|
60
|
+
} else {
|
|
61
|
+
console.log(`Source deactivated: device ${source.logicalAddress}`);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Handle alerts
|
|
66
|
+
this.cec.on('alert', (alert) => {
|
|
67
|
+
console.log(`Alert received: type ${alert.type}`);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Handle configuration changes
|
|
71
|
+
this.cec.on('configurationChanged', (config) => {
|
|
72
|
+
console.log('Configuration changed:', config);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
handleKeyPress(keycode) {
|
|
77
|
+
const keyName = Object.keys(UserControlCode).find(k => UserControlCode[k] === keycode) || keycode;
|
|
78
|
+
console.log(`Key pressed: ${keyName}`);
|
|
79
|
+
|
|
80
|
+
// Example: Handle specific keys
|
|
81
|
+
switch (keycode) {
|
|
82
|
+
case UserControlCode.PLAY:
|
|
83
|
+
console.log(' -> Play action');
|
|
84
|
+
break;
|
|
85
|
+
case UserControlCode.PAUSE:
|
|
86
|
+
console.log(' -> Pause action');
|
|
87
|
+
break;
|
|
88
|
+
case UserControlCode.STOP:
|
|
89
|
+
console.log(' -> Stop action');
|
|
90
|
+
break;
|
|
91
|
+
case UserControlCode.UP:
|
|
92
|
+
case UserControlCode.DOWN:
|
|
93
|
+
case UserControlCode.LEFT:
|
|
94
|
+
case UserControlCode.RIGHT:
|
|
95
|
+
console.log(' -> Navigation');
|
|
96
|
+
break;
|
|
97
|
+
case UserControlCode.SELECT:
|
|
98
|
+
console.log(' -> Select/Enter');
|
|
99
|
+
break;
|
|
100
|
+
case UserControlCode.EXIT:
|
|
101
|
+
case UserControlCode.ROOT_MENU:
|
|
102
|
+
console.log(' -> Menu action');
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
handleKeyRelease(keycode, duration) {
|
|
108
|
+
console.log(`Key released after ${duration}ms`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
handleCommand(cmd) {
|
|
112
|
+
const opcodeName = Object.keys(Opcode).find(k => Opcode[k] === cmd.opcode) || `0x${cmd.opcode.toString(16)}`;
|
|
113
|
+
console.log(`Command: ${opcodeName} from ${cmd.initiator} to ${cmd.destination}`);
|
|
114
|
+
|
|
115
|
+
// Handle specific commands
|
|
116
|
+
switch (cmd.opcode) {
|
|
117
|
+
case Opcode.STANDBY:
|
|
118
|
+
console.log(' -> Standby requested');
|
|
119
|
+
break;
|
|
120
|
+
case Opcode.GIVE_DEVICE_POWER_STATUS:
|
|
121
|
+
console.log(' -> Power status requested');
|
|
122
|
+
break;
|
|
123
|
+
case Opcode.REQUEST_ACTIVE_SOURCE:
|
|
124
|
+
console.log(' -> Active source requested');
|
|
125
|
+
break;
|
|
126
|
+
case Opcode.SET_STREAM_PATH:
|
|
127
|
+
if (cmd.parameters.length >= 2) {
|
|
128
|
+
const physAddr = (cmd.parameters[0] << 8) | cmd.parameters[1];
|
|
129
|
+
console.log(` -> Stream path set to ${formatPhysicalAddress(physAddr)}`);
|
|
130
|
+
}
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
connect() {
|
|
136
|
+
const adapters = CECAdapter.detectAdapters();
|
|
137
|
+
if (adapters.length === 0) {
|
|
138
|
+
throw new Error('No CEC adapters found');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log('Detected adapters:');
|
|
142
|
+
adapters.forEach((a, i) => {
|
|
143
|
+
console.log(` ${i}: ${a.comName} (${a.comPath})`);
|
|
144
|
+
console.log(` Vendor: 0x${a.vendorId.toString(16)}, Product: 0x${a.productId.toString(16)}`);
|
|
145
|
+
console.log(` Firmware: v${a.firmwareVersion}`);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (!this.cec.open()) {
|
|
149
|
+
throw new Error('Failed to open CEC adapter');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
console.log('\nConnected to CEC adapter');
|
|
153
|
+
console.log('Library info:', this.cec.getLibInfo());
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async scanDevices() {
|
|
157
|
+
console.log('\nScanning CEC bus...');
|
|
158
|
+
this.cec.rescanActiveDevices();
|
|
159
|
+
|
|
160
|
+
// Small delay to let scan complete
|
|
161
|
+
await sleep(1000);
|
|
162
|
+
|
|
163
|
+
const activeDevices = this.cec.getActiveDevices();
|
|
164
|
+
console.log(`Found ${activeDevices.length} devices\n`);
|
|
165
|
+
|
|
166
|
+
for (const addr of activeDevices) {
|
|
167
|
+
const device = {
|
|
168
|
+
address: addr,
|
|
169
|
+
name: this.cec.getDeviceOSDName(addr),
|
|
170
|
+
vendorId: this.cec.getDeviceVendorId(addr),
|
|
171
|
+
physicalAddress: this.cec.getDevicePhysicalAddress(addr),
|
|
172
|
+
cecVersion: this.cec.getDeviceCecVersion(addr),
|
|
173
|
+
powerStatus: this.cec.getDevicePowerStatus(addr),
|
|
174
|
+
language: this.cec.getDeviceMenuLanguage(addr),
|
|
175
|
+
isActiveSource: this.cec.isActiveSource(addr)
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
this.devices.set(addr, device);
|
|
179
|
+
this.printDeviceInfo(device);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
printDeviceInfo(device) {
|
|
184
|
+
const addrName = Object.keys(LogicalAddress).find(k => LogicalAddress[k] === device.address) || device.address;
|
|
185
|
+
const powerName = Object.keys(PowerStatus).find(k => PowerStatus[k] === device.powerStatus) || device.powerStatus;
|
|
186
|
+
const versionName = Object.keys(Version).find(k => Version[k] === device.cecVersion) || device.cecVersion;
|
|
187
|
+
|
|
188
|
+
console.log(`Device: ${addrName} (${device.address})`);
|
|
189
|
+
console.log(` OSD Name: ${device.name || '(unknown)'}`);
|
|
190
|
+
console.log(` Physical Address: ${formatPhysicalAddress(device.physicalAddress)}`);
|
|
191
|
+
console.log(` Vendor: ${formatVendorId(device.vendorId)}`);
|
|
192
|
+
console.log(` CEC Version: ${versionName}`);
|
|
193
|
+
console.log(` Power Status: ${powerName}`);
|
|
194
|
+
console.log(` Menu Language: ${device.language || '(unknown)'}`);
|
|
195
|
+
console.log(` Active Source: ${device.isActiveSource ? 'Yes' : 'No'}`);
|
|
196
|
+
console.log();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Power control
|
|
200
|
+
async powerOn(address = LogicalAddress.TV) {
|
|
201
|
+
console.log(`Powering on device ${address}...`);
|
|
202
|
+
return this.cec.powerOnDevices(address);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async powerOff(address = LogicalAddress.BROADCAST) {
|
|
206
|
+
console.log(`Putting device ${address} into standby...`);
|
|
207
|
+
return this.cec.standbyDevices(address);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Source control
|
|
211
|
+
becomeActiveSource() {
|
|
212
|
+
console.log('Becoming active source...');
|
|
213
|
+
return this.cec.setActiveSource();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
becomeInactive() {
|
|
217
|
+
console.log('Becoming inactive...');
|
|
218
|
+
return this.cec.setInactiveView();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Volume control
|
|
222
|
+
async adjustVolume(direction, steps = 1) {
|
|
223
|
+
for (let i = 0; i < steps; i++) {
|
|
224
|
+
if (direction === 'up') {
|
|
225
|
+
this.cec.volumeUp();
|
|
226
|
+
} else {
|
|
227
|
+
this.cec.volumeDown();
|
|
228
|
+
}
|
|
229
|
+
await sleep(100);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
toggleMute() {
|
|
234
|
+
return this.cec.audioToggleMute();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
getAudioStatus() {
|
|
238
|
+
const status = this.cec.audioStatus();
|
|
239
|
+
const muted = (status & 0x80) !== 0;
|
|
240
|
+
const volume = status & 0x7F;
|
|
241
|
+
return { muted, volume };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Display message on TV
|
|
245
|
+
showMessage(message, address = LogicalAddress.TV) {
|
|
246
|
+
console.log(`Showing message on device ${address}: "${message}"`);
|
|
247
|
+
return this.cec.setOSDString(address, DisplayControl.DISPLAY_FOR_DEFAULT_TIME, message);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Send raw CEC command
|
|
251
|
+
sendCommand(initiator, destination, opcode, parameters = []) {
|
|
252
|
+
return this.cec.transmit({
|
|
253
|
+
initiator,
|
|
254
|
+
destination,
|
|
255
|
+
opcode,
|
|
256
|
+
parameters
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Simulate remote control
|
|
261
|
+
pressButton(address, button) {
|
|
262
|
+
this.cec.sendKeypress(address, button);
|
|
263
|
+
return sleep(100).then(() => this.cec.sendKeyRelease(address));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
close() {
|
|
267
|
+
console.log('Closing CEC connection...');
|
|
268
|
+
this.cec.close();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Helper functions
|
|
273
|
+
function formatPhysicalAddress(addr) {
|
|
274
|
+
return `${(addr >> 12) & 0xF}.${(addr >> 8) & 0xF}.${(addr >> 4) & 0xF}.${addr & 0xF}`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function formatVendorId(id) {
|
|
278
|
+
const vendors = {
|
|
279
|
+
0x000039: 'Toshiba',
|
|
280
|
+
0x0000F0: 'Samsung',
|
|
281
|
+
0x0005CD: 'Denon',
|
|
282
|
+
0x000678: 'Marantz',
|
|
283
|
+
0x000982: 'Loewe',
|
|
284
|
+
0x0009B0: 'Onkyo',
|
|
285
|
+
0x001582: 'Pulse-Eight',
|
|
286
|
+
0x001950: 'Harman Kardon',
|
|
287
|
+
0x001A11: 'Google',
|
|
288
|
+
0x00903E: 'Philips',
|
|
289
|
+
0x009053: 'Daewoo',
|
|
290
|
+
0x00A0DE: 'Yamaha',
|
|
291
|
+
0x00D0D5: 'Pioneer',
|
|
292
|
+
0x00E036: 'Sharp',
|
|
293
|
+
0x00E091: 'LG',
|
|
294
|
+
0x080046: 'Sony',
|
|
295
|
+
0x18C086: 'Broadcom',
|
|
296
|
+
0x6B746D: 'Vizio',
|
|
297
|
+
0x8065E9: 'Benq'
|
|
298
|
+
};
|
|
299
|
+
return vendors[id] || `0x${id.toString(16).padStart(6, '0')}`;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function sleep(ms) {
|
|
303
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Interactive CLI
|
|
307
|
+
async function interactiveMode(controller) {
|
|
308
|
+
const rl = readline.createInterface({
|
|
309
|
+
input: process.stdin,
|
|
310
|
+
output: process.stdout
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const prompt = () => {
|
|
314
|
+
rl.question('\ncec> ', async (input) => {
|
|
315
|
+
const [cmd, ...args] = input.trim().split(' ');
|
|
316
|
+
|
|
317
|
+
try {
|
|
318
|
+
switch (cmd) {
|
|
319
|
+
case 'scan':
|
|
320
|
+
await controller.scanDevices();
|
|
321
|
+
break;
|
|
322
|
+
case 'on':
|
|
323
|
+
await controller.powerOn(parseInt(args[0]) || LogicalAddress.TV);
|
|
324
|
+
break;
|
|
325
|
+
case 'off':
|
|
326
|
+
case 'standby':
|
|
327
|
+
await controller.powerOff(parseInt(args[0]) || LogicalAddress.BROADCAST);
|
|
328
|
+
break;
|
|
329
|
+
case 'active':
|
|
330
|
+
controller.becomeActiveSource();
|
|
331
|
+
break;
|
|
332
|
+
case 'inactive':
|
|
333
|
+
controller.becomeInactive();
|
|
334
|
+
break;
|
|
335
|
+
case 'vol+':
|
|
336
|
+
await controller.adjustVolume('up', parseInt(args[0]) || 1);
|
|
337
|
+
break;
|
|
338
|
+
case 'vol-':
|
|
339
|
+
await controller.adjustVolume('down', parseInt(args[0]) || 1);
|
|
340
|
+
break;
|
|
341
|
+
case 'mute':
|
|
342
|
+
controller.toggleMute();
|
|
343
|
+
break;
|
|
344
|
+
case 'audio':
|
|
345
|
+
const audio = controller.getAudioStatus();
|
|
346
|
+
console.log(`Volume: ${audio.volume}%, Muted: ${audio.muted}`);
|
|
347
|
+
break;
|
|
348
|
+
case 'msg':
|
|
349
|
+
controller.showMessage(args.join(' ') || 'Hello from Node.js!');
|
|
350
|
+
break;
|
|
351
|
+
case 'status':
|
|
352
|
+
const addr = parseInt(args[0]) || LogicalAddress.TV;
|
|
353
|
+
const power = controller.cec.getDevicePowerStatus(addr);
|
|
354
|
+
const powerName = Object.keys(PowerStatus).find(k => PowerStatus[k] === power);
|
|
355
|
+
console.log(`Device ${addr} power status: ${powerName}`);
|
|
356
|
+
break;
|
|
357
|
+
case 'config':
|
|
358
|
+
console.log(controller.cec.getCurrentConfiguration());
|
|
359
|
+
break;
|
|
360
|
+
case 'help':
|
|
361
|
+
console.log(`
|
|
362
|
+
Commands:
|
|
363
|
+
scan - Scan for CEC devices
|
|
364
|
+
on [addr] - Power on device (default: TV)
|
|
365
|
+
off [addr] - Standby device (default: all)
|
|
366
|
+
active - Become active source
|
|
367
|
+
inactive - Become inactive
|
|
368
|
+
vol+ [steps] - Volume up
|
|
369
|
+
vol- [steps] - Volume down
|
|
370
|
+
mute - Toggle mute
|
|
371
|
+
audio - Show audio status
|
|
372
|
+
msg <text> - Display message on TV
|
|
373
|
+
status [addr] - Get device power status
|
|
374
|
+
config - Show current configuration
|
|
375
|
+
quit - Exit
|
|
376
|
+
`);
|
|
377
|
+
break;
|
|
378
|
+
case 'quit':
|
|
379
|
+
case 'exit':
|
|
380
|
+
controller.close();
|
|
381
|
+
rl.close();
|
|
382
|
+
process.exit(0);
|
|
383
|
+
break;
|
|
384
|
+
case '':
|
|
385
|
+
break;
|
|
386
|
+
default:
|
|
387
|
+
console.log(`Unknown command: ${cmd}. Type 'help' for commands.`);
|
|
388
|
+
}
|
|
389
|
+
} catch (err) {
|
|
390
|
+
console.error('Error:', err.message);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
prompt();
|
|
394
|
+
});
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
prompt();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Main
|
|
401
|
+
async function main() {
|
|
402
|
+
const controller = new CECController();
|
|
403
|
+
|
|
404
|
+
process.on('SIGINT', () => {
|
|
405
|
+
console.log('\nInterrupted');
|
|
406
|
+
controller.close();
|
|
407
|
+
process.exit(0);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
controller.connect();
|
|
412
|
+
await controller.scanDevices();
|
|
413
|
+
|
|
414
|
+
console.log("Type 'help' for available commands\n");
|
|
415
|
+
await interactiveMode(controller);
|
|
416
|
+
} catch (err) {
|
|
417
|
+
console.error('Error:', err.message);
|
|
418
|
+
process.exit(1);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
main();
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CECAdapter,
|
|
3
|
+
LogicalAddress,
|
|
4
|
+
DeviceType,
|
|
5
|
+
PowerStatus,
|
|
6
|
+
UserControlCode,
|
|
7
|
+
DisplayControl
|
|
8
|
+
} from '../lib/index.js';
|
|
9
|
+
|
|
10
|
+
async function main() {
|
|
11
|
+
// Detect available adapters
|
|
12
|
+
const adapters = CECAdapter.detectAdapters();
|
|
13
|
+
console.log('Found adapters:', adapters);
|
|
14
|
+
|
|
15
|
+
if (adapters.length === 0) {
|
|
16
|
+
console.log('No CEC adapters found');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Create adapter instance
|
|
21
|
+
const cec = new CECAdapter({
|
|
22
|
+
deviceName: 'NodeCEC',
|
|
23
|
+
deviceTypes: [DeviceType.RECORDING_DEVICE],
|
|
24
|
+
activateSource: false
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Set up event listeners
|
|
28
|
+
cec.on('log', (msg) => {
|
|
29
|
+
console.log(`[LOG ${msg.level}] ${msg.message}`);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
cec.on('keypress', (key) => {
|
|
33
|
+
console.log(`Key pressed: ${key.keycode} (duration: ${key.duration}ms)`);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
cec.on('command', (cmd) => {
|
|
37
|
+
console.log(`Command received:`, cmd);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
cec.on('sourceActivated', (source) => {
|
|
41
|
+
console.log(`Source ${source.logicalAddress} activated: ${source.activated}`);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Open connection (auto-detect port)
|
|
45
|
+
console.log('Opening connection...');
|
|
46
|
+
if (!cec.open()) {
|
|
47
|
+
console.error('Failed to open CEC adapter');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
console.log('Connected!');
|
|
51
|
+
|
|
52
|
+
// Get library info
|
|
53
|
+
console.log('Library:', cec.getLibInfo());
|
|
54
|
+
|
|
55
|
+
// Scan for active devices
|
|
56
|
+
console.log('\nScanning for devices...');
|
|
57
|
+
const devices = cec.getActiveDevices();
|
|
58
|
+
console.log('Active devices:', devices);
|
|
59
|
+
|
|
60
|
+
// Get info about each device
|
|
61
|
+
for (const addr of devices) {
|
|
62
|
+
const name = cec.getDeviceOSDName(addr);
|
|
63
|
+
const vendor = cec.getDeviceVendorId(addr);
|
|
64
|
+
const power = cec.getDevicePowerStatus(addr);
|
|
65
|
+
const physical = cec.getDevicePhysicalAddress(addr);
|
|
66
|
+
|
|
67
|
+
console.log(`\nDevice ${addr}:`);
|
|
68
|
+
console.log(` Name: ${name}`);
|
|
69
|
+
console.log(` Vendor ID: 0x${vendor.toString(16)}`);
|
|
70
|
+
console.log(` Power: ${PowerStatus[Object.keys(PowerStatus).find(k => PowerStatus[k] === power)] || power}`);
|
|
71
|
+
console.log(` Physical Address: ${(physical >> 12) & 0xF}.${(physical >> 8) & 0xF}.${(physical >> 4) & 0xF}.${physical & 0xF}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Example: Power on the TV
|
|
75
|
+
// console.log('\nPowering on TV...');
|
|
76
|
+
// cec.powerOnDevices(LogicalAddress.TV);
|
|
77
|
+
|
|
78
|
+
// Example: Set as active source
|
|
79
|
+
// console.log('Setting as active source...');
|
|
80
|
+
// cec.setActiveSource();
|
|
81
|
+
|
|
82
|
+
// Example: Display message on TV
|
|
83
|
+
// cec.setOSDString(LogicalAddress.TV, DisplayControl.DISPLAY_FOR_DEFAULT_TIME, 'Hello from Node!');
|
|
84
|
+
|
|
85
|
+
// Example: Send remote button press
|
|
86
|
+
// cec.sendKeypress(LogicalAddress.TV, UserControlCode.VOLUME_UP);
|
|
87
|
+
|
|
88
|
+
// Keep running to receive events
|
|
89
|
+
console.log('\nListening for CEC events (Ctrl+C to exit)...');
|
|
90
|
+
|
|
91
|
+
process.on('SIGINT', () => {
|
|
92
|
+
console.log('\nClosing...');
|
|
93
|
+
cec.close();
|
|
94
|
+
process.exit(0);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
|
+
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
console.log('=== node-libcec Diagnostic Script ===\n');
|
|
9
|
+
console.log('Step 1: Loading native module...');
|
|
10
|
+
|
|
11
|
+
let native;
|
|
12
|
+
try {
|
|
13
|
+
native = require(join(__dirname, '../build/Release/cec.node'));
|
|
14
|
+
console.log(' OK: Loaded Release build');
|
|
15
|
+
} catch (e) {
|
|
16
|
+
try {
|
|
17
|
+
native = require(join(__dirname, '../build/Debug/cec.node'));
|
|
18
|
+
console.log(' OK: Loaded Debug build');
|
|
19
|
+
} catch (e2) {
|
|
20
|
+
console.error(' FAILED: Could not load native module');
|
|
21
|
+
console.error(' Error:', e2.message);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log(' Native module exports:', Object.keys(native));
|
|
27
|
+
|
|
28
|
+
console.log('\nStep 2: Detecting adapters (static method, no instance)...');
|
|
29
|
+
const detectStart = Date.now();
|
|
30
|
+
try {
|
|
31
|
+
const adapters = native.CECAdapter.detectAdapters();
|
|
32
|
+
console.log(` OK: Found ${adapters.length} adapter(s) in ${Date.now() - detectStart}ms`);
|
|
33
|
+
adapters.forEach((a, i) => {
|
|
34
|
+
console.log(` Adapter ${i}: ${a.comName} @ ${a.comPath}`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (adapters.length === 0) {
|
|
38
|
+
console.log('\n No adapters found. Make sure your CEC adapter is connected.');
|
|
39
|
+
console.log(' Try: ls -la /dev/ttyACM* /dev/ttyUSB*');
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.error(' FAILED:', e.message);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log('\nStep 3: Creating CECAdapter instance (no callbacks)...');
|
|
48
|
+
const createStart = Date.now();
|
|
49
|
+
let adapter;
|
|
50
|
+
try {
|
|
51
|
+
adapter = new native.CECAdapter({
|
|
52
|
+
deviceName: 'Diagnose',
|
|
53
|
+
deviceTypes: [1], // RECORDING_DEVICE
|
|
54
|
+
activateSource: false
|
|
55
|
+
});
|
|
56
|
+
console.log(` OK: Instance created in ${Date.now() - createStart}ms`);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
console.error(' FAILED:', e.message);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.log('\nStep 4: Setting up callbacks...');
|
|
63
|
+
const cbStart = Date.now();
|
|
64
|
+
try {
|
|
65
|
+
adapter.setCallbacks({
|
|
66
|
+
onLogMessage: (msg) => {
|
|
67
|
+
console.log(` [LOG] level=${msg.level}: ${msg.message}`);
|
|
68
|
+
},
|
|
69
|
+
onKeyPress: (key) => {
|
|
70
|
+
console.log(` [KEY] keycode=${key.keycode} duration=${key.duration}`);
|
|
71
|
+
},
|
|
72
|
+
onCommand: (cmd) => {
|
|
73
|
+
console.log(` [CMD] opcode=${cmd.opcode} from=${cmd.initiator} to=${cmd.destination}`);
|
|
74
|
+
},
|
|
75
|
+
onAlert: (alert) => {
|
|
76
|
+
console.log(` [ALERT] type=${alert.type}`);
|
|
77
|
+
},
|
|
78
|
+
onSourceActivated: (src) => {
|
|
79
|
+
console.log(` [SRC] address=${src.logicalAddress} activated=${src.activated}`);
|
|
80
|
+
},
|
|
81
|
+
onConfigurationChanged: (cfg) => {
|
|
82
|
+
console.log(` [CFG] changed`);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
console.log(` OK: Callbacks set in ${Date.now() - cbStart}ms`);
|
|
86
|
+
} catch (e) {
|
|
87
|
+
console.error(' FAILED:', e.message);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log('\nStep 5: Opening connection (this is where it might hang)...');
|
|
92
|
+
console.log(' Starting open() - will timeout after 30 seconds...');
|
|
93
|
+
const openStart = Date.now();
|
|
94
|
+
|
|
95
|
+
// Set a timeout to detect hang
|
|
96
|
+
const timeout = setTimeout(() => {
|
|
97
|
+
console.error(`\n TIMEOUT: open() has been running for 30 seconds`);
|
|
98
|
+
console.error(' This indicates a deadlock in the native code.');
|
|
99
|
+
console.error(' The callbacks are likely using BlockingCall instead of NonBlockingCall.');
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}, 30000);
|
|
102
|
+
|
|
103
|
+
let opened = false;
|
|
104
|
+
try {
|
|
105
|
+
opened = adapter.open();
|
|
106
|
+
clearTimeout(timeout);
|
|
107
|
+
console.log(` OK: open() returned ${opened} in ${Date.now() - openStart}ms`);
|
|
108
|
+
} catch (e) {
|
|
109
|
+
clearTimeout(timeout);
|
|
110
|
+
console.error(` FAILED: open() threw after ${Date.now() - openStart}ms`);
|
|
111
|
+
console.error(' Error:', e.message);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!opened) {
|
|
116
|
+
console.log('\n open() returned false - adapter could not be opened');
|
|
117
|
+
console.log(' Check permissions: ls -la /dev/ttyACM*');
|
|
118
|
+
console.log(' You may need: sudo usermod -a -G dialout $USER');
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log('\nStep 6: Getting library info...');
|
|
123
|
+
try {
|
|
124
|
+
const info = adapter.getLibInfo();
|
|
125
|
+
console.log(' OK:', info);
|
|
126
|
+
} catch (e) {
|
|
127
|
+
console.error(' FAILED:', e.message);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log('\nStep 7: Getting active devices...');
|
|
131
|
+
try {
|
|
132
|
+
const devices = adapter.getActiveDevices();
|
|
133
|
+
console.log(` OK: Found ${devices.length} active device(s):`, devices);
|
|
134
|
+
} catch (e) {
|
|
135
|
+
console.error(' FAILED:', e.message);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
console.log('\nStep 8: Closing connection...');
|
|
139
|
+
try {
|
|
140
|
+
adapter.close();
|
|
141
|
+
console.log(' OK: Closed');
|
|
142
|
+
} catch (e) {
|
|
143
|
+
console.error(' FAILED:', e.message);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.log('\n=== Diagnostic Complete ===');
|
|
147
|
+
console.log('If you see this message, the basic operations are working.');
|
|
148
|
+
process.exit(0);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-libcec",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "Node.js bindings for libcec - control CEC devices over HDMI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"files": [
|
|
42
42
|
"lib/",
|
|
43
43
|
"src/",
|
|
44
|
+
"examples/",
|
|
44
45
|
"binding.gyp"
|
|
45
46
|
],
|
|
46
47
|
"dependencies": {
|