Wiki sections

Wiki SDR SA PRO

Webhook, MQTT and local REST

PRO The Scanner hands its alarms to other programs in three ways: it sends every alarm as an HTTP request to your address (a webhook), publishes it to your MQTT broker, and shows its state through a read-only local REST server on this very computer. All of this is off by default, and the app connects only to addresses you entered yourself. This page covers where the switches are, what exactly goes over the network, and the limits and refusals. The alarms themselves are described on the page Frequency table and alarms, the other alarm actions on the page Alarm zones and alarm actions.

What goes where 0.9.58

Webhook
The Webhook (HTTP POST) box and the address field are in the Alarm actions fold of the Frequency table: N list. Every new alarm sends a POST request with JSON to that address. See below.
MQTT
The MQTT block in the How far to trust the list fold of the Signals: N list: the Publish alarms box, the broker, topic, user name and password. Every alarm becomes a message in a topic of the broker. See below.
Local REST
The Local REST block in the same fold: the Serve the state over HTTP box and the port. This server sends nothing by itself — it only answers requests from programs on this computer. See below.
  • Everything is off by default. The boxes, addresses, port and topic are saved between runs in the Scanner’s settings.json file (see What is saved between runs). A webhook and MQTT left on at exit work again after the next start; the REST server does not: after every start it stays down until you check its box again.
  • The app itself does not go on the Internet. SDR SA PRO connects over the network only where you set it up: to a Pluto receiver, to the webhook address and to the MQTT broker. See also The network.
  • Only the alarm goes over the network. The webhook, MQTT and the /alarms path of the local REST carry it as the same JSON (below): frequency, width, level, time, reason and the class from the frequency table.
  • The air only. A recording from a file and the demo signal give no alarms, so there is nothing to send.
  • Each station has its own. When the window holds several stations, each has its own Scanner with its own settings, and so its own webhook, its own MQTT connection and its own REST server. The alarm body does not say which station it came from. Two stations need different REST ports — a server will not start on a busy port — and preferably different MQTT topics, or the online/offline state of both lands in one topic. Two stations on one broker have not been tested. See The Scanner on several stations.

The alarm body

An alarm is a JSON object in UTF-8. Its fields are the same as in the event log, only as numbers:

FieldWhat it holds
eventalarm; for “Test” — test
programSDR-SA-PRO
time_utcThe time of the measurement in which the row first fell under the rule, UTC to the second: 2026-11-03T10:40:00Z
frequency_hzThe centre of the row, Hz
f_lo_hz, f_hi_hzThe lower and upper edge of the row, Hz
width_hzThe width of the row, Hz
level_dbThe level in dB above the local floor, one decimal place; null — no level
reasonThe reason: guard, watch, kind or video
name, kindThe class by the frequency table: the name and type of the range; empty — the signal lies in no range
numberThe serial number of the alarm since the list started, from one
channelThe channel number; 0 — the row is not a channel
videoPAL or NTSC when the video channel search confirmed video; empty otherwise

The body has no what field of the event log (a signal or an activity range).

Webhook 0.9.58

On the Scanner tab go to the Frequency table: N list, unfold Alarm actions and check Webhook (HTTP POST); type the address into the field next to it, for example http://192.168.1.20:8080/alarm. While the box is checked, a hint below it says what exactly goes where.

  • What is sent. Every new alarm sends a POST request with the alarm body to this address and nowhere else. The headers are Content-Type: application/json; charset=utf-8 and User-Agent: SDR-SA-PRO.
  • No more than 8 per look. The other alarms of the same look are not sent but counted as skipped: webhooks skipped: N (cap 8 per look).
  • In the background. The reply is awaited for up to 5 s, and the Scanner’s look does not wait for it. Success is a 2xx reply; any other code is the refusal HTTP N; no reply in time is timed out (5 s).
  • No redirects and no retries. A 3xx reply is a refusal with its code, for example HTTP 302: the data never goes to an address other than the one you typed. A request that failed is not repeated.
  • http:// and https://. http:// travels over your network as plain text; https:// has its certificate checked through Windows. Any other address is refused with the webhook address must start with http:// or https://, an empty one with the webhook address is not set.

Test and counters

The Test button of the same fold is active while the voice, the siren or the webhook is on. With the webhook on, it sends the address an event without an alarm — just three fields: {"event": "test", "program": "SDR-SA-PRO", "time_utc": "…"}.

What came of it is stated by the line below the action switches (see What came of the actions):

  • webhook: N — how many requests got a 2xx reply since the program started, tests included; when something failed or was skipped, (N failed, N skipped) is added;
  • in Last — webhook: N on the way or webhook: test on the way;
  • the latest refusal — at the end of the line, for example webhook: HTTP 500.

While the webhook is on, the Actions line in the “How far to trust the list” fold names it among the actions that are on.

MQTT 0.9.58

The MQTT block sits in the How far to trust the list fold of the Signals: N list, right after the “Local REST” block.

Publish alarms
Turns the connection to the broker on. Off by default. If the box was checked at exit, the app connects again as soon as it starts.
broker
host, host:port, mqtt://host[:port] — TCP, port 1883 by default — or mqtts://host[:port] — TLS, 8883 by default, the broker’s certificate checked by Windows. An address with a path, another scheme or a port outside 1…65535 is not taken: the box is unchecked, and the line below the block says why.
topic
The root of the topics, sdrsa by default. An empty topic or one with + or # is not accepted.
user and password
As the broker requires them; empty — none. The password field shows dots.

Every change of a field reconnects.

What is published

  • Every new alarm goes to the topic <topic>/alarm with the same body as the webhook, at QoS 0, not retained.
  • The program’s state goes to the topic <topic>/status: online right after connecting and offline before exit or when the box is unchecked; both are retained messages. offline is also set as the will: when the connection breaks without warning, the broker publishes it by itself.
  • There are no subscriptions: the app reads nothing from the broker except the protocol’s own replies, and takes no commands over MQTT.
  • The client is the app’s own, MQTT 3.1.1, with no third-party libraries. The client identifier is sdrsa-pro- and five digits; every session is clean.
  • A connection attempt waits up to 5 s. If it fails, the next attempt comes after 1, 2, 5, 10, 30 s, then every minute.
  • When the broker itself refuses, the pause is longer: 10 s, then 30 s, then every minute.
  • When nothing has been sent for 30 s, the client asks the broker whether it is still there; with no answer within another 30 s the connection counts as lost, and a second later the client connects again.
  • While there is no link, alarms wait in a queue of up to 500 messages; when the queue is full, the oldest is dropped and counted.

The line below the block shows the state: connected, with sent N, queued N, dropped N; connecting to mqtt://…; a failed connection or a broker refusal with the pause before the next attempt; or a lost link. While the box is unchecked, the line holds a short hint about the client.

In build 0.9.58 the words about the connection, the refusals of the broker and the refusals of the broker address and topic come in Ukrainian even with the English interface. The table below gives their meaning.

The app names a broker’s refusal in the protocol’s terms:

Refusal codeWhat it means
1the broker does not accept this protocol version
2the broker rejected the client identifier
3the broker service is unavailable
4wrong user name or password
5not authorised
otherthe refusal code itself

The app keeps the broker’s user name and password in the Scanner’s settings.json file as plain text — just as you typed them. Anyone who can read that file can read the password too. Give the app its own user on the broker, allowed only to publish to its topic.

Local REST 0.9.58

The Local REST block is in the How far to trust the list fold, before the “MQTT” block. The Serve the state over HTTP box brings the server up, the port field sets its port: from 1024 to 65535, 8765 by default. Next to it stands the address, for example http://127.0.0.1:8765/.

  • This computer only. The server listens on 127.0.0.1 only, and that address cannot be changed: the receiver’s state cannot become visible on the network because of one checkbox. Requests come only from programs on this same computer.
  • Read only. The server answers GET and HEAD; POST, PUT, DELETE and PATCH get code 405 with the header Allow: GET, HEAD. It takes no commands, changes nothing in the app and connects nowhere by itself.
  • The box tells the truth. The server does not share a port another program already listens on: the box is unchecked, and the line below the block says could not open port 8765: …. A number outside the limits gives the port must be from 1024 to 65535. A new port, while the server runs, moves it there at once.
  • After the program starts, the server is down. The port is saved, but the server comes up only when you check the box.
  • While the server runs, the line below the block says running at http://127.0.0.1:8765/; requests: N.

Paths

Replies are JSON in UTF-8 with the header Cache-Control: no-store. The state is updated on every change of it, the list and the alarms after every look.

PathWhat it returns
/The index: program, read_only (true), endpoints — the list of paths, events_query — how to ask for the log.
/stateThe Scanner’s state: on — whether the search is on; state — the state line in words, as on the tab; why — its code (empty — running; stopped, paused, tx, passport, power, eof, closed, failed); mode — sweep or tuned; driver; f_lo_hz, f_hi_hz, df_hz — the edges and step of the measurement; looks — spectra per cell; rows, tentative, alarms_unacked; method and method_used; video_search; sweep_bands_hz — the sweep bands; events_log — whether the event log is being written; time_utc; requests — how many requests since the program started.
/listrows and count. A list row: rowId — the row’s steady number; centerHz, widthHz; levelDb (dB above the floor, null — none), levelP90Db (the 90th percentile of the hit levels), levelN; firstMs, lastMs — the time of the first and the latest hit, milliseconds UTC since 1970; hits; state (1 — there, 2 — gone); className, classKind; activity — an activity range; alarm (0 — none, 1 guard, 2 watch, 3 type, 4 video) and alarmNo; channel; video (0 — not checked yet, 1 PAL, 2 NTSC, 3 not video, 4 sync without a field, 5 not checked).
/alarmsalarms — the last 200 alarms since the program started, oldest first, with the same body; count; kept (200).
/daysdays — the days for which there is an event-log file, newest first.
/events?day=YYYY-MM-DDThe event log of a day: day, rows (up to 5000), total — rows in the file, skipped — rows not read, error — what failed, in words; empty — all is well. Without a valid day — code 400.

An unknown path gets code 404 and {"error": "unknown path; see /"}.

Limits

  • The webhook and MQTT speak only of alarms. The Scanner’s list and state are visible only through the local REST.
  • Webhook — up to 8 requests per look, no retries: a request that did not get through is lost, and the counter says so.
  • MQTT — QoS 0 only: the broker does not confirm delivery; while there is no link, up to 500 messages are kept in the queue.
  • The alarm body does not say which station it came from.
  • Local REST — 127.0.0.1 only, and read only.
  • The MQTT password lies in settings.json as plain text.
  • In build 0.9.58 the MQTT state line and the broker’s refusals come in Ukrainian even with the English interface.
  • The webhook has not yet been tried against a live server, MQTT against a live broker, or the REST with third-party client programs; a mqtts:// connection has not been tried at all.

The wiki describes version 0.9.58. Your app's version is shown in the Help menu, under About. Found a mistake? Write to us.