icom-wlan-node 0.6.0 → 0.6.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/README.md CHANGED
@@ -126,6 +126,27 @@ console.log({ tuner, swr, watts: power?.watts, powerPercent: power?.percent });
126
126
 
127
127
  Profile-specific behavior includes IC-905 6-byte frequency BCD above 5.85 GHz, model-specific scope fixed-edge ranges, and calibrated SWR/ALC/RF power/COMP/voltage/current meters. Private connector commands such as WLAN level or connector data mode are only enabled when the active profile declares the vendor extension; unsupported writes throw `UnsupportedCommandError`.
128
128
 
129
+ ### CI-V Query Concurrency
130
+
131
+ Standard CI-V frames do not include a transaction/request ID, so the library does not add custom bytes to the CI-V payload. Instead, read/query helpers are internally deduplicated by response signature:
132
+
133
+ ```ts
134
+ // Same response key: one real CI-V request, shared result
135
+ const [a, b] = await Promise.all([
136
+ rig.readOperatingFrequency(),
137
+ rig.readOperatingFrequency()
138
+ ]);
139
+
140
+ // Different response keys: concurrent requests are allowed
141
+ const [freq, mode, ptt] = await Promise.all([
142
+ rig.readOperatingFrequency(),
143
+ rig.readOperatingMode(),
144
+ rig.readPtt()
145
+ ]);
146
+ ```
147
+
148
+ Raw `sendCiv()` is not managed by this query layer. Write methods such as `setFrequency()`, `setMode()`, and `setPtt()` remain fire-and-forget; if your application needs write-after-read consistency, send the write first and then issue the read sequentially.
149
+
129
150
  ### PTT and Audio TX
130
151
 
131
152
  ```ts
@@ -0,0 +1,22 @@
1
+ import type { RigEventEmitter } from '../types';
2
+ export interface CivQueryOptions {
3
+ key: string;
4
+ predicate: (frame: Buffer) => boolean;
5
+ timeoutMs: number;
6
+ send: () => void;
7
+ }
8
+ /**
9
+ * Deduplicates identical CI-V read queries while allowing different reply
10
+ * signatures to wait concurrently on the shared civFrame stream.
11
+ */
12
+ export declare class IcomCivRequestManager {
13
+ private readonly events;
14
+ private pending;
15
+ private readonly onFrameBound;
16
+ constructor(events: RigEventEmitter);
17
+ query(options: CivQueryOptions): Promise<Buffer | null>;
18
+ dispose(): void;
19
+ getPendingCount(): number;
20
+ private onFrame;
21
+ private finish;
22
+ }
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IcomCivRequestManager = void 0;
4
+ /**
5
+ * Deduplicates identical CI-V read queries while allowing different reply
6
+ * signatures to wait concurrently on the shared civFrame stream.
7
+ */
8
+ class IcomCivRequestManager {
9
+ constructor(events) {
10
+ this.events = events;
11
+ this.pending = new Map();
12
+ this.onFrameBound = (frame) => this.onFrame(frame);
13
+ this.events.on('civFrame', this.onFrameBound);
14
+ }
15
+ query(options) {
16
+ const existing = this.pending.get(options.key);
17
+ if (existing) {
18
+ return existing.promise;
19
+ }
20
+ let entry;
21
+ const promise = new Promise((resolve, reject) => {
22
+ const timer = setTimeout(() => {
23
+ if (this.pending.get(options.key) === entry) {
24
+ this.pending.delete(options.key);
25
+ }
26
+ resolve(null);
27
+ }, options.timeoutMs);
28
+ entry = {
29
+ promise: undefined,
30
+ predicate: options.predicate,
31
+ resolve,
32
+ reject,
33
+ timer,
34
+ };
35
+ });
36
+ entry.promise = promise;
37
+ this.pending.set(options.key, entry);
38
+ try {
39
+ options.send();
40
+ }
41
+ catch (err) {
42
+ this.finish(options.key, entry, null, err);
43
+ }
44
+ return promise;
45
+ }
46
+ dispose() {
47
+ this.events.off('civFrame', this.onFrameBound);
48
+ for (const [key, entry] of this.pending) {
49
+ this.finish(key, entry, null);
50
+ }
51
+ }
52
+ getPendingCount() {
53
+ return this.pending.size;
54
+ }
55
+ onFrame(frame) {
56
+ for (const [key, entry] of Array.from(this.pending.entries())) {
57
+ if (entry.predicate(frame)) {
58
+ this.finish(key, entry, frame);
59
+ }
60
+ }
61
+ }
62
+ finish(key, entry, frame, err) {
63
+ if (this.pending.get(key) === entry) {
64
+ this.pending.delete(key);
65
+ }
66
+ clearTimeout(entry.timer);
67
+ if (err !== undefined) {
68
+ entry.reject(err);
69
+ return;
70
+ }
71
+ entry.resolve(frame);
72
+ }
73
+ }
74
+ exports.IcomCivRequestManager = IcomCivRequestManager;
@@ -16,6 +16,7 @@ export declare class IcomControl {
16
16
  private macAddress;
17
17
  private tokenTimer?;
18
18
  private civAssembleBuf;
19
+ private civRequestManager;
19
20
  private meterTimer?;
20
21
  private activeProfile;
21
22
  private lastFilter;
@@ -52,6 +52,7 @@ const IcomScopeService_1 = require("../scope/IcomScopeService");
52
52
  const IcomCivFrame_1 = require("./IcomCivFrame");
53
53
  const IcomCivSpec_1 = require("./IcomCivSpec");
54
54
  const IcomProfiles_1 = require("./IcomProfiles");
55
+ const IcomCivRequestManager_1 = require("./IcomCivRequestManager");
55
56
  const DEFAULT_SCOPE_SPANS_HZ = [25000000, 10000000, 5000000, 2500000, 1000000, 500000, 250000, 100000, 50000, 25000, 10000, 5000, 2500];
56
57
  function modeCodeToName(mode) {
57
58
  switch (mode) {
@@ -119,6 +120,7 @@ class IcomControl {
119
120
  this.civ = new IcomCiv_1.IcomCiv(this.civSess);
120
121
  this.audio = new IcomAudio_1.IcomAudio(this.audioSess);
121
122
  this.scope = new IcomScopeService_1.IcomScopeService();
123
+ this.civRequestManager = new IcomCivRequestManager_1.IcomCivRequestManager(this.ev);
122
124
  this.scope.on('scopeSegment', (segment) => this.ev.emit('scopeSegment', segment));
123
125
  this.scope.on('scopeFrame', (frame) => this.ev.emit('scopeFrame', frame));
124
126
  }
@@ -576,7 +578,7 @@ class IcomControl {
576
578
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
577
579
  const rigAddr = this.civ.civAddress & 0xff;
578
580
  const req = IcomScopeCommands_1.IcomScopeCommands.readScopeSpan(ctrAddr, rigAddr, receiver);
579
- const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x15, receiver], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
581
+ const resp = await this.waitForCivFrame(`scope:0x27:0x15:${receiver}`, (frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x15, receiver], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
580
582
  if (!resp || resp.length < 13) {
581
583
  return null;
582
584
  }
@@ -598,7 +600,7 @@ class IcomControl {
598
600
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
599
601
  const rigAddr = this.civ.civAddress & 0xff;
600
602
  const req = IcomScopeCommands_1.IcomScopeCommands.readScopeMode(ctrAddr, rigAddr, receiver);
601
- const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x14, receiver], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
603
+ const resp = await this.waitForCivFrame(`scope:0x27:0x14:${receiver}`, (frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x14, receiver], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
602
604
  if (!resp || resp.length < 9) {
603
605
  return null;
604
606
  }
@@ -622,7 +624,7 @@ class IcomControl {
622
624
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
623
625
  const rigAddr = this.civ.civAddress & 0xff;
624
626
  const req = IcomScopeCommands_1.IcomScopeCommands.readScopeEdge(ctrAddr, rigAddr, receiver);
625
- const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x16, receiver], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
627
+ const resp = await this.waitForCivFrame(`scope:0x27:0x16:${receiver}`, (frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x16, receiver], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
626
628
  if (!resp || resp.length < 9) {
627
629
  return null;
628
630
  }
@@ -645,7 +647,7 @@ class IcomControl {
645
647
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
646
648
  const rigAddr = this.civ.civAddress & 0xff;
647
649
  const req = IcomScopeCommands_1.IcomScopeCommands.readScopeFixedEdge(ctrAddr, rigAddr, rangeId, edgeSlot);
648
- const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x1e, rangeId, edgeSlot], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
650
+ const resp = await this.waitForCivFrame(`scope:0x27:0x1e:${rangeId}:${edgeSlot}`, (frame) => IcomControl.matchCommandFrame(frame, 0x27, [0x1e, rangeId, edgeSlot], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
649
651
  if (!resp || resp.length < 18) {
650
652
  return null;
651
653
  }
@@ -855,7 +857,7 @@ class IcomControl {
855
857
  const req = useX25
856
858
  ? IcomRigCommands_1.IcomRigCommands.readSelectedFrequency(ctrAddr, rigAddr, 0)
857
859
  : IcomRigCommands_1.IcomRigCommands.readOperatingFrequency(ctrAddr, rigAddr);
858
- const resp = await this.waitForCivFrame((frame) => useX25
860
+ const resp = await this.waitForCivFrame(useX25 ? 'freq:0x25:0' : 'freq:0x03', (frame) => useX25
859
861
  ? IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_SEND_SEL_FREQ, [0x00], ctrAddr, rigAddr)
860
862
  : IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_RD_FREQ, [], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
861
863
  if (!resp)
@@ -872,7 +874,7 @@ class IcomControl {
872
874
  const rigAddr = this.civ.civAddress & 0xff;
873
875
  const useX26 = this.activeProfile.supportsX25X26 && this.activeProfile.modeWithFilter;
874
876
  const req = useX26 ? IcomRigCommands_1.IcomRigCommands.readSelectedMode(ctrAddr, rigAddr, 0) : IcomRigCommands_1.IcomRigCommands.readOperatingMode(ctrAddr, rigAddr);
875
- const resp = await this.waitForCivFrame((frame) => useX26
877
+ const resp = await this.waitForCivFrame(useX26 ? 'mode:0x26:0' : 'mode:0x04', (frame) => useX26
876
878
  ? IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_SEND_SEL_MODE, [0x00], ctrAddr, rigAddr)
877
879
  : IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_RD_MODE, [], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
878
880
  if (!resp)
@@ -899,7 +901,7 @@ class IcomControl {
899
901
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
900
902
  const rigAddr = this.civ.civAddress & 0xff;
901
903
  const req = IcomRigCommands_1.IcomRigCommands.readTransmitFrequency(ctrAddr, rigAddr);
902
- const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_PTT, [IcomCivSpec_1.CIV.S_RD_TX_FREQ], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
904
+ const resp = await this.waitForCivFrame('freq:0x1c:0x03', (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_PTT, [IcomCivSpec_1.CIV.S_RD_TX_FREQ], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
903
905
  if (!resp)
904
906
  return null;
905
907
  return IcomControl.parseFrequencyReply(resp, 1);
@@ -909,7 +911,7 @@ class IcomControl {
909
911
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
910
912
  const rigAddr = this.civ.civAddress & 0xff;
911
913
  const req = IcomRigCommands_1.IcomRigCommands.readPTT(ctrAddr, rigAddr);
912
- const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_PTT, [IcomCivSpec_1.CIV.S_PTT], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
914
+ const resp = await this.waitForCivFrame('ptt:0x1c:0x00', (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_PTT, [IcomCivSpec_1.CIV.S_PTT], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
913
915
  if (!resp || resp.length < 7)
914
916
  return null;
915
917
  return resp[6] !== 0x00;
@@ -929,7 +931,7 @@ class IcomControl {
929
931
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
930
932
  const rigAddr = this.civ.civAddress & 0xff;
931
933
  const req = IcomRigCommands_1.IcomRigCommands.readBandEdges(ctrAddr, rigAddr);
932
- const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, 0x02, [], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
934
+ const resp = await this.waitForCivFrame('band:0x02', (frame) => IcomControl.matchCommandFrame(frame, 0x02, [], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
933
935
  if (!resp)
934
936
  return null;
935
937
  // Return raw payload bytes after command
@@ -950,7 +952,7 @@ class IcomControl {
950
952
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
951
953
  const rigAddr = this.civ.civAddress & 0xff;
952
954
  const req = IcomRigCommands_1.IcomRigCommands.getSWRState(ctrAddr, rigAddr);
953
- const resp = await this.waitForCivFrame((frame) => IcomControl.isMeterReply(frame, 0x12, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
955
+ const resp = await this.waitForCivFrame('meter:0x15:0x12', (frame) => IcomControl.isMeterReply(frame, 0x12, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
954
956
  const raw = IcomControl.extractMeterData(resp);
955
957
  if (raw === null)
956
958
  return null;
@@ -975,7 +977,7 @@ class IcomControl {
975
977
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
976
978
  const rigAddr = this.civ.civAddress & 0xff;
977
979
  const req = IcomRigCommands_1.IcomRigCommands.getALCState(ctrAddr, rigAddr);
978
- const resp = await this.waitForCivFrame((frame) => IcomControl.isMeterReply(frame, 0x13, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
980
+ const resp = await this.waitForCivFrame('meter:0x15:0x13', (frame) => IcomControl.isMeterReply(frame, 0x13, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
979
981
  const raw = IcomControl.extractMeterData(resp);
980
982
  if (raw === null)
981
983
  return null;
@@ -1003,7 +1005,7 @@ class IcomControl {
1003
1005
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1004
1006
  const rigAddr = this.civ.civAddress & 0xff;
1005
1007
  const req = IcomRigCommands_1.IcomRigCommands.getConnectorWLanLevel(ctrAddr, rigAddr, ext.subext);
1006
- const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, ext.command, [ext.subcmd, ...ext.subext], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1008
+ const resp = await this.waitForCivFrame(`ext:0x${ext.command.toString(16)}:0x${ext.subcmd.toString(16)}:${ext.subext.join('.')}`, (frame) => IcomControl.matchCommandFrame(frame, ext.command, [ext.subcmd, ...ext.subext], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1007
1009
  const raw = IcomControl.extractTrailingBcd(resp, ext.dataBytes);
1008
1010
  if (raw === null)
1009
1011
  return null;
@@ -1034,7 +1036,7 @@ class IcomControl {
1034
1036
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1035
1037
  const rigAddr = this.civ.civAddress & 0xff;
1036
1038
  const req = IcomRigCommands_1.IcomRigCommands.getLevelMeter(ctrAddr, rigAddr);
1037
- const resp = await this.waitForCivFrame((frame) => IcomControl.isMeterReply(frame, 0x02, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1039
+ const resp = await this.waitForCivFrame('meter:0x15:0x02', (frame) => IcomControl.isMeterReply(frame, 0x02, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1038
1040
  if (!resp)
1039
1041
  return null;
1040
1042
  const data = resp.subarray(6, resp.length - 1);
@@ -1064,7 +1066,7 @@ class IcomControl {
1064
1066
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1065
1067
  const rigAddr = this.civ.civAddress & 0xff;
1066
1068
  const req = IcomRigCommands_1.IcomRigCommands.getUsbAfLevel(ctrAddr, rigAddr, ext.subext);
1067
- const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, ext.command, [ext.subcmd, ...ext.subext], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1069
+ const resp = await this.waitForCivFrame(`ext:0x${ext.command.toString(16)}:0x${ext.subcmd.toString(16)}:${ext.subext.join('.')}`, (frame) => IcomControl.matchCommandFrame(frame, ext.command, [ext.subcmd, ...ext.subext], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1068
1070
  const raw = IcomControl.extractTrailingBcd(resp, ext.dataBytes);
1069
1071
  if (raw === null)
1070
1072
  return null;
@@ -1111,7 +1113,7 @@ class IcomControl {
1111
1113
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1112
1114
  const rigAddr = this.civ.civAddress & 0xff;
1113
1115
  const req = IcomRigCommands_1.IcomRigCommands.getTunerStatus(ctrAddr, rigAddr);
1114
- const resp = await this.waitForCivFrame((frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_PTT, [IcomCivSpec_1.CIV.S_ANT_TUN], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1116
+ const resp = await this.waitForCivFrame('tuner:0x1c:0x01', (frame) => IcomControl.matchCommandFrame(frame, IcomCivSpec_1.CIV.C_CTL_PTT, [IcomCivSpec_1.CIV.S_ANT_TUN], ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1115
1117
  if (!resp)
1116
1118
  return null;
1117
1119
  // Expect FE FE [ctr] [rig] 0x1C 0x01 [status] FD
@@ -1233,7 +1235,7 @@ class IcomControl {
1233
1235
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1234
1236
  const rigAddr = this.civ.civAddress & 0xff;
1235
1237
  const req = IcomRigCommands_1.IcomRigCommands.getSquelchStatus(ctrAddr, rigAddr);
1236
- const resp = await this.waitForCivFrame((frame) => IcomControl.isMeterReply(frame, 0x01, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1238
+ const resp = await this.waitForCivFrame('meter:0x15:0x01', (frame) => IcomControl.isMeterReply(frame, 0x01, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1237
1239
  const raw = IcomControl.extractMeterData(resp);
1238
1240
  if (raw === null)
1239
1241
  return null;
@@ -1257,7 +1259,7 @@ class IcomControl {
1257
1259
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1258
1260
  const rigAddr = this.civ.civAddress & 0xff;
1259
1261
  const req = IcomRigCommands_1.IcomRigCommands.getAudioSquelch(ctrAddr, rigAddr);
1260
- const resp = await this.waitForCivFrame((frame) => IcomControl.isMeterReply(frame, 0x05, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1262
+ const resp = await this.waitForCivFrame('meter:0x15:0x05', (frame) => IcomControl.isMeterReply(frame, 0x05, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1261
1263
  const raw = IcomControl.extractMeterData(resp);
1262
1264
  if (raw === null)
1263
1265
  return null;
@@ -1281,7 +1283,7 @@ class IcomControl {
1281
1283
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1282
1284
  const rigAddr = this.civ.civAddress & 0xff;
1283
1285
  const req = IcomRigCommands_1.IcomRigCommands.getOvfStatus(ctrAddr, rigAddr);
1284
- const resp = await this.waitForCivFrame((frame) => IcomControl.isMeterReply(frame, 0x07, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1286
+ const resp = await this.waitForCivFrame('meter:0x15:0x07', (frame) => IcomControl.isMeterReply(frame, 0x07, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1285
1287
  const raw = IcomControl.extractMeterData(resp);
1286
1288
  if (raw === null)
1287
1289
  return null;
@@ -1305,7 +1307,7 @@ class IcomControl {
1305
1307
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1306
1308
  const rigAddr = this.civ.civAddress & 0xff;
1307
1309
  const req = IcomRigCommands_1.IcomRigCommands.getPowerLevel(ctrAddr, rigAddr);
1308
- const resp = await this.waitForCivFrame((frame) => IcomControl.isMeterReply(frame, 0x11, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1310
+ const resp = await this.waitForCivFrame('meter:0x15:0x11', (frame) => IcomControl.isMeterReply(frame, 0x11, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1309
1311
  const raw = IcomControl.extractMeterData(resp);
1310
1312
  if (raw === null)
1311
1313
  return null;
@@ -1332,7 +1334,7 @@ class IcomControl {
1332
1334
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1333
1335
  const rigAddr = this.civ.civAddress & 0xff;
1334
1336
  const req = IcomRigCommands_1.IcomRigCommands.getCompLevel(ctrAddr, rigAddr);
1335
- const resp = await this.waitForCivFrame((frame) => IcomControl.isMeterReply(frame, 0x14, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1337
+ const resp = await this.waitForCivFrame('meter:0x15:0x14', (frame) => IcomControl.isMeterReply(frame, 0x14, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1336
1338
  const raw = IcomControl.extractMeterData(resp);
1337
1339
  if (raw === null)
1338
1340
  return null;
@@ -1358,7 +1360,7 @@ class IcomControl {
1358
1360
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1359
1361
  const rigAddr = this.civ.civAddress & 0xff;
1360
1362
  const req = IcomRigCommands_1.IcomRigCommands.getVoltage(ctrAddr, rigAddr);
1361
- const resp = await this.waitForCivFrame((frame) => IcomControl.isMeterReply(frame, 0x15, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1363
+ const resp = await this.waitForCivFrame('meter:0x15:0x15', (frame) => IcomControl.isMeterReply(frame, 0x15, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1362
1364
  const raw = IcomControl.extractMeterData(resp);
1363
1365
  if (raw === null)
1364
1366
  return null;
@@ -1382,7 +1384,7 @@ class IcomControl {
1382
1384
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1383
1385
  const rigAddr = this.civ.civAddress & 0xff;
1384
1386
  const req = IcomRigCommands_1.IcomRigCommands.getCurrent(ctrAddr, rigAddr);
1385
- const resp = await this.waitForCivFrame((frame) => IcomControl.isMeterReply(frame, 0x16, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1387
+ const resp = await this.waitForCivFrame('meter:0x15:0x16', (frame) => IcomControl.isMeterReply(frame, 0x16, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1386
1388
  const raw = IcomControl.extractMeterData(resp);
1387
1389
  if (raw === null)
1388
1390
  return null;
@@ -1822,26 +1824,14 @@ class IcomControl {
1822
1824
  // Continue loop in case multiple frames are in buffer
1823
1825
  }
1824
1826
  }
1825
- // Wait for single CI-V frame that matches predicate (fed by civFrame event)
1826
- async waitForCivFrame(predicate, timeoutMs, onSend) {
1827
- return new Promise((resolve) => {
1828
- let done = false;
1829
- const onFrame = (frame) => {
1830
- if (!done && predicate(frame)) {
1831
- done = true;
1832
- this.ev.off('civFrame', onFrame);
1833
- resolve(frame);
1834
- }
1835
- };
1836
- this.ev.on('civFrame', onFrame);
1837
- if (onSend)
1838
- onSend();
1839
- setTimeout(() => {
1840
- if (!done) {
1841
- this.ev.off('civFrame', onFrame);
1842
- resolve(null);
1843
- }
1844
- }, timeoutMs);
1827
+ // Wait for a CI-V reply by response key. Same-key queries are deduplicated.
1828
+ async waitForCivFrame(key, predicate, timeoutMs, onSend) {
1829
+ return this.civRequestManager.query({
1830
+ key,
1831
+ predicate,
1832
+ timeoutMs,
1833
+ send: () => { if (onSend)
1834
+ onSend(); },
1845
1835
  });
1846
1836
  }
1847
1837
  // Strict meter reply matcher: FE FE [ctr|00] [rig] 0x15 [sub] ... FD
@@ -1889,7 +1879,7 @@ class IcomControl {
1889
1879
  const ctrAddr = IcomConstants_1.DEFAULT_CONTROLLER_ADDR;
1890
1880
  const rigAddr = this.civ.civAddress & 0xff;
1891
1881
  const req = IcomRigCommands_1.IcomRigCommands.get0x14Level(ctrAddr, rigAddr, subcmd);
1892
- const resp = await this.waitForCivFrame((frame) => IcomControl.is0x14DataReply(frame, subcmd, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1882
+ const resp = await this.waitForCivFrame(`level:0x14:0x${subcmd.toString(16)}`, (frame) => IcomControl.is0x14DataReply(frame, subcmd, ctrAddr, rigAddr), timeoutMs, () => this.sendCiv(req));
1893
1883
  if (!resp || resp.length < 9)
1894
1884
  return null;
1895
1885
  const raw = (0, bcd_1.parseTwoByteBcd)(resp.subarray(6, 8));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icom-wlan-node",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Icom WLAN (CI‑V, audio) protocol implementation for Node.js/TypeScript.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",