@ham2k/extension-sdk 0.12.2 → 0.12.5
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 +228 -5
- package/dist/index.js +4 -0
- package/docs/distribution.md +1 -1
- package/docs/hooks.md +65 -10
- 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.5
|
|
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 {
|
|
@@ -492,6 +676,7 @@ export interface Ref {
|
|
|
492
676
|
program?: string;
|
|
493
677
|
activationRadiusInMeters?: number;
|
|
494
678
|
activationRadiusInFeet?: number;
|
|
679
|
+
largeArea?: boolean;
|
|
495
680
|
retired?: boolean;
|
|
496
681
|
for?: "operation" | "qso";
|
|
497
682
|
[key: string]: JSONValue | undefined;
|
|
@@ -1544,6 +1729,40 @@ export interface PanelRadioState {
|
|
|
1544
1729
|
transmitting: boolean | null;
|
|
1545
1730
|
meters: Partial<Record<"signalDbm" | "powerOut" | "swr" | "alc" | "supplyVolts", number>>;
|
|
1546
1731
|
canTune: boolean;
|
|
1732
|
+
/**
|
|
1733
|
+
* The radio's scope, on hosts with the panadapter enabled: absent there otherwise, and null
|
|
1734
|
+
* for a radio without one. The sweeps are never sent — a `spectrum` scene control draws them.
|
|
1735
|
+
*/
|
|
1736
|
+
spectrum?: PanelRadioSpectrum | null;
|
|
1737
|
+
}
|
|
1738
|
+
export type PanelRadioSpectrumSpeed = "slow" | "mid" | "fast";
|
|
1739
|
+
export interface PanelRadioSpectrum {
|
|
1740
|
+
/** Why nothing can be drawn, in the operator's language, or null. */
|
|
1741
|
+
problem: string | null;
|
|
1742
|
+
mode: "center" | "fixed" | null;
|
|
1743
|
+
/** Half the width shown while centered — what the radio's menu shows as ±. */
|
|
1744
|
+
spanHz: number | null;
|
|
1745
|
+
/** The edges while fixed. */
|
|
1746
|
+
lowHz: number | null;
|
|
1747
|
+
highHz: number | null;
|
|
1748
|
+
refLevelDb: number | null;
|
|
1749
|
+
speed: PanelRadioSpectrumSpeed | null;
|
|
1750
|
+
/** What `setRadioSpectrum` accepts. A span between two is snapped to the nearer. */
|
|
1751
|
+
spansHz: number[];
|
|
1752
|
+
refMinDb: number;
|
|
1753
|
+
refMaxDb: number;
|
|
1754
|
+
refStepDb: number;
|
|
1755
|
+
modes: ("center" | "fixed")[];
|
|
1756
|
+
speeds: PanelRadioSpectrumSpeed[];
|
|
1757
|
+
}
|
|
1758
|
+
export interface PanelRadioSpectrumSet {
|
|
1759
|
+
/** Reject if this identity no longer matches the selected radio. */
|
|
1760
|
+
id: string;
|
|
1761
|
+
selection?: string;
|
|
1762
|
+
mode?: "center" | "fixed";
|
|
1763
|
+
spanHz?: number;
|
|
1764
|
+
refLevelDb?: number;
|
|
1765
|
+
speed?: PanelRadioSpectrumSpeed;
|
|
1547
1766
|
}
|
|
1548
1767
|
export interface PanelRadioTune {
|
|
1549
1768
|
/** Reject if this identity no longer matches the selected radio. */
|
|
@@ -1556,7 +1775,8 @@ export interface PanelRadioTune {
|
|
|
1556
1775
|
export interface PanelRadioTuneResult {
|
|
1557
1776
|
/** Accepted by the host; confirmation still comes from reported state. */
|
|
1558
1777
|
accepted: boolean;
|
|
1559
|
-
|
|
1778
|
+
/** `stopped`: a scope change while no display has the scope running — nothing was sent. */
|
|
1779
|
+
reason?: "changed" | "disconnected" | "transmitting" | "invalid" | "stopped";
|
|
1560
1780
|
state: PanelRadioState | null;
|
|
1561
1781
|
}
|
|
1562
1782
|
export interface PanelRenderArgs {
|
|
@@ -1650,7 +1870,8 @@ export interface ParsedCallsign {
|
|
|
1650
1870
|
indicators?: string[];
|
|
1651
1871
|
ssid?: string;
|
|
1652
1872
|
}
|
|
1653
|
-
export type CombinedCallInfo = ParsedCallsign & {
|
|
1873
|
+
export type CombinedCallInfo = Omit<ParsedCallsign, "call"> & {
|
|
1874
|
+
call?: string;
|
|
1654
1875
|
isoPrefix?: string;
|
|
1655
1876
|
dxccCode?: number;
|
|
1656
1877
|
regionCode?: string;
|
|
@@ -1998,6 +2219,8 @@ export declare const host: {
|
|
|
1998
2219
|
connected: boolean;
|
|
1999
2220
|
}): Promise<PanelRadioTuneResult>;
|
|
2000
2221
|
tuneRadio(args: PanelRadioTune): Promise<PanelRadioTuneResult>;
|
|
2222
|
+
/** Changes the radio's scope. Needs `requiresRadioWrite` and extension API 6; the result's state reports what the radio took. */
|
|
2223
|
+
setRadioSpectrum(args: PanelRadioSpectrumSet): Promise<PanelRadioTuneResult>;
|
|
2001
2224
|
fetch(url: string, options?: FetchOptions): Promise<FetchResponse>;
|
|
2002
2225
|
webSocket(url: string, options?: WebSocketOptions): ExtensionWebSocket;
|
|
2003
2226
|
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
|
@@ -247,14 +247,17 @@ interface LookupServiceHook {
|
|
|
247
247
|
carried one (`preferStationValues`) — only while the guess's scope is
|
|
248
248
|
`qth`, since on a portable or prefixed contact a source describes the
|
|
249
249
|
home QTH. `tz` is a UTC offset with its ordinary sign, `"GMT-5"` being
|
|
250
|
-
five hours behind
|
|
251
|
-
|
|
250
|
+
five hours behind. lib-country-files states it that way from 1.1; the
|
|
251
|
+
SDK's `annotateCallAgainstCountryFile` answers with the ordinary signs
|
|
252
|
+
whichever version the app ships, flipping an older one's west-positive
|
|
253
|
+
`tz` and `lon`.
|
|
252
254
|
- **`locSource: 'prefix'` is reserved**, and a `lat`/`lon` merged under it is
|
|
253
255
|
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
|
|
256
|
+
lib-country-files' DXCC entity centroid, which synced records carry signed
|
|
257
|
+
either way — west-positive from versions before 1.1 — with nothing to tell
|
|
258
|
+
the two apart. A source reporting a real position must leave `locSource`
|
|
259
|
+
unset or name itself; tagging one `'prefix'` gets its coordinate
|
|
260
|
+
discarded.
|
|
258
261
|
|
|
259
262
|
Implemented by: `lookups` (key `core`). The merge algorithm itself
|
|
260
263
|
(`mergeLookupIntoGuess`/`runLookupPipeline` in
|
|
@@ -441,7 +444,7 @@ interface RefHandlerHook {
|
|
|
441
444
|
geojsonUrlForRef?(args: { ref: Ref }, ctx): Promise<RefGeojson | null>
|
|
442
445
|
}
|
|
443
446
|
// Ref: {type, ref?, name?, label?, shortLabel?, grid?, lat?, lon?, location?, program?,
|
|
444
|
-
// activationRadiusInMeters?, activationRadiusInFeet?, ...}
|
|
447
|
+
// activationRadiusInMeters?, activationRadiusInFeet?, largeArea?, ...}
|
|
445
448
|
// TitleSuggestion: {title?, for?, at?, subtitle?, priority?} — a fragment of
|
|
446
449
|
// the operation's title/subtitle sentence (ham2k/halo-dist#174). Only
|
|
447
450
|
// return one for the ref type that means "this operation is happening AT/FOR
|
|
@@ -558,6 +561,15 @@ and is not offered for an outline report. Programs built on the SDK's
|
|
|
558
561
|
`referenceActivity` factory get this from one `activationRadius` config line —
|
|
559
562
|
a value, or a function of the list row for rules that vary.
|
|
560
563
|
|
|
564
|
+
**A reference too large to stand for one place says so.** An operation with
|
|
565
|
+
no location of its own takes the first of its references that has one
|
|
566
|
+
(docs/design/locations.md), which is right for a summit or a park a few
|
|
567
|
+
kilometres across and wrong for a 3,500 km trail. A reference whose point is
|
|
568
|
+
representative rather than where an activator stands — POTA's parks that span
|
|
569
|
+
several states — carries `largeArea: true`, and that reference never places
|
|
570
|
+
the operation. Nothing else reads it: the maps draw it, and the nearby search
|
|
571
|
+
finds it, exactly as before.
|
|
572
|
+
|
|
561
573
|
`suggestOperationTitle` lets an activity build a natural-language operation
|
|
562
574
|
title/subtitle from its refs — "at Yosemite NP" (POTA), "for Field Day"
|
|
563
575
|
(contest programs, when ported) — mirroring app-polo's
|
|
@@ -1647,6 +1659,7 @@ the extension. `value` names what it shows and sets.
|
|
|
1647
1659
|
| `nativeSwitch`, `nativeCheckbox` | number, 1 or 0 | `commit` | — (shows `label` beside it) |
|
|
1648
1660
|
| `nativeChip` | number, 1 or 0; or none | `commit`; `activate` without a value | `icon`; needs `event` without a value |
|
|
1649
1661
|
| `nativeText` | number, string, or none | nothing | `style` (a typography role), `align` (`start`, `center`, `end`) |
|
|
1662
|
+
| `spectrum` | — | `commit` with the frequency clicked; `activate` from an annotation | see *Spectrum* below |
|
|
1650
1663
|
|
|
1651
1664
|
The three drawn kinds are invisible hit areas over artwork the extension paints;
|
|
1652
1665
|
the native kinds are Material widgets the app paints and themes. Every control
|
|
@@ -1758,6 +1771,38 @@ export const spotter: PanelHook = {
|
|
|
1758
1771
|
}
|
|
1759
1772
|
```
|
|
1760
1773
|
|
|
1774
|
+
#### Spectrum
|
|
1775
|
+
|
|
1776
|
+
A `spectrum` control is a radio's panadapter and waterfall, drawn by the host
|
|
1777
|
+
from sweeps that never reach the extension, on a host with the panadapter
|
|
1778
|
+
enabled. It is **extension API 6**, as is `host.setRadioSpectrum`: a bundle
|
|
1779
|
+
using either declares `"api": 6`, and an app speaking less refuses it at install
|
|
1780
|
+
rather than failing to render the scene. It needs `requiresRadioRead`; without it, or on a host or radio
|
|
1781
|
+
without a spectrum, the control says why instead. It is placed like a native
|
|
1782
|
+
control and takes the fields of `PanelSceneSpectrum` in the SDK:
|
|
1783
|
+
|
|
1784
|
+
- `radio` (an id from `listRadios`; omitted, the designated radio), `display`
|
|
1785
|
+
(`both`, `pan`, `waterfall`), `split`, and `flow` — which way the waterfall
|
|
1786
|
+
moves (`down`, `up`, `right`, `left`), the panadapter on the side it comes
|
|
1787
|
+
from.
|
|
1788
|
+
- `floor`, `range`, `average` (0–99, default 40), `fps` (default 15),
|
|
1789
|
+
`waterfallSpeed`, `waterfallFloor`, `waterfallRange`: numbers, or the names
|
|
1790
|
+
of scene values the control reads on every sweep.
|
|
1791
|
+
- Colors (`#RRGGBB` or `#AARRGGBB`): `palette` or `waterfallColors`,
|
|
1792
|
+
`traceColor`/`traceWidth`, `fillColor`/`fillColorBottom`, `backgroundColor`,
|
|
1793
|
+
`gridColor`/`gridDashed`, `waterfallGridColor`.
|
|
1794
|
+
- `center`: the tuned-frequency line, styled per part.
|
|
1795
|
+
- `overlays`: up to 16 lines (`at`) and areas (`from`, `to`) in Hz from the
|
|
1796
|
+
tuned frequency, the sweep's middle, or absolute, each styled per part.
|
|
1797
|
+
- `scale`: the gap between the panadapter and the waterfall, its fill and
|
|
1798
|
+
borders, and frequency `labels` and `ticks`.
|
|
1799
|
+
- `annotations`: up to 64 pieces of text at frequencies, with a pointer to
|
|
1800
|
+
where they sit. One with an `event` sends `activate` when clicked, its
|
|
1801
|
+
frequency as `value` and its `id` as `annotation`, and does not tune.
|
|
1802
|
+
- `hoverHz`, `hoverDb`: scene values the pointer writes, for a text layer.
|
|
1803
|
+
- `tuneStep`: a click elsewhere sends `commit` with the frequency under it,
|
|
1804
|
+
rounded to this many Hz. The extension decides whether to tune.
|
|
1805
|
+
|
|
1761
1806
|
The callsign comes back upper-cased, and the field shows the extension's
|
|
1762
1807
|
version once the typing is committed. A bundle carrying this declares
|
|
1763
1808
|
`"api": 5`. The `k2hrc-panel-scenes` sample in the SDK has every kind, a
|
|
@@ -1773,8 +1818,8 @@ repository's dashboard README for building them.
|
|
|
1773
1818
|
The radio host calls are manifest capabilities, refused unless declared — the
|
|
1774
1819
|
same shape as `requiresLocation`, and shown to the operator at install:
|
|
1775
1820
|
`"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
|
|
1821
|
+
`"requiresRadioWrite": true` allows `host.tuneRadio()`,
|
|
1822
|
+
`host.setRadioConnection()` and `host.setRadioSpectrum()`, and implies read, since a command answers with the
|
|
1778
1823
|
radio's state. An undeclared call rejects rather than answering null.
|
|
1779
1824
|
A capability belongs to the bundle that declared it, and to the sub-extensions
|
|
1780
1825
|
that bundle defines under its own key. A separately installed bundle never
|
|
@@ -1805,7 +1850,17 @@ reported state confirms it. Drivers retain their own protocol/echo handling.
|
|
|
1805
1850
|
`host.setRadioConnection({id, selection?, connected})` starts or ends a local CAT
|
|
1806
1851
|
connection using the same selection and identity checks. Acceptance starts the
|
|
1807
1852
|
operation; follow reported connection state for completion. It does not switch
|
|
1808
|
-
the hardware's mains power or initiate transmission.
|
|
1853
|
+
the hardware's mains power or initiate transmission.
|
|
1854
|
+
On a host with the panadapter enabled, `readRadio()` carries `spectrum`: null
|
|
1855
|
+
for a radio without a scope, otherwise its settings as the radio reports them
|
|
1856
|
+
(`mode`, `spanHz`, `lowHz`/`highHz`, `refLevelDb`, `speed`), what it accepts
|
|
1857
|
+
(`spansHz`, `refMinDb`…`refStepDb`, `modes`, `speeds`) and `problem`, why it
|
|
1858
|
+
cannot run, in the operator's language. The settings are known only while a
|
|
1859
|
+
`spectrum` control has the scope running, and null otherwise.
|
|
1860
|
+
`host.setRadioSpectrum({id, selection?, mode?, spanHz?, refLevelDb?, speed?})`
|
|
1861
|
+
changes them; a span between two the radio offers is snapped to the nearer. It
|
|
1862
|
+
answers `stopped` when no control is running the scope: nothing is queued for
|
|
1863
|
+
later. The reference panel samples
|
|
1809
1864
|
state once per second while visible; knob motion and LCD digit updates run locally,
|
|
1810
1865
|
and tuning is sent continuously during dragging, paced by the CAT adapter. The
|
|
1811
1866
|
main readout follows the requested value immediately; confirmation status distinguishes pending requests from the radio
|