jooby-codec

Jooby message encoders/decoders

GitHub Workflow Status Docs Netlify Status

This library contains message encoders and decoders for Jooby LoRaWAN devices.

Installation

Install required dependencies:

npm install jooby-codec

This will provide 3 protocols of codecs:

  • Analog
  • MTX
  • OBIS Observer

There is a low-level documentation available in a separate repository.

Usage of Analog codecs

Add to the project:

import {commands, message} from 'jooby-codec/analog';

// output all available downlink and uplink commands tree
console.log(commands);
// all uplink commands
console.log(commands.uplink);
// one particular command
console.log(commands.uplink.correctTime2000);

// output main namespace for work with messages
console.log(message);

But it's better to add only necessary commands to the project:

// to get only downlink commands
import {downlink} from 'jooby-codec/analog/commands';
// or slightly more efficient
import * as downlink from 'jooby-codec/analog/commands/downlink';

// to get one particular command
import * as setTime2000 from 'jooby-codec/analog/commands/downlink/setTime2000.js';

The last approach is preferred as it is more efficient and will init only a necessary commands.

Prepare and parse downlink message:

import * as message from 'jooby-codec/analog/message/downlink';
import * as downlinkCommands from 'jooby-codec/analog/commands/downlink';
import getHexFromBytes from 'jooby-codec/utils/getHexFromBytes.js';

const commands = [
{
id: downlinkCommands.correctTime2000.id,
parameters: {sequenceNumber: 45, seconds: -120}
}
];
const bytes = message.toBytes(commands);

console.log('message encoded:', JSON.stringify(bytes));
// output:
[12,2,45,136,254]

console.log('message encoded in HEX:', getHexFromBytes(bytes));
// output:
'0c 02 2d 88 fe'

// decode message back from bytes
const parsedMessage = message.fromBytes(bytes);

console.log('parsedMessage:', parsedMessage);
// output:
{
commands: [
{
id: 12,
name: 'correctTime2000',
headerSize: 2,
bytes: [Array],
parameters: [Object]
}
],
bytes: [ 12, 2, 45, 136, 254 ],
lrc: { expected: 254, actual: 254 }
}

Parse uplink message:

import * as message from 'jooby-codec/analog/message/uplink';

// binary data received from a device
const bytes = [0x0c, 0x01, 0x00, 0x58];

// decode it
const payload = message.fromBytes(bytes);

if ( 'error' in payload ) {
console.log('decode failure:', payload.error, payload.message);
} else {
console.log('message decoded:', payload.commands);
// output:
[
{
id: 12,
headerSize: 2,
bytes: [ 12, 1, 0 ],
parameters: { status: 0 }
}
]
}

Usage of MTX codecs

Add to the project:

import {commands, message} from 'jooby-codec/mtx';

// output all available downlink and uplink commands tree
console.log(commands);
// all uplink commands
console.log(commands.uplink);
// one particular command
console.log(commands.uplink.setDateTime);

// output main namespace for work with messages
console.log(message);

But it's better to add only necessary commands to the project:

// to get only downlink commands
import {downlink} from 'jooby-codec/mtx/commands';
// or slightly more efficient
import * as downlink from 'jooby-codec/mtx/commands/downlink';

// to get one particular command
import * as setDateTime from 'jooby-codec/mtx/commands/downlink/setDateTime.js';

The last approach is preferred as it is more efficient and will init only a necessary commands.

Prepare and parse downlink message and frame:

import * as message from 'jooby-codec/mtx/message/downlink';
import * as downlinkCommands from 'jooby-codec/mtx/commands/downlink';
import getHexFromBytes from 'jooby-codec/utils/getHexFromBytes.js';
import * as frameTypes from 'jooby-codec/mtx/constants/frameTypes.js';

const aesKey = [...Array(16).keys()];
const messageId = 10;

const commands = [
{
id: downlinkCommands.setDateTime.id,
parameters: {
isSummerTime: false,
seconds: 55,
minutes: 31,
hours: 18,
day: 2,
date: 19,
month: 2,
year: 24
}
}
];
const bytes = message.toBytes(
commands,
{
messageId,
accessLevel: downlinkCommands.setDateTime.accessLevel,
aesKey
}
);

console.log('message encoded:', JSON.stringify(bytes));
// output:
[10,19,237,116,10,174,74,186,200,66,196,27,231,245,13,60,40,132]

console.log('message encoded in HEX:', getHexFromBytes(bytes));
// output:
'0a 13 ed 74 0a ae 4a ba c8 42 c4 1b e7 f5 0d 3c 28 84'


const frameBytes = message.toFrame(
bytes,
{
type: frameTypes.DATA_REQUEST,
source: 0xffff,
destination: 0xaaaa
}
);

console.log('frame encoded:', frameBytes);
// output:
[126,80,170,170,255,255,10,125,51,237,116,10,174,74,186,200,66,196,27,231,245,13,60,40,132,97,187,126]

console.log('frame encoded in HEX:', getHexFromBytes(frameBytes));
// output:
'7e 50 aa aa ff ff 0a 7d 33 ed 74 0a ae 4a ba c8 42 c4 1b e7 f5 0d 3c 28 84 61 bb 7e'


// decode message back from bytes
const parsedMessage = message.fromBytes(bytes, {aesKey});

console.log('parsed message:', parsedMessage);
// output:
{
messageId: 3,
accessLevel: 3,
commands: [
{
id: 8,
name: 'setDateTime',
headerSize: 2,
bytes: [Array],
parameters: [Object]
}
],
bytes: [
3, 19, 237, 116, 10, 174,
74, 186, 200, 66, 196, 27,
231, 245, 13, 60, 40, 132
],
lrc: { expected: 119, actual: 119 }
}

// decode message back from frame
const parsedFrame = message.fromFrame(frameBytes, {aesKey});

console.log('parsed frame:', parsedFrame);
// output:
{
type: 80,
destination: 43690,
source: 65535,
messageId: 10,
accessLevel: 3,
commands: [
{
id: 8,
name: 'setDateTime',
headerSize: 2,
bytes: [Array],
parameters: [Object]
}
],
bytes: [
10, 19, 237, 116, 10, 174,
74, 186, 200, 66, 196, 27,
231, 245, 13, 60, 40, 132
],
lrc: { expected: 119, actual: 119 },
crc: 25019
}

Parse uplink message:

import * as message from 'jooby-codec/mtx/message/uplink';
import getBytesFromHex from 'jooby-codec/utils/getBytesFromHex.js';

const aesKey = [...Array(16).keys()];

const messageBytes = getBytesFromHex('0a 13 9b 4b f7 2a d1 e5 49 a5 09 50 9a 59 7e c2 b5 88');
const frameBytes = getBytesFromHex('7e 51 aa aa ff ff 0a 7d 33 9b 4b f7 2a d1 e5 49 a5 09 50 9a 59 7d 5e c2 b5 88 21 54 7e');

const parsedMessage = message.fromBytes(messageBytes, {aesKey});

console.log('parsed message:', parsedMessage);
// output:
{
messageId: 10,
accessLevel: 3,
commands: [
{
id: 112,
name: 'getBuildVersion',
headerSize: 2,
bytes: [Array],
parameters: [Object]
}
],
bytes: [
10, 19, 155, 75, 247, 42,
209, 229, 73, 165, 9, 80,
154, 89, 126, 194, 181, 136
],
lrc: { expected: 53, actual: 53 }
}

const parsedFrame = message.fromFrame(frameBytes, {aesKey});

console.log('parsed frame:', parsedFrame);
// output:
{
type: 81,
destination: 43690,
source: 65535,
messageId: 10,
accessLevel: 3,
commands: [
{
id: 112,
name: 'getBuildVersion',
headerSize: 2,
bytes: [Array],
parameters: [Object]
}
],
bytes: [
10, 19, 155, 75, 247, 42,
209, 229, 73, 165, 9, 80,
154, 89, 126, 194, 181, 136
],
lrc: { expected: 53, actual: 53 },
crc: 8532
}

Usage of OBIS Observer

Add to the project:

import {commands, message} from 'jooby-codec/obis-observer';

// output all available downlink and uplink commands tree
console.log(commands);
// all uplink commands
console.log(commands.uplink);
// one particular command
console.log(commands.uplink.getMeterId);

// output main namespace for work with messages
console.log(message);

But it's better to add only necessary commands to the project:

// to get only downlink commands
import {downlink} from 'jooby-codec/obis-observer/commands';
// or slightly more efficient
import * as downlink from 'jooby-codec/obis-observer/commands/downlink';

// to get one particular command
import * as getMeterInfo from 'jooby-codec/obis-observer/commands/downlink/getMeterInfo.js';

The last approach is preferred as it is more efficient and will init only a necessary commands.

Prepare and parse downlink message:

import * as message from 'jooby-codec/obis-observer/message/downlink';
import * as downlinkCommands from 'jooby-codec/obis-observer/commands/downlink';
import getHexFromBytes from 'jooby-codec/utils/getHexFromBytes.js';

const commands = [
{
id: downlinkCommands.getMeterProfile.id,
parameters: {requestId: 3, meterProfileId: 4}
}
];
const bytes = message.toBytes(commands);

console.log('message encoded:', JSON.stringify(bytes));
// output:
[102,2,3,4]

console.log('message encoded in HEX:', getHexFromBytes(bytes));
// output:
'66 02 03 04'

// decode message back from bytes
const parsedMessage = message.fromBytes(bytes);

console.log('parsedMessage:', parsedMessage);
// output:
{
commands: [
{
id: 102,
name: 'getMeterProfile',
headerSize: 2,
bytes: [Array],
parameters: [Object]
}
],
bytes: [102, 2, 3, 4]
}

Parse uplink message:

import * as message from 'jooby-codec/obis-observer/message/uplink';

// binary data received from a device
const bytes = [0x10, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x51, 0x2c, 0x2d, 0xea, 0xae, 0x2c, 0x2f, 0x0a, 0xf6];

// decode it
const payload = message.fromBytes(bytes);

if ( 'error' in payload ) {
console.log('decode failure:', payload.error, payload.message);
} else {
console.log('message decoded:', payload.commands);
// output:
[
{
id: 16,
name: 'getArchiveState',
headerSize: 2,
bytes: [
16, 13, 2, 0, 0, 0, 81, 44, 45, 234, 174, 44, 47, 10, 246
],
parameters: {
requestId: 2,
archiveRecordsNumber: 81,
eldestTime2000: 741206702,
newestTime2000: 741280502
}
}
]
}