← Ordo Artificum

js8net-legacy

JS8Call Python API library

A Python library for interacting with a running JS8Call instance over its TCP API. Wraps JS8Call's asynchronous, undocumented JSON protocol behind a straightforward query/reply interface, so your scripts can send messages, read configuration, and process received traffic without managing raw JSON or tracking async replies.

Open Source — GPL-3.0 GitHub

Installation

# clone the repo
git clone https://github.com/jfrancis42/js8net-legacy
# install dependencies
pip3 install yattag maidenhead

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

Quick start

Import the library and connect to a running JS8Call instance. Two threads start automatically — one delivers commands, the other processes incoming data and routes it to the receive queue.

from js8net import *
start_net("10.1.1.141", 2442)
# query and set configuration
grid = get_grid()
set_grid("DM79")
# send a message
send_message("W4ABC DE N0GQ HELLO 73")
# get frequency (returns dial, offset, effective tx)
get_freq() # {'dial': 7078000, 'freq': 7080000, 'offset': 2000}

Receiving traffic

Decoded traffic from other stations is routed into rx_queue. The three message types you'll see from other users are RX.SPOT, RX.ACTIVITY, and RX.DIRECTED. For most applications, RX.DIRECTED is what you want — it's the complete reassembled message.

import time
while True:
  if not rx_queue.empty():
    with rx_lock:
      msg = rx_queue.get()
      if msg['type'] == "RX.DIRECTED":
        print(msg['value'])
  time.sleep(0.1)

API reference

Radio & Configuration

get_freq() Returns {'dial': …, 'freq': …, 'offset': …} (all Hz)
set_freq(dial, offset) Set dial frequency and audio passband offset (Hz)
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_speed() / set_speed(speed) JS8 submode: slow=4, normal=0, fast=1, turbo=2

Transmit

send_message(message) Queue a message for the next transmit cycle
send_inbox_message(dest, msg) Send directly to another station's inbox
send_aprs_grid(grid) Send grid info to APRS via JS8
send_sms(phone, message) Send an SMS message via JS8
send_email(address, message) Send an email via JS8

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

GUI & Status

get_call_activity() Contents of the call activity pane
get_band_activity() Contents of the band activity pane
get_rx_text() / get_tx_text() Read the RX/TX text boxes
raise_window() Raise the JS8Call window to the foreground

Bundled utilities

groups.py

Processes your JS8Call DIRECTED.TXT log file and produces a groups.json file listing every @ group seen and all callsigns that participated. Useful for understanding group membership and activity patterns from your own receive data.

collector.py / monitor.py

A distributed data collection and web interface for aggregating JS8Call traffic from multiple stations. collector.py connects to a JS8Call instance and forwards decoded frames to a central monitor.py process. The monitor serves a web interface showing recent traffic, callsign information, and propagation maps. Pre-alpha; intended for protected LAN use.

Notes on the JS8Call API

JS8Call's TCP API has several well-known limitations that js8net works around where possible:

  • The API is essentially undocumented. Reading MainWindow::networkMessage() in the JS8Call source is the authoritative reference.
  • All communication is asynchronous — replies arrive whenever JS8Call is ready, unordered relative to requests. js8net handles the correlation for you.
  • API changes are not reflected in the GUI. If you change the grid via the API, the GUI still shows the old value — the change is real, just not visible.
  • Set the JS8Call idle timeout to Disabled. Any other setting will halt transmissions after the timeout elapses without mouse or keyboard input.

See the README for a full accounting of known API gaps and the work-arounds js8net employs.