1.0.3 • Published 2 years ago

@trippnology/lib-js8call v1.0.3

Weekly downloads
4
License
MIT
Repository
bitbucket
Last release
2 years ago

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:

NameDescription
errorSomething went wrong
rig.ptt.onPTT has been enabled
rig.ptt.offPTT has been disabled
rig.safe_to_txReturns true when PTT is off AND the send buffer is empty.
rx.directed.to_meReceived a RX.DIRECTED packet addressed to the station callsign. Will change to rx.directed.me when enabled in JS8Call. See #5
tcp.connectedSuccessfully connected to JS8Call via TCP. Returns a connection object with address, port, and mode keys.
tcp.disconnectedThe connection to JS8Call has been closed or dropped.
tcp.errorSomething went wrong with the TCP connection. Returns an error.
udp.connectedListening for messages from JS8Call. Returns a connection object with address, port, and mode keys.
udp.errorSomething 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

OptionTypeDefaultDescription
debugbooleanfalseEnables verbose output
exit_when_js8call_closedbooleantrueEnd the process when we receive a CLOSE message from JS8Call
get_metadata_at_launchbooleantrueQueries JS8Call for station metadata at launch. Only available when using TCP. Disable for cli use.
tcpobject{ auto_reconnect: false, enabled: true, host: 'localhost', port: 2442, seconds_between_reconnect: 5 }Options for the TCP server
udpobject{ 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

NameTypeDescription
js8.help.valid_message_typespropertyObject with 2 keys; incoming and outgoing that list valid API message types.

Inbox

NameTypeDescription
js8.inbox.getMessages()functionPromise that resolves with an array of message objects
js8.inbox.storeMessage(callsign, text)functionPromise that resolves with an INBOX.MESSAGE packet

Mode

NameTypeDescription
js8.mode.getSpeed()functionPromise that resolves with a number representing the current speed. 0 = normal, 1 = fast, 2 = turbo, 4 = slow
js8.mode.getSpeedDetailed()functionPromise that resolves with an object with both the setting and a name: { setting: 0, name: 'normal' }
js8.mode.setSpeed(speed)functionPromise 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

NameTypeDescription
js8.rig.getFreq()functionPromise that resolves with an object containing properties about the current operating frequency
js8.rig.setFreq(options)functionPromise 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()functionCurrently 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.pttpropertyCurrently 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_txpropertyReturns true when PTT is off AND the send buffer is empty.

RX

NameTypeDescription
js8.rx.getBandActivity()functionPromise that resolves with an object representing the band activity window.
js8.rx.getCallActivity()functionPromise that resolves with an object representing the call window.
js8.rx.getCallSelected()functionPromise that resolves with a string containing the currently selected callsign.
js8.rx.getText()functionPromise that resolves with a string containing the contents of the QSO window.

Station

NameTypeDescription
js8.station.callsignpropertyString containing the station callsign
js8.station.gridpropertyString containing the station grid
js8.station.infopropertyString containing the station info
js8.station.statuspropertyThe last STATION.STATUS packet received
js8.station.getMetadata()functionPromise that resolves with an object with the keys; callsign, grid, info, status.
js8.station.getCallsign()functionPromise that resolves with a string containing the station callsign.
js8.station.getGrid()functionPromise that resolves with a string containing the station grid.
js8.station.setGrid(grid)functionPromise that resolves with a string containing the new station grid. grid should be a string containing a valid Maidenhead locator.
js8.station.getInfo()functionPromise that resolves with a string containing the station info.
js8.station.setInfo(info)functionPromise that resolves with a string containing the new station info. info should be a string containing the station QTH/info.
js8.station.getStatus()functionPromise that resolves with a string containing the station status.
js8.station.setStatus(status)functionPromise that resolves with a string containing the new station status. status should be a string containing the station status.

TCP

NameTypeDescription
js8.tcp.connect()functionTries to establish a TCP connection. Promise that resolves with a connection object.

TX

NameTypeDescription
js8.tx.getText()functionPromise that resolves with a string containing the contents of the TX window.
js8.tx.sendMessage(text)functionPromise 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)functionPromise that resolves with a TX.TEXT packet. text should be the string you wish to place into the TX window.

Utils

NameTypeDescription
js8.utils.messageIsToMe(packet)functionReturns 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

1.0.3

2 years ago

1.0.2

2 years ago

1.0.1

3 years ago

1.0.0

3 years ago

0.17.1

3 years ago

0.17.0

3 years ago

0.16.1

3 years ago

0.15.0

3 years ago

0.16.0

3 years ago

0.12.0

3 years ago

0.13.0

3 years ago

0.14.0

3 years ago

0.13.1

3 years ago

0.11.6

3 years ago

0.11.4

3 years ago

0.11.5

3 years ago

0.11.1

3 years ago

0.11.2

3 years ago

0.11.3

3 years ago

0.11.0

3 years ago

0.10.4

3 years ago

0.10.2

3 years ago

0.10.3

3 years ago

0.10.1

3 years ago

0.10.0

3 years ago

0.9.1

3 years ago

0.9.0

3 years ago

0.8.0

3 years ago

0.5.0

3 years ago

0.7.0

3 years ago

0.6.0

3 years ago

0.4.0

3 years ago

0.3.1

3 years ago

0.3.0

3 years ago