hamlib 0.4.1 → 0.4.3

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/README.md CHANGED
@@ -8,6 +8,8 @@ Control amateur radio transceivers from Node.js using the [Hamlib](https://hamli
8
8
  - **Full Async/Promise API** - Non-blocking operations with async/await support
9
9
  - **Comprehensive Serial Control** - 13 parameters for complete serial port configuration
10
10
  - **Multiple Connections** - Serial ports, network (rigctld), direct control
11
+ - **Rotator Bridge** - Control antenna rotators through Hamlib's `rot_*` API with the same Promise-first bridge style
12
+ - **Antenna Switching** - Rig antenna APIs now use true 1-based antenna numbers and optional antenna option values
11
13
  - **Official Spectrum Streaming** - Wraps Hamlib's official spectrum callback API with Promise helpers and typed events
12
14
  - **TypeScript Ready** - Complete type definitions included
13
15
  - **Cross-platform** - Windows, Linux, macOS
@@ -38,7 +40,7 @@ For faster installation or offline environments, you can manually install precom
38
40
  ## Quick Start
39
41
 
40
42
  ```javascript
41
- const { HamLib } = require('hamlib');
43
+ const { HamLib, Rotator } = require('hamlib');
42
44
 
43
45
  async function main() {
44
46
  // Create rig instance (model 1035 = FT-991A)
@@ -55,6 +57,13 @@ async function main() {
55
57
  console.log(`${freq/1000000} MHz ${mode.mode}`);
56
58
 
57
59
  await rig.close();
60
+
61
+ // Create rotator instance (model 1 = Dummy rotator)
62
+ const rotator = new Rotator(1);
63
+ await rotator.open();
64
+ await rotator.setPosition(180, 30);
65
+ console.log(await rotator.getPosition());
66
+ await rotator.close();
58
67
  }
59
68
 
60
69
  main().catch(console.error);
@@ -77,6 +86,27 @@ await rig.open();
77
86
  await rig.close();
78
87
  ```
79
88
 
89
+ ### Rotator Control
90
+
91
+ ```javascript
92
+ const rotators = Rotator.getSupportedRotators();
93
+ const dummy = rotators.find(r => r.modelName === 'Dummy');
94
+
95
+ const rotator = new Rotator(dummy.rotModel);
96
+ await rotator.open();
97
+
98
+ await rotator.setPosition(135, 20);
99
+ const position = await rotator.getPosition();
100
+
101
+ await rotator.move('RIGHT', 2);
102
+ await rotator.stop();
103
+
104
+ const status = await rotator.getStatus();
105
+ const caps = rotator.getRotatorCaps();
106
+
107
+ await rotator.close();
108
+ ```
109
+
80
110
  ### Basic Control
81
111
 
82
112
  ```javascript
@@ -136,6 +166,12 @@ const audioLevel = await rig.getLevel('AF');
136
166
  await rig.setFunction('NB', true); // Noise blanker on
137
167
  const voxEnabled = await rig.getFunction('VOX');
138
168
 
169
+ // Antenna switching (1-based antenna number)
170
+ await rig.setAntenna(2); // Select antenna 2
171
+ await rig.setAntenna(2, 7); // Select antenna 2 with backend-specific option
172
+ const antenna = await rig.getAntenna();
173
+ console.log(antenna.currentAntenna, antenna.option);
174
+
139
175
  // Split operation
140
176
  await rig.setSplit(true); // Enable split
141
177
  await rig.setSplitFreq(144340000); // TX frequency
package/binding.gyp CHANGED
@@ -4,6 +4,7 @@
4
4
  "target_name": "hamlib",
5
5
  "sources": [
6
6
  "src/hamlib.cpp",
7
+ "src/node_rotator.cpp",
7
8
  "src/decoder.cpp",
8
9
  "src/addon.cpp"
9
10
  ],
package/index.d.ts CHANGED
@@ -9,11 +9,15 @@ interface ConnectionInfo {
9
9
  /** Port path or network address */
10
10
  portPath: string;
11
11
  /** Connection status */
12
- connected: boolean;
12
+ isOpen: boolean;
13
13
  /** Original model number */
14
14
  originalModel: number;
15
15
  /** Actual model number used */
16
- actualModel: number;
16
+ currentModel: number;
17
+ /** @deprecated Use isOpen */
18
+ connected?: boolean;
19
+ /** @deprecated Use currentModel */
20
+ actualModel?: number;
17
21
  }
18
22
 
19
23
  /**
@@ -44,6 +48,16 @@ interface SupportedRigInfo {
44
48
  rigType: string;
45
49
  }
46
50
 
51
+ interface SupportedRotatorInfo {
52
+ rotModel: number;
53
+ modelName: string;
54
+ mfgName: string;
55
+ version: string;
56
+ status: string;
57
+ rotType: 'azimuth' | 'elevation' | 'azel' | 'other';
58
+ rotTypeMask: number;
59
+ }
60
+
47
61
  /**
48
62
  * Antenna information interface
49
63
  */
@@ -58,6 +72,53 @@ interface AntennaInfo {
58
72
  option: number;
59
73
  }
60
74
 
75
+ interface RotatorConnectionInfo {
76
+ connectionType: 'serial' | 'network';
77
+ portPath: string;
78
+ isOpen: boolean;
79
+ originalModel: number;
80
+ currentModel: number;
81
+ }
82
+
83
+ interface RotatorPosition {
84
+ azimuth: number;
85
+ elevation: number;
86
+ }
87
+
88
+ interface RotatorStatus {
89
+ mask: number;
90
+ flags: string[];
91
+ }
92
+
93
+ type RotatorDirection =
94
+ | 'UP'
95
+ | 'DOWN'
96
+ | 'LEFT'
97
+ | 'RIGHT'
98
+ | 'CCW'
99
+ | 'CW'
100
+ | 'UP_LEFT'
101
+ | 'UP_RIGHT'
102
+ | 'DOWN_LEFT'
103
+ | 'DOWN_RIGHT'
104
+ | 'UP_CCW'
105
+ | 'UP_CW'
106
+ | 'DOWN_CCW'
107
+ | 'DOWN_CW'
108
+ | number;
109
+
110
+ type RotatorResetType = 'ALL' | number;
111
+
112
+ interface RotatorCaps {
113
+ rotType: 'azimuth' | 'elevation' | 'azel' | 'other';
114
+ rotTypeMask: number;
115
+ minAz: number;
116
+ maxAz: number;
117
+ minEl: number;
118
+ maxEl: number;
119
+ supportedStatuses: string[];
120
+ }
121
+
61
122
  /**
62
123
  * Hamlib VFO token.
63
124
  * 实际支持的 token 取决于具体 backend / 电台型号。
@@ -378,6 +439,62 @@ interface SerialConfigOptions {
378
439
  dcd_type: string[];
379
440
  }
380
441
 
442
+ type HamlibConfigFieldType =
443
+ | 'string'
444
+ | 'combo'
445
+ | 'numeric'
446
+ | 'checkbutton'
447
+ | 'button'
448
+ | 'binary'
449
+ | 'int'
450
+ | 'unknown';
451
+
452
+ interface HamlibConfigFieldDescriptor {
453
+ token: number;
454
+ name: string;
455
+ label: string;
456
+ tooltip: string;
457
+ defaultValue: string;
458
+ type: HamlibConfigFieldType;
459
+ numeric?: {
460
+ min: number;
461
+ max: number;
462
+ step: number;
463
+ };
464
+ options?: string[];
465
+ }
466
+
467
+ type HamlibPortType =
468
+ | 'none'
469
+ | 'serial'
470
+ | 'network'
471
+ | 'device'
472
+ | 'packet'
473
+ | 'dtmf'
474
+ | 'ultra'
475
+ | 'rpc'
476
+ | 'parallel'
477
+ | 'usb'
478
+ | 'udp-network'
479
+ | 'cm108'
480
+ | 'gpio'
481
+ | 'gpion'
482
+ | 'other';
483
+
484
+ interface HamlibPortCaps {
485
+ portType: HamlibPortType;
486
+ serialRateMin?: number;
487
+ serialRateMax?: number;
488
+ serialDataBits?: number;
489
+ serialStopBits?: number;
490
+ serialParity?: string;
491
+ serialHandshake?: string;
492
+ writeDelay: number;
493
+ postWriteDelay: number;
494
+ timeout: number;
495
+ retry: number;
496
+ }
497
+
381
498
  /**
382
499
  * HamLib class - for controlling amateur radio devices
383
500
  */
@@ -856,6 +973,18 @@ declare class HamLib extends EventEmitter {
856
973
  */
857
974
  getSupportedSerialConfigs(): SerialConfigOptions;
858
975
 
976
+ /**
977
+ * Get backend-specific configuration schema for the current rig model.
978
+ * This can be queried before opening the connection and is suitable for
979
+ * driving dynamic configuration UIs.
980
+ */
981
+ getConfigSchema(): HamlibConfigFieldDescriptor[];
982
+
983
+ /**
984
+ * Get backend port capabilities and effective connection defaults.
985
+ */
986
+ getPortCaps(): HamlibPortCaps;
987
+
859
988
  // Power Control
860
989
 
861
990
  /**
@@ -1231,6 +1360,7 @@ declare class HamLib extends EventEmitter {
1231
1360
  * await rig.setAntenna(2); // Select antenna 2
1232
1361
  */
1233
1362
  setAntenna(antenna: number, vfo?: VFO): Promise<number>;
1363
+ setAntenna(antenna: number, option: number, vfo?: VFO): Promise<number>;
1234
1364
 
1235
1365
  /**
1236
1366
  * Get comprehensive antenna information
@@ -1506,6 +1636,50 @@ declare class HamLib extends EventEmitter {
1506
1636
  getVfoInfo(vfo?: VFO): Promise<VfoInfo>;
1507
1637
  }
1508
1638
 
1639
+ declare class Rotator extends EventEmitter {
1640
+ constructor(model: number, port?: string);
1641
+
1642
+ static getSupportedRotators(): SupportedRotatorInfo[];
1643
+ static getHamlibVersion(): string;
1644
+ static setDebugLevel(level: RigDebugLevel): void;
1645
+ static getDebugLevel(): never;
1646
+ static getCopyright(): string;
1647
+ static getLicense(): string;
1648
+
1649
+ open(): Promise<number>;
1650
+ close(): Promise<number>;
1651
+ destroy(): Promise<number>;
1652
+ getConnectionInfo(): RotatorConnectionInfo;
1653
+
1654
+ setPosition(azimuth: number, elevation: number): Promise<number>;
1655
+ getPosition(): Promise<RotatorPosition>;
1656
+ move(direction: RotatorDirection, speed: number): Promise<number>;
1657
+ stop(): Promise<number>;
1658
+ park(): Promise<number>;
1659
+ reset(resetType: RotatorResetType): Promise<number>;
1660
+
1661
+ getInfo(): Promise<string>;
1662
+ getStatus(): Promise<RotatorStatus>;
1663
+
1664
+ setConf(name: string, value: string): Promise<number>;
1665
+ getConf(name: string): Promise<string>;
1666
+ getConfigSchema(): HamlibConfigFieldDescriptor[];
1667
+ getPortCaps(): HamlibPortCaps;
1668
+ getRotatorCaps(): RotatorCaps;
1669
+
1670
+ setLevel(level: string, value: number): Promise<number>;
1671
+ getLevel(level: string): Promise<number>;
1672
+ getSupportedLevels(): string[];
1673
+
1674
+ setFunction(func: string, enable: boolean): Promise<number>;
1675
+ getFunction(func: string): Promise<boolean>;
1676
+ getSupportedFunctions(): string[];
1677
+
1678
+ setParm(parm: string, value: number): Promise<number>;
1679
+ getParm(parm: string): Promise<number>;
1680
+ getSupportedParms(): string[];
1681
+ }
1682
+
1509
1683
  /**
1510
1684
  * Clock information for rig's internal clock
1511
1685
  */
@@ -1536,16 +1710,19 @@ interface VfoInfo {
1536
1710
  */
1537
1711
  declare const nodeHamlib: {
1538
1712
  HamLib: typeof HamLib;
1713
+ Rotator: typeof Rotator;
1539
1714
  };
1540
1715
 
1541
1716
  // Export types for use elsewhere
1542
- export { ConnectionInfo, ModeInfo, SupportedRigInfo, AntennaInfo, VFO, RadioMode, MemoryChannelData,
1717
+ export { ConnectionInfo, ModeInfo, SupportedRigInfo, SupportedRotatorInfo, AntennaInfo, RotatorConnectionInfo,
1718
+ RotatorPosition, RotatorStatus, RotatorDirection, RotatorResetType, RotatorCaps, VFO, RadioMode, MemoryChannelData,
1543
1719
  MemoryChannelInfo, SplitModeInfo, SplitStatusInfo, LevelType, FunctionType,
1544
1720
  ScanType, VfoOperationType, SerialConfigParam, SerialBaudRate, SerialParity,
1545
1721
  SerialHandshake, SerialControlState, PttType, DcdType, SerialConfigOptions,
1722
+ HamlibConfigFieldType, HamlibConfigFieldDescriptor, HamlibPortType, HamlibPortCaps,
1546
1723
  SpectrumScopeInfo, SpectrumModeInfo, SpectrumAverageModeInfo, SpectrumLine,
1547
1724
  SpectrumCapabilities, SpectrumSupportSummary, SpectrumConfig, SpectrumDisplayState,
1548
- ClockInfo, VfoInfo, HamLib };
1725
+ ClockInfo, VfoInfo, HamLib, Rotator };
1549
1726
 
1550
1727
  // Support both CommonJS and ES module exports
1551
1728
  // @ts-ignore
package/lib/index.js CHANGED
@@ -589,6 +589,25 @@ class HamLib extends EventEmitter {
589
589
  return this._nativeInstance.getSupportedSerialConfigs();
590
590
  }
591
591
 
592
+ /**
593
+ * Get backend-specific configuration schema for the selected rig model.
594
+ * This can be queried before opening the connection and is intended for
595
+ * dynamic configuration UIs.
596
+ * @returns {Array<Object>} List of config field descriptors
597
+ */
598
+ getConfigSchema() {
599
+ return this._nativeInstance.getConfigSchema();
600
+ }
601
+
602
+ /**
603
+ * Get backend port capabilities and effective connection defaults derived
604
+ * from Hamlib rig caps for the selected model.
605
+ * @returns {Object} Port type and default connection parameters
606
+ */
607
+ getPortCaps() {
608
+ return this._nativeInstance.getPortCaps();
609
+ }
610
+
592
611
  // Power Control
593
612
 
594
613
  /**
@@ -1054,18 +1073,21 @@ class HamLib extends EventEmitter {
1054
1073
  /**
1055
1074
  * Set antenna selection
1056
1075
  * @param {number} antenna - Antenna number to select (1-based)
1076
+ * @param {number|string} [optionOrVfo] - Optional antenna option or VFO token
1057
1077
  * @param {string} [vfo] - Optional VFO ('VFOA' or 'VFOB')
1058
1078
  * @returns {Promise<number>} Success status
1059
1079
  * @example
1060
1080
  * await rig.setAntenna(1); // Select antenna 1
1061
1081
  * await rig.setAntenna(2); // Select antenna 2
1062
1082
  */
1063
- async setAntenna(antenna, vfo) {
1083
+ async setAntenna(antenna, optionOrVfo, vfo) {
1064
1084
  if (vfo !== undefined) {
1065
- return this._nativeInstance.setAntenna(antenna, vfo);
1066
- } else {
1067
- return this._nativeInstance.setAntenna(antenna);
1085
+ return this._nativeInstance.setAntenna(antenna, optionOrVfo, vfo);
1068
1086
  }
1087
+ if (optionOrVfo !== undefined) {
1088
+ return this._nativeInstance.setAntenna(antenna, optionOrVfo);
1089
+ }
1090
+ return this._nativeInstance.setAntenna(antenna);
1069
1091
  }
1070
1092
 
1071
1093
  /**
@@ -1391,7 +1413,143 @@ class HamLib extends EventEmitter {
1391
1413
  }
1392
1414
  }
1393
1415
 
1416
+ class Rotator extends EventEmitter {
1417
+ constructor(model, port) {
1418
+ super();
1419
+ this._nativeInstance = new nativeModule.Rotator(model, port);
1420
+ }
1421
+
1422
+ static getSupportedRotators() {
1423
+ return nativeModule.Rotator.getSupportedRotators();
1424
+ }
1425
+
1426
+ static getHamlibVersion() {
1427
+ return nativeModule.Rotator.getHamlibVersion();
1428
+ }
1429
+
1430
+ static setDebugLevel(level) {
1431
+ return nativeModule.Rotator.setDebugLevel(level);
1432
+ }
1433
+
1434
+ static getDebugLevel() {
1435
+ return nativeModule.Rotator.getDebugLevel();
1436
+ }
1437
+
1438
+ static getCopyright() {
1439
+ return nativeModule.Rotator.getCopyright();
1440
+ }
1441
+
1442
+ static getLicense() {
1443
+ return nativeModule.Rotator.getLicense();
1444
+ }
1445
+
1446
+ async open() {
1447
+ return this._nativeInstance.open();
1448
+ }
1449
+
1450
+ async close() {
1451
+ return this._nativeInstance.close();
1452
+ }
1453
+
1454
+ async destroy() {
1455
+ return this._nativeInstance.destroy();
1456
+ }
1457
+
1458
+ getConnectionInfo() {
1459
+ return this._nativeInstance.getConnectionInfo();
1460
+ }
1461
+
1462
+ async setPosition(azimuth, elevation) {
1463
+ return this._nativeInstance.setPosition(azimuth, elevation);
1464
+ }
1465
+
1466
+ async getPosition() {
1467
+ return this._nativeInstance.getPosition();
1468
+ }
1469
+
1470
+ async move(direction, speed) {
1471
+ return this._nativeInstance.move(direction, speed);
1472
+ }
1473
+
1474
+ async stop() {
1475
+ return this._nativeInstance.stop();
1476
+ }
1477
+
1478
+ async park() {
1479
+ return this._nativeInstance.park();
1480
+ }
1481
+
1482
+ async reset(resetType) {
1483
+ return this._nativeInstance.reset(resetType);
1484
+ }
1485
+
1486
+ async getInfo() {
1487
+ return this._nativeInstance.getInfo();
1488
+ }
1489
+
1490
+ async getStatus() {
1491
+ return this._nativeInstance.getStatus();
1492
+ }
1493
+
1494
+ async setConf(name, value) {
1495
+ return this._nativeInstance.setConf(name, value);
1496
+ }
1497
+
1498
+ async getConf(name) {
1499
+ return this._nativeInstance.getConf(name);
1500
+ }
1501
+
1502
+ getConfigSchema() {
1503
+ return this._nativeInstance.getConfigSchema();
1504
+ }
1505
+
1506
+ getPortCaps() {
1507
+ return this._nativeInstance.getPortCaps();
1508
+ }
1509
+
1510
+ getRotatorCaps() {
1511
+ return this._nativeInstance.getRotatorCaps();
1512
+ }
1513
+
1514
+ async setLevel(level, value) {
1515
+ return this._nativeInstance.setLevel(level, value);
1516
+ }
1517
+
1518
+ async getLevel(level) {
1519
+ return this._nativeInstance.getLevel(level);
1520
+ }
1521
+
1522
+ getSupportedLevels() {
1523
+ return this._nativeInstance.getSupportedLevels();
1524
+ }
1525
+
1526
+ async setFunction(func, enable) {
1527
+ return this._nativeInstance.setFunction(func, enable);
1528
+ }
1529
+
1530
+ async getFunction(func) {
1531
+ return this._nativeInstance.getFunction(func);
1532
+ }
1533
+
1534
+ getSupportedFunctions() {
1535
+ return this._nativeInstance.getSupportedFunctions();
1536
+ }
1537
+
1538
+ async setParm(parm, value) {
1539
+ return this._nativeInstance.setParm(parm, value);
1540
+ }
1541
+
1542
+ async getParm(parm) {
1543
+ return this._nativeInstance.getParm(parm);
1544
+ }
1545
+
1546
+ getSupportedParms() {
1547
+ return this._nativeInstance.getSupportedParms();
1548
+ }
1549
+ }
1550
+
1394
1551
  // Export for CommonJS
1395
- module.exports = { HamLib };
1552
+ module.exports = { HamLib, Rotator };
1396
1553
  module.exports.HamLib = HamLib;
1397
- module.exports.default = { HamLib };
1554
+ module.exports.Rotator = Rotator;
1555
+ module.exports.default = { HamLib, Rotator };
package/lib/index.mjs CHANGED
@@ -7,8 +7,8 @@ const __dirname = path.dirname(__filename);
7
7
  const require = createRequire(import.meta.url);
8
8
 
9
9
  // Import the CommonJS module
10
- const { HamLib } = require('./index.js');
10
+ const { HamLib, Rotator } = require('./index.js');
11
11
 
12
12
  // Export for ES modules
13
- export { HamLib };
14
- export default { HamLib };
13
+ export { HamLib, Rotator };
14
+ export default { HamLib, Rotator };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hamlib",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Node.js bindings for Hamlib rig control with official spectrum streaming support",
5
5
  "main": "index.js",
6
6
  "module": "lib/index.mjs",
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/src/addon.cpp CHANGED
@@ -1,5 +1,6 @@
1
1
  #include <napi.h>
2
2
  #include "hamlib.h"
3
+ #include "node_rotator.h"
3
4
  #include "decoder.h"
4
5
  #include "shim/hamlib_shim.h"
5
6
 
@@ -11,6 +12,9 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
11
12
  Napi::String name = Napi::String::New(env, "HamLib");
12
13
  exports.Set(name, NodeHamLib::GetClass(env));
13
14
 
15
+ Napi::String rotatorName = Napi::String::New(env, "Rotator");
16
+ exports.Set(rotatorName, NodeRotator::GetClass(env));
17
+
14
18
 
15
19
  // Napi::String decoder_name = Napi::String::New(env, "Decoder");
16
20
  // exports.Set(decoder_name, Decoder::GetClass(env));
@@ -18,4 +22,4 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
18
22
  return exports;
19
23
  }
20
24
 
21
- NODE_API_MODULE(radio, Init)
25
+ NODE_API_MODULE(radio, Init)