A Python library for interacting with JS8Call's TCP API, featuring GPS integration and automated grid square management.
Project description
JS8Call API Python Client
A comprehensive Python client library for interacting with JS8Call's TCP API
Developed by Tiran Dagan, BackstopRadio.com
Table of Contents
- Overview
- API Features Summary
- Installation
- Basic Usage
- Advanced Examples
- API Reference
- Response Data Structures
- Error Handling
- Requirements
- Contributing
- License and Legal
- Acknowledgments
Overview
This Python client library provides a comprehensive, high-level interface to JS8Call's TCP API. It allows you to programmatically control JS8Call and integrate it with other applications or systems.
The package includes:
- A robust API client library (
JS8CallAPI.py) - An interactive demo script (
demo.py) showcasing all features - Supporting utilities for the demo interface (
demo_functions.py)
Key Features
|
|
API Features Summary
The following table provides a complete overview of all available API methods:
| Feature | Get | Set | Return Type | Description |
|---|---|---|---|---|
| Frequency | ✅ get_frequency() |
✅ set_frequency() |
Dict[str, int] |
Get/set current dial frequency and offset |
| Callsign | ✅ get_callsign() |
❌ | str |
Get current station callsign |
| Grid Square | ✅ get_grid() |
✅ set_grid() |
str / bool |
Get/set station grid locator |
| Station Info | ✅ get_station_info() |
✅ set_station_info() |
str / bool |
Get/set station information text |
| Status | ✅ get_status() |
✅ set_status() |
str / bool |
Get/set current station status message |
| Call Activity | ✅ get_call_activity() |
❌ | Dict[str, Dict[str, Any]] |
Get structured list of recently heard stations |
| Selected Call | ✅ get_selected_call() |
❌ | str |
Get currently selected callsign in UI |
| Band Activity | ✅ get_band_activity() |
❌ | Dict[str, Dict[str, Any]] |
Get structured activity across the band |
| RX Text | ✅ get_rx_text() |
❌ | str |
Get text from receive window |
| TX Text | ✅ get_tx_text() |
✅ set_tx_text() |
str / bool |
Get/set text in transmit buffer |
| Send Message | ❌ | ✅ send_message_text() |
bool |
Send a message immediately |
| Speed | ✅ get_speed() |
✅ set_speed() |
int / bool |
Get/set JS8Call speed mode |
| Inbox | ✅ get_inbox_messages() |
✅ store_message() |
List[Dict[str, Any]] / Dict[str, Any] |
Get/store inbox messages |
| Window | ❌ | ✅ raise_window() |
bool |
Raise JS8Call window to foreground |
| GPS Grid | ✅ get_gps_grid_square() |
❌ | Optional[str] |
Get grid square from current GPS position |
| Ping | ✅ ping() |
❌ | bool |
Check if JS8Call is responsive |
Installation
Prerequisites
- Python 3.6 or higher
- JS8Call running with TCP API enabled
- Network connectivity to the JS8Call server
- gpsd (for GPS functionality)
Setup
- Clone this repository:
git clone https://github.com/tirandagan/js8call-api.git
cd js8call-api
- Install dependencies:
pip install gpsd-py3
- The package is ready to use.
File Structure
The repository contains the following main files:
JS8CallAPI.py: The main API client librarydemo.py: An interactive demo script showcasing all API featuresdemo_functions.py: Supporting functions for the demo script's UI and utilities
Basic Usage
The simplest way to use the library is through the JS8CallAPI class, which ensures proper message formatting for the JS8Call API.
from JS8CallAPI import JS8CallAPI
# Create API client
api = JS8CallAPI()
try:
# Connect to JS8Call
api.connect()
# Get current frequency
freq = api.get_frequency()
print(f"Current frequency: {freq['freq']:,} Hz")
print(f"Dial frequency: {freq['dial']:,} Hz")
print(f"Offset: {freq['offset']} Hz")
# Get station information
callsign = api.get_callsign()
grid = api.get_grid()
print(f"Station: {callsign} in {grid}")
finally:
# Always close the connection
api.close()
Running the Interactive Demo
The package includes an interactive demo script (demo.py) that showcases all API features:
python demo.py
This will launch a menu-driven interface allowing you to:
- Test basic API connectivity
- View and update station information
- Monitor station and band activity
- Send and receive messages
- Control JS8Call modes and speeds
- Integrate with GPS for grid square updates
Advanced Examples
Using the Direct Implementation
from JS8CallAPI import JS8CallAPI
api = JS8CallAPI()
try:
api.connect()
# Get current frequency
freq = api.get_frequency()
print(f"Current frequency: {freq['freq']:,} Hz")
# Get current grid and update it
current_grid = api.get_grid()
print(f"Current grid: {current_grid}")
# Update grid square (correctly sends in 'value' field)
success = api.set_grid("FN42")
if success:
print("Grid updated successfully")
finally:
api.close()
Messaging Operations
from JS8CallAPI import JS8CallAPI
api = JS8CallAPI()
try:
api.connect()
# Get the content of the transmit buffer
tx_text = api.get_tx_text()
print(f"Current TX text: {tx_text}")
# Set new text to transmit
api.set_tx_text("CQ CQ CQ DE K1ABC")
# Send a message immediately
api.send_message_text("CQ CQ CQ DE K1ABC")
# Get received text
rx_text = api.get_rx_text()
print(f"Received text: {rx_text}")
# Store a message in the inbox
response = api.store_message("W1AW", "Hello from my API script!")
print(f"Message ID: {response['params'].get('ID')}")
# Get messages from the inbox
messages = api.get_inbox_messages()
for msg in messages:
print(f"From: {msg['params'].get('FROM')}")
print(f"Text: {msg['params'].get('TEXT')}")
print(f"Time: {msg['params'].get('UTC')}")
print("---")
finally:
api.close()
Station Monitoring
from JS8CallAPI import JS8CallAPI
import time
from datetime import datetime
api = JS8CallAPI()
try:
api.connect()
# Monitor call activity for 60 seconds
end_time = time.time() + 60
while time.time() < end_time:
# Get all recently heard stations
stations = api.get_call_activity()
print(f"\n--- Station Activity at {datetime.now().strftime('%H:%M:%S')} ---")
# Display info for each station
for call, info in stations.items():
print(f"Call: {call}")
print(f" SNR: {info.get('SNR')} dB")
print(f" Grid: {info.get('GRID')}")
print(f" Time: {datetime.fromtimestamp(info.get('UTC', 0)/1000).strftime('%Y-%m-%d %H:%M:%S')}")
# Get band activity
band = api.get_band_activity()
print(f"\n--- Band Activity: {len(band)} frequencies ---")
time.sleep(10) # Check every 10 seconds
finally:
api.close()
Mode Control
from JS8CallAPI import JS8CallAPI
api = JS8CallAPI()
try:
api.connect()
# Get current speed mode
speed = api.get_speed()
speed_names = {
api.JS8_NORMAL: "Normal",
api.JS8_FAST: "Fast",
api.JS8_TURBO: "Turbo",
api.JS8_SLOW: "Slow",
api.JS8_ULTRA: "Ultra"
}
print(f"Current speed: {speed_names.get(speed, 'Unknown')}")
# Set to turbo mode
api.set_speed(api.JS8_TURBO)
print("Switched to Turbo mode")
# Set to normal mode
api.set_speed(api.JS8_NORMAL)
print("Switched back to Normal mode")
finally:
api.close()
GPS Integration
from JS8CallAPI import JS8CallAPI
api = JS8CallAPI()
try:
api.connect()
# Get current grid from JS8Call
js8call_grid = api.get_grid()
print(f"JS8Call grid: {js8call_grid}")
# Connect to GPS and get current position as grid square
api.connect_gps()
gps_grid = api.get_gps_grid_square()
if gps_grid:
print(f"GPS grid: {gps_grid}")
# Compare and update if different
if gps_grid.upper() != js8call_grid.upper():
print("Grid square mismatch detected!")
success = api.set_grid(gps_grid)
if success:
print(f"Grid square updated to: {gps_grid}")
else:
print("Error updating grid square")
finally:
api.close()
Frequency Control
from JS8CallAPI import JS8CallAPI
import time
api = JS8CallAPI()
try:
api.connect()
# Store current frequency
current_freq = api.get_frequency()
print(f"Current dial frequency: {current_freq['dial']:,} Hz")
print(f"Current offset: {current_freq['offset']} Hz")
print(f"Current operating frequency: {current_freq['freq']:,} Hz")
# Change to 14.078 MHz
api.set_frequency(dial_freq=14078000)
print("Changed to 14.078 MHz")
time.sleep(5)
# Change back to original frequency
api.set_frequency(dial_freq=current_freq['dial'])
print(f"Changed back to {current_freq['dial']/1000000:.3f} MHz")
finally:
api.close()
API Reference
JS8CallAPI Class
A direct implementation that ensures correct message formatting for the JS8Call API and proper parsing of responses.
Class Constants
from JS8CallAPI import JS8_NORMAL, JS8_FAST, JS8_TURBO, JS8_SLOW, JS8_ULTRA
JS8_NORMAL # Normal speed mode (JS8)
JS8_FAST # Fast speed mode (JS8Fast)
JS8_TURBO # Turbo speed mode (JS8Turbo)
JS8_SLOW # Slow speed mode (JS8Slow)
JS8_ULTRA # Ultra slow mode (JS8Ultra)
Constructor
JS8CallAPI(host='127.0.0.1', port=2442)
Parameters:
host(str): The hostname or IP address of the JS8Call server (default: '127.0.0.1')port(int): The TCP port number for the JS8Call API (default: 2442)
Connection Methods
connect()
Establishes a connection to the JS8Call server.
Returns: None
Raises:
- ConnectionRefusedError: If JS8Call is not running or API not enabled
- Exception: For other connection errors
connect_gps()
Connects to the GPS daemon (gpsd).
Returns: None
Raises:
- Exception: If connection to gpsd fails
close()
Closes the socket connection to JS8Call.
Returns: None
ping()
Sends a ping message to check if JS8Call is responsive.
Returns:
bool: True if JS8Call responds, False otherwise
Frequency Methods
get_frequency()
Gets the current frequency information from JS8Call.
Returns:
{
'freq': int, # Actual operating frequency in Hz
'dial': int, # Dial frequency in Hz
'offset': int # Frequency offset in Hz
}
set_frequency(dial_freq=None, offset=None)
Sets the current frequency.
Parameters:
dial_freq(int, optional): The dial frequency in Hzoffset(int, optional): The frequency offset in Hz
Returns:
bool: True if the command was sent successfully
Station Information Methods
get_callsign()
Gets the current station callsign.
Returns:
str: The current station callsign
get_grid()
Gets the current grid locator.
Returns:
str: The current Maidenhead grid locator
set_grid(grid)
Sets the current grid locator.
Parameters:
grid(str): The Maidenhead grid locator to set
Returns:
bool: True if successful
get_station_info()
Gets the station information text.
Returns:
str: The station information text
set_station_info(info)
Sets the station information text.
Parameters:
info(str): The station information text to set
Returns:
bool: True if successful
get_status()
Gets the current station status text.
Returns:
str: The current status message
set_status(status)
Sets the current station status text.
Parameters:
status(str): The status text to set
Returns:
bool: True if successful
Messaging Methods
get_rx_text()
Gets the text from the receive window.
Returns:
str: The received text (up to 1024 characters)
get_tx_text()
Gets the text from the transmit buffer.
Returns:
str: The text in the transmit buffer
set_tx_text(text)
Sets the text in the transmit buffer.
Parameters:
text(str): The text to set in the transmit buffer
Returns:
bool: True if successful
send_message_text(text)
Sends a message immediately.
Parameters:
text(str): The message text to send
Returns:
bool: True if the command was sent successfully
Activity Monitoring Methods
get_call_activity()
Gets information about recently heard stations.
Returns:
{
'CALLSIGN1': {
'SNR': int, # Signal-to-noise ratio in dB
'GRID': str, # Grid square
'UTC': int # UTC timestamp in milliseconds
},
'CALLSIGN2': {
# Same structure...
},
# More callsigns...
}
get_selected_call()
Gets the currently selected callsign in the UI.
Returns:
str: The selected callsign or empty string if none selected
get_band_activity()
Gets activity across the band.
Returns:
{
'OFFSET1': {
'FREQ': int, # Operating frequency in Hz
'DIAL': int, # Dial frequency in Hz
'OFFSET': int, # Frequency offset in Hz
'TEXT': str, # Decoded text
'SNR': int, # Signal-to-noise ratio in dB
'UTC': int # UTC timestamp in milliseconds
},
'OFFSET2': {
# Same structure...
},
# More offsets...
}
Mode Control Methods
get_speed()
Gets the current JS8Call speed setting.
Returns:
int: The current speed mode (use class constants to interpret)
set_speed(speed)
Sets the JS8Call speed mode.
Parameters:
speed(int): The speed mode (use class constants, e.g., JS8_NORMAL)
Returns:
bool: True if successful
Inbox Methods
get_inbox_messages(callsign=None)
Gets messages from the inbox.
Parameters:
callsign(str, optional): Filter messages by callsign
Returns:
[
{
'type': str, # Message type
'value': str, # Message value
'params': {
'FROM': str, # Sender callsign
'TO': str, # Recipient callsign
'TEXT': str, # Message text
'UTC': int # UTC timestamp in milliseconds
}
},
# More messages...
]
store_message(callsign, text)
Stores a message in the inbox.
Parameters:
callsign(str): Destination callsigntext(str): Message text
Returns:
{
'type': 'INBOX.MESSAGE',
'params': {
'_ID': int, # Message request ID
'ID': int # Stored message ID
}
}
UI Control Methods
raise_window()
Raises the JS8Call window to the foreground.
Returns:
bool: True if the command was sent successfully
GPS Methods
get_gps_grid_square()
Gets the current grid square from GPS coordinates.
Returns:
str: The Maidenhead grid square calculated from current GPS positionNone: If GPS error or no fix
Response Data Structures
Most API methods return structured data extracted from the JS8Call API response. The library automatically parses JSON responses and provides appropriate Python data types.
Frequency Data
{
'freq': 14074000, # Current operating frequency in Hz
'dial': 14073000, # Current dial frequency in Hz
'offset': 1000 # Current offset in Hz
}
Call Activity Data
{
'K1ABC': {
'SNR': -5,
'GRID': 'FN42eq',
'UTC': 1617981234567
},
'W1XYZ': {
'SNR': 12,
'GRID': 'EM73',
'UTC': 1617981234123
}
}
Inbox Message Data
[
{
'type': 'MESSAGE',
'value': '',
'params': {
'FROM': 'K1ABC',
'TO': 'W1XYZ',
'TEXT': 'Hello there!',
'UTC': 1617981234567
}
}
]
Error Handling
The library includes comprehensive error handling with informative messages for common issues:
from JS8CallAPI import JS8CallAPI
api = JS8CallAPI()
try:
api.connect()
# Structured error handling for different operations
try:
api.connect_gps()
gps_grid = api.get_gps_grid_square()
if gps_grid:
print(f"GPS grid: {gps_grid}")
except ConnectionError as e:
print(f"GPS connection error: {e}")
except TimeoutError as e:
print(f"GPS timeout: {e}")
except Exception as e:
print(f"GPS error: {e}")
# Frequency operations
try:
freq = api.get_frequency()
print(f"Current frequency: {freq['freq']:,} Hz")
except ConnectionError:
print("Connection to JS8Call lost")
except TimeoutError:
print("JS8Call did not respond in time")
except Exception as e:
print(f"Error getting frequency: {e}")
finally:
api.close()
Requirements
- Python 3.6+: For type annotations and modern language features
- JS8Call: Running with TCP API enabled (Settings -> Reporting -> "Enable TCP Server API")
- gpsd (optional): For GPS functionality, install with
sudo apt install gpsdon Linux - Network connectivity: To the JS8Call server
Contributing
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature-name - Commit your changes:
git commit -am 'Add some feature' - Push to the branch:
git push origin feature-name - Submit a pull request
For bug reports, please open an issue with a detailed description.
License and Legal
Copyright Notice
© 2023 Tiran Dagan, BackstopRadio.com. All rights reserved.
MIT License
This project is licensed under the MIT License - see the LICENSE file for details.
Disclaimer
This software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.
JS8Call is a separate project created by Jordan Sherer (KN4CRD). This API client is not officially affiliated with or endorsed by JS8Call.
Amateur Radio Notice
This software is designed for use by licensed amateur radio operators. Users are responsible for ensuring all transmissions comply with local regulations and licensing requirements.
Acknowledgments
Developed with ❤️ for the amateur radio community
BackstopRadio.com
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file backstop_python_js8call_api-0.1.0.tar.gz.
File metadata
- Download URL: backstop_python_js8call_api-0.1.0.tar.gz
- Upload date:
- Size: 13.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1f15ea699eaa7e4926ce0259c1335e24c98a01deeb44401ecfd85fac3d43ace
|
|
| MD5 |
93ab93ad29e580eedb7111a380b9ba66
|
|
| BLAKE2b-256 |
85d8a67455da85a13599d630efaedd0b6a05d54734062332106f5a50959a9f83
|
File details
Details for the file backstop_python_js8call_api-0.1.0-py3-none-any.whl.
File metadata
- Download URL: backstop_python_js8call_api-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5e007a9a0aaeee83ad92de1a4bca84bd2a78c7da23a0c08acef7b19a42c8bec7
|
|
| MD5 |
0297d3adf059431a6c2155cfbf4f92ab
|
|
| BLAKE2b-256 |
04a17b4274e93c0cd116f411833b9a64148e943541857f29599377f33ffb7c20
|