@trippnology/lib-js8call
v1.0.3
Published
A library to interface with JS8Call (js8call.com)
Downloads
37
Maintainers
Readme
lib-js8call
A library to help you interface your NodeJS app with JS8Call (tested with JS8Call v2.2.0).
What can I use this for?
Integrate JS8Call into your existing app, or come up with something entirely new! Here are a couple of apps that use this library:
- js8-cli - Pipe text from anywhere into JS8Call via the command line.
- JS8Assistant - Companion app that adds some nifty features to make your JS8 sessions more fun and useful.
How about an LED "ON AIR" light to show when you are transmitting? A Rasperry Pi and the Johnny Five library are an easy way to hook up all sorts of hardware. The only limit is your imagination! If you make something cool, create an issue with the details, and I'll add you to the list.
Installation
npm install @trippnology/lib-js8call
Usage
const js8 = require('@trippnology/lib-js8call')();
// Note the extra () at the end
You can now listen to events and act on them as you wish. It's a good idea to wait for a tcp.connected
event before you try to interact with JS8Call:
js8.on('tcp.connected', (connection) => {
// At this point, we have setup the connection
console.log(
'Server listening %s:%s Mode: %s',
connection.address,
connection.port,
connection.mode
);
// You can now safely do your thing!
MyApp.init();
});
If you don't want to auto connect, disable TCP and connect when you're ready:
const js8 = require('@trippnology/lib-js8call')({ tcp: { enabled: false } });
// Later, when your app is ready...
js8.tcp.connect().then((connection) => {
console.log(connection);
});
You can either listen to the "firehose" packet
event, emitted on every message we get from JS8Call:
js8.on('packet', (packet) => {
// Do your custom stuff
processPacket(packet);
});
... or individual events you are interested in:
js8.on('rig.ptt', (packet) => {
console.log('[Rig] PTT is %s', packet.value);
});
You can find runnable examples of both approaches in demo/manual-processing.js and demo/individual-events.js respectively.
Don't forget to handle errors:
js8.on('error', (err) => {
console.log('Something went wrong:');
console.error(err);
});
Events
Individual event names mirror the JS8Call JSON API, with a few additions:
| Name | Description |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| error
| Something went wrong |
| rig.ptt.on
| PTT has been enabled |
| rig.ptt.off
| PTT has been disabled |
| rig.safe_to_tx
| Returns true when PTT is off AND the send buffer is empty. |
| rx.directed.to_me
| Received a RX.DIRECTED
packet addressed to the station callsign. Will change to rx.directed.me
when enabled in JS8Call. See #5 |
| tcp.connected
| Successfully connected to JS8Call via TCP. Returns a connection
object with address
, port
, and mode
keys. |
| tcp.disconnected
| The connection to JS8Call has been closed or dropped. |
| tcp.error
| Something went wrong with the TCP connection. Returns an error. |
| udp.connected
| Listening for messages from JS8Call. Returns a connection
object with address
, port
, and mode
keys. |
| udp.error
| Something went wrong with the UDP connection. Returns an error. |
Please note that while JS8Call uses FULL CAPS for message types, event names are in all lower case.
Options
You can pass in any options you wish to set manually when you require the module. Sensible defaults will be provided for anything you omit.
const js8 = require('@trippnology/lib-js8call')({
debug: true,
tcp: { host: '192.168.1.123', port: 12345 },
});
Available options
| Option | Type | Default | Description |
| -------------------------- | ------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| debug
| boolean | false | Enables verbose output |
| exit_when_js8call_closed
| boolean | true | End the process when we receive a CLOSE
message from JS8Call |
| get_metadata_at_launch
| boolean | true | Queries JS8Call for station metadata at launch. Only available when using TCP. Disable for cli use. |
| tcp
| object | { auto_reconnect: false, enabled: true, host: 'localhost', port: 2442, seconds_between_reconnect: 5 }
| Options for the TCP server |
| udp
| object | { enabled: false, port: 2332 }
| Options for the UDP server |
Features
Sending an API request to JS8Call
This is a low level feature that allows you to construct the request to your own requirements.
// You can send your own JSON string
js8.send(
'{ "type": "INBOX.STORE_MESSAGE", "params": { "CALLSIGN": "M7GMT", "TEXT": "Testing JSON API" } }'
);
// Or just send an object and it will be converted for you
js8.send({
type: 'INBOX.STORE_MESSAGE',
params: { CALLSIGN: 'M7GMT', TEXT: 'Testing JSON API' },
});
Shortcuts
As well as being able to construct your own API messages, some convenience methods and properties have been provided.
Help
| Name | Type | Description |
| ------------------------------ | -------- | -------------------------------------------------------------------------------- |
| js8.help.valid_message_types
| property | Object with 2 keys; incoming
and outgoing
that list valid API message types. |
Inbox
| Name | Type | Description |
| ---------------------------------------- | -------- | ------------------------------------------------------ |
| js8.inbox.getMessages()
| function | Promise that resolves with an array of message objects |
| js8.inbox.storeMessage(callsign, text)
| function | Promise that resolves with an INBOX.MESSAGE
packet |
Mode
| Name | Type | Description |
| ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| js8.mode.getSpeed()
| function | Promise that resolves with a number representing the current speed. 0 = normal, 1 = fast, 2 = turbo, 4 = slow |
| js8.mode.getSpeedDetailed()
| function | Promise that resolves with an object with both the setting and a name: { setting: 0, name: 'normal' }
|
| js8.mode.setSpeed(speed)
| function | Promise that resolves with a number representing the current speed. speed
should be a number, one of: 0 = normal, 1 = fast, 2 = turbo, 4 = slow |
Rig
| Name | Type | Description |
| -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| js8.rig.getFreq()
| function | Promise that resolves with an object containing properties about the current operating frequency |
| js8.rig.setFreq(options)
| function | Promise that resolves with an object containing properties about the current operating frequency. options
should be an object containing at least an OFFSET
property, and optional properties DIAL
and FREQ
|
| js8.rig.getPTT()
| function | Currently unreliable Promise that resolves with one of; null
- the status is unknown, 0
- the PTT is off, or 1
- the PTT is on. |
| js8.rig.ptt
| property | Currently unreliable The status of the PTT. One of; null
- the status is unknown, 0
- the PTT is off, or 1
- the PTT is on. |
| js8.rig.safe_to_tx
| property | Returns true when PTT is off AND the send buffer is empty. |
RX
| Name | Type | Description |
| -------------------------- | -------- | ------------------------------------------------------------------------------- |
| js8.rx.getBandActivity()
| function | Promise that resolves with an object representing the band activity window. |
| js8.rx.getCallActivity()
| function | Promise that resolves with an object representing the call window. |
| js8.rx.getCallSelected()
| function | Promise that resolves with a string containing the currently selected callsign. |
| js8.rx.getText()
| function | Promise that resolves with a string containing the contents of the QSO window. |
Station
| Name | Type | Description |
| ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| js8.station.callsign
| property | String containing the station callsign |
| js8.station.grid
| property | String containing the station grid |
| js8.station.info
| property | String containing the station info |
| js8.station.status
| property | The last STATION.STATUS
packet received |
| js8.station.getMetadata()
| function | Promise that resolves with an object with the keys; callsign
, grid
, info
, status
. |
| js8.station.getCallsign()
| function | Promise that resolves with a string containing the station callsign. |
| js8.station.getGrid()
| function | Promise that resolves with a string containing the station grid. |
| js8.station.setGrid(grid)
| function | Promise that resolves with a string containing the new station grid. grid
should be a string containing a valid Maidenhead locator. |
| js8.station.getInfo()
| function | Promise that resolves with a string containing the station info. |
| js8.station.setInfo(info)
| function | Promise that resolves with a string containing the new station info. info
should be a string containing the station QTH/info. |
| js8.station.getStatus()
| function | Promise that resolves with a string containing the station status. |
| js8.station.setStatus(status)
| function | Promise that resolves with a string containing the new station status. status
should be a string containing the station status. |
TCP
| Name | Type | Description |
| ------------------- | -------- | -------------------------------------------------------------------------------------- |
| js8.tcp.connect()
| function | Tries to establish a TCP connection. Promise that resolves with a connection
object. |
TX
| Name | Type | Description |
| -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| js8.tx.getText()
| function | Promise that resolves with a string containing the contents of the TX window. |
| js8.tx.sendMessage(text)
| function | Promise that resolves when the message has been transmitted. text
should be the string you wish to TX. Will reject if the rig is in use. |
| js8.tx.setText(text)
| function | Promise that resolves with a TX.TEXT
packet. text
should be the string you wish to place into the TX window. |
Utils
| Name | Type | Description |
| --------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| js8.utils.messageIsToMe(packet)
| function | Returns true if the packet is addressed to the configured station callsign. packet
should be a packet object. |
Promises
Most functions return a promise, so you can do stuff once it resolves or rejects, if you need to:
js8.tx
.sendMessage('Your custom text')
.then(() => {
console.log('TX finished');
})
.catch((err) => {
console.log('Something went wrong:');
console.error(err);
});
Considerations
You will get at error if both TCP and UDP interfaces are enabled at the same time. While this does technically work, it will likely lead to duplicate traffic and headaches for you! TCP only is preferred.
Detecting when JS8Call is TXing is currently unreliable. We currently listen for RIG.PTT
messages and cache the value, but a RIG.PTT
with a value of off
doesn't mean that the
send buffer is empty. As a workaround, we manually check the buffer with js8.tx.getText()
when we receive a rig.ptt.off
, and if the buffer is empty, we set rig.safe_to_tx
to true
.
js8.tx.sendMessage()
will check the value of rig.safe_to_tx
before sending a message:
js8.tx
.sendMessage('Hello, world!')
// If it's safe to TX, message is sent and promise resolves
.then((message) => {
console.log('Message sent: %s', message);
})
// If the rig is busy, the promise rejects
.catch((err) => {
console.log(err); // Prints: 'Rig busy'
});
You can also listen to the rig.safe_to_tx
event to monitor this value in your app:
js8.on('rig.safe_to_tx', (safety) => {
console.log('Is it safe to TX? %s', safety ? 'yes' : 'no');
});
Hopefully, in the future, there will be additional API methods that allow us to check
if it is safe to TX before trying to do so. Maybe JS8Call could send a TX.START
message
when it starts sending a message, and then a TX.END
message once the whole transmission
has finished or been aborted?
A future enhancement could be a message queue, so users could call js8.tx.sendMessage()
,
and the message would be sent once the rig becomes available.
Message types
The JS8Call API documentation is incomplete, so we are implementing API features as they are understood. API handling starts here in the source code.
For message types discovered so far, see JS8Call JSON API or console.log(js8.help.valid_message_types)
from within your app.
Contributing
- Fork it!
- Create your feature branch:
git checkout -b my-new-feature develop
- Commit your changes:
git commit -am 'Add some feature'
- Push to the branch:
git push origin my-new-feature
- Submit a pull request :D
History
The bulk of this project was written over the Christmas break 2020. Future additions and changes will be noted here.
Credits
Copyright (c) 2021 Rikki Tripp.
A huge thanks to Rick VA1UAV for his feedback, feature suggestions, testing, and bug reports.
License
See LICENSE