hamlib 0.2.7 → 0.3.1

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/lib/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const path = require('path');
2
+ const { EventEmitter } = require('events');
2
3
  const nodeGypBuild = require('node-gyp-build');
3
4
  // Ensure loader resolves from package root (contains prebuilds/ and build/)
4
5
  const nativeModule = nodeGypBuild(path.join(__dirname, '..'));
@@ -9,7 +10,7 @@ const nativeModule = nodeGypBuild(path.join(__dirname, '..'));
9
10
  * This is a wrapper around the native hamlib module that provides
10
11
  * a consistent interface for controlling amateur radio devices.
11
12
  */
12
- class HamLib {
13
+ class HamLib extends EventEmitter {
13
14
  /**
14
15
  * Create a new HamLib instance
15
16
  * @param {number} model - Radio model number (run `rigctl -l` to find your device model)
@@ -18,7 +19,9 @@ class HamLib {
18
19
  * - Network: 'localhost:4532' or '192.168.1.100:4532' (auto-switches to NETRIGCTL mode)
19
20
  */
20
21
  constructor(model, port) {
22
+ super();
21
23
  this._nativeInstance = new nativeModule.HamLib(model, port);
24
+ this._managedSpectrumRunning = false;
22
25
  }
23
26
 
24
27
  /**
@@ -187,6 +190,9 @@ class HamLib {
187
190
  * Connection can be re-established by calling open()
188
191
  */
189
192
  async close() {
193
+ if (this._managedSpectrumRunning) {
194
+ await this.stopManagedSpectrum();
195
+ }
190
196
  return this._nativeInstance.close();
191
197
  }
192
198
 
@@ -195,6 +201,9 @@ class HamLib {
195
201
  * Object reference should be deleted after calling this
196
202
  */
197
203
  async destroy() {
204
+ if (this._managedSpectrumRunning) {
205
+ await this.stopManagedSpectrum();
206
+ }
198
207
  return this._nativeInstance.destroy();
199
208
  }
200
209
 
@@ -1151,6 +1160,158 @@ class HamLib {
1151
1160
  return this._nativeInstance.sendRaw(data, replyMaxLen);
1152
1161
  }
1153
1162
 
1163
+ /**
1164
+ * Get official Hamlib spectrum capability metadata from the backend.
1165
+ * @returns {Promise<Object>} Spectrum capability object.
1166
+ */
1167
+ async getSpectrumCapabilities() {
1168
+ return this._nativeInstance.getSpectrumCapabilities();
1169
+ }
1170
+
1171
+ /**
1172
+ * Summarize whether official Hamlib spectrum streaming is usable for this rig.
1173
+ * @returns {Promise<Object>} Product-oriented support summary.
1174
+ */
1175
+ async getSpectrumSupportSummary() {
1176
+ const capabilities = await this.getSpectrumCapabilities();
1177
+ const supportedFunctions = this.getSupportedFunctions();
1178
+ const supportedLevels = this.getSupportedLevels();
1179
+ const hasSpectrumFunction = supportedFunctions.includes('SPECTRUM');
1180
+ const hasSpectrumHoldFunction = supportedFunctions.includes('SPECTRUM_HOLD');
1181
+ const hasTransceiveFunction = supportedFunctions.includes('TRANSCEIVE');
1182
+ const asyncDataSupported = capabilities.asyncDataSupported ?? hasSpectrumFunction;
1183
+ const configurableLevels = ['SPECTRUM_MODE', 'SPECTRUM_SPAN', 'SPECTRUM_SPEED', 'SPECTRUM_REF', 'SPECTRUM_AVG']
1184
+ .filter((name) => supportedLevels.includes(name));
1185
+
1186
+ return {
1187
+ supported: Boolean(hasSpectrumFunction),
1188
+ asyncDataSupported: Boolean(asyncDataSupported),
1189
+ hasSpectrumFunction,
1190
+ hasSpectrumHoldFunction,
1191
+ hasTransceiveFunction,
1192
+ configurableLevels,
1193
+ scopes: capabilities.scopes ?? [],
1194
+ modes: capabilities.modes ?? [],
1195
+ spans: capabilities.spans ?? [],
1196
+ avgModes: capabilities.avgModes ?? [],
1197
+ };
1198
+ }
1199
+
1200
+ /**
1201
+ * Apply official Hamlib spectrum settings using high-level level/function APIs.
1202
+ * Unsupported settings are skipped silently.
1203
+ * @param {Object} [config]
1204
+ */
1205
+ async configureSpectrum(config = {}) {
1206
+ const summary = await this.getSpectrumSupportSummary();
1207
+ const applyLevel = async (name, value) => {
1208
+ if (value === undefined || !summary.configurableLevels.includes(name)) return;
1209
+ await this.setLevel(name, value);
1210
+ };
1211
+
1212
+ if (summary.hasSpectrumHoldFunction && config.hold !== undefined) {
1213
+ await this.setFunction('SPECTRUM_HOLD', Boolean(config.hold));
1214
+ }
1215
+
1216
+ await applyLevel('SPECTRUM_MODE', config.mode);
1217
+ await applyLevel('SPECTRUM_SPAN', config.spanHz);
1218
+ await applyLevel('SPECTRUM_SPEED', config.speed);
1219
+ await applyLevel('SPECTRUM_REF', config.referenceLevel);
1220
+ await applyLevel('SPECTRUM_AVG', config.averageMode);
1221
+
1222
+ return summary;
1223
+ }
1224
+
1225
+ /**
1226
+ * Start the official Hamlib spectrum callback stream.
1227
+ * @param {(line: Object) => void} [callback]
1228
+ * @returns {Promise<boolean>}
1229
+ */
1230
+ async startSpectrumStream(callback) {
1231
+ const listener = typeof callback === 'function'
1232
+ ? callback
1233
+ : (line) => this.emit('spectrumLine', line);
1234
+ return this._nativeInstance.startSpectrumStream(listener);
1235
+ }
1236
+
1237
+ /**
1238
+ * Stop the official Hamlib spectrum callback stream.
1239
+ * @returns {Promise<boolean>}
1240
+ */
1241
+ async stopSpectrumStream() {
1242
+ return this._nativeInstance.stopSpectrumStream();
1243
+ }
1244
+
1245
+ /**
1246
+ * High-level managed spectrum startup for application use.
1247
+ * @param {Object} [config]
1248
+ */
1249
+ async startManagedSpectrum(config = {}) {
1250
+ const summary = await this.getSpectrumSupportSummary();
1251
+ if (!summary.supported) {
1252
+ throw new Error('Official Hamlib spectrum streaming is not supported by this rig/backend');
1253
+ }
1254
+
1255
+ if (this._managedSpectrumRunning) {
1256
+ return true;
1257
+ }
1258
+
1259
+ await this.startSpectrumStream((line) => this.emit('spectrumLine', line));
1260
+
1261
+ try {
1262
+ try {
1263
+ await this.setConf('async', '1');
1264
+ } catch (_) {
1265
+ try {
1266
+ await this.setConf('async', 'True');
1267
+ } catch (_) {
1268
+ // Not every backend accepts the config write even if async is supported.
1269
+ }
1270
+ }
1271
+
1272
+ await this.configureSpectrum({ hold: false, ...config });
1273
+ await this.setFunction('SPECTRUM', true);
1274
+ if (summary.hasTransceiveFunction) {
1275
+ await this.setFunction('TRANSCEIVE', true);
1276
+ }
1277
+ } catch (error) {
1278
+ try {
1279
+ await this.stopManagedSpectrum();
1280
+ } catch (_) {
1281
+ // Ignore cleanup failures and surface the original startup error.
1282
+ }
1283
+ throw error;
1284
+ }
1285
+
1286
+ this._managedSpectrumRunning = true;
1287
+ this.emit('spectrumStateChanged', { active: true });
1288
+ return true;
1289
+ }
1290
+
1291
+ /**
1292
+ * High-level managed spectrum shutdown for application use.
1293
+ */
1294
+ async stopManagedSpectrum() {
1295
+ try {
1296
+ const supportedFunctions = this.getSupportedFunctions();
1297
+ if (supportedFunctions.includes('TRANSCEIVE')) {
1298
+ await this.setFunction('TRANSCEIVE', false);
1299
+ }
1300
+ if (supportedFunctions.includes('SPECTRUM_HOLD')) {
1301
+ await this.setFunction('SPECTRUM_HOLD', false);
1302
+ }
1303
+ if (supportedFunctions.includes('SPECTRUM')) {
1304
+ await this.setFunction('SPECTRUM', false);
1305
+ }
1306
+ } finally {
1307
+ await this.stopSpectrumStream();
1308
+ }
1309
+
1310
+ this._managedSpectrumRunning = false;
1311
+ this.emit('spectrumStateChanged', { active: false });
1312
+ return true;
1313
+ }
1314
+
1154
1315
  /**
1155
1316
  * Set a backend configuration parameter
1156
1317
  * @param {string} name - Configuration token name
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hamlib",
3
- "version": "0.2.7",
4
- "description": "Node.js wrapper for hamlib radio control library",
3
+ "version": "0.3.1",
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",
7
7
  "types": "index.d.ts",
@@ -30,6 +30,7 @@
30
30
  "lib/",
31
31
  "prebuilds/",
32
32
  "src/",
33
+ "docs/",
33
34
  "index.js",
34
35
  "index.d.ts",
35
36
  "binding.gyp",
@@ -43,9 +44,11 @@
43
44
  "rebuild": "node-gyp rebuild",
44
45
  "clean": "node-gyp clean",
45
46
  "test": "node test/test_loader.js",
47
+ "test:version": "node test/test_version.js",
46
48
  "test:network": "node test/test_network.js",
47
49
  "test:dummy": "node test/test_dummy_complete.js",
48
50
  "test:serial": "node test/test_serial_config.js",
51
+ "test:spectrum": "node test/test_spectrum_stream.js",
49
52
  "prebuild": "prebuildify --napi --strip",
50
53
  "prepare": "",
51
54
  "bundle": "node scripts/bundle-deps.js",
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file