Commit Graph
8 Commits
Author SHA1 Message Date
rouggy 26b8dade20 feat(relays): generic HTTP relay board, and point band switching at it
An operator was about to build band→relay mapping into the custom URL rows.
It already exists, and better: relay auto-control has a per-relay "band" rule
driving webswitch, KMTronic, Dingtian, Denkovi and USB boards. It holds state
so a relay is only commanded when its wanted position changed, reads the
boards live before the first apply so it does not re-command one already in
place, and carries per-relay labels. A URL fired on a band change has none of
that, and would have been a second definition of "which antenna on which band"
— the drift that cost us a week on counties.

So the gap was only the hardware: a hand-made switch is none of the five named
types. It is now the sixth, "HTTP relay": an ON URL and an OFF URL, either as
one pattern with {relay} substituted, or one pair per relay. The per-relay
form is the reason it exists — these boxes often have URLs with nothing in
common between channels (…/FF0101 and …/FF0201), which no pattern can express.

Status is remembered rather than read: most have no endpoint worth trusting.
The cost is stated in the code and the panel — after a restart every relay is
re-commanded once, which is harmless on a board with no memory and far better
than assuming an antenna is already selected.

And the Connections panel now says so, exactly where the wrong choice is made:
picking a band change with a URL transport shows a note pointing at Station
Control. The URL transport stays — a lookup pushed to a webhook or a QSO to a
dashboard is an event, not a state, and Station Control has no place for it.
2026-08-15 02:23:03 +02:00
rouggy 6c2c1f106a feat(udp): custom outbound messages — a trigger, a template, UDP or a URL
The hard-coded emitters each speak one published format at one fixed moment.
This is the escape hatch, and it exists mainly for antenna switches: they are
driven by a URL, and the band change is the trigger they want.

Four triggers, each carrying its own field set: band change (the RADIO's band,
not the entry form — a switch follows the rig, not what is being typed), QSO
logged, rotator command and callsign lookup. The panel prints the fields the
selected trigger can fill, which is the point of the whole thing: a placeholder
the trigger does not carry renders as nothing, and without the list an operator
writes {freq} on a band change and has no way to learn why the switch never
moved.

Three things the panel cannot show, handled here:

  - Escaping. A URL needs every value percent-encoded; a UDP payload must not
    be touched. The first portable callsign, F4BPO/P, puts a path separator in
    the middle of a query string otherwise — it works on the bench and fails on
    the air.
  - Blocking. An HTTP call to a switch that is unplugged would otherwise sit
    for the operating system's timeout, on the path of a band change. It runs
    in the background with a 3 s limit.
  - Silence. A switch answering 404 after a firmware update fails exactly like
    success looks from here, so the status code is logged, throttled per row.

The rotator trigger hooks the COMMAND rather than the SP/LP buttons, so the
compass and a spot click fire it too, and the path ("SP"/"LP") is passed
through because an azimuth alone cannot say which was taken — 137° is short
path to one station and long to another.

Credentials in a URL are stored as typed. That is the operator's call, on the
grounds that this is LAN gear, and the hint in the panel says so.
2026-08-15 01:39:16 +02:00
rouggy 03d5376d01 chore(udp): name the service for the protocol, not for one program
"WSJT-X logged QSO (Logger32)" reads as Logger32-only, and it is not: the row
speaks the WSJT-X UDP interface, which several loggers listen on. An operator
running something else would have scrolled past a service that would have
worked for them.

Logger32 stays in the hint as the example it is — it is what this was built
against, and its additional sockets are where most people will point it — and
port 2250 stays as the default, a starting point rather than a rule.
2026-08-15 01:18:44 +02:00
rouggy d95c998081 feat(udp): speak WSJT-X to Logger32, and lighten Sahara
"sent 1153 bytes to 127.0.0.1:2250" and nothing in Logger32. Both true: the
line the operator screenshotted is titled "Setup additional WSJT/JTDX UDP
sockets", and those receivers take UDP LOGGING PACKETS — the WSJT-X v2
protocol. A bare ADIF record posted to them is dropped without a word.

So there is a new outbound service rather than a change to the existing one:
"WSJT-X logged QSO (Logger32)" sends the same ADIF wrapped in a Logged ADIF
datagram (magic 0xadbccbda, schema 2, type 12), defaulting to port 2250. The
plain-text row stays for JTAlert/GridTracker-style receivers, because the two
really are different wire formats and one row cannot be both.

The id string is "OpsLog", not "WSJT-X": a receiver uses it to tell instances
apart, and impersonating the real thing would make a second WSJT-X
indistinguishable from us.

Tested both ways — the header byte for byte, and a round trip back through our
own ParseWSJT, since that is the same decoding every receiver applies.

Sahara moves to the parchment an operator asked for: page #e6ded0, panels
#f3eee2, toolbars #ded5c4, terracotta #c4501e on the buttons and the focus
ring. Lighter and less saturated than the deep sand it was.
2026-08-14 17:51:31 +02:00
rouggy 73f3ec51f7 fix: Flex with multiple slices opened was always showing slice A s-meter. 2026-07-10 23:13:06 +02:00
rouggy 4f32012930 feat: Implemented UDP Outbound Adif message, freq to pstrotator 2026-07-05 18:17:30 +02:00
rouggy 2d742be7df feat: Complete translation in French 2026-07-05 10:44:40 +02:00
rouggy e82e30dd02 up 2026-05-28 21:32:46 +02:00