@ham2k/extension-sdk 0.12.2 → 0.12.6
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/dist/dxcc.js +11 -5
- package/dist/index.d.ts +247 -5
- package/dist/index.js +4 -0
- package/docs/distribution.md +1 -1
- package/docs/hooks.md +93 -11
- package/package.json +1 -1
package/dist/dxcc.js
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
import { parseCallsign } from "@ham2k/lib-callsigns";
|
|
2
|
-
import { annotateFromCountryFile, useBuiltinCountryFile } from "@ham2k/lib-country-files";
|
|
2
|
+
import { annotateFromCountryFile, BIGCTY, useBuiltinCountryFile } from "@ham2k/lib-country-files";
|
|
3
3
|
import { DXCC_BY_CODE } from "@ham2k/lib-dxcc-data";
|
|
4
4
|
useBuiltinCountryFile();
|
|
5
5
|
function annotateCallAgainstCountryFile(call, options) {
|
|
6
6
|
const annotated = annotateFromCountryFile(parseCallsign(call), options);
|
|
7
|
-
|
|
7
|
+
if (!WEST_POSITIVE) return annotated;
|
|
8
|
+
return {
|
|
9
|
+
...annotated,
|
|
10
|
+
...typeof annotated.lon === "number" ? { lon: 0 - annotated.lon } : {},
|
|
11
|
+
...annotated.tz ? { tz: flippedTz(annotated.tz) } : {}
|
|
12
|
+
};
|
|
8
13
|
}
|
|
9
|
-
|
|
14
|
+
const WEST_POSITIVE = (BIGCTY.entities.K?.lon ?? 0) > 0;
|
|
15
|
+
function flippedTz(tz) {
|
|
10
16
|
const match = /^GMT([+-]?\d+(?:\.\d+)?)$/.exec(tz);
|
|
11
17
|
if (!match) return tz;
|
|
12
|
-
const offset = -Number(match[1]);
|
|
13
|
-
return offset > 0 ? `GMT+${offset}` : `GMT${offset
|
|
18
|
+
const offset = 0 - Number(match[1]);
|
|
19
|
+
return offset > 0 ? `GMT+${offset}` : `GMT${offset}`;
|
|
14
20
|
}
|
|
15
21
|
function entityPrefixForCall(call) {
|
|
16
22
|
if (!call) return void 0;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @ham2k/extension-sdk 0.12.
|
|
1
|
+
// @ham2k/extension-sdk 0.12.6
|
|
2
2
|
/** Experimental v1 panel scene contract. All coordinates are in the scene's viewBox. */
|
|
3
3
|
export interface PanelScene {
|
|
4
4
|
version: 1;
|
|
@@ -97,11 +97,193 @@ export interface PanelSceneLayer {
|
|
|
97
97
|
* - `nativeSwitch`, `nativeCheckbox`: a number, 1 or 0. Shows `label` beside it.
|
|
98
98
|
* - `nativeChip`: with `value`, a toggle (1 or 0); without, an action that sends `activate`.
|
|
99
99
|
* - `nativeText`: shows `label`, or its bound value. Sends nothing.
|
|
100
|
+
* - `spectrum`: the radio's panadapter and waterfall, drawn by the host from sweeps that never
|
|
101
|
+
* reach the extension. Needs `requiresRadioRead`; a host without the feature, or a radio
|
|
102
|
+
* without a scope, shows why instead. A click sends `commit` with the frequency as `value`.
|
|
103
|
+
* Takes no `value`; see `PanelSceneSpectrum` for the rest. Extension API 6: declare `"api": 6`.
|
|
100
104
|
*
|
|
101
105
|
* `label` is every control's accessible name. There are no tooltips.
|
|
102
106
|
*/
|
|
103
|
-
export type PanelSceneControlKind = "button" | "slider" | "knob" | "nativeButton" | "nativeSlider" | "nativeTextField" | "nativeDropdown" | "nativeSwitch" | "nativeCheckbox" | "nativeRadio" | "nativeSegmented" | "nativeChip" | "nativeText";
|
|
104
|
-
|
|
107
|
+
export type PanelSceneControlKind = "button" | "slider" | "knob" | "nativeButton" | "nativeSlider" | "nativeTextField" | "nativeDropdown" | "nativeSwitch" | "nativeCheckbox" | "nativeRadio" | "nativeSegmented" | "nativeChip" | "nativeText" | "spectrum";
|
|
108
|
+
/** A literal, or the name of a scene value — which a slider with no `event` retunes with no bridge call. */
|
|
109
|
+
export type PanelSceneSpectrumNumber = number | string;
|
|
110
|
+
/**
|
|
111
|
+
* A `spectrum` control's display. All of it happens on the host after the sweep arrives, so it
|
|
112
|
+
* means the same on every radio; the radio's own scope (span, reference level, sweep speed) is
|
|
113
|
+
* `host.setRadioSpectrum`'s. Colors are `#RRGGBB`, or `#AARRGGBB` with transparency. Defaults
|
|
114
|
+
* follow the app's theme.
|
|
115
|
+
*/
|
|
116
|
+
export interface PanelSceneSpectrum {
|
|
117
|
+
/** A radio's id from `listRadios`. Omitted, the designated radio. */
|
|
118
|
+
radio?: string;
|
|
119
|
+
/** Default `both`. */
|
|
120
|
+
display?: "both" | "pan" | "waterfall";
|
|
121
|
+
/**
|
|
122
|
+
* Which way the waterfall moves; the panadapter sits on the side it comes from. Frequency rises
|
|
123
|
+
* left to right for `down` (the default) and `up`, bottom to top for `right` and `left`. Text
|
|
124
|
+
* stays upright; offsets and `corner`s are measured as if flowing down.
|
|
125
|
+
*/
|
|
126
|
+
flow?: "down" | "up" | "right" | "left";
|
|
127
|
+
/** The panadapter's share of the height with `both`, 0.1–0.9. Default 0.4. */
|
|
128
|
+
split?: number;
|
|
129
|
+
/** dB added to the radio's own floor. */
|
|
130
|
+
floor?: PanelSceneSpectrumNumber;
|
|
131
|
+
/** The panadapter's height in dB. Omitted, the radio's. */
|
|
132
|
+
range?: PanelSceneSpectrumNumber;
|
|
133
|
+
/** Written as the pointer moves: the frequency and level under it. */
|
|
134
|
+
hoverHz?: string;
|
|
135
|
+
hoverDb?: string;
|
|
136
|
+
/** 0–99: the share of the trace each sweep keeps from the last. Default 40. The waterfall stays raw. */
|
|
137
|
+
average?: PanelSceneSpectrumNumber;
|
|
138
|
+
/** The most times a second the panadapter redraws, 1–60. Default 15. The waterfall keeps its own pace. */
|
|
139
|
+
fps?: PanelSceneSpectrumNumber;
|
|
140
|
+
/** Waterfall rows a second, 0.1–60; each row keeps the loudest reading since the last. Omitted, one per sweep. */
|
|
141
|
+
waterfallSpeed?: PanelSceneSpectrumNumber;
|
|
142
|
+
/** The waterfall's own dB window, like `floor` and `range`. Omitted, the panadapter's. */
|
|
143
|
+
waterfallFloor?: PanelSceneSpectrumNumber;
|
|
144
|
+
waterfallRange?: PanelSceneSpectrumNumber;
|
|
145
|
+
/** Default `classic`. */
|
|
146
|
+
palette?: "classic" | "gray" | "heat";
|
|
147
|
+
/** 2 to 8 stops, lowest level first, in place of `palette`. */
|
|
148
|
+
waterfallColors?: string[];
|
|
149
|
+
traceColor?: string;
|
|
150
|
+
/** 0.25–8. Default 1. */
|
|
151
|
+
traceWidth?: PanelSceneSpectrumNumber;
|
|
152
|
+
/** Under the trace. With `fillColorBottom`, a gradient from the top of the panadapter to its bottom. */
|
|
153
|
+
fillColor?: string;
|
|
154
|
+
fillColorBottom?: string;
|
|
155
|
+
backgroundColor?: string;
|
|
156
|
+
/** The panadapter's dB and frequency lines. */
|
|
157
|
+
gridColor?: string;
|
|
158
|
+
/** The frequency lines carried down through the waterfall. Omitted, none. */
|
|
159
|
+
waterfallGridColor?: string;
|
|
160
|
+
/** A click tunes to the nearest multiple of this many Hz. Omitted, wherever it lands. */
|
|
161
|
+
tuneStep?: PanelSceneSpectrumNumber;
|
|
162
|
+
/** Dashes the panadapter's grid lines. */
|
|
163
|
+
gridDashed?: boolean;
|
|
164
|
+
/** The gap between the panadapter and the waterfall, with frequency labels and ticks. `true` takes the defaults. */
|
|
165
|
+
scale?: boolean | PanelSceneSpectrumScale;
|
|
166
|
+
/** The tuned-frequency line, styled per part; a part it leaves out has none. Omitted, the theme's on both. */
|
|
167
|
+
center?: {
|
|
168
|
+
pan?: PanelSceneSpectrumStyle;
|
|
169
|
+
waterfall?: PanelSceneSpectrumStyle;
|
|
170
|
+
};
|
|
171
|
+
/** Up to 16 lines and areas at Hz offsets — a passband, a CW offset. */
|
|
172
|
+
overlays?: PanelSceneSpectrumOverlay[];
|
|
173
|
+
/** Up to 64 pieces of text at frequencies — a spot, a net, a beacon. */
|
|
174
|
+
annotations?: PanelSceneSpectrumAnnotation[];
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* The gap sits between the panadapter and the waterfall — above the waterfall with no panadapter,
|
|
178
|
+
* below the panadapter with no waterfall. Labels and ticks are drawn over the whole control, so an
|
|
179
|
+
* `offset` can place them anywhere: it is pixels from the base of the panadapter (the top of the
|
|
180
|
+
* waterfall where there is none), negative rising into the panadapter.
|
|
181
|
+
*/
|
|
182
|
+
export interface PanelSceneSpectrumScale {
|
|
183
|
+
/** Pixels, 0–200. Default 20. */
|
|
184
|
+
gap?: PanelSceneSpectrumNumber;
|
|
185
|
+
background?: string;
|
|
186
|
+
borderTop?: {
|
|
187
|
+
color?: string;
|
|
188
|
+
width?: PanelSceneSpectrumNumber;
|
|
189
|
+
};
|
|
190
|
+
borderBottom?: {
|
|
191
|
+
color?: string;
|
|
192
|
+
width?: PanelSceneSpectrumNumber;
|
|
193
|
+
};
|
|
194
|
+
/** On by default; `false` hides them. */
|
|
195
|
+
labels?: boolean | {
|
|
196
|
+
/** Hz between labels. Omitted, the gridlines' step. */
|
|
197
|
+
interval?: PanelSceneSpectrumNumber;
|
|
198
|
+
fontFamily?: string;
|
|
199
|
+
size?: PanelSceneSpectrumNumber;
|
|
200
|
+
/** 100–900. */
|
|
201
|
+
weight?: PanelSceneSpectrumNumber;
|
|
202
|
+
italic?: boolean;
|
|
203
|
+
color?: string;
|
|
204
|
+
/** To the top of the text. Omitted, centered in the gap. */
|
|
205
|
+
offset?: PanelSceneSpectrumNumber;
|
|
206
|
+
};
|
|
207
|
+
/** Off by default. */
|
|
208
|
+
ticks?: boolean | {
|
|
209
|
+
/** Hz between ticks. Omitted, the labels'. */
|
|
210
|
+
interval?: PanelSceneSpectrumNumber;
|
|
211
|
+
color?: string;
|
|
212
|
+
width?: PanelSceneSpectrumNumber;
|
|
213
|
+
/** To the top of the tick. Default 0. */
|
|
214
|
+
offset?: PanelSceneSpectrumNumber;
|
|
215
|
+
/** Default 4. */
|
|
216
|
+
length?: PanelSceneSpectrumNumber;
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Text at a frequency, drawn by the host. With an `event`, a click sends `activate` with the
|
|
221
|
+
* frequency as `value` and this `id` as `annotation`, and does not tune; without one, a click
|
|
222
|
+
* on it tunes like anywhere else.
|
|
223
|
+
*/
|
|
224
|
+
export interface PanelSceneSpectrumAnnotation {
|
|
225
|
+
/** Unique within the control. */
|
|
226
|
+
id: string;
|
|
227
|
+
/** At most 256 characters. */
|
|
228
|
+
text: string;
|
|
229
|
+
/** A frequency, or an offset from the tuned frequency or the sweep's middle with `anchor`. */
|
|
230
|
+
hz: PanelSceneSpectrumNumber;
|
|
231
|
+
/** Default `absolute`. */
|
|
232
|
+
anchor?: "vfo" | "center" | "absolute";
|
|
233
|
+
/** Default `pan`. */
|
|
234
|
+
part?: "pan" | "waterfall";
|
|
235
|
+
/** Pixels from the panadapter's bottom up to the text's, or from the waterfall's top down to the text's. Default 4. */
|
|
236
|
+
offset?: PanelSceneSpectrumNumber;
|
|
237
|
+
/** Which end of the text sits at `hz`. Default `center`. */
|
|
238
|
+
align?: "start" | "center" | "end";
|
|
239
|
+
/**
|
|
240
|
+
* The point the box makes at `hz`: a drawn-out corner for `start`/`end`, a notch for `center`.
|
|
241
|
+
* With the default `corner`, its tip sits at `offset`; turned the other way, the box's edge
|
|
242
|
+
* does. Shown by default (size 4, pointing toward the panadapter's bottom
|
|
243
|
+
* or the waterfall's top); `false` or `show: false` hides it.
|
|
244
|
+
*/
|
|
245
|
+
pointer?: boolean | {
|
|
246
|
+
show?: boolean;
|
|
247
|
+
size?: PanelSceneSpectrumNumber;
|
|
248
|
+
corner?: "top" | "bottom";
|
|
249
|
+
};
|
|
250
|
+
fontFamily?: string;
|
|
251
|
+
size?: PanelSceneSpectrumNumber;
|
|
252
|
+
weight?: PanelSceneSpectrumNumber;
|
|
253
|
+
italic?: boolean;
|
|
254
|
+
color?: string;
|
|
255
|
+
background?: string;
|
|
256
|
+
borderColor?: string;
|
|
257
|
+
borderWidth?: PanelSceneSpectrumNumber;
|
|
258
|
+
/** Default 2. */
|
|
259
|
+
padding?: PanelSceneSpectrumNumber;
|
|
260
|
+
/** Default 2. */
|
|
261
|
+
radius?: PanelSceneSpectrumNumber;
|
|
262
|
+
event?: string;
|
|
263
|
+
}
|
|
264
|
+
/** `color` and `width` draw a line, or an area's edges; `fill` fills an area. */
|
|
265
|
+
export interface PanelSceneSpectrumStyle {
|
|
266
|
+
color?: string;
|
|
267
|
+
width?: PanelSceneSpectrumNumber;
|
|
268
|
+
fill?: string;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* A line at `at`, or an area between `from` and `to`, in Hz from the tuned frequency — from the
|
|
272
|
+
* middle of the sweep with `anchor: "center"` (a Flex's panadapter is not centered on its slice),
|
|
273
|
+
* or as frequencies with `anchor: "absolute"` (a band edge, a segment). On the panadapter an area
|
|
274
|
+
* draws behind the trace and a line in front of it unless `behind` — which absolute ones default to.
|
|
275
|
+
* On the waterfall all of it draws over the rows. A part with no style is not drawn on.
|
|
276
|
+
*/
|
|
277
|
+
export interface PanelSceneSpectrumOverlay {
|
|
278
|
+
at?: PanelSceneSpectrumNumber;
|
|
279
|
+
from?: PanelSceneSpectrumNumber;
|
|
280
|
+
to?: PanelSceneSpectrumNumber;
|
|
281
|
+
anchor?: "vfo" | "center" | "absolute";
|
|
282
|
+
behind?: boolean;
|
|
283
|
+
pan?: PanelSceneSpectrumStyle;
|
|
284
|
+
waterfall?: PanelSceneSpectrumStyle;
|
|
285
|
+
}
|
|
286
|
+
export interface PanelSceneControl extends PanelSceneSpectrum {
|
|
105
287
|
id: string;
|
|
106
288
|
label: string;
|
|
107
289
|
kind: PanelSceneControlKind;
|
|
@@ -177,6 +359,8 @@ export interface PanelSceneEvent {
|
|
|
177
359
|
value?: number;
|
|
178
360
|
/** The control's string, for a control bound to `strings`. */
|
|
179
361
|
text?: string;
|
|
362
|
+
/** The `id` of the spectrum annotation clicked; `value` is its frequency. */
|
|
363
|
+
annotation?: string;
|
|
180
364
|
}
|
|
181
365
|
/** Patch only named scene values and strings. The host ignores stale responses. */
|
|
182
366
|
export interface PanelSceneEventResult {
|
|
@@ -372,6 +556,7 @@ export interface HookContext {
|
|
|
372
556
|
getOperation?(uuid: string): Promise<Record<string, JSONValue> | null>;
|
|
373
557
|
getQsos?(operationUuid: string): Promise<Record<string, JSONValue>[] | null>;
|
|
374
558
|
getSpotsForCall?(call: string, maxAgeMinutes?: number): Promise<Spot[]>;
|
|
559
|
+
getSpots?(options?: GetSpotsOptions): Promise<BoardSpot[]>;
|
|
375
560
|
getHistoryForCall?(call: string, options?: HistoryForCallOptions): Promise<Record<string, JSONValue>[]>;
|
|
376
561
|
}
|
|
377
562
|
export interface HistoryForCallOptions {
|
|
@@ -440,6 +625,24 @@ export interface Spot {
|
|
|
440
625
|
sourceInfo?: Record<string, JSONValue>;
|
|
441
626
|
};
|
|
442
627
|
}
|
|
628
|
+
export interface GetSpotsOptions {
|
|
629
|
+
filter?: "user" | "all";
|
|
630
|
+
maxAgeMinutes?: number;
|
|
631
|
+
band?: string | string[];
|
|
632
|
+
mode?: string | string[];
|
|
633
|
+
source?: string | string[];
|
|
634
|
+
annotate?: boolean;
|
|
635
|
+
operationUuid?: string;
|
|
636
|
+
}
|
|
637
|
+
export interface BoardSpot extends Spot {
|
|
638
|
+
hookKey?: string;
|
|
639
|
+
annotations?: SpotAnnotations;
|
|
640
|
+
}
|
|
641
|
+
export interface SpotAnnotations {
|
|
642
|
+
worked: boolean;
|
|
643
|
+
isSelf: boolean;
|
|
644
|
+
notices: string[];
|
|
645
|
+
}
|
|
443
646
|
export interface SpotEligibility {
|
|
444
647
|
enabled: boolean;
|
|
445
648
|
icon?: string;
|
|
@@ -492,6 +695,7 @@ export interface Ref {
|
|
|
492
695
|
program?: string;
|
|
493
696
|
activationRadiusInMeters?: number;
|
|
494
697
|
activationRadiusInFeet?: number;
|
|
698
|
+
largeArea?: boolean;
|
|
495
699
|
retired?: boolean;
|
|
496
700
|
for?: "operation" | "qso";
|
|
497
701
|
[key: string]: JSONValue | undefined;
|
|
@@ -1544,6 +1748,40 @@ export interface PanelRadioState {
|
|
|
1544
1748
|
transmitting: boolean | null;
|
|
1545
1749
|
meters: Partial<Record<"signalDbm" | "powerOut" | "swr" | "alc" | "supplyVolts", number>>;
|
|
1546
1750
|
canTune: boolean;
|
|
1751
|
+
/**
|
|
1752
|
+
* The radio's scope, on hosts with the panadapter enabled: absent there otherwise, and null
|
|
1753
|
+
* for a radio without one. The sweeps are never sent — a `spectrum` scene control draws them.
|
|
1754
|
+
*/
|
|
1755
|
+
spectrum?: PanelRadioSpectrum | null;
|
|
1756
|
+
}
|
|
1757
|
+
export type PanelRadioSpectrumSpeed = "slow" | "mid" | "fast";
|
|
1758
|
+
export interface PanelRadioSpectrum {
|
|
1759
|
+
/** Why nothing can be drawn, in the operator's language, or null. */
|
|
1760
|
+
problem: string | null;
|
|
1761
|
+
mode: "center" | "fixed" | null;
|
|
1762
|
+
/** Half the width shown while centered — what the radio's menu shows as ±. */
|
|
1763
|
+
spanHz: number | null;
|
|
1764
|
+
/** The edges while fixed. */
|
|
1765
|
+
lowHz: number | null;
|
|
1766
|
+
highHz: number | null;
|
|
1767
|
+
refLevelDb: number | null;
|
|
1768
|
+
speed: PanelRadioSpectrumSpeed | null;
|
|
1769
|
+
/** What `setRadioSpectrum` accepts. A span between two is snapped to the nearer. */
|
|
1770
|
+
spansHz: number[];
|
|
1771
|
+
refMinDb: number;
|
|
1772
|
+
refMaxDb: number;
|
|
1773
|
+
refStepDb: number;
|
|
1774
|
+
modes: ("center" | "fixed")[];
|
|
1775
|
+
speeds: PanelRadioSpectrumSpeed[];
|
|
1776
|
+
}
|
|
1777
|
+
export interface PanelRadioSpectrumSet {
|
|
1778
|
+
/** Reject if this identity no longer matches the selected radio. */
|
|
1779
|
+
id: string;
|
|
1780
|
+
selection?: string;
|
|
1781
|
+
mode?: "center" | "fixed";
|
|
1782
|
+
spanHz?: number;
|
|
1783
|
+
refLevelDb?: number;
|
|
1784
|
+
speed?: PanelRadioSpectrumSpeed;
|
|
1547
1785
|
}
|
|
1548
1786
|
export interface PanelRadioTune {
|
|
1549
1787
|
/** Reject if this identity no longer matches the selected radio. */
|
|
@@ -1556,7 +1794,8 @@ export interface PanelRadioTune {
|
|
|
1556
1794
|
export interface PanelRadioTuneResult {
|
|
1557
1795
|
/** Accepted by the host; confirmation still comes from reported state. */
|
|
1558
1796
|
accepted: boolean;
|
|
1559
|
-
|
|
1797
|
+
/** `stopped`: a scope change while no display has the scope running — nothing was sent. */
|
|
1798
|
+
reason?: "changed" | "disconnected" | "transmitting" | "invalid" | "stopped";
|
|
1560
1799
|
state: PanelRadioState | null;
|
|
1561
1800
|
}
|
|
1562
1801
|
export interface PanelRenderArgs {
|
|
@@ -1650,7 +1889,8 @@ export interface ParsedCallsign {
|
|
|
1650
1889
|
indicators?: string[];
|
|
1651
1890
|
ssid?: string;
|
|
1652
1891
|
}
|
|
1653
|
-
export type CombinedCallInfo = ParsedCallsign & {
|
|
1892
|
+
export type CombinedCallInfo = Omit<ParsedCallsign, "call"> & {
|
|
1893
|
+
call?: string;
|
|
1654
1894
|
isoPrefix?: string;
|
|
1655
1895
|
dxccCode?: number;
|
|
1656
1896
|
regionCode?: string;
|
|
@@ -1998,6 +2238,8 @@ export declare const host: {
|
|
|
1998
2238
|
connected: boolean;
|
|
1999
2239
|
}): Promise<PanelRadioTuneResult>;
|
|
2000
2240
|
tuneRadio(args: PanelRadioTune): Promise<PanelRadioTuneResult>;
|
|
2241
|
+
/** Changes the radio's scope. Needs `requiresRadioWrite` and extension API 6; the result's state reports what the radio took. */
|
|
2242
|
+
setRadioSpectrum(args: PanelRadioSpectrumSet): Promise<PanelRadioTuneResult>;
|
|
2001
2243
|
fetch(url: string, options?: FetchOptions): Promise<FetchResponse>;
|
|
2002
2244
|
webSocket(url: string, options?: WebSocketOptions): ExtensionWebSocket;
|
|
2003
2245
|
kvGet(key: string): Promise<JSONValue | null>;
|
package/dist/index.js
CHANGED
|
@@ -172,6 +172,10 @@ const host = {
|
|
|
172
172
|
async tuneRadio(args) {
|
|
173
173
|
return await hostCallFn()("tuneRadio", { ...args });
|
|
174
174
|
},
|
|
175
|
+
/** Changes the radio's scope. Needs `requiresRadioWrite` and extension API 6; the result's state reports what the radio took. */
|
|
176
|
+
async setRadioSpectrum(args) {
|
|
177
|
+
return await hostCallFn()("setRadioSpectrum", { ...args });
|
|
178
|
+
},
|
|
175
179
|
async fetch(url, options) {
|
|
176
180
|
return await hostCallFn()("fetch", { url, ...options });
|
|
177
181
|
},
|
package/docs/distribution.md
CHANGED
|
@@ -262,7 +262,7 @@ It reports every problem at once rather than one per run:
|
|
|
262
262
|
h2kext-pack: ./build is not ready to package:
|
|
263
263
|
manifest.json: missing required field 'name'
|
|
264
264
|
manifest.json: key 'my-clock' must start with your callsign — 'my' is not one (e.g. ki2d-my-clock)
|
|
265
|
-
manifest.json: missing 'api' — declare the extension API this was built against (currently
|
|
265
|
+
manifest.json: missing 'api' — declare the extension API this was built against (currently 6)
|
|
266
266
|
```
|
|
267
267
|
|
|
268
268
|
`--force-name` waives the naming convention below. It exists for Ham2K's own
|
package/docs/hooks.md
CHANGED
|
@@ -70,6 +70,31 @@ hook method receives `(args, ctx)` where `ctx: HookContext` is:
|
|
|
70
70
|
reads above it is cheap enough for the lookup path; it resolves `[]`
|
|
71
71
|
whenever the cache is cold or the host keeps no spots. Used by
|
|
72
72
|
`spot-history` to pull a hunted park or summit into the QSO being logged.
|
|
73
|
+
* `getSpots?(options?)` — the whole spots board the host already holds,
|
|
74
|
+
for a panel showing spots its own way. Like `getSpotsForCall` it reads the
|
|
75
|
+
cache and never fetches, and resolves `[]` while the cache is cold. By
|
|
76
|
+
default it answers what the operator's Spots Panel shows: their sources,
|
|
77
|
+
enabled bands, band and mode filters (an "auto" filter follows the radio),
|
|
78
|
+
age and continents, merged and ordered as that panel does — so one spot
|
|
79
|
+
can stand for several stations, its `their.call` comma-joined
|
|
80
|
+
(`'K1ABC,W2XYZ'`). `filter: 'all'`
|
|
81
|
+
starts instead from every held spot, one per source report, newest first.
|
|
82
|
+
Either way `maxAgeMinutes`, `band`, `mode` (a mode, or `SSB`/`Phone`/`CW`/
|
|
83
|
+
`Digital` for a family) and `source` (a spot source's hook key, carried on
|
|
84
|
+
each spot as `hookKey`) narrow it further. Spots come back with
|
|
85
|
+
`annotations` — `worked`, `isSelf`, `notices`, judged the way the Spots
|
|
86
|
+
Panel strikes and accents its rows — whenever there is an operation to judge
|
|
87
|
+
them against: `operationUuid`, or the `operation` the calling hook was
|
|
88
|
+
handed, so a panel's render gets them without asking. The verdicts are the
|
|
89
|
+
ones the app already holds for the operation open on screen, so the call
|
|
90
|
+
never waits on a scorer; another operation's spots, and that one's until
|
|
91
|
+
its first verdicts land, come back unannotated rather than guessed.
|
|
92
|
+
`annotate: false` returns the spots as their sources gave them.
|
|
93
|
+
A panel's `'spots'` trigger fires whenever this answer may have moved: a
|
|
94
|
+
refresh, new verdicts, the operator's spot filters or bands, or the radio's
|
|
95
|
+
band or mode. A malformed or unknown option rejects the call. The CLI host
|
|
96
|
+
has no operator: its `'user'` board is every spot handed to it, never
|
|
97
|
+
annotated.
|
|
73
98
|
* `getHistoryForCall?(call, options?)` — the most recent non-deleted contacts
|
|
74
99
|
logged with `call` (five unless `limit` says otherwise, never more than
|
|
75
100
|
50), across every operation, newest first, as raw QSON — the one log read
|
|
@@ -247,14 +272,17 @@ interface LookupServiceHook {
|
|
|
247
272
|
carried one (`preferStationValues`) — only while the guess's scope is
|
|
248
273
|
`qth`, since on a portable or prefixed contact a source describes the
|
|
249
274
|
home QTH. `tz` is a UTC offset with its ordinary sign, `"GMT-5"` being
|
|
250
|
-
five hours behind
|
|
251
|
-
|
|
275
|
+
five hours behind. lib-country-files states it that way from 1.1; the
|
|
276
|
+
SDK's `annotateCallAgainstCountryFile` answers with the ordinary signs
|
|
277
|
+
whichever version the app ships, flipping an older one's west-positive
|
|
278
|
+
`tz` and `lon`.
|
|
252
279
|
- **`locSource: 'prefix'` is reserved**, and a `lat`/`lon` merged under it is
|
|
253
280
|
never read as a coordinate (`locationFromGuess`, halo_core). It marks
|
|
254
|
-
lib-country-files' DXCC entity centroid,
|
|
255
|
-
west-positive
|
|
256
|
-
source reporting a real position must leave `locSource`
|
|
257
|
-
itself; tagging one `'prefix'` gets its coordinate
|
|
281
|
+
lib-country-files' DXCC entity centroid, which synced records carry signed
|
|
282
|
+
either way — west-positive from versions before 1.1 — with nothing to tell
|
|
283
|
+
the two apart. A source reporting a real position must leave `locSource`
|
|
284
|
+
unset or name itself; tagging one `'prefix'` gets its coordinate
|
|
285
|
+
discarded.
|
|
258
286
|
|
|
259
287
|
Implemented by: `lookups` (key `core`). The merge algorithm itself
|
|
260
288
|
(`mergeLookupIntoGuess`/`runLookupPipeline` in
|
|
@@ -441,7 +469,7 @@ interface RefHandlerHook {
|
|
|
441
469
|
geojsonUrlForRef?(args: { ref: Ref }, ctx): Promise<RefGeojson | null>
|
|
442
470
|
}
|
|
443
471
|
// Ref: {type, ref?, name?, label?, shortLabel?, grid?, lat?, lon?, location?, program?,
|
|
444
|
-
// activationRadiusInMeters?, activationRadiusInFeet?, ...}
|
|
472
|
+
// activationRadiusInMeters?, activationRadiusInFeet?, largeArea?, ...}
|
|
445
473
|
// TitleSuggestion: {title?, for?, at?, subtitle?, priority?} — a fragment of
|
|
446
474
|
// the operation's title/subtitle sentence (ham2k/halo-dist#174). Only
|
|
447
475
|
// return one for the ref type that means "this operation is happening AT/FOR
|
|
@@ -558,6 +586,15 @@ and is not offered for an outline report. Programs built on the SDK's
|
|
|
558
586
|
`referenceActivity` factory get this from one `activationRadius` config line —
|
|
559
587
|
a value, or a function of the list row for rules that vary.
|
|
560
588
|
|
|
589
|
+
**A reference too large to stand for one place says so.** An operation with
|
|
590
|
+
no location of its own takes the first of its references that has one
|
|
591
|
+
(docs/design/locations.md), which is right for a summit or a park a few
|
|
592
|
+
kilometres across and wrong for a 3,500 km trail. A reference whose point is
|
|
593
|
+
representative rather than where an activator stands — POTA's parks that span
|
|
594
|
+
several states — carries `largeArea: true`, and that reference never places
|
|
595
|
+
the operation. Nothing else reads it: the maps draw it, and the nearby search
|
|
596
|
+
finds it, exactly as before.
|
|
597
|
+
|
|
561
598
|
`suggestOperationTitle` lets an activity build a natural-language operation
|
|
562
599
|
title/subtitle from its refs — "at Yosemite NP" (POTA), "for Field Day"
|
|
563
600
|
(contest programs, when ported) — mirroring app-polo's
|
|
@@ -1647,6 +1684,7 @@ the extension. `value` names what it shows and sets.
|
|
|
1647
1684
|
| `nativeSwitch`, `nativeCheckbox` | number, 1 or 0 | `commit` | — (shows `label` beside it) |
|
|
1648
1685
|
| `nativeChip` | number, 1 or 0; or none | `commit`; `activate` without a value | `icon`; needs `event` without a value |
|
|
1649
1686
|
| `nativeText` | number, string, or none | nothing | `style` (a typography role), `align` (`start`, `center`, `end`) |
|
|
1687
|
+
| `spectrum` | — | `commit` with the frequency clicked; `activate` from an annotation | see *Spectrum* below |
|
|
1650
1688
|
|
|
1651
1689
|
The three drawn kinds are invisible hit areas over artwork the extension paints;
|
|
1652
1690
|
the native kinds are Material widgets the app paints and themes. Every control
|
|
@@ -1758,6 +1796,38 @@ export const spotter: PanelHook = {
|
|
|
1758
1796
|
}
|
|
1759
1797
|
```
|
|
1760
1798
|
|
|
1799
|
+
#### Spectrum
|
|
1800
|
+
|
|
1801
|
+
A `spectrum` control is a radio's panadapter and waterfall, drawn by the host
|
|
1802
|
+
from sweeps that never reach the extension, on a host with the panadapter
|
|
1803
|
+
enabled. It is **extension API 6**, as is `host.setRadioSpectrum`: a bundle
|
|
1804
|
+
using either declares `"api": 6`, and an app speaking less refuses it at install
|
|
1805
|
+
rather than failing to render the scene. It needs `requiresRadioRead`; without it, or on a host or radio
|
|
1806
|
+
without a spectrum, the control says why instead. It is placed like a native
|
|
1807
|
+
control and takes the fields of `PanelSceneSpectrum` in the SDK:
|
|
1808
|
+
|
|
1809
|
+
- `radio` (an id from `listRadios`; omitted, the designated radio), `display`
|
|
1810
|
+
(`both`, `pan`, `waterfall`), `split`, and `flow` — which way the waterfall
|
|
1811
|
+
moves (`down`, `up`, `right`, `left`), the panadapter on the side it comes
|
|
1812
|
+
from.
|
|
1813
|
+
- `floor`, `range`, `average` (0–99, default 40), `fps` (default 15),
|
|
1814
|
+
`waterfallSpeed`, `waterfallFloor`, `waterfallRange`: numbers, or the names
|
|
1815
|
+
of scene values the control reads on every sweep.
|
|
1816
|
+
- Colors (`#RRGGBB` or `#AARRGGBB`): `palette` or `waterfallColors`,
|
|
1817
|
+
`traceColor`/`traceWidth`, `fillColor`/`fillColorBottom`, `backgroundColor`,
|
|
1818
|
+
`gridColor`/`gridDashed`, `waterfallGridColor`.
|
|
1819
|
+
- `center`: the tuned-frequency line, styled per part.
|
|
1820
|
+
- `overlays`: up to 16 lines (`at`) and areas (`from`, `to`) in Hz from the
|
|
1821
|
+
tuned frequency, the sweep's middle, or absolute, each styled per part.
|
|
1822
|
+
- `scale`: the gap between the panadapter and the waterfall, its fill and
|
|
1823
|
+
borders, and frequency `labels` and `ticks`.
|
|
1824
|
+
- `annotations`: up to 64 pieces of text at frequencies, with a pointer to
|
|
1825
|
+
where they sit. One with an `event` sends `activate` when clicked, its
|
|
1826
|
+
frequency as `value` and its `id` as `annotation`, and does not tune.
|
|
1827
|
+
- `hoverHz`, `hoverDb`: scene values the pointer writes, for a text layer.
|
|
1828
|
+
- `tuneStep`: a click elsewhere sends `commit` with the frequency under it,
|
|
1829
|
+
rounded to this many Hz. The extension decides whether to tune.
|
|
1830
|
+
|
|
1761
1831
|
The callsign comes back upper-cased, and the field shows the extension's
|
|
1762
1832
|
version once the typing is committed. A bundle carrying this declares
|
|
1763
1833
|
`"api": 5`. The `k2hrc-panel-scenes` sample in the SDK has every kind, a
|
|
@@ -1773,8 +1843,8 @@ repository's dashboard README for building them.
|
|
|
1773
1843
|
The radio host calls are manifest capabilities, refused unless declared — the
|
|
1774
1844
|
same shape as `requiresLocation`, and shown to the operator at install:
|
|
1775
1845
|
`"requiresRadioRead": true` allows `host.listRadios()` and `host.readRadio()`;
|
|
1776
|
-
`"requiresRadioWrite": true` allows `host.tuneRadio()
|
|
1777
|
-
`host.setRadioConnection()`, and implies read, since a command answers with the
|
|
1846
|
+
`"requiresRadioWrite": true` allows `host.tuneRadio()`,
|
|
1847
|
+
`host.setRadioConnection()` and `host.setRadioSpectrum()`, and implies read, since a command answers with the
|
|
1778
1848
|
radio's state. An undeclared call rejects rather than answering null.
|
|
1779
1849
|
A capability belongs to the bundle that declared it, and to the sub-extensions
|
|
1780
1850
|
that bundle defines under its own key. A separately installed bundle never
|
|
@@ -1805,7 +1875,17 @@ reported state confirms it. Drivers retain their own protocol/echo handling.
|
|
|
1805
1875
|
`host.setRadioConnection({id, selection?, connected})` starts or ends a local CAT
|
|
1806
1876
|
connection using the same selection and identity checks. Acceptance starts the
|
|
1807
1877
|
operation; follow reported connection state for completion. It does not switch
|
|
1808
|
-
the hardware's mains power or initiate transmission.
|
|
1878
|
+
the hardware's mains power or initiate transmission.
|
|
1879
|
+
On a host with the panadapter enabled, `readRadio()` carries `spectrum`: null
|
|
1880
|
+
for a radio without a scope, otherwise its settings as the radio reports them
|
|
1881
|
+
(`mode`, `spanHz`, `lowHz`/`highHz`, `refLevelDb`, `speed`), what it accepts
|
|
1882
|
+
(`spansHz`, `refMinDb`…`refStepDb`, `modes`, `speeds`) and `problem`, why it
|
|
1883
|
+
cannot run, in the operator's language. The settings are known only while a
|
|
1884
|
+
`spectrum` control has the scope running, and null otherwise.
|
|
1885
|
+
`host.setRadioSpectrum({id, selection?, mode?, spanHz?, refLevelDb?, speed?})`
|
|
1886
|
+
changes them; a span between two the radio offers is snapped to the nearer. It
|
|
1887
|
+
answers `stopped` when no control is running the scope: nothing is queued for
|
|
1888
|
+
later. The reference panel samples
|
|
1809
1889
|
state once per second while visible; knob motion and LCD digit updates run locally,
|
|
1810
1890
|
and tuning is sent continuously during dragging, paced by the CAT adapter. The
|
|
1811
1891
|
main readout follows the requested value immediately; confirmation status distinguishes pending requests from the radio
|
|
@@ -1920,7 +2000,9 @@ its first render.
|
|
|
1920
2000
|
|
|
1921
2001
|
The whole log is deliberately absent from `render`'s args. A panel that
|
|
1922
2002
|
needs it calls `ctx.getQsos(operation.uuid)` — right for a `'qsoLogged'`
|
|
1923
|
-
render, wrong for a per-keystroke one.
|
|
2003
|
+
render, wrong for a per-keystroke one. Spots are absent for the same reason:
|
|
2004
|
+
a panel showing them declares `'spots'` and calls `ctx.getSpots()`, which
|
|
2005
|
+
answers the operator's own filtered board, judged against this operation.
|
|
1924
2006
|
|
|
1925
2007
|
## ✅ `settingsPanel` — extension settings forms
|
|
1926
2008
|
|