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.
@@ -0,0 +1,909 @@
1
+ #include "cec_adapter.h"
2
+ #include "cec_callbacks.h"
3
+ #include <cstring>
4
+
5
+ Napi::FunctionReference CECAdapter::constructor;
6
+
7
+ CECAdapter::CECAdapter(const Napi::CallbackInfo& info)
8
+ : Napi::ObjectWrap<CECAdapter>(info), adapter_(nullptr), isOpen_(false) {
9
+ Napi::Env env = info.Env();
10
+
11
+ // Initialize configuration with defaults
12
+ config_.Clear();
13
+
14
+ // Set device name
15
+ std::string deviceName = "node-libcec";
16
+ if (info.Length() > 0 && info[0].IsObject()) {
17
+ Napi::Object options = info[0].As<Napi::Object>();
18
+ if (options.Has("deviceName") && options.Get("deviceName").IsString()) {
19
+ deviceName = options.Get("deviceName").As<Napi::String>().Utf8Value();
20
+ }
21
+ if (options.Has("deviceTypes") && options.Get("deviceTypes").IsArray()) {
22
+ Napi::Array types = options.Get("deviceTypes").As<Napi::Array>();
23
+ for (uint32_t i = 0; i < types.Length() && i < 5; i++) {
24
+ config_.deviceTypes.Add(static_cast<cec_device_type>(types.Get(i).As<Napi::Number>().Int32Value()));
25
+ }
26
+ } else {
27
+ config_.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
28
+ }
29
+ if (options.Has("physicalAddress") && options.Get("physicalAddress").IsNumber()) {
30
+ config_.iPhysicalAddress = static_cast<uint16_t>(options.Get("physicalAddress").As<Napi::Number>().Int32Value());
31
+ }
32
+ if (options.Has("baseDevice") && options.Get("baseDevice").IsNumber()) {
33
+ config_.baseDevice = static_cast<cec_logical_address>(options.Get("baseDevice").As<Napi::Number>().Int32Value());
34
+ }
35
+ if (options.Has("hdmiPort") && options.Get("hdmiPort").IsNumber()) {
36
+ config_.iHDMIPort = static_cast<uint8_t>(options.Get("hdmiPort").As<Napi::Number>().Int32Value());
37
+ }
38
+ if (options.Has("activateSource") && options.Get("activateSource").IsBoolean()) {
39
+ config_.bActivateSource = options.Get("activateSource").As<Napi::Boolean>().Value() ? 1 : 0;
40
+ }
41
+ } else {
42
+ config_.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
43
+ }
44
+
45
+ snprintf(config_.strDeviceName, LIBCEC_OSD_NAME_SIZE, "%s", deviceName.c_str());
46
+ config_.clientVersion = LIBCEC_VERSION_CURRENT;
47
+
48
+ // Initialize callback handler
49
+ callbackHandler_ = std::make_unique<CECCallbackHandler>(env);
50
+ config_.callbacks = callbackHandler_->GetCallbacks();
51
+ config_.callbackParam = callbackHandler_.get();
52
+
53
+ // Initialize the adapter
54
+ adapter_ = CECInitialise(&config_);
55
+ if (!adapter_) {
56
+ Napi::Error::New(env, "Failed to initialize libcec").ThrowAsJavaScriptException();
57
+ return;
58
+ }
59
+
60
+ adapter_->InitVideoStandalone();
61
+ }
62
+
63
+ CECAdapter::~CECAdapter() {
64
+ if (adapter_) {
65
+ if (isOpen_) {
66
+ adapter_->Close();
67
+ }
68
+ CECDestroy(adapter_);
69
+ adapter_ = nullptr;
70
+ }
71
+ }
72
+
73
+ Napi::Object CECAdapter::Init(Napi::Env env, Napi::Object exports) {
74
+ Napi::Function func = DefineClass(env, "CECAdapter", {
75
+ // Static methods
76
+ StaticMethod("detectAdapters", &CECAdapter::DetectAdapters),
77
+
78
+ // Instance methods
79
+ InstanceMethod("open", &CECAdapter::Open),
80
+ InstanceMethod("close", &CECAdapter::Close),
81
+ InstanceMethod("pingAdapter", &CECAdapter::PingAdapter),
82
+ InstanceMethod("getAdapterVendorId", &CECAdapter::GetAdapterVendorId),
83
+ InstanceMethod("getAdapterProductId", &CECAdapter::GetAdapterProductId),
84
+ InstanceMethod("getLibInfo", &CECAdapter::GetLibInfo),
85
+
86
+ InstanceMethod("powerOnDevices", &CECAdapter::PowerOnDevices),
87
+ InstanceMethod("standbyDevices", &CECAdapter::StandbyDevices),
88
+ InstanceMethod("setActiveSource", &CECAdapter::SetActiveSource),
89
+ InstanceMethod("setInactiveView", &CECAdapter::SetInactiveView),
90
+
91
+ InstanceMethod("transmit", &CECAdapter::Transmit),
92
+ InstanceMethod("sendKeypress", &CECAdapter::SendKeypress),
93
+ InstanceMethod("sendKeyRelease", &CECAdapter::SendKeyRelease),
94
+
95
+ InstanceMethod("getDeviceCecVersion", &CECAdapter::GetDeviceCecVersion),
96
+ InstanceMethod("getDeviceMenuLanguage", &CECAdapter::GetDeviceMenuLanguage),
97
+ InstanceMethod("getDeviceVendorId", &CECAdapter::GetDeviceVendorId),
98
+ InstanceMethod("getDevicePowerStatus", &CECAdapter::GetDevicePowerStatus),
99
+ InstanceMethod("getDeviceOSDName", &CECAdapter::GetDeviceOSDName),
100
+ InstanceMethod("getDevicePhysicalAddress", &CECAdapter::GetDevicePhysicalAddress),
101
+
102
+ InstanceMethod("pollDevice", &CECAdapter::PollDevice),
103
+ InstanceMethod("getActiveDevices", &CECAdapter::GetActiveDevices),
104
+ InstanceMethod("isActiveDevice", &CECAdapter::IsActiveDevice),
105
+ InstanceMethod("isActiveDeviceType", &CECAdapter::IsActiveDeviceType),
106
+ InstanceMethod("getActiveSource", &CECAdapter::GetActiveSource),
107
+ InstanceMethod("isActiveSource", &CECAdapter::IsActiveSource),
108
+ InstanceMethod("isLibCECActiveSource", &CECAdapter::IsLibCECActiveSource),
109
+
110
+ InstanceMethod("volumeUp", &CECAdapter::VolumeUp),
111
+ InstanceMethod("volumeDown", &CECAdapter::VolumeDown),
112
+ InstanceMethod("muteAudio", &CECAdapter::MuteAudio),
113
+ InstanceMethod("audioToggleMute", &CECAdapter::AudioToggleMute),
114
+ InstanceMethod("audioStatus", &CECAdapter::AudioStatus),
115
+
116
+ InstanceMethod("setOSDString", &CECAdapter::SetOSDString),
117
+
118
+ InstanceMethod("setLogicalAddress", &CECAdapter::SetLogicalAddress),
119
+ InstanceMethod("setPhysicalAddress", &CECAdapter::SetPhysicalAddress),
120
+ InstanceMethod("setHDMIPort", &CECAdapter::SetHDMIPort),
121
+ InstanceMethod("getLogicalAddresses", &CECAdapter::GetLogicalAddresses),
122
+
123
+ InstanceMethod("getCurrentConfiguration", &CECAdapter::GetCurrentConfiguration),
124
+ InstanceMethod("setConfiguration", &CECAdapter::SetConfiguration),
125
+ InstanceMethod("rescanActiveDevices", &CECAdapter::RescanActiveDevices),
126
+
127
+ InstanceMethod("setCallbacks", &CECAdapter::SetCallbacks),
128
+ InstanceMethod("disableCallbacks", &CECAdapter::DisableCallbacks),
129
+ });
130
+
131
+ constructor = Napi::Persistent(func);
132
+ constructor.SuppressDestruct();
133
+
134
+ exports.Set("CECAdapter", func);
135
+ return exports;
136
+ }
137
+
138
+ Napi::Value CECAdapter::DetectAdapters(const Napi::CallbackInfo& info) {
139
+ Napi::Env env = info.Env();
140
+
141
+ // Create temporary configuration for detection
142
+ libcec_configuration config;
143
+ config.Clear();
144
+ config.clientVersion = LIBCEC_VERSION_CURRENT;
145
+ config.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
146
+ snprintf(config.strDeviceName, LIBCEC_OSD_NAME_SIZE, "detector");
147
+
148
+ ICECAdapter* tempAdapter = CECInitialise(&config);
149
+ if (!tempAdapter) {
150
+ return Napi::Array::New(env, 0);
151
+ }
152
+
153
+ cec_adapter_descriptor adapters[10];
154
+ int8_t count = tempAdapter->DetectAdapters(adapters, 10);
155
+
156
+ CECDestroy(tempAdapter);
157
+
158
+ Napi::Array result = Napi::Array::New(env, count > 0 ? count : 0);
159
+ for (int8_t i = 0; i < count; i++) {
160
+ Napi::Object adapterObj = Napi::Object::New(env);
161
+ adapterObj.Set("comPath", Napi::String::New(env, adapters[i].strComPath));
162
+ adapterObj.Set("comName", Napi::String::New(env, adapters[i].strComName));
163
+ adapterObj.Set("vendorId", Napi::Number::New(env, adapters[i].iVendorId));
164
+ adapterObj.Set("productId", Napi::Number::New(env, adapters[i].iProductId));
165
+ adapterObj.Set("firmwareVersion", Napi::Number::New(env, adapters[i].iFirmwareVersion));
166
+ adapterObj.Set("physicalAddress", Napi::Number::New(env, adapters[i].iPhysicalAddress));
167
+ adapterObj.Set("firmwareBuildDate", Napi::Number::New(env, static_cast<double>(adapters[i].iFirmwareBuildDate)));
168
+ adapterObj.Set("adapterType", Napi::Number::New(env, static_cast<int>(adapters[i].adapterType)));
169
+ result.Set(static_cast<uint32_t>(i), adapterObj);
170
+ }
171
+
172
+ return result;
173
+ }
174
+
175
+ Napi::Value CECAdapter::Open(const Napi::CallbackInfo& info) {
176
+ Napi::Env env = info.Env();
177
+ std::lock_guard<std::mutex> lock(mutex_);
178
+
179
+ if (!adapter_) {
180
+ Napi::Error::New(env, "Adapter not initialized").ThrowAsJavaScriptException();
181
+ return env.Undefined();
182
+ }
183
+
184
+ std::string port;
185
+ uint32_t timeout = 10000;
186
+
187
+ if (info.Length() > 0 && info[0].IsString()) {
188
+ port = info[0].As<Napi::String>().Utf8Value();
189
+ } else {
190
+ // Auto-detect adapter
191
+ cec_adapter_descriptor adapters[1];
192
+ int8_t count = adapter_->DetectAdapters(adapters, 1);
193
+ if (count < 1) {
194
+ return Napi::Boolean::New(env, false);
195
+ }
196
+ port = adapters[0].strComPath;
197
+ }
198
+
199
+ if (info.Length() > 1 && info[1].IsNumber()) {
200
+ timeout = info[1].As<Napi::Number>().Uint32Value();
201
+ }
202
+
203
+ bool result = adapter_->Open(port.c_str(), timeout);
204
+ isOpen_ = result;
205
+ return Napi::Boolean::New(env, result);
206
+ }
207
+
208
+ Napi::Value CECAdapter::Close(const Napi::CallbackInfo& info) {
209
+ Napi::Env env = info.Env();
210
+ std::lock_guard<std::mutex> lock(mutex_);
211
+
212
+ if (adapter_ && isOpen_) {
213
+ adapter_->Close();
214
+ isOpen_ = false;
215
+ }
216
+ return env.Undefined();
217
+ }
218
+
219
+ Napi::Value CECAdapter::PingAdapter(const Napi::CallbackInfo& info) {
220
+ Napi::Env env = info.Env();
221
+ std::lock_guard<std::mutex> lock(mutex_);
222
+
223
+ if (!adapter_ || !isOpen_) {
224
+ return Napi::Boolean::New(env, false);
225
+ }
226
+ return Napi::Boolean::New(env, adapter_->PingAdapter());
227
+ }
228
+
229
+ Napi::Value CECAdapter::GetAdapterVendorId(const Napi::CallbackInfo& info) {
230
+ Napi::Env env = info.Env();
231
+ std::lock_guard<std::mutex> lock(mutex_);
232
+
233
+ if (!adapter_) {
234
+ return Napi::Number::New(env, 0);
235
+ }
236
+ return Napi::Number::New(env, adapter_->GetAdapterVendorId());
237
+ }
238
+
239
+ Napi::Value CECAdapter::GetAdapterProductId(const Napi::CallbackInfo& info) {
240
+ Napi::Env env = info.Env();
241
+ std::lock_guard<std::mutex> lock(mutex_);
242
+
243
+ if (!adapter_) {
244
+ return Napi::Number::New(env, 0);
245
+ }
246
+ return Napi::Number::New(env, adapter_->GetAdapterProductId());
247
+ }
248
+
249
+ Napi::Value CECAdapter::GetLibInfo(const Napi::CallbackInfo& info) {
250
+ Napi::Env env = info.Env();
251
+ std::lock_guard<std::mutex> lock(mutex_);
252
+
253
+ if (!adapter_) {
254
+ return Napi::String::New(env, "");
255
+ }
256
+ return Napi::String::New(env, adapter_->GetLibInfo());
257
+ }
258
+
259
+ Napi::Value CECAdapter::PowerOnDevices(const Napi::CallbackInfo& info) {
260
+ Napi::Env env = info.Env();
261
+ std::lock_guard<std::mutex> lock(mutex_);
262
+
263
+ if (!adapter_ || !isOpen_) {
264
+ return Napi::Boolean::New(env, false);
265
+ }
266
+
267
+ cec_logical_address address = CECDEVICE_TV;
268
+ if (info.Length() > 0 && info[0].IsNumber()) {
269
+ address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
270
+ }
271
+
272
+ return Napi::Boolean::New(env, adapter_->PowerOnDevices(address));
273
+ }
274
+
275
+ Napi::Value CECAdapter::StandbyDevices(const Napi::CallbackInfo& info) {
276
+ Napi::Env env = info.Env();
277
+ std::lock_guard<std::mutex> lock(mutex_);
278
+
279
+ if (!adapter_ || !isOpen_) {
280
+ return Napi::Boolean::New(env, false);
281
+ }
282
+
283
+ cec_logical_address address = CECDEVICE_BROADCAST;
284
+ if (info.Length() > 0 && info[0].IsNumber()) {
285
+ address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
286
+ }
287
+
288
+ return Napi::Boolean::New(env, adapter_->StandbyDevices(address));
289
+ }
290
+
291
+ Napi::Value CECAdapter::SetActiveSource(const Napi::CallbackInfo& info) {
292
+ Napi::Env env = info.Env();
293
+ std::lock_guard<std::mutex> lock(mutex_);
294
+
295
+ if (!adapter_ || !isOpen_) {
296
+ return Napi::Boolean::New(env, false);
297
+ }
298
+
299
+ cec_device_type type = CEC_DEVICE_TYPE_RESERVED;
300
+ if (info.Length() > 0 && info[0].IsNumber()) {
301
+ type = static_cast<cec_device_type>(info[0].As<Napi::Number>().Int32Value());
302
+ }
303
+
304
+ return Napi::Boolean::New(env, adapter_->SetActiveSource(type));
305
+ }
306
+
307
+ Napi::Value CECAdapter::SetInactiveView(const Napi::CallbackInfo& info) {
308
+ Napi::Env env = info.Env();
309
+ std::lock_guard<std::mutex> lock(mutex_);
310
+
311
+ if (!adapter_ || !isOpen_) {
312
+ return Napi::Boolean::New(env, false);
313
+ }
314
+
315
+ return Napi::Boolean::New(env, adapter_->SetInactiveView());
316
+ }
317
+
318
+ Napi::Value CECAdapter::Transmit(const Napi::CallbackInfo& info) {
319
+ Napi::Env env = info.Env();
320
+ std::lock_guard<std::mutex> lock(mutex_);
321
+
322
+ if (!adapter_ || !isOpen_) {
323
+ return Napi::Boolean::New(env, false);
324
+ }
325
+
326
+ if (info.Length() < 1 || !info[0].IsObject()) {
327
+ Napi::TypeError::New(env, "Expected command object").ThrowAsJavaScriptException();
328
+ return env.Undefined();
329
+ }
330
+
331
+ Napi::Object cmdObj = info[0].As<Napi::Object>();
332
+ cec_command command;
333
+ command.Clear();
334
+
335
+ if (cmdObj.Has("initiator")) {
336
+ command.initiator = static_cast<cec_logical_address>(cmdObj.Get("initiator").As<Napi::Number>().Int32Value());
337
+ }
338
+ if (cmdObj.Has("destination")) {
339
+ command.destination = static_cast<cec_logical_address>(cmdObj.Get("destination").As<Napi::Number>().Int32Value());
340
+ }
341
+ if (cmdObj.Has("opcode")) {
342
+ command.opcode = static_cast<cec_opcode>(cmdObj.Get("opcode").As<Napi::Number>().Int32Value());
343
+ command.opcode_set = 1;
344
+ }
345
+ if (cmdObj.Has("parameters") && cmdObj.Get("parameters").IsArray()) {
346
+ Napi::Array params = cmdObj.Get("parameters").As<Napi::Array>();
347
+ for (uint32_t i = 0; i < params.Length() && i < 64; i++) {
348
+ command.parameters.PushBack(static_cast<uint8_t>(params.Get(i).As<Napi::Number>().Int32Value()));
349
+ }
350
+ }
351
+
352
+ return Napi::Boolean::New(env, adapter_->Transmit(command));
353
+ }
354
+
355
+ Napi::Value CECAdapter::SendKeypress(const Napi::CallbackInfo& info) {
356
+ Napi::Env env = info.Env();
357
+ std::lock_guard<std::mutex> lock(mutex_);
358
+
359
+ if (!adapter_ || !isOpen_) {
360
+ return Napi::Boolean::New(env, false);
361
+ }
362
+
363
+ if (info.Length() < 2) {
364
+ Napi::TypeError::New(env, "Expected destination and keycode").ThrowAsJavaScriptException();
365
+ return env.Undefined();
366
+ }
367
+
368
+ cec_logical_address destination = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
369
+ cec_user_control_code key = static_cast<cec_user_control_code>(info[1].As<Napi::Number>().Int32Value());
370
+ bool wait = false;
371
+ if (info.Length() > 2 && info[2].IsBoolean()) {
372
+ wait = info[2].As<Napi::Boolean>().Value();
373
+ }
374
+
375
+ return Napi::Boolean::New(env, adapter_->SendKeypress(destination, key, wait));
376
+ }
377
+
378
+ Napi::Value CECAdapter::SendKeyRelease(const Napi::CallbackInfo& info) {
379
+ Napi::Env env = info.Env();
380
+ std::lock_guard<std::mutex> lock(mutex_);
381
+
382
+ if (!adapter_ || !isOpen_) {
383
+ return Napi::Boolean::New(env, false);
384
+ }
385
+
386
+ if (info.Length() < 1) {
387
+ Napi::TypeError::New(env, "Expected destination").ThrowAsJavaScriptException();
388
+ return env.Undefined();
389
+ }
390
+
391
+ cec_logical_address destination = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
392
+ bool wait = false;
393
+ if (info.Length() > 1 && info[1].IsBoolean()) {
394
+ wait = info[1].As<Napi::Boolean>().Value();
395
+ }
396
+
397
+ return Napi::Boolean::New(env, adapter_->SendKeyRelease(destination, wait));
398
+ }
399
+
400
+ Napi::Value CECAdapter::GetDeviceCecVersion(const Napi::CallbackInfo& info) {
401
+ Napi::Env env = info.Env();
402
+ std::lock_guard<std::mutex> lock(mutex_);
403
+
404
+ if (!adapter_ || !isOpen_) {
405
+ return Napi::Number::New(env, -1);
406
+ }
407
+
408
+ if (info.Length() < 1 || !info[0].IsNumber()) {
409
+ Napi::TypeError::New(env, "Expected logical address").ThrowAsJavaScriptException();
410
+ return env.Undefined();
411
+ }
412
+
413
+ cec_logical_address address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
414
+ return Napi::Number::New(env, static_cast<int>(adapter_->GetDeviceCecVersion(address)));
415
+ }
416
+
417
+ Napi::Value CECAdapter::GetDeviceMenuLanguage(const Napi::CallbackInfo& info) {
418
+ Napi::Env env = info.Env();
419
+ std::lock_guard<std::mutex> lock(mutex_);
420
+
421
+ if (!adapter_ || !isOpen_) {
422
+ return Napi::String::New(env, "");
423
+ }
424
+
425
+ if (info.Length() < 1 || !info[0].IsNumber()) {
426
+ Napi::TypeError::New(env, "Expected logical address").ThrowAsJavaScriptException();
427
+ return env.Undefined();
428
+ }
429
+
430
+ cec_logical_address address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
431
+ std::string lang = adapter_->GetDeviceMenuLanguage(address);
432
+ return Napi::String::New(env, lang);
433
+ }
434
+
435
+ Napi::Value CECAdapter::GetDeviceVendorId(const Napi::CallbackInfo& info) {
436
+ Napi::Env env = info.Env();
437
+ std::lock_guard<std::mutex> lock(mutex_);
438
+
439
+ if (!adapter_ || !isOpen_) {
440
+ return Napi::Number::New(env, 0);
441
+ }
442
+
443
+ if (info.Length() < 1 || !info[0].IsNumber()) {
444
+ Napi::TypeError::New(env, "Expected logical address").ThrowAsJavaScriptException();
445
+ return env.Undefined();
446
+ }
447
+
448
+ cec_logical_address address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
449
+ return Napi::Number::New(env, adapter_->GetDeviceVendorId(address));
450
+ }
451
+
452
+ Napi::Value CECAdapter::GetDevicePowerStatus(const Napi::CallbackInfo& info) {
453
+ Napi::Env env = info.Env();
454
+ std::lock_guard<std::mutex> lock(mutex_);
455
+
456
+ if (!adapter_ || !isOpen_) {
457
+ return Napi::Number::New(env, -1);
458
+ }
459
+
460
+ if (info.Length() < 1 || !info[0].IsNumber()) {
461
+ Napi::TypeError::New(env, "Expected logical address").ThrowAsJavaScriptException();
462
+ return env.Undefined();
463
+ }
464
+
465
+ cec_logical_address address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
466
+ return Napi::Number::New(env, static_cast<int>(adapter_->GetDevicePowerStatus(address)));
467
+ }
468
+
469
+ Napi::Value CECAdapter::GetDeviceOSDName(const Napi::CallbackInfo& info) {
470
+ Napi::Env env = info.Env();
471
+ std::lock_guard<std::mutex> lock(mutex_);
472
+
473
+ if (!adapter_ || !isOpen_) {
474
+ return Napi::String::New(env, "");
475
+ }
476
+
477
+ if (info.Length() < 1 || !info[0].IsNumber()) {
478
+ Napi::TypeError::New(env, "Expected logical address").ThrowAsJavaScriptException();
479
+ return env.Undefined();
480
+ }
481
+
482
+ cec_logical_address address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
483
+ std::string name = adapter_->GetDeviceOSDName(address);
484
+ return Napi::String::New(env, name);
485
+ }
486
+
487
+ Napi::Value CECAdapter::GetDevicePhysicalAddress(const Napi::CallbackInfo& info) {
488
+ Napi::Env env = info.Env();
489
+ std::lock_guard<std::mutex> lock(mutex_);
490
+
491
+ if (!adapter_ || !isOpen_) {
492
+ return Napi::Number::New(env, 0);
493
+ }
494
+
495
+ if (info.Length() < 1 || !info[0].IsNumber()) {
496
+ Napi::TypeError::New(env, "Expected logical address").ThrowAsJavaScriptException();
497
+ return env.Undefined();
498
+ }
499
+
500
+ cec_logical_address address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
501
+ return Napi::Number::New(env, adapter_->GetDevicePhysicalAddress(address));
502
+ }
503
+
504
+ Napi::Value CECAdapter::PollDevice(const Napi::CallbackInfo& info) {
505
+ Napi::Env env = info.Env();
506
+ std::lock_guard<std::mutex> lock(mutex_);
507
+
508
+ if (!adapter_ || !isOpen_) {
509
+ return Napi::Boolean::New(env, false);
510
+ }
511
+
512
+ if (info.Length() < 1 || !info[0].IsNumber()) {
513
+ Napi::TypeError::New(env, "Expected logical address").ThrowAsJavaScriptException();
514
+ return env.Undefined();
515
+ }
516
+
517
+ cec_logical_address address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
518
+ return Napi::Boolean::New(env, adapter_->PollDevice(address));
519
+ }
520
+
521
+ Napi::Value CECAdapter::GetActiveDevices(const Napi::CallbackInfo& info) {
522
+ Napi::Env env = info.Env();
523
+ std::lock_guard<std::mutex> lock(mutex_);
524
+
525
+ Napi::Array result = Napi::Array::New(env);
526
+
527
+ if (!adapter_ || !isOpen_) {
528
+ return result;
529
+ }
530
+
531
+ cec_logical_addresses addresses = adapter_->GetActiveDevices();
532
+ uint32_t idx = 0;
533
+ for (int i = 0; i < 16; i++) {
534
+ if (addresses.IsSet(static_cast<cec_logical_address>(i))) {
535
+ result.Set(idx++, Napi::Number::New(env, i));
536
+ }
537
+ }
538
+
539
+ return result;
540
+ }
541
+
542
+ Napi::Value CECAdapter::IsActiveDevice(const Napi::CallbackInfo& info) {
543
+ Napi::Env env = info.Env();
544
+ std::lock_guard<std::mutex> lock(mutex_);
545
+
546
+ if (!adapter_ || !isOpen_) {
547
+ return Napi::Boolean::New(env, false);
548
+ }
549
+
550
+ if (info.Length() < 1 || !info[0].IsNumber()) {
551
+ Napi::TypeError::New(env, "Expected logical address").ThrowAsJavaScriptException();
552
+ return env.Undefined();
553
+ }
554
+
555
+ cec_logical_address address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
556
+ return Napi::Boolean::New(env, adapter_->IsActiveDevice(address));
557
+ }
558
+
559
+ Napi::Value CECAdapter::IsActiveDeviceType(const Napi::CallbackInfo& info) {
560
+ Napi::Env env = info.Env();
561
+ std::lock_guard<std::mutex> lock(mutex_);
562
+
563
+ if (!adapter_ || !isOpen_) {
564
+ return Napi::Boolean::New(env, false);
565
+ }
566
+
567
+ if (info.Length() < 1 || !info[0].IsNumber()) {
568
+ Napi::TypeError::New(env, "Expected device type").ThrowAsJavaScriptException();
569
+ return env.Undefined();
570
+ }
571
+
572
+ cec_device_type type = static_cast<cec_device_type>(info[0].As<Napi::Number>().Int32Value());
573
+ return Napi::Boolean::New(env, adapter_->IsActiveDeviceType(type));
574
+ }
575
+
576
+ Napi::Value CECAdapter::GetActiveSource(const Napi::CallbackInfo& info) {
577
+ Napi::Env env = info.Env();
578
+ std::lock_guard<std::mutex> lock(mutex_);
579
+
580
+ if (!adapter_ || !isOpen_) {
581
+ return Napi::Number::New(env, -1);
582
+ }
583
+
584
+ return Napi::Number::New(env, static_cast<int>(adapter_->GetActiveSource()));
585
+ }
586
+
587
+ Napi::Value CECAdapter::IsActiveSource(const Napi::CallbackInfo& info) {
588
+ Napi::Env env = info.Env();
589
+ std::lock_guard<std::mutex> lock(mutex_);
590
+
591
+ if (!adapter_ || !isOpen_) {
592
+ return Napi::Boolean::New(env, false);
593
+ }
594
+
595
+ if (info.Length() < 1 || !info[0].IsNumber()) {
596
+ Napi::TypeError::New(env, "Expected logical address").ThrowAsJavaScriptException();
597
+ return env.Undefined();
598
+ }
599
+
600
+ cec_logical_address address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
601
+ return Napi::Boolean::New(env, adapter_->IsActiveSource(address));
602
+ }
603
+
604
+ Napi::Value CECAdapter::IsLibCECActiveSource(const Napi::CallbackInfo& info) {
605
+ Napi::Env env = info.Env();
606
+ std::lock_guard<std::mutex> lock(mutex_);
607
+
608
+ if (!adapter_ || !isOpen_) {
609
+ return Napi::Boolean::New(env, false);
610
+ }
611
+
612
+ return Napi::Boolean::New(env, adapter_->IsLibCECActiveSource());
613
+ }
614
+
615
+ Napi::Value CECAdapter::VolumeUp(const Napi::CallbackInfo& info) {
616
+ Napi::Env env = info.Env();
617
+ std::lock_guard<std::mutex> lock(mutex_);
618
+
619
+ if (!adapter_ || !isOpen_) {
620
+ return Napi::Number::New(env, 0);
621
+ }
622
+
623
+ bool sendRelease = true;
624
+ if (info.Length() > 0 && info[0].IsBoolean()) {
625
+ sendRelease = info[0].As<Napi::Boolean>().Value();
626
+ }
627
+
628
+ return Napi::Number::New(env, adapter_->VolumeUp(sendRelease));
629
+ }
630
+
631
+ Napi::Value CECAdapter::VolumeDown(const Napi::CallbackInfo& info) {
632
+ Napi::Env env = info.Env();
633
+ std::lock_guard<std::mutex> lock(mutex_);
634
+
635
+ if (!adapter_ || !isOpen_) {
636
+ return Napi::Number::New(env, 0);
637
+ }
638
+
639
+ bool sendRelease = true;
640
+ if (info.Length() > 0 && info[0].IsBoolean()) {
641
+ sendRelease = info[0].As<Napi::Boolean>().Value();
642
+ }
643
+
644
+ return Napi::Number::New(env, adapter_->VolumeDown(sendRelease));
645
+ }
646
+
647
+ Napi::Value CECAdapter::MuteAudio(const Napi::CallbackInfo& info) {
648
+ Napi::Env env = info.Env();
649
+ std::lock_guard<std::mutex> lock(mutex_);
650
+
651
+ if (!adapter_ || !isOpen_) {
652
+ return Napi::Number::New(env, 0);
653
+ }
654
+
655
+ return Napi::Number::New(env, adapter_->MuteAudio());
656
+ }
657
+
658
+ Napi::Value CECAdapter::AudioToggleMute(const Napi::CallbackInfo& info) {
659
+ Napi::Env env = info.Env();
660
+ std::lock_guard<std::mutex> lock(mutex_);
661
+
662
+ if (!adapter_ || !isOpen_) {
663
+ return Napi::Number::New(env, 0);
664
+ }
665
+
666
+ return Napi::Number::New(env, adapter_->AudioToggleMute());
667
+ }
668
+
669
+ Napi::Value CECAdapter::AudioStatus(const Napi::CallbackInfo& info) {
670
+ Napi::Env env = info.Env();
671
+ std::lock_guard<std::mutex> lock(mutex_);
672
+
673
+ if (!adapter_ || !isOpen_) {
674
+ return Napi::Number::New(env, 0);
675
+ }
676
+
677
+ return Napi::Number::New(env, adapter_->AudioStatus());
678
+ }
679
+
680
+ Napi::Value CECAdapter::SetOSDString(const Napi::CallbackInfo& info) {
681
+ Napi::Env env = info.Env();
682
+ std::lock_guard<std::mutex> lock(mutex_);
683
+
684
+ if (!adapter_ || !isOpen_) {
685
+ return Napi::Boolean::New(env, false);
686
+ }
687
+
688
+ if (info.Length() < 3) {
689
+ Napi::TypeError::New(env, "Expected address, duration, and message").ThrowAsJavaScriptException();
690
+ return env.Undefined();
691
+ }
692
+
693
+ cec_logical_address address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
694
+ cec_display_control duration = static_cast<cec_display_control>(info[1].As<Napi::Number>().Int32Value());
695
+ std::string message = info[2].As<Napi::String>().Utf8Value();
696
+
697
+ return Napi::Boolean::New(env, adapter_->SetOSDString(address, duration, message.c_str()));
698
+ }
699
+
700
+ Napi::Value CECAdapter::SetLogicalAddress(const Napi::CallbackInfo& info) {
701
+ Napi::Env env = info.Env();
702
+ std::lock_guard<std::mutex> lock(mutex_);
703
+
704
+ if (!adapter_ || !isOpen_) {
705
+ return Napi::Boolean::New(env, false);
706
+ }
707
+
708
+ cec_logical_address address = CECDEVICE_PLAYBACKDEVICE1;
709
+ if (info.Length() > 0 && info[0].IsNumber()) {
710
+ address = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
711
+ }
712
+
713
+ return Napi::Boolean::New(env, adapter_->SetLogicalAddress(address));
714
+ }
715
+
716
+ Napi::Value CECAdapter::SetPhysicalAddress(const Napi::CallbackInfo& info) {
717
+ Napi::Env env = info.Env();
718
+ std::lock_guard<std::mutex> lock(mutex_);
719
+
720
+ if (!adapter_ || !isOpen_) {
721
+ return Napi::Boolean::New(env, false);
722
+ }
723
+
724
+ uint16_t address = CEC_DEFAULT_PHYSICAL_ADDRESS;
725
+ if (info.Length() > 0 && info[0].IsNumber()) {
726
+ address = static_cast<uint16_t>(info[0].As<Napi::Number>().Int32Value());
727
+ }
728
+
729
+ return Napi::Boolean::New(env, adapter_->SetPhysicalAddress(address));
730
+ }
731
+
732
+ Napi::Value CECAdapter::SetHDMIPort(const Napi::CallbackInfo& info) {
733
+ Napi::Env env = info.Env();
734
+ std::lock_guard<std::mutex> lock(mutex_);
735
+
736
+ if (!adapter_ || !isOpen_) {
737
+ return Napi::Boolean::New(env, false);
738
+ }
739
+
740
+ if (info.Length() < 2) {
741
+ Napi::TypeError::New(env, "Expected base device and port number").ThrowAsJavaScriptException();
742
+ return env.Undefined();
743
+ }
744
+
745
+ cec_logical_address baseDevice = static_cast<cec_logical_address>(info[0].As<Napi::Number>().Int32Value());
746
+ uint8_t port = static_cast<uint8_t>(info[1].As<Napi::Number>().Int32Value());
747
+
748
+ return Napi::Boolean::New(env, adapter_->SetHDMIPort(baseDevice, port));
749
+ }
750
+
751
+ Napi::Value CECAdapter::GetLogicalAddresses(const Napi::CallbackInfo& info) {
752
+ Napi::Env env = info.Env();
753
+ std::lock_guard<std::mutex> lock(mutex_);
754
+
755
+ Napi::Object result = Napi::Object::New(env);
756
+
757
+ if (!adapter_ || !isOpen_) {
758
+ result.Set("primary", Napi::Number::New(env, -1));
759
+ result.Set("addresses", Napi::Array::New(env));
760
+ return result;
761
+ }
762
+
763
+ cec_logical_addresses addresses = adapter_->GetLogicalAddresses();
764
+ result.Set("primary", Napi::Number::New(env, static_cast<int>(addresses.primary)));
765
+
766
+ Napi::Array arr = Napi::Array::New(env);
767
+ uint32_t idx = 0;
768
+ for (int i = 0; i < 16; i++) {
769
+ if (addresses.IsSet(static_cast<cec_logical_address>(i))) {
770
+ arr.Set(idx++, Napi::Number::New(env, i));
771
+ }
772
+ }
773
+ result.Set("addresses", arr);
774
+
775
+ return result;
776
+ }
777
+
778
+ Napi::Value CECAdapter::GetCurrentConfiguration(const Napi::CallbackInfo& info) {
779
+ Napi::Env env = info.Env();
780
+ std::lock_guard<std::mutex> lock(mutex_);
781
+
782
+ Napi::Object result = Napi::Object::New(env);
783
+
784
+ if (!adapter_) {
785
+ return result;
786
+ }
787
+
788
+ libcec_configuration config;
789
+ config.Clear();
790
+
791
+ if (adapter_->GetCurrentConfiguration(&config)) {
792
+ result.Set("deviceName", Napi::String::New(env, config.strDeviceName));
793
+ result.Set("physicalAddress", Napi::Number::New(env, config.iPhysicalAddress));
794
+ result.Set("baseDevice", Napi::Number::New(env, static_cast<int>(config.baseDevice)));
795
+ result.Set("hdmiPort", Napi::Number::New(env, config.iHDMIPort));
796
+ result.Set("clientVersion", Napi::Number::New(env, static_cast<double>(config.clientVersion)));
797
+ result.Set("serverVersion", Napi::Number::New(env, static_cast<double>(config.serverVersion)));
798
+ result.Set("activateSource", Napi::Boolean::New(env, config.bActivateSource != 0));
799
+
800
+ Napi::Array deviceTypes = Napi::Array::New(env);
801
+ uint32_t deviceTypeIndex = 0;
802
+ for (uint8_t i = 0; i < 5; i++) {
803
+ if (config.deviceTypes.types[i] != CEC_DEVICE_TYPE_RESERVED) {
804
+ deviceTypes.Set(deviceTypeIndex++, Napi::Number::New(env, static_cast<int>(config.deviceTypes.types[i])));
805
+ }
806
+ }
807
+ result.Set("deviceTypes", deviceTypes);
808
+ }
809
+
810
+ return result;
811
+ }
812
+
813
+ Napi::Value CECAdapter::SetConfiguration(const Napi::CallbackInfo& info) {
814
+ Napi::Env env = info.Env();
815
+ std::lock_guard<std::mutex> lock(mutex_);
816
+
817
+ if (!adapter_) {
818
+ return Napi::Boolean::New(env, false);
819
+ }
820
+
821
+ if (info.Length() < 1 || !info[0].IsObject()) {
822
+ Napi::TypeError::New(env, "Expected configuration object").ThrowAsJavaScriptException();
823
+ return env.Undefined();
824
+ }
825
+
826
+ Napi::Object configObj = info[0].As<Napi::Object>();
827
+ libcec_configuration config;
828
+ config.Clear();
829
+ config.clientVersion = LIBCEC_VERSION_CURRENT;
830
+
831
+ if (configObj.Has("deviceName") && configObj.Get("deviceName").IsString()) {
832
+ std::string name = configObj.Get("deviceName").As<Napi::String>().Utf8Value();
833
+ snprintf(config.strDeviceName, LIBCEC_OSD_NAME_SIZE, "%s", name.c_str());
834
+ }
835
+ if (configObj.Has("physicalAddress") && configObj.Get("physicalAddress").IsNumber()) {
836
+ config.iPhysicalAddress = static_cast<uint16_t>(configObj.Get("physicalAddress").As<Napi::Number>().Int32Value());
837
+ }
838
+ if (configObj.Has("baseDevice") && configObj.Get("baseDevice").IsNumber()) {
839
+ config.baseDevice = static_cast<cec_logical_address>(configObj.Get("baseDevice").As<Napi::Number>().Int32Value());
840
+ }
841
+ if (configObj.Has("hdmiPort") && configObj.Get("hdmiPort").IsNumber()) {
842
+ config.iHDMIPort = static_cast<uint8_t>(configObj.Get("hdmiPort").As<Napi::Number>().Int32Value());
843
+ }
844
+ if (configObj.Has("activateSource") && configObj.Get("activateSource").IsBoolean()) {
845
+ config.bActivateSource = configObj.Get("activateSource").As<Napi::Boolean>().Value() ? 1 : 0;
846
+ }
847
+ if (configObj.Has("deviceTypes") && configObj.Get("deviceTypes").IsArray()) {
848
+ Napi::Array types = configObj.Get("deviceTypes").As<Napi::Array>();
849
+ for (uint32_t i = 0; i < types.Length() && i < 5; i++) {
850
+ config.deviceTypes.Add(static_cast<cec_device_type>(types.Get(i).As<Napi::Number>().Int32Value()));
851
+ }
852
+ }
853
+
854
+ return Napi::Boolean::New(env, adapter_->SetConfiguration(&config));
855
+ }
856
+
857
+ Napi::Value CECAdapter::RescanActiveDevices(const Napi::CallbackInfo& info) {
858
+ Napi::Env env = info.Env();
859
+ std::lock_guard<std::mutex> lock(mutex_);
860
+
861
+ if (!adapter_ || !isOpen_) {
862
+ return env.Undefined();
863
+ }
864
+
865
+ adapter_->RescanActiveDevices();
866
+ return env.Undefined();
867
+ }
868
+
869
+ Napi::Value CECAdapter::SetCallbacks(const Napi::CallbackInfo& info) {
870
+ Napi::Env env = info.Env();
871
+
872
+ if (info.Length() < 1 || !info[0].IsObject()) {
873
+ Napi::TypeError::New(env, "Expected callbacks object").ThrowAsJavaScriptException();
874
+ return env.Undefined();
875
+ }
876
+
877
+ Napi::Object callbacks = info[0].As<Napi::Object>();
878
+
879
+ if (callbacks.Has("onLogMessage") && callbacks.Get("onLogMessage").IsFunction()) {
880
+ callbackHandler_->SetLogMessageCallback(callbacks.Get("onLogMessage").As<Napi::Function>());
881
+ }
882
+ if (callbacks.Has("onKeyPress") && callbacks.Get("onKeyPress").IsFunction()) {
883
+ callbackHandler_->SetKeyPressCallback(callbacks.Get("onKeyPress").As<Napi::Function>());
884
+ }
885
+ if (callbacks.Has("onCommand") && callbacks.Get("onCommand").IsFunction()) {
886
+ callbackHandler_->SetCommandCallback(callbacks.Get("onCommand").As<Napi::Function>());
887
+ }
888
+ if (callbacks.Has("onConfigurationChanged") && callbacks.Get("onConfigurationChanged").IsFunction()) {
889
+ callbackHandler_->SetConfigurationChangedCallback(callbacks.Get("onConfigurationChanged").As<Napi::Function>());
890
+ }
891
+ if (callbacks.Has("onAlert") && callbacks.Get("onAlert").IsFunction()) {
892
+ callbackHandler_->SetAlertCallback(callbacks.Get("onAlert").As<Napi::Function>());
893
+ }
894
+ if (callbacks.Has("onSourceActivated") && callbacks.Get("onSourceActivated").IsFunction()) {
895
+ callbackHandler_->SetSourceActivatedCallback(callbacks.Get("onSourceActivated").As<Napi::Function>());
896
+ }
897
+
898
+ return Napi::Boolean::New(env, true);
899
+ }
900
+
901
+ Napi::Value CECAdapter::DisableCallbacks(const Napi::CallbackInfo& info) {
902
+ Napi::Env env = info.Env();
903
+
904
+ if (callbackHandler_) {
905
+ callbackHandler_->ClearCallbacks();
906
+ }
907
+
908
+ return Napi::Boolean::New(env, true);
909
+ }