Jeff Francis gjfrancis@protonmail.com, N0GQ
This library should be considered Alpha quality software. It has not been extensively tested against a running JS8Call-improved instance. APIs may change, functions may behave unexpectedly, and some features may not yet work correctly. Use with caution, especially in any automated or unattended scenario involving actual transmissions.
The library is also subject to change as the JS8Call-improved API itself evolves and improves. Function signatures, return values, and the set of supported commands may all shift in future releases without notice until the API stabilizes. Watch the changelog and pin to a specific version in production code.
Note: This library targets the JS8Call-improved fork. It is not compatible with the original (legacy) JS8Call software. For the original JS8Call, see js8call-legacy.
pip install js8call-improved
Or from source:
git clone https://github.com/jfrancis42/js8net-improved.git
cd js8net-improved
pip install .
In your Python code, import the library as:
from js8call_improved import *js8call-improved is a python3 package for interacting with the JS8Call-improved API. It works exclusively in TCP mode and targets the API extensions introduced in JS8Call-improved (API v2.6+), while also supporting all commands available in the original API (v2.5).
The JS8Call-improved API is a significant improvement over the legacy API, adding remote configuration control, filter management, PTT status, queue depth queries, OS/version introspection, and much more. The underlying protocol is the same JSON-over-TCP transport used by the legacy software.
This library is closely modeled on js8call-legacy to make migration as smooth as possible. See the Differences from js8call-legacy section below for a complete list of changes.
To get started, import the library, then tell it to connect to your JS8Call-improved instance:
from js8call_improved import *
start_net("10.1.1.141", 2242)Note that the default port is 2242, not 2442. This is the default API port in JS8Call-improved (configurable under Settings → Reporting → API).
At this point, three daemon threads are running: one receives messages from JS8Call-improved and updates internal state; one sends queued commands; one sends keepalive heartbeats every five minutes.
start_net(host, port)Connect to a JS8Call-improved instance. Default: localhost, port 2242.
get_freq()Returns {'dial': hz, 'freq': hz, 'offset': hz}.
set_freq(dial, offset)Set dial frequency and audio offset, both in Hz.
get_ptt()New in js8call-improved. Get current PTT state. Returns True if
transmitting, False if not.
set_tune(enabled)New in js8call-improved. Enable (True) or disable (False) continuous
carrier tune mode for antenna tuning.
tx_halt()New in js8call-improved. Emergency stop: immediately halt any ongoing transmission.
get_callsign()
get_grid()
set_grid(grid)
get_info()
set_info(info)These work identically to their js8call-legacy counterparts.
get_status()
set_status(status_text)New in js8call-improved. Get or set the station status message.
get_version()New in js8call-improved. Returns the JS8Call-improved version string.
get_os()New in js8call-improved. Returns OS information from the running JS8Call-improved instance.
get_spot()
set_spot(enabled)New in js8call-improved. Get or set the spot-enabled state.
get_config()New in js8call-improved. Fetch the full station configuration as a dict.
Keys include: AUTO_REPLY, JS8HB, HBACK, MULTI_DECODER, HB_INTERVAL,
HB_TIMER_ACTIVE, MONITOR, TX_ENABLED, SPEED, CAN_HB,
AUTOREPLY_CONFIRMATION, MY_GROUPS, AVOID_ALLCALL.
set_auto_reply(enabled)
set_js8hb(enabled)
set_hback(enabled)
set_multi_decoder(enabled)New in js8call-improved. Toggle autoreply, JS8 heartbeat networking, heartbeat acknowledgements, and simultaneous multi-decoder on or off.
set_hb_interval(minutes)
set_hb_timer(active)
send_hb()New in js8call-improved. Control the automated heartbeat: set the interval
in minutes, start/stop the timer, or trigger an immediate API-level heartbeat.
Note: send_hb() triggers JS8Call-improved's built-in heartbeat mechanism and
is distinct from send_heartbeat(), which constructs and queues a heartbeat
message directly.
set_groups(groups)New in js8call-improved. Replace the list of subscribed groups. Pass a
list of strings, e.g. ["@GROUPONE", "@GROUPTWO"].
set_avoid_allcall(enabled)New in js8call-improved. Opt out of (True) or back in to (False)
@ALLCALL messages.
get_call_activity()Get the list of recently heard callsigns (right panel). Returns a list of
Callstation objects.
get_call_selected()Return the callsign currently selected in the GUI.
get_band_activity()Get the band activity list (left panel). Returns a list of Bandstation
objects.
get_free_offsets()New in js8call-improved. Get available (unoccupied) frequency segments in the passband. Returns a dict with:
FREE: list of{'LOW': hz, 'HIGH': hz, 'WIDTH': hz}segmentsBANDWIDTH: signal bandwidth in Hz for the current speedSPEED: current speed modeLOW,HIGH: usable passband limits in Hz
get_filter()
set_filter(center, width)
set_filter_enabled(enabled)New in js8call-improved. Get, set, or toggle the bandpass filter. center
and width are in Hz.
get_rx_text()
get_tx_text()
set_tx_text(text)These work identically to their js8call-legacy counterparts.
get_queue_depth()New in js8call-improved. Returns the number of messages currently waiting in the transmit queue.
get_speed()
set_speed(speed)
speed_name(speed)Speed values: 0=Normal, 1=Fast, 2=Turbo, 4=Slow, 8=Ultra.
Change from js8call-legacy: Speed 8 (Ultra/JS8-60) is newly supported.
speed_name() now returns 'Ultra' for speed 8 instead of 'Invalid'.
raise_window()Bring the JS8Call-improved window to the foreground.
send_message(message)
send_directed_message(dest_call, message)
send_inbox_message(dest_call, message)These work identically to their js8call-legacy counterparts.
send_heartbeat(grid=False)Construct and queue a heartbeat message over the air. See also send_hb().
send_aprs(dest, message)
send_aprs_grid(grid)
send_sms(phone, message)
send_email(address, message)
send_sota(summit, freq, mode, comment=False)
send_pota(park, freq, mode, comment=False)These work identically to their js8call-legacy counterparts.
query_snr(dest_call)
query_grid(dest_call)
query_status(dest_call)
query_info(dest_call)
query_hearing(dest_call)These work identically to their js8call-legacy counterparts.
get_messages()
store_message(callsign, text)These work identically to their js8call-legacy counterparts.
alive()Returns True if a valid response was received in the last ~5.5 minutes.
Incoming messages that don't map to a query response are placed into rx_queue
(protected by rx_lock). The message types you'll typically see are:
RX.SPOT— spot messageRX.ACTIVITY— partial frame (a fragment of a larger message)RX.DIRECTED— complete reassembled directed messageRX.TEXT— received text buffer updateSTATION.CLOSING— new in js8call-improved: JS8Call-improved is shutting down; theclosingglobal is also set toTrue
Example receive loop:
while True:
if not rx_queue.empty():
with rx_lock:
message = rx_queue.get()
if message['type'] == 'RX.DIRECTED':
print(message['params']['FROM'], '->', message['params']['TO'])
print(message['value'])
elif message['type'] == 'STATION.CLOSING':
print("JS8Call-improved is shutting down.")
time.sleep(0.1)Users migrating from js8call-legacy should note the following changes:
| Item | js8call-legacy | js8call-improved |
|---|---|---|
| Import | from js8call_legacy import * |
from js8call_improved import * |
| Default port | 2442 |
2242 |
| Speed 8 (Ultra) | Not supported; speed_name(8) returns 'Invalid' |
Supported; speed_name(8) returns 'Ultra' |
send_heartbeat() |
Sends heartbeat over the air | Same behavior; see also new send_hb() |
STATION.CLOSING |
Not handled | Sets closing=True global; queued for user processing |
get_status() / set_status() |
Not available | New functions |
get_version() / get_os() |
Not available | New functions |
get_ptt() / set_tune() / tx_halt() |
Not available | New functions |
get_spot() / set_spot() |
Not available | New functions |
get_config() |
Not available | New function; returns full config dict |
set_auto_reply() / set_js8hb() / set_hback() / set_multi_decoder() |
Not available | New functions |
set_hb_interval() / set_hb_timer() / send_hb() |
Not available | New functions |
set_groups() / set_avoid_allcall() |
Not available | New functions |
get_free_offsets() |
Not available | New function |
get_filter() / set_filter() / set_filter_enabled() |
Not available | New functions |
get_queue_depth() |
Not available | New function |
All other functions (get_callsign(), get_grid(), set_grid(), get_info(),
set_info(), get_freq(), set_freq(), get_call_activity(),
get_band_activity(), get_call_selected(), get_rx_text(), get_tx_text(),
set_tx_text(), get_speed(), set_speed(), raise_window(),
send_message(), send_directed_message(), send_inbox_message(),
send_heartbeat(), send_aprs(), send_aprs_grid(), send_sms(),
send_email(), send_sota(), send_pota(), query_snr(), query_grid(),
query_status(), query_info(), query_hearing(), get_messages(),
store_message(), alive()) are drop-in compatible with js8call-legacy.
| Script | Description |
|---|---|
send_aprs.py |
Send an APRS message to a callsign |
send_email.py |
Send an email via a JS8 gateway |
send_sms.py |
Send an SMS via a JS8 gateway |
send_heartbeat.py |
Send a heartbeat message |
send_message.py |
Send a directed message |
send_pota.py |
Submit a POTA spot |
send_sota.py |
Submit a SOTA spot |
send_grid.py |
Send your grid square to APRS; optionally track via GPSD |
fill_grids.py |
Query stations with missing grid squares |
make_calldb.py |
Build a local callsign database (USA, Canada, Australia) |
stations.py |
Report current station activity |
example.py |
Example code showing basic library usage |
All scripts accept --js8-host and --js8-port (default localhost:2242), or
the environment variables JS8HOST and JS8PORT combined with --env. Speed
arguments now accept 8 for Ultra mode in addition to the legacy values.
For GPS tracking with send_grid.py, install the optional dependency:
pip install gpsd-py3
- The SVG icons are from Creative Commons and Font Awesome Free.
- Thanks to COAS Book in Las Cruces, NM for being awesome. https://www.coasbooks.com/