@trippnology/lib-js8call

1.0.3 • Public • Published

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 offdoesn'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

  1. Fork it!
  2. Create your feature branch: git checkout -b my-new-feature develop
  3. Commit your changes: git commit -am 'Add some feature'
  4. Push to the branch: git push origin my-new-feature
  5. 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

Package Sidebar

Install

npm i @trippnology/lib-js8call

Weekly Downloads

1

Version

1.0.3

License

MIT

Unpacked Size

57 kB

Total Files

16

Last publish

Collaborators

  • trippnology