← Ordo Artificum

js8net-improved

JS8Call-improved Python API library

A Python library for interacting with a running JS8Call-improved instance over its TCP API. A drop-in successor to js8net-legacy targeting the JS8Call-improved API (v2.6+), with full coverage of all new commands — PTT control, filter management, remote configuration, queue depth, heartbeat control, and more — while remaining compatible with all original API commands.

Alpha software. APIs may change, and some features may behave unexpectedly. Use with caution in automated or unattended scenarios involving actual transmissions. Pin to a specific version in production code.
Open Source — GPL-3.0 GitHub

Installation

# install from PyPI
pip install js8call-improved
# or from source
git clone https://github.com/jfrancis42/js8net-improved.git
cd js8net-improved && pip install .

Requires Python 3. JS8Call-improved must be running with the TCP API enabled (default port 2242).

Quick start

Import the library and connect to a running JS8Call-improved instance. Three daemon threads start automatically — one receives messages and updates internal state, one sends queued commands, and one sends keepalive heartbeats every five minutes.

from js8call_improved import *
start_net("10.1.1.141", 2242)
# query and set configuration
grid = get_grid()
set_grid("DM79")
# check if transmitting
get_ptt() # True if TX active
# get frequency
get_freq() # {'dial': 7078000, 'freq': 7080000, 'offset': 2000}

Note: the default port is 2242, not 2442. This is the JS8Call-improved default (configurable under Settings → Reporting → API).

Receiving traffic

Incoming messages are placed into rx_queue (protected by rx_lock). Message types include RX.SPOT, RX.ACTIVITY, RX.DIRECTED, RX.TEXT, and the new STATION.CLOSING (sets the closing global when JS8Call-improved shuts down).

import time
while True:
  if not rx_queue.empty():
    with rx_lock:
      msg = rx_queue.get()
      if msg['type'] == 'RX.DIRECTED':
        print(msg['params']['FROM'], '->', msg['params']['TO'])
        print(msg['value'])
      elif msg['type'] == 'STATION.CLOSING':
        print("JS8Call-improved is shutting down.")
  time.sleep(0.1)

API reference

Radio & Frequency

get_freq() Returns {'dial': …, 'freq': …, 'offset': …} (all Hz)
set_freq(dial, offset) Set dial frequency and audio passband offset (Hz)
get_ptt() New. Returns True if currently transmitting
set_tune(enabled) New. Enable/disable continuous carrier tune mode
tx_halt() New. Emergency stop — immediately halt any ongoing transmission

Station Configuration

get_callsign() Return the configured callsign
get_grid() / set_grid(grid) Read or write the Maidenhead grid square
get_info() / set_info(info) Read or write the station info field
get_status() / set_status(text) New. Get or set the station status message
get_version() New. Returns the JS8Call-improved version string
get_os() New. Returns OS information from the running instance
get_config() New. Fetch the full station configuration as a dict
get_speed() / set_speed(speed) JS8 submode: 0=Normal, 1=Fast, 2=Turbo, 4=Slow, 8=Ultra

Remote Configuration

set_auto_reply(enabled) New. Toggle automatic reply
set_js8hb(enabled) New. Toggle JS8 heartbeat networking
set_hback(enabled) New. Toggle heartbeat acknowledgements
set_multi_decoder(enabled) New. Toggle simultaneous multi-decoder
set_hb_interval(minutes) New. Set the heartbeat interval in minutes
set_hb_timer(active) New. Start or stop the heartbeat timer
set_groups(groups) New. Replace the list of subscribed groups (list of strings)
set_avoid_allcall(enabled) New. Opt out of @ALLCALL messages
get_spot() / set_spot(enabled) New. Get or set the spot-enabled state

Activity & Filters

get_call_activity() Recently heard callsigns (right panel); list of Callstation objects
get_band_activity() Band activity list (left panel); list of Bandstation objects
get_call_selected() Callsign currently selected in the GUI
get_free_offsets() New. Available (unoccupied) frequency segments in the passband
get_filter() New. Get the current bandpass filter settings
set_filter(center, width) New. Set the bandpass filter center and width (Hz)
set_filter_enabled(enabled) New. Toggle the bandpass filter on or off

Transmit

send_message(message) Queue a message for the next transmit cycle
send_directed_message(dest, msg) Send a directed message to a specific callsign
send_inbox_message(dest, msg) Send directly to another station's inbox
send_heartbeat(grid=False) Construct and queue a heartbeat over the air; see also send_hb()
send_hb() New. Trigger JS8Call-improved's built-in heartbeat mechanism via the API
send_aprs(dest, message) Send an APRS message via JS8
send_aprs_grid(grid) Send grid info to APRS via JS8
send_sms(phone, message) Send an SMS via JS8
send_email(address, message) Send an email via JS8
send_sota(summit, freq, mode, comment=False) Submit a SOTA spot via JS8
send_pota(park, freq, mode, comment=False) Submit a POTA spot via JS8
get_queue_depth() New. Number of messages currently waiting in the transmit queue

Queries

query_snr(dest_call) Query a station's SNR
query_grid(dest_call) Query a station's grid square
query_status(dest_call) Query a station's status
query_info(dest_call) Query a station's info field
query_hearing(dest_call) Query which stations a callsign is hearing

Inbox / Messages

get_messages() Return all messages (READ, UNREAD, STORED) in your inbox
store_message(callsign, text) Store a message for pickup; returns the updated inbox

Text Buffers & Window

get_rx_text() / get_tx_text() Read the RX/TX text boxes
set_tx_text(text) Write to the TX text box
raise_window() Raise the JS8Call-improved window to the foreground
alive() Returns True if a valid response was received in the last ~5.5 minutes

Bundled scripts

send_aprs.pySend an APRS message to a callsign
send_email.pySend an email via a JS8 gateway
send_sms.pySend an SMS via a JS8 gateway
send_heartbeat.pySend a heartbeat message
send_message.pySend a directed message
send_pota.pySubmit a POTA spot
send_sota.pySubmit a SOTA spot
send_grid.pySend your grid square to APRS; optionally track position via GPSD
fill_grids.pyQuery stations with missing grid squares
make_calldb.pyBuild a local callsign database (USA, Canada, Australia)
stations.pyReport current station activity
example.pyExample code showing basic library usage

All scripts accept --js8-host and --js8-port (default localhost:2242), or the environment variables JS8HOST and JS8PORT with --env. Speed arguments accept 8 for Ultra mode.

Migrating from js8net-legacy

js8net-improved is closely modeled on js8net-legacy to make migration smooth. The main changes are:

  • Import as from js8call_improved import * instead of from js8net import *
  • Default port is 2242, not 2442
  • Speed 8 (Ultra/JS8-60) is now supported; speed_name(8) returns 'Ultra'
  • Targets JS8Call-improved only — not compatible with the original (legacy) JS8Call

All other functions are drop-in compatible. See the README for the full migration table.