hamlib 0.2.0 → 0.2.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/src/hamlib.cpp CHANGED
@@ -2191,19 +2191,17 @@ NodeHamLib::NodeHamLib(const Napi::CallbackInfo & info): ObjectWrap(info) {
2191
2191
  return;
2192
2192
  }
2193
2193
 
2194
- // Set default port path if not provided
2195
- strncpy(port_path, "/dev/ttyUSB0", SHIM_HAMLIB_FILPATHLEN - 1);
2196
- port_path[SHIM_HAMLIB_FILPATHLEN - 1] = '\0';
2197
-
2198
2194
  // Check if port path is provided as second argument
2199
- if (info.Length() >= 2) {
2200
- if (info[1].IsString()) {
2201
- std::string portStr = info[1].As<Napi::String>().Utf8Value();
2195
+ bool has_port = false;
2196
+ port_path[0] = '\0';
2197
+
2198
+ if (info.Length() >= 2 && info[1].IsString()) {
2199
+ std::string portStr = info[1].As<Napi::String>().Utf8Value();
2200
+ if (!portStr.empty()) {
2202
2201
  strncpy(port_path, portStr.c_str(), SHIM_HAMLIB_FILPATHLEN - 1);
2203
2202
  port_path[SHIM_HAMLIB_FILPATHLEN - 1] = '\0';
2203
+ has_port = true;
2204
2204
  }
2205
- // Note: Debug level is now controlled globally via HamLib.setDebugLevel()
2206
- // and set to RIG_DEBUG_NONE by default in addon initialization
2207
2205
  }
2208
2206
  //unsigned int myrig_model;
2209
2207
  // hamlib_port_t myport;
@@ -2242,13 +2240,16 @@ NodeHamLib::NodeHamLib(const Napi::CallbackInfo & info): ObjectWrap(info) {
2242
2240
  Napi::TypeError::New(env, errorMsg).ThrowAsJavaScriptException();
2243
2241
  }
2244
2242
 
2245
- // Set port path and type based on connection type
2246
- shim_rig_set_port_path(my_rig, port_path);
2243
+ // Only set port path and type when user explicitly provided a port.
2244
+ // Otherwise, let rig_init() defaults take effect (e.g., dummy rig uses RIG_PORT_NONE).
2245
+ if (has_port) {
2246
+ shim_rig_set_port_path(my_rig, port_path);
2247
2247
 
2248
- if (is_network_rig) {
2249
- shim_rig_set_port_type(my_rig, SHIM_RIG_PORT_NETWORK);
2250
- } else {
2251
- shim_rig_set_port_type(my_rig, SHIM_RIG_PORT_SERIAL);
2248
+ if (is_network_rig) {
2249
+ shim_rig_set_port_type(my_rig, SHIM_RIG_PORT_NETWORK);
2250
+ } else {
2251
+ shim_rig_set_port_type(my_rig, SHIM_RIG_PORT_SERIAL);
2252
+ }
2252
2253
  }
2253
2254
 
2254
2255
  // this->freq_emit_cb = [info](double freq) {
@@ -3701,6 +3702,46 @@ Napi::Function NodeHamLib::GetClass(Napi::Env env) {
3701
3702
  // Reset Function
3702
3703
  NodeHamLib::InstanceMethod("reset", & NodeHamLib::Reset),
3703
3704
 
3705
+ // Lock Mode (Hamlib >= 4.7.0)
3706
+ NodeHamLib::InstanceMethod("setLockMode", & NodeHamLib::SetLockMode),
3707
+ NodeHamLib::InstanceMethod("getLockMode", & NodeHamLib::GetLockMode),
3708
+
3709
+ // Clock (Hamlib >= 4.7.0)
3710
+ NodeHamLib::InstanceMethod("setClock", & NodeHamLib::SetClock),
3711
+ NodeHamLib::InstanceMethod("getClock", & NodeHamLib::GetClock),
3712
+
3713
+ // VFO Info (Hamlib >= 4.7.0)
3714
+ NodeHamLib::InstanceMethod("getVfoInfo", & NodeHamLib::GetVfoInfo),
3715
+
3716
+ // Rig Info / Raw / Conf (async)
3717
+ NodeHamLib::InstanceMethod("getInfo", & NodeHamLib::GetInfo),
3718
+ NodeHamLib::InstanceMethod("sendRaw", & NodeHamLib::SendRaw),
3719
+ NodeHamLib::InstanceMethod("setConf", & NodeHamLib::SetConf),
3720
+ NodeHamLib::InstanceMethod("getConf", & NodeHamLib::GetConf),
3721
+
3722
+ // Passband / Resolution (sync)
3723
+ NodeHamLib::InstanceMethod("getPassbandNormal", & NodeHamLib::GetPassbandNormal),
3724
+ NodeHamLib::InstanceMethod("getPassbandNarrow", & NodeHamLib::GetPassbandNarrow),
3725
+ NodeHamLib::InstanceMethod("getPassbandWide", & NodeHamLib::GetPassbandWide),
3726
+ NodeHamLib::InstanceMethod("getResolution", & NodeHamLib::GetResolution),
3727
+
3728
+ // Capability queries (sync)
3729
+ NodeHamLib::InstanceMethod("getSupportedParms", & NodeHamLib::GetSupportedParms),
3730
+ NodeHamLib::InstanceMethod("getSupportedVfoOps", & NodeHamLib::GetSupportedVfoOps),
3731
+ NodeHamLib::InstanceMethod("getSupportedScanTypes", & NodeHamLib::GetSupportedScanTypes),
3732
+
3733
+ // Capability queries - batch 2 (sync)
3734
+ NodeHamLib::InstanceMethod("getPreampValues", & NodeHamLib::GetPreampValues),
3735
+ NodeHamLib::InstanceMethod("getAttenuatorValues", & NodeHamLib::GetAttenuatorValues),
3736
+ NodeHamLib::InstanceMethod("getMaxRit", & NodeHamLib::GetMaxRit),
3737
+ NodeHamLib::InstanceMethod("getMaxXit", & NodeHamLib::GetMaxXit),
3738
+ NodeHamLib::InstanceMethod("getMaxIfShift", & NodeHamLib::GetMaxIfShift),
3739
+ NodeHamLib::InstanceMethod("getAvailableCtcssTones", & NodeHamLib::GetAvailableCtcssTones),
3740
+ NodeHamLib::InstanceMethod("getAvailableDcsCodes", & NodeHamLib::GetAvailableDcsCodes),
3741
+ NodeHamLib::InstanceMethod("getFrequencyRanges", & NodeHamLib::GetFrequencyRanges),
3742
+ NodeHamLib::InstanceMethod("getTuningSteps", & NodeHamLib::GetTuningSteps),
3743
+ NodeHamLib::InstanceMethod("getFilterList", & NodeHamLib::GetFilterList),
3744
+
3704
3745
  NodeHamLib::InstanceMethod("close", & NodeHamLib::Close),
3705
3746
  NodeHamLib::InstanceMethod("destroy", & NodeHamLib::Destroy),
3706
3747
  NodeHamLib::InstanceMethod("getConnectionInfo", & NodeHamLib::GetConnectionInfo),
@@ -3710,6 +3751,8 @@ Napi::Function NodeHamLib::GetClass(Napi::Env env) {
3710
3751
  NodeHamLib::StaticMethod("getHamlibVersion", & NodeHamLib::GetHamlibVersion),
3711
3752
  NodeHamLib::StaticMethod("setDebugLevel", & NodeHamLib::SetDebugLevel),
3712
3753
  NodeHamLib::StaticMethod("getDebugLevel", & NodeHamLib::GetDebugLevel),
3754
+ NodeHamLib::StaticMethod("getCopyright", & NodeHamLib::GetCopyright),
3755
+ NodeHamLib::StaticMethod("getLicense", & NodeHamLib::GetLicense),
3713
3756
  });
3714
3757
  constructor = Napi::Persistent(ret);
3715
3758
  constructor.SuppressDestruct();
@@ -5358,3 +5401,774 @@ Napi::Value NodeHamLib::Reset(const Napi::CallbackInfo& info) {
5358
5401
  asyncWorker->Queue();
5359
5402
  return asyncWorker->GetPromise();
5360
5403
  }
5404
+
5405
+ // ===== Lock Mode (Hamlib >= 4.7.0) =====
5406
+
5407
+ class SetLockModeAsyncWorker : public HamLibAsyncWorker {
5408
+ public:
5409
+ SetLockModeAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance, int lock)
5410
+ : HamLibAsyncWorker(env, hamlib_instance), lock_(lock) {}
5411
+
5412
+ void Execute() override {
5413
+ CHECK_RIG_VALID();
5414
+ result_code_ = shim_rig_set_lock_mode(hamlib_instance_->my_rig, lock_);
5415
+ if (result_code_ != SHIM_RIG_OK) {
5416
+ error_message_ = shim_rigerror(result_code_);
5417
+ }
5418
+ }
5419
+
5420
+ void OnOK() override {
5421
+ Napi::Env env = Env();
5422
+ if (result_code_ != SHIM_RIG_OK && !error_message_.empty()) {
5423
+ deferred_.Reject(Napi::Error::New(env, error_message_).Value());
5424
+ } else {
5425
+ deferred_.Resolve(Napi::Number::New(env, result_code_));
5426
+ }
5427
+ }
5428
+
5429
+ void OnError(const Napi::Error& e) override {
5430
+ deferred_.Reject(Napi::Error::New(Env(), error_message_).Value());
5431
+ }
5432
+
5433
+ private:
5434
+ int lock_;
5435
+ };
5436
+
5437
+ class GetLockModeAsyncWorker : public HamLibAsyncWorker {
5438
+ public:
5439
+ GetLockModeAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance)
5440
+ : HamLibAsyncWorker(env, hamlib_instance), lock_(0) {}
5441
+
5442
+ void Execute() override {
5443
+ CHECK_RIG_VALID();
5444
+ result_code_ = shim_rig_get_lock_mode(hamlib_instance_->my_rig, &lock_);
5445
+ if (result_code_ != SHIM_RIG_OK) {
5446
+ error_message_ = shim_rigerror(result_code_);
5447
+ }
5448
+ }
5449
+
5450
+ void OnOK() override {
5451
+ Napi::Env env = Env();
5452
+ if (result_code_ != SHIM_RIG_OK && !error_message_.empty()) {
5453
+ deferred_.Reject(Napi::Error::New(env, error_message_).Value());
5454
+ } else {
5455
+ deferred_.Resolve(Napi::Number::New(env, lock_));
5456
+ }
5457
+ }
5458
+
5459
+ void OnError(const Napi::Error& e) override {
5460
+ deferred_.Reject(Napi::Error::New(Env(), error_message_).Value());
5461
+ }
5462
+
5463
+ private:
5464
+ int lock_;
5465
+ };
5466
+
5467
+ Napi::Value NodeHamLib::SetLockMode(const Napi::CallbackInfo& info) {
5468
+ Napi::Env env = info.Env();
5469
+
5470
+ if (info.Length() < 1 || !info[0].IsNumber()) {
5471
+ Napi::TypeError::New(env, "Expected lock mode as number").ThrowAsJavaScriptException();
5472
+ return env.Null();
5473
+ }
5474
+
5475
+ int lock = info[0].As<Napi::Number>().Int32Value();
5476
+
5477
+ SetLockModeAsyncWorker* asyncWorker = new SetLockModeAsyncWorker(env, this, lock);
5478
+ asyncWorker->Queue();
5479
+ return asyncWorker->GetPromise();
5480
+ }
5481
+
5482
+ Napi::Value NodeHamLib::GetLockMode(const Napi::CallbackInfo& info) {
5483
+ Napi::Env env = info.Env();
5484
+
5485
+ GetLockModeAsyncWorker* asyncWorker = new GetLockModeAsyncWorker(env, this);
5486
+ asyncWorker->Queue();
5487
+ return asyncWorker->GetPromise();
5488
+ }
5489
+
5490
+ // ===== Clock (Hamlib >= 4.7.0) =====
5491
+
5492
+ class SetClockAsyncWorker : public HamLibAsyncWorker {
5493
+ public:
5494
+ SetClockAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance,
5495
+ int year, int month, int day,
5496
+ int hour, int min, int sec, double msec, int utc_offset)
5497
+ : HamLibAsyncWorker(env, hamlib_instance),
5498
+ year_(year), month_(month), day_(day),
5499
+ hour_(hour), min_(min), sec_(sec), msec_(msec), utc_offset_(utc_offset) {}
5500
+
5501
+ void Execute() override {
5502
+ CHECK_RIG_VALID();
5503
+ result_code_ = shim_rig_set_clock(hamlib_instance_->my_rig,
5504
+ year_, month_, day_,
5505
+ hour_, min_, sec_, msec_, utc_offset_);
5506
+ if (result_code_ != SHIM_RIG_OK) {
5507
+ error_message_ = shim_rigerror(result_code_);
5508
+ }
5509
+ }
5510
+
5511
+ void OnOK() override {
5512
+ Napi::Env env = Env();
5513
+ if (result_code_ != SHIM_RIG_OK && !error_message_.empty()) {
5514
+ deferred_.Reject(Napi::Error::New(env, error_message_).Value());
5515
+ } else {
5516
+ deferred_.Resolve(Napi::Number::New(env, result_code_));
5517
+ }
5518
+ }
5519
+
5520
+ void OnError(const Napi::Error& e) override {
5521
+ deferred_.Reject(Napi::Error::New(Env(), error_message_).Value());
5522
+ }
5523
+
5524
+ private:
5525
+ int year_, month_, day_, hour_, min_, sec_;
5526
+ double msec_;
5527
+ int utc_offset_;
5528
+ };
5529
+
5530
+ class GetClockAsyncWorker : public HamLibAsyncWorker {
5531
+ public:
5532
+ GetClockAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance)
5533
+ : HamLibAsyncWorker(env, hamlib_instance),
5534
+ year_(0), month_(0), day_(0),
5535
+ hour_(0), min_(0), sec_(0), msec_(0.0), utc_offset_(0) {}
5536
+
5537
+ void Execute() override {
5538
+ CHECK_RIG_VALID();
5539
+ result_code_ = shim_rig_get_clock(hamlib_instance_->my_rig,
5540
+ &year_, &month_, &day_,
5541
+ &hour_, &min_, &sec_, &msec_, &utc_offset_);
5542
+ if (result_code_ != SHIM_RIG_OK) {
5543
+ error_message_ = shim_rigerror(result_code_);
5544
+ }
5545
+ }
5546
+
5547
+ void OnOK() override {
5548
+ Napi::Env env = Env();
5549
+ if (result_code_ != SHIM_RIG_OK && !error_message_.empty()) {
5550
+ deferred_.Reject(Napi::Error::New(env, error_message_).Value());
5551
+ } else {
5552
+ Napi::Object obj = Napi::Object::New(env);
5553
+ obj.Set("year", Napi::Number::New(env, year_));
5554
+ obj.Set("month", Napi::Number::New(env, month_));
5555
+ obj.Set("day", Napi::Number::New(env, day_));
5556
+ obj.Set("hour", Napi::Number::New(env, hour_));
5557
+ obj.Set("min", Napi::Number::New(env, min_));
5558
+ obj.Set("sec", Napi::Number::New(env, sec_));
5559
+ obj.Set("msec", Napi::Number::New(env, msec_));
5560
+ obj.Set("utcOffset", Napi::Number::New(env, utc_offset_));
5561
+ deferred_.Resolve(obj);
5562
+ }
5563
+ }
5564
+
5565
+ void OnError(const Napi::Error& e) override {
5566
+ deferred_.Reject(Napi::Error::New(Env(), error_message_).Value());
5567
+ }
5568
+
5569
+ private:
5570
+ int year_, month_, day_, hour_, min_, sec_;
5571
+ double msec_;
5572
+ int utc_offset_;
5573
+ };
5574
+
5575
+ Napi::Value NodeHamLib::SetClock(const Napi::CallbackInfo& info) {
5576
+ Napi::Env env = info.Env();
5577
+
5578
+ if (info.Length() < 1 || !info[0].IsObject()) {
5579
+ Napi::TypeError::New(env, "Expected clock object with year, month, day, hour, min, sec, msec, utcOffset").ThrowAsJavaScriptException();
5580
+ return env.Null();
5581
+ }
5582
+
5583
+ Napi::Object obj = info[0].As<Napi::Object>();
5584
+
5585
+ auto getInt = [&](const char* key, int defaultVal) -> int {
5586
+ if (obj.Has(key) && obj.Get(key).IsNumber()) {
5587
+ return obj.Get(key).As<Napi::Number>().Int32Value();
5588
+ }
5589
+ return defaultVal;
5590
+ };
5591
+ auto getDouble = [&](const char* key, double defaultVal) -> double {
5592
+ if (obj.Has(key) && obj.Get(key).IsNumber()) {
5593
+ return obj.Get(key).As<Napi::Number>().DoubleValue();
5594
+ }
5595
+ return defaultVal;
5596
+ };
5597
+
5598
+ int year = getInt("year", 0);
5599
+ int month = getInt("month", 0);
5600
+ int day = getInt("day", 0);
5601
+ int hour = getInt("hour", 0);
5602
+ int min = getInt("min", 0);
5603
+ int sec = getInt("sec", 0);
5604
+ double msec = getDouble("msec", 0.0);
5605
+ int utcOffset = getInt("utcOffset", 0);
5606
+
5607
+ SetClockAsyncWorker* asyncWorker = new SetClockAsyncWorker(env, this,
5608
+ year, month, day, hour, min, sec, msec, utcOffset);
5609
+ asyncWorker->Queue();
5610
+ return asyncWorker->GetPromise();
5611
+ }
5612
+
5613
+ Napi::Value NodeHamLib::GetClock(const Napi::CallbackInfo& info) {
5614
+ Napi::Env env = info.Env();
5615
+
5616
+ GetClockAsyncWorker* asyncWorker = new GetClockAsyncWorker(env, this);
5617
+ asyncWorker->Queue();
5618
+ return asyncWorker->GetPromise();
5619
+ }
5620
+
5621
+ // ===== VFO Info (Hamlib >= 4.7.0) =====
5622
+
5623
+ class GetVfoInfoAsyncWorker : public HamLibAsyncWorker {
5624
+ public:
5625
+ GetVfoInfoAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance, int vfo)
5626
+ : HamLibAsyncWorker(env, hamlib_instance),
5627
+ vfo_(vfo), freq_(0.0), mode_(0), width_(0), split_(0), satmode_(0) {}
5628
+
5629
+ void Execute() override {
5630
+ CHECK_RIG_VALID();
5631
+ result_code_ = shim_rig_get_vfo_info(hamlib_instance_->my_rig, vfo_,
5632
+ &freq_, &mode_, &width_, &split_, &satmode_);
5633
+ if (result_code_ != SHIM_RIG_OK) {
5634
+ error_message_ = shim_rigerror(result_code_);
5635
+ }
5636
+ }
5637
+
5638
+ void OnOK() override {
5639
+ Napi::Env env = Env();
5640
+ if (result_code_ != SHIM_RIG_OK && !error_message_.empty()) {
5641
+ deferred_.Reject(Napi::Error::New(env, error_message_).Value());
5642
+ } else {
5643
+ Napi::Object obj = Napi::Object::New(env);
5644
+ obj.Set("frequency", Napi::Number::New(env, freq_));
5645
+ obj.Set("mode", Napi::String::New(env, shim_rig_strrmode(static_cast<int>(mode_))));
5646
+ obj.Set("bandwidth", Napi::Number::New(env, static_cast<double>(width_)));
5647
+ obj.Set("split", Napi::Boolean::New(env, split_ != 0));
5648
+ obj.Set("satMode", Napi::Boolean::New(env, satmode_ != 0));
5649
+ deferred_.Resolve(obj);
5650
+ }
5651
+ }
5652
+
5653
+ void OnError(const Napi::Error& e) override {
5654
+ deferred_.Reject(Napi::Error::New(Env(), error_message_).Value());
5655
+ }
5656
+
5657
+ private:
5658
+ int vfo_;
5659
+ double freq_;
5660
+ uint64_t mode_;
5661
+ long width_;
5662
+ int split_;
5663
+ int satmode_;
5664
+ };
5665
+
5666
+ Napi::Value NodeHamLib::GetVfoInfo(const Napi::CallbackInfo& info) {
5667
+ Napi::Env env = info.Env();
5668
+
5669
+ int vfo = parseVfoParameter(info, 0, SHIM_RIG_VFO_CURR);
5670
+
5671
+ GetVfoInfoAsyncWorker* asyncWorker = new GetVfoInfoAsyncWorker(env, this, vfo);
5672
+ asyncWorker->Queue();
5673
+ return asyncWorker->GetPromise();
5674
+ }
5675
+
5676
+ // ===== GetInfo (async) =====
5677
+
5678
+ class GetInfoAsyncWorker : public HamLibAsyncWorker {
5679
+ public:
5680
+ GetInfoAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance)
5681
+ : HamLibAsyncWorker(env, hamlib_instance) {}
5682
+
5683
+ void Execute() override {
5684
+ CHECK_RIG_VALID();
5685
+ const char* info = shim_rig_get_info(hamlib_instance_->my_rig);
5686
+ info_str_ = info ? info : "";
5687
+ }
5688
+
5689
+ void OnOK() override {
5690
+ Napi::Env env = Env();
5691
+ deferred_.Resolve(Napi::String::New(env, info_str_));
5692
+ }
5693
+
5694
+ void OnError(const Napi::Error& e) override {
5695
+ deferred_.Reject(Napi::Error::New(Env(), error_message_).Value());
5696
+ }
5697
+
5698
+ private:
5699
+ std::string info_str_;
5700
+ };
5701
+
5702
+ Napi::Value NodeHamLib::GetInfo(const Napi::CallbackInfo& info) {
5703
+ Napi::Env env = info.Env();
5704
+
5705
+ if (!rig_is_open) {
5706
+ Napi::TypeError::New(env, "Rig is not open!").ThrowAsJavaScriptException();
5707
+ return env.Null();
5708
+ }
5709
+
5710
+ GetInfoAsyncWorker* asyncWorker = new GetInfoAsyncWorker(env, this);
5711
+ asyncWorker->Queue();
5712
+ return asyncWorker->GetPromise();
5713
+ }
5714
+
5715
+ // ===== SendRaw (async) =====
5716
+
5717
+ class SendRawAsyncWorker : public HamLibAsyncWorker {
5718
+ public:
5719
+ SendRawAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance,
5720
+ std::vector<unsigned char> send_data, int reply_max_len,
5721
+ std::vector<unsigned char> terminator, bool has_terminator)
5722
+ : HamLibAsyncWorker(env, hamlib_instance),
5723
+ send_data_(std::move(send_data)), reply_max_len_(reply_max_len),
5724
+ terminator_(std::move(terminator)), has_terminator_(has_terminator),
5725
+ reply_len_(0) {
5726
+ reply_buf_.resize(reply_max_len > 0 ? reply_max_len : 1);
5727
+ }
5728
+
5729
+ void Execute() override {
5730
+ CHECK_RIG_VALID();
5731
+ const unsigned char* term = has_terminator_ ? terminator_.data() : nullptr;
5732
+ result_code_ = shim_rig_send_raw(hamlib_instance_->my_rig,
5733
+ send_data_.data(), (int)send_data_.size(),
5734
+ reply_buf_.data(), reply_max_len_, term);
5735
+ if (result_code_ < 0) {
5736
+ error_message_ = shim_rigerror(result_code_);
5737
+ } else {
5738
+ reply_len_ = result_code_;
5739
+ result_code_ = SHIM_RIG_OK;
5740
+ }
5741
+ }
5742
+
5743
+ void OnOK() override {
5744
+ Napi::Env env = Env();
5745
+ if (!error_message_.empty()) {
5746
+ deferred_.Reject(Napi::Error::New(env, error_message_).Value());
5747
+ } else {
5748
+ deferred_.Resolve(Napi::Buffer<unsigned char>::Copy(env, reply_buf_.data(), reply_len_));
5749
+ }
5750
+ }
5751
+
5752
+ void OnError(const Napi::Error& e) override {
5753
+ deferred_.Reject(Napi::Error::New(Env(), error_message_).Value());
5754
+ }
5755
+
5756
+ private:
5757
+ std::vector<unsigned char> send_data_;
5758
+ int reply_max_len_;
5759
+ std::vector<unsigned char> terminator_;
5760
+ bool has_terminator_;
5761
+ std::vector<unsigned char> reply_buf_;
5762
+ int reply_len_;
5763
+ };
5764
+
5765
+ Napi::Value NodeHamLib::SendRaw(const Napi::CallbackInfo& info) {
5766
+ Napi::Env env = info.Env();
5767
+
5768
+ if (!rig_is_open) {
5769
+ Napi::TypeError::New(env, "Rig is not open!").ThrowAsJavaScriptException();
5770
+ return env.Null();
5771
+ }
5772
+
5773
+ if (info.Length() < 2 || !info[0].IsBuffer() || !info[1].IsNumber()) {
5774
+ Napi::TypeError::New(env, "Expected (data: Buffer, replyMaxLen: number, terminator?: Buffer)").ThrowAsJavaScriptException();
5775
+ return env.Null();
5776
+ }
5777
+
5778
+ Napi::Buffer<unsigned char> dataBuf = info[0].As<Napi::Buffer<unsigned char>>();
5779
+ std::vector<unsigned char> send_data(dataBuf.Data(), dataBuf.Data() + dataBuf.Length());
5780
+ int replyMaxLen = info[1].As<Napi::Number>().Int32Value();
5781
+
5782
+ std::vector<unsigned char> terminator;
5783
+ bool has_terminator = false;
5784
+ if (info.Length() >= 3 && info[2].IsBuffer()) {
5785
+ Napi::Buffer<unsigned char> termBuf = info[2].As<Napi::Buffer<unsigned char>>();
5786
+ terminator.assign(termBuf.Data(), termBuf.Data() + termBuf.Length());
5787
+ has_terminator = true;
5788
+ }
5789
+
5790
+ SendRawAsyncWorker* asyncWorker = new SendRawAsyncWorker(env, this, std::move(send_data), replyMaxLen, std::move(terminator), has_terminator);
5791
+ asyncWorker->Queue();
5792
+ return asyncWorker->GetPromise();
5793
+ }
5794
+
5795
+ // ===== SetConf (async) =====
5796
+
5797
+ class SetConfAsyncWorker : public HamLibAsyncWorker {
5798
+ public:
5799
+ SetConfAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance,
5800
+ std::string name, std::string value)
5801
+ : HamLibAsyncWorker(env, hamlib_instance),
5802
+ name_(std::move(name)), value_(std::move(value)) {}
5803
+
5804
+ void Execute() override {
5805
+ CHECK_RIG_VALID();
5806
+ result_code_ = shim_rig_set_conf(hamlib_instance_->my_rig, name_.c_str(), value_.c_str());
5807
+ if (result_code_ != SHIM_RIG_OK) {
5808
+ error_message_ = shim_rigerror(result_code_);
5809
+ }
5810
+ }
5811
+
5812
+ void OnOK() override {
5813
+ Napi::Env env = Env();
5814
+ if (result_code_ != SHIM_RIG_OK && !error_message_.empty()) {
5815
+ deferred_.Reject(Napi::Error::New(env, error_message_).Value());
5816
+ } else {
5817
+ deferred_.Resolve(Napi::Number::New(env, result_code_));
5818
+ }
5819
+ }
5820
+
5821
+ void OnError(const Napi::Error& e) override {
5822
+ deferred_.Reject(Napi::Error::New(Env(), error_message_).Value());
5823
+ }
5824
+
5825
+ private:
5826
+ std::string name_;
5827
+ std::string value_;
5828
+ };
5829
+
5830
+ Napi::Value NodeHamLib::SetConf(const Napi::CallbackInfo& info) {
5831
+ Napi::Env env = info.Env();
5832
+
5833
+ if (!rig_is_open) {
5834
+ Napi::TypeError::New(env, "Rig is not open!").ThrowAsJavaScriptException();
5835
+ return env.Null();
5836
+ }
5837
+
5838
+ if (info.Length() < 2 || !info[0].IsString() || !info[1].IsString()) {
5839
+ Napi::TypeError::New(env, "Expected (name: string, value: string)").ThrowAsJavaScriptException();
5840
+ return env.Null();
5841
+ }
5842
+
5843
+ std::string name = info[0].As<Napi::String>().Utf8Value();
5844
+ std::string value = info[1].As<Napi::String>().Utf8Value();
5845
+
5846
+ SetConfAsyncWorker* asyncWorker = new SetConfAsyncWorker(env, this, std::move(name), std::move(value));
5847
+ asyncWorker->Queue();
5848
+ return asyncWorker->GetPromise();
5849
+ }
5850
+
5851
+ // ===== GetConf (async) =====
5852
+
5853
+ class GetConfAsyncWorker : public HamLibAsyncWorker {
5854
+ public:
5855
+ GetConfAsyncWorker(Napi::Env env, NodeHamLib* hamlib_instance, std::string name)
5856
+ : HamLibAsyncWorker(env, hamlib_instance), name_(std::move(name)) {
5857
+ memset(buf_, 0, sizeof(buf_));
5858
+ }
5859
+
5860
+ void Execute() override {
5861
+ CHECK_RIG_VALID();
5862
+ result_code_ = shim_rig_get_conf(hamlib_instance_->my_rig, name_.c_str(), buf_, sizeof(buf_));
5863
+ if (result_code_ != SHIM_RIG_OK) {
5864
+ error_message_ = shim_rigerror(result_code_);
5865
+ }
5866
+ }
5867
+
5868
+ void OnOK() override {
5869
+ Napi::Env env = Env();
5870
+ if (result_code_ != SHIM_RIG_OK && !error_message_.empty()) {
5871
+ deferred_.Reject(Napi::Error::New(env, error_message_).Value());
5872
+ } else {
5873
+ deferred_.Resolve(Napi::String::New(env, buf_));
5874
+ }
5875
+ }
5876
+
5877
+ void OnError(const Napi::Error& e) override {
5878
+ deferred_.Reject(Napi::Error::New(Env(), error_message_).Value());
5879
+ }
5880
+
5881
+ private:
5882
+ std::string name_;
5883
+ char buf_[256];
5884
+ };
5885
+
5886
+ Napi::Value NodeHamLib::GetConf(const Napi::CallbackInfo& info) {
5887
+ Napi::Env env = info.Env();
5888
+
5889
+ if (!rig_is_open) {
5890
+ Napi::TypeError::New(env, "Rig is not open!").ThrowAsJavaScriptException();
5891
+ return env.Null();
5892
+ }
5893
+
5894
+ if (info.Length() < 1 || !info[0].IsString()) {
5895
+ Napi::TypeError::New(env, "Expected (name: string)").ThrowAsJavaScriptException();
5896
+ return env.Null();
5897
+ }
5898
+
5899
+ std::string name = info[0].As<Napi::String>().Utf8Value();
5900
+
5901
+ GetConfAsyncWorker* asyncWorker = new GetConfAsyncWorker(env, this, std::move(name));
5902
+ asyncWorker->Queue();
5903
+ return asyncWorker->GetPromise();
5904
+ }
5905
+
5906
+ // ===== Passband methods (sync) =====
5907
+
5908
+ Napi::Value NodeHamLib::GetPassbandNormal(const Napi::CallbackInfo& info) {
5909
+ Napi::Env env = info.Env();
5910
+
5911
+ if (info.Length() < 1 || !info[0].IsString()) {
5912
+ Napi::TypeError::New(env, "Expected (mode: string)").ThrowAsJavaScriptException();
5913
+ return env.Null();
5914
+ }
5915
+
5916
+ std::string modeStr = info[0].As<Napi::String>().Utf8Value();
5917
+ int mode = shim_rig_parse_mode(modeStr.c_str());
5918
+ int pb = shim_rig_passband_normal(my_rig, mode);
5919
+ return Napi::Number::New(env, pb);
5920
+ }
5921
+
5922
+ Napi::Value NodeHamLib::GetPassbandNarrow(const Napi::CallbackInfo& info) {
5923
+ Napi::Env env = info.Env();
5924
+
5925
+ if (info.Length() < 1 || !info[0].IsString()) {
5926
+ Napi::TypeError::New(env, "Expected (mode: string)").ThrowAsJavaScriptException();
5927
+ return env.Null();
5928
+ }
5929
+
5930
+ std::string modeStr = info[0].As<Napi::String>().Utf8Value();
5931
+ int mode = shim_rig_parse_mode(modeStr.c_str());
5932
+ int pb = shim_rig_passband_narrow(my_rig, mode);
5933
+ return Napi::Number::New(env, pb);
5934
+ }
5935
+
5936
+ Napi::Value NodeHamLib::GetPassbandWide(const Napi::CallbackInfo& info) {
5937
+ Napi::Env env = info.Env();
5938
+
5939
+ if (info.Length() < 1 || !info[0].IsString()) {
5940
+ Napi::TypeError::New(env, "Expected (mode: string)").ThrowAsJavaScriptException();
5941
+ return env.Null();
5942
+ }
5943
+
5944
+ std::string modeStr = info[0].As<Napi::String>().Utf8Value();
5945
+ int mode = shim_rig_parse_mode(modeStr.c_str());
5946
+ int pb = shim_rig_passband_wide(my_rig, mode);
5947
+ return Napi::Number::New(env, pb);
5948
+ }
5949
+
5950
+ // ===== Resolution (sync) =====
5951
+
5952
+ Napi::Value NodeHamLib::GetResolution(const Napi::CallbackInfo& info) {
5953
+ Napi::Env env = info.Env();
5954
+
5955
+ if (info.Length() < 1 || !info[0].IsString()) {
5956
+ Napi::TypeError::New(env, "Expected (mode: string)").ThrowAsJavaScriptException();
5957
+ return env.Null();
5958
+ }
5959
+
5960
+ std::string modeStr = info[0].As<Napi::String>().Utf8Value();
5961
+ int mode = shim_rig_parse_mode(modeStr.c_str());
5962
+ int res = shim_rig_get_resolution(my_rig, mode);
5963
+ return Napi::Number::New(env, res);
5964
+ }
5965
+
5966
+ // ===== getSupportedParms (sync) =====
5967
+
5968
+ Napi::Value NodeHamLib::GetSupportedParms(const Napi::CallbackInfo& info) {
5969
+ Napi::Env env = info.Env();
5970
+
5971
+ uint64_t parms = shim_rig_get_caps_has_get_parm(my_rig) | shim_rig_get_caps_has_set_parm(my_rig);
5972
+ Napi::Array parmArray = Napi::Array::New(env);
5973
+ uint32_t index = 0;
5974
+
5975
+ if (parms & SHIM_RIG_PARM_ANN) parmArray[index++] = Napi::String::New(env, "ANN");
5976
+ if (parms & SHIM_RIG_PARM_APO) parmArray[index++] = Napi::String::New(env, "APO");
5977
+ if (parms & SHIM_RIG_PARM_BACKLIGHT) parmArray[index++] = Napi::String::New(env, "BACKLIGHT");
5978
+ if (parms & SHIM_RIG_PARM_BEEP) parmArray[index++] = Napi::String::New(env, "BEEP");
5979
+ if (parms & SHIM_RIG_PARM_TIME) parmArray[index++] = Napi::String::New(env, "TIME");
5980
+ if (parms & SHIM_RIG_PARM_BAT) parmArray[index++] = Napi::String::New(env, "BAT");
5981
+ if (parms & SHIM_RIG_PARM_KEYLIGHT) parmArray[index++] = Napi::String::New(env, "KEYLIGHT");
5982
+ if (parms & SHIM_RIG_PARM_SCREENSAVER) parmArray[index++] = Napi::String::New(env, "SCREENSAVER");
5983
+
5984
+ return parmArray;
5985
+ }
5986
+
5987
+ // ===== getSupportedVfoOps (sync) =====
5988
+
5989
+ Napi::Value NodeHamLib::GetSupportedVfoOps(const Napi::CallbackInfo& info) {
5990
+ Napi::Env env = info.Env();
5991
+
5992
+ int ops = shim_rig_get_caps_vfo_ops(my_rig);
5993
+ Napi::Array opsArray = Napi::Array::New(env);
5994
+ uint32_t index = 0;
5995
+
5996
+ if (ops & SHIM_RIG_OP_CPY) opsArray[index++] = Napi::String::New(env, "CPY");
5997
+ if (ops & SHIM_RIG_OP_XCHG) opsArray[index++] = Napi::String::New(env, "XCHG");
5998
+ if (ops & SHIM_RIG_OP_FROM_VFO) opsArray[index++] = Napi::String::New(env, "FROM_VFO");
5999
+ if (ops & SHIM_RIG_OP_TO_VFO) opsArray[index++] = Napi::String::New(env, "TO_VFO");
6000
+ if (ops & SHIM_RIG_OP_MCL) opsArray[index++] = Napi::String::New(env, "MCL");
6001
+ if (ops & SHIM_RIG_OP_UP) opsArray[index++] = Napi::String::New(env, "UP");
6002
+ if (ops & SHIM_RIG_OP_DOWN) opsArray[index++] = Napi::String::New(env, "DOWN");
6003
+ if (ops & SHIM_RIG_OP_BAND_UP) opsArray[index++] = Napi::String::New(env, "BAND_UP");
6004
+ if (ops & SHIM_RIG_OP_BAND_DOWN) opsArray[index++] = Napi::String::New(env, "BAND_DOWN");
6005
+ if (ops & SHIM_RIG_OP_LEFT) opsArray[index++] = Napi::String::New(env, "LEFT");
6006
+ if (ops & SHIM_RIG_OP_RIGHT) opsArray[index++] = Napi::String::New(env, "RIGHT");
6007
+ if (ops & SHIM_RIG_OP_TUNE) opsArray[index++] = Napi::String::New(env, "TUNE");
6008
+ if (ops & SHIM_RIG_OP_TOGGLE) opsArray[index++] = Napi::String::New(env, "TOGGLE");
6009
+
6010
+ return opsArray;
6011
+ }
6012
+
6013
+ // ===== getSupportedScanTypes (sync) =====
6014
+
6015
+ Napi::Value NodeHamLib::GetSupportedScanTypes(const Napi::CallbackInfo& info) {
6016
+ Napi::Env env = info.Env();
6017
+
6018
+ int scan = shim_rig_get_caps_has_scan(my_rig);
6019
+ Napi::Array scanArray = Napi::Array::New(env);
6020
+ uint32_t index = 0;
6021
+
6022
+ if (scan & SHIM_RIG_SCAN_MEM) scanArray[index++] = Napi::String::New(env, "MEM");
6023
+ if (scan & SHIM_RIG_SCAN_VFO) scanArray[index++] = Napi::String::New(env, "VFO");
6024
+ if (scan & SHIM_RIG_SCAN_PROG) scanArray[index++] = Napi::String::New(env, "PROG");
6025
+ if (scan & SHIM_RIG_SCAN_DELTA) scanArray[index++] = Napi::String::New(env, "DELTA");
6026
+ if (scan & SHIM_RIG_SCAN_PRIO) scanArray[index++] = Napi::String::New(env, "PRIO");
6027
+
6028
+ return scanArray;
6029
+ }
6030
+
6031
+ // ===== Capability Query Batch 2 (sync) =====
6032
+
6033
+ // Helper: convert mode bitmask to array of mode strings
6034
+ static Napi::Array ModeBitmaskToArray(Napi::Env env, uint64_t modes) {
6035
+ Napi::Array arr = Napi::Array::New(env);
6036
+ uint32_t idx = 0;
6037
+ for (unsigned int i = 0; i < SHIM_HAMLIB_MAX_MODES; i++) {
6038
+ uint64_t bit = modes & (1ULL << i);
6039
+ if (bit) {
6040
+ const char* name = shim_rig_strrmode(bit);
6041
+ if (name && name[0] != '\0') {
6042
+ arr[idx++] = Napi::String::New(env, name);
6043
+ }
6044
+ }
6045
+ }
6046
+ return arr;
6047
+ }
6048
+
6049
+ Napi::Value NodeHamLib::GetPreampValues(const Napi::CallbackInfo& info) {
6050
+ Napi::Env env = info.Env();
6051
+ int buf[SHIM_HAMLIB_MAX_MODES];
6052
+ int count = shim_rig_get_caps_preamp(my_rig, buf, SHIM_HAMLIB_MAX_MODES);
6053
+ Napi::Array arr = Napi::Array::New(env, count);
6054
+ for (int i = 0; i < count; i++) {
6055
+ arr[(uint32_t)i] = Napi::Number::New(env, buf[i]);
6056
+ }
6057
+ return arr;
6058
+ }
6059
+
6060
+ Napi::Value NodeHamLib::GetAttenuatorValues(const Napi::CallbackInfo& info) {
6061
+ Napi::Env env = info.Env();
6062
+ int buf[SHIM_HAMLIB_MAX_MODES];
6063
+ int count = shim_rig_get_caps_attenuator(my_rig, buf, SHIM_HAMLIB_MAX_MODES);
6064
+ Napi::Array arr = Napi::Array::New(env, count);
6065
+ for (int i = 0; i < count; i++) {
6066
+ arr[(uint32_t)i] = Napi::Number::New(env, buf[i]);
6067
+ }
6068
+ return arr;
6069
+ }
6070
+
6071
+ Napi::Value NodeHamLib::GetMaxRit(const Napi::CallbackInfo& info) {
6072
+ return Napi::Number::New(info.Env(), (double)shim_rig_get_caps_max_rit(my_rig));
6073
+ }
6074
+
6075
+ Napi::Value NodeHamLib::GetMaxXit(const Napi::CallbackInfo& info) {
6076
+ return Napi::Number::New(info.Env(), (double)shim_rig_get_caps_max_xit(my_rig));
6077
+ }
6078
+
6079
+ Napi::Value NodeHamLib::GetMaxIfShift(const Napi::CallbackInfo& info) {
6080
+ return Napi::Number::New(info.Env(), (double)shim_rig_get_caps_max_ifshift(my_rig));
6081
+ }
6082
+
6083
+ Napi::Value NodeHamLib::GetAvailableCtcssTones(const Napi::CallbackInfo& info) {
6084
+ Napi::Env env = info.Env();
6085
+ unsigned int buf[256];
6086
+ int count = shim_rig_get_caps_ctcss_list(my_rig, buf, 256);
6087
+ Napi::Array arr = Napi::Array::New(env, count);
6088
+ for (int i = 0; i < count; i++) {
6089
+ // CTCSS tones stored as tenths of Hz, convert to Hz
6090
+ arr[(uint32_t)i] = Napi::Number::New(env, buf[i] / 10.0);
6091
+ }
6092
+ return arr;
6093
+ }
6094
+
6095
+ Napi::Value NodeHamLib::GetAvailableDcsCodes(const Napi::CallbackInfo& info) {
6096
+ Napi::Env env = info.Env();
6097
+ unsigned int buf[256];
6098
+ int count = shim_rig_get_caps_dcs_list(my_rig, buf, 256);
6099
+ Napi::Array arr = Napi::Array::New(env, count);
6100
+ for (int i = 0; i < count; i++) {
6101
+ arr[(uint32_t)i] = Napi::Number::New(env, buf[i]);
6102
+ }
6103
+ return arr;
6104
+ }
6105
+
6106
+ Napi::Value NodeHamLib::GetFrequencyRanges(const Napi::CallbackInfo& info) {
6107
+ Napi::Env env = info.Env();
6108
+
6109
+ shim_freq_range_t rx_buf[30];
6110
+ shim_freq_range_t tx_buf[30];
6111
+ int rx_count = shim_rig_get_caps_rx_range(my_rig, rx_buf, 30);
6112
+ int tx_count = shim_rig_get_caps_tx_range(my_rig, tx_buf, 30);
6113
+
6114
+ auto buildArray = [&](shim_freq_range_t* buf, int count) -> Napi::Array {
6115
+ Napi::Array arr = Napi::Array::New(env, count);
6116
+ for (int i = 0; i < count; i++) {
6117
+ Napi::Object obj = Napi::Object::New(env);
6118
+ obj.Set("startFreq", Napi::Number::New(env, buf[i].start_freq));
6119
+ obj.Set("endFreq", Napi::Number::New(env, buf[i].end_freq));
6120
+ obj.Set("modes", ModeBitmaskToArray(env, buf[i].modes));
6121
+ obj.Set("lowPower", Napi::Number::New(env, buf[i].low_power));
6122
+ obj.Set("highPower", Napi::Number::New(env, buf[i].high_power));
6123
+ obj.Set("vfo", Napi::Number::New(env, buf[i].vfo));
6124
+ obj.Set("antenna", Napi::Number::New(env, buf[i].ant));
6125
+ arr[(uint32_t)i] = obj;
6126
+ }
6127
+ return arr;
6128
+ };
6129
+
6130
+ Napi::Object result = Napi::Object::New(env);
6131
+ result.Set("rx", buildArray(rx_buf, rx_count));
6132
+ result.Set("tx", buildArray(tx_buf, tx_count));
6133
+ return result;
6134
+ }
6135
+
6136
+ Napi::Value NodeHamLib::GetTuningSteps(const Napi::CallbackInfo& info) {
6137
+ Napi::Env env = info.Env();
6138
+ shim_mode_value_t buf[20];
6139
+ int count = shim_rig_get_caps_tuning_steps(my_rig, buf, 20);
6140
+ Napi::Array arr = Napi::Array::New(env, count);
6141
+ for (int i = 0; i < count; i++) {
6142
+ Napi::Object obj = Napi::Object::New(env);
6143
+ obj.Set("modes", ModeBitmaskToArray(env, buf[i].modes));
6144
+ obj.Set("stepHz", Napi::Number::New(env, buf[i].value));
6145
+ arr[(uint32_t)i] = obj;
6146
+ }
6147
+ return arr;
6148
+ }
6149
+
6150
+ Napi::Value NodeHamLib::GetFilterList(const Napi::CallbackInfo& info) {
6151
+ Napi::Env env = info.Env();
6152
+ shim_mode_value_t buf[60];
6153
+ int count = shim_rig_get_caps_filters(my_rig, buf, 60);
6154
+ Napi::Array arr = Napi::Array::New(env, count);
6155
+ for (int i = 0; i < count; i++) {
6156
+ Napi::Object obj = Napi::Object::New(env);
6157
+ obj.Set("modes", ModeBitmaskToArray(env, buf[i].modes));
6158
+ obj.Set("width", Napi::Number::New(env, buf[i].value));
6159
+ arr[(uint32_t)i] = obj;
6160
+ }
6161
+ return arr;
6162
+ }
6163
+
6164
+ // ===== Static: getCopyright / getLicense =====
6165
+
6166
+ Napi::Value NodeHamLib::GetCopyright(const Napi::CallbackInfo& info) {
6167
+ Napi::Env env = info.Env();
6168
+ return Napi::String::New(env, shim_rig_copyright());
6169
+ }
6170
+
6171
+ Napi::Value NodeHamLib::GetLicense(const Napi::CallbackInfo& info) {
6172
+ Napi::Env env = info.Env();
6173
+ return Napi::String::New(env, shim_rig_license());
6174
+ }