mirror of
https://github.com/meshcore-dev/meshcore.js.git
synced 2026-04-20 22:13:49 +00:00
Migrate from JavaScript to TypeScript (#2)
* Migrate from JavaScript to TypeScript Convert all 16 source files from .js to .ts with full type annotations. Add types.ts with interfaces for all data structures. Export BufferReader and BufferWriter from index. Use 'as const' for literal types on constants. * Switch from pnpm to npm * Remove unused EpochSeconds type
This commit is contained in:
parent
f59b0f7ce4
commit
bed46e52b5
21 changed files with 713 additions and 315 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
/.idea
|
||||
/node_modules
|
||||
/dist
|
||||
|
|
|
|||
35
package-lock.json
generated
35
package-lock.json
generated
|
|
@ -11,6 +11,10 @@
|
|||
"dependencies": {
|
||||
"@noble/curves": "^1.8.1",
|
||||
"serialport": "^13.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"typescript": "^5.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/curves": {
|
||||
|
|
@ -227,6 +231,16 @@
|
|||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.19.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz",
|
||||
"integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
|
|
@ -292,6 +306,27 @@
|
|||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
package.json
11
package.json
|
|
@ -2,15 +2,22 @@
|
|||
"name": "@liamcottle/meshcore.js",
|
||||
"version": "1.11.0",
|
||||
"description": "",
|
||||
"main": "src/index.js",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"build": "tsc",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"files": ["dist"],
|
||||
"author": "Liam Cottle <liam@liamcottle.com>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/curves": "^1.8.1",
|
||||
"serialport": "^13.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"typescript": "^5.7.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import BufferReader from "./buffer_reader.js";
|
||||
import BufferWriter from "./buffer_writer.js";
|
||||
import type { ParsedAdvertAppData } from "./types.js";
|
||||
|
||||
class Advert {
|
||||
|
||||
|
|
@ -13,7 +14,13 @@ class Advert {
|
|||
static ADV_FEAT2_MASK = 0x40;
|
||||
static ADV_NAME_MASK = 0x80;
|
||||
|
||||
constructor(publicKey, timestamp, signature, appData) {
|
||||
publicKey: Uint8Array;
|
||||
timestamp: number;
|
||||
signature: Uint8Array;
|
||||
appData: Uint8Array;
|
||||
parsed: ParsedAdvertAppData;
|
||||
|
||||
constructor(publicKey: Uint8Array, timestamp: number, signature: Uint8Array, appData: Uint8Array) {
|
||||
this.publicKey = publicKey;
|
||||
this.timestamp = timestamp;
|
||||
this.signature = signature;
|
||||
|
|
@ -21,7 +28,7 @@ class Advert {
|
|||
this.parsed = this.parseAppData();
|
||||
}
|
||||
|
||||
static fromBytes(bytes) {
|
||||
static fromBytes(bytes: Uint8Array): Advert {
|
||||
|
||||
// read bytes
|
||||
const bufferReader = new BufferReader(bytes);
|
||||
|
|
@ -34,16 +41,16 @@ class Advert {
|
|||
|
||||
}
|
||||
|
||||
getFlags() {
|
||||
getFlags(): number {
|
||||
return this.appData[0];
|
||||
}
|
||||
|
||||
getType() {
|
||||
getType(): number {
|
||||
const flags = this.getFlags();
|
||||
return flags & 0x0F;
|
||||
}
|
||||
|
||||
getTypeString() {
|
||||
getTypeString(): string | null {
|
||||
const type = this.getType();
|
||||
if(type === Advert.ADV_TYPE_NONE) return "NONE";
|
||||
if(type === Advert.ADV_TYPE_CHAT) return "CHAT";
|
||||
|
|
@ -52,7 +59,7 @@ class Advert {
|
|||
return null;
|
||||
}
|
||||
|
||||
async isVerified() {
|
||||
async isVerified(): Promise<boolean> {
|
||||
|
||||
const { ed25519 } = await import("@noble/curves/ed25519");
|
||||
|
||||
|
|
@ -67,34 +74,34 @@ class Advert {
|
|||
|
||||
}
|
||||
|
||||
parseAppData() {
|
||||
parseAppData(): ParsedAdvertAppData {
|
||||
|
||||
// read app data
|
||||
const bufferReader = new BufferReader(this.appData);
|
||||
const flags = bufferReader.readByte();
|
||||
|
||||
// parse lat lon
|
||||
var lat = null;
|
||||
var lon = null;
|
||||
var lat: number | null = null;
|
||||
var lon: number | null = null;
|
||||
if(flags & Advert.ADV_LATLON_MASK){
|
||||
lat = bufferReader.readInt32LE();
|
||||
lon = bufferReader.readInt32LE();
|
||||
}
|
||||
|
||||
// parse feat1
|
||||
var feat1 = null;
|
||||
var feat1: number | null = null;
|
||||
if(flags & Advert.ADV_FEAT1_MASK){
|
||||
feat1 = bufferReader.readUInt16LE();
|
||||
}
|
||||
|
||||
// parse feat2
|
||||
var feat2 = null;
|
||||
var feat2: number | null = null;
|
||||
if(flags & Advert.ADV_FEAT2_MASK){
|
||||
feat2 = bufferReader.readUInt16LE();
|
||||
}
|
||||
|
||||
// parse name (remainder of app data)
|
||||
var name = null;
|
||||
var name: string | null = null;
|
||||
if(flags & Advert.ADV_NAME_MASK){
|
||||
name = bufferReader.readString();
|
||||
}
|
||||
|
|
@ -1,34 +1,37 @@
|
|||
class BufferReader {
|
||||
|
||||
constructor(data) {
|
||||
pointer: number;
|
||||
buffer: Uint8Array;
|
||||
|
||||
constructor(data: ArrayLike<number> | ArrayBuffer) {
|
||||
this.pointer = 0;
|
||||
this.buffer = new Uint8Array(data);
|
||||
}
|
||||
|
||||
getRemainingBytesCount() {
|
||||
getRemainingBytesCount(): number {
|
||||
return this.buffer.length - this.pointer;
|
||||
}
|
||||
|
||||
readByte() {
|
||||
readByte(): number {
|
||||
return this.readBytes(1)[0];
|
||||
}
|
||||
|
||||
readBytes(count) {
|
||||
readBytes(count: number): Uint8Array {
|
||||
const data = this.buffer.slice(this.pointer, this.pointer + count);
|
||||
this.pointer += count;
|
||||
return data;
|
||||
}
|
||||
|
||||
readRemainingBytes() {
|
||||
readRemainingBytes(): Uint8Array {
|
||||
return this.readBytes(this.getRemainingBytesCount());
|
||||
}
|
||||
|
||||
readString() {
|
||||
readString(): string {
|
||||
return new TextDecoder().decode(this.readRemainingBytes());
|
||||
}
|
||||
|
||||
readCString(maxLength) {
|
||||
const value = [];
|
||||
readCString(maxLength: number): string {
|
||||
const value: number[] = [];
|
||||
const bytes = this.readBytes(maxLength);
|
||||
for(const byte of bytes){
|
||||
|
||||
|
|
@ -40,63 +43,64 @@ class BufferReader {
|
|||
value.push(byte);
|
||||
|
||||
}
|
||||
return new TextDecoder().decode(new Uint8Array(value));
|
||||
}
|
||||
|
||||
readInt8() {
|
||||
readInt8(): number {
|
||||
const bytes = this.readBytes(1);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt8(0);
|
||||
}
|
||||
|
||||
readUInt8() {
|
||||
readUInt8(): number {
|
||||
const bytes = this.readBytes(1);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint8(0);
|
||||
}
|
||||
|
||||
readUInt16LE() {
|
||||
readUInt16LE(): number {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint16(0, true);
|
||||
}
|
||||
|
||||
readUInt16BE() {
|
||||
readUInt16BE(): number {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint16(0, false);
|
||||
}
|
||||
|
||||
readUInt32LE() {
|
||||
readUInt32LE(): number {
|
||||
const bytes = this.readBytes(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint32(0, true);
|
||||
}
|
||||
|
||||
readUInt32BE() {
|
||||
readUInt32BE(): number {
|
||||
const bytes = this.readBytes(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint32(0, false);
|
||||
}
|
||||
|
||||
readInt16LE() {
|
||||
readInt16LE(): number {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt16(0, true);
|
||||
}
|
||||
|
||||
readInt16BE() {
|
||||
readInt16BE(): number {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt16(0, false);
|
||||
}
|
||||
|
||||
readInt32LE() {
|
||||
readInt32LE(): number {
|
||||
const bytes = this.readBytes(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt32(0, true);
|
||||
}
|
||||
|
||||
readInt24BE() {
|
||||
readInt24BE(): number {
|
||||
|
||||
// read 24-bit (3 bytes) big endian integer
|
||||
var value = (this.readByte() << 16) | (this.readByte() << 8) | this.readByte();
|
||||
|
|
@ -1,22 +1,22 @@
|
|||
class BufferUtils {
|
||||
|
||||
static bytesToHex(uint8Array) {
|
||||
static bytesToHex(uint8Array: Uint8Array): string {
|
||||
return Array.from(uint8Array).map(byte => {
|
||||
return byte.toString(16).padStart(2, '0');
|
||||
}).join('');
|
||||
}
|
||||
|
||||
static hexToBytes(hex) {
|
||||
return Uint8Array.from(hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
|
||||
static hexToBytes(hex: string): Uint8Array {
|
||||
return Uint8Array.from(hex.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16)));
|
||||
}
|
||||
|
||||
static base64ToBytes(base64) {
|
||||
static base64ToBytes(base64: string): Uint8Array {
|
||||
return Uint8Array.from(atob(base64), (c) => {
|
||||
return c.charCodeAt(0);
|
||||
});
|
||||
}
|
||||
|
||||
static areBuffersEqual(byteArray1, byteArray2) {
|
||||
static areBuffersEqual(byteArray1: Uint8Array, byteArray2: Uint8Array): boolean {
|
||||
|
||||
// ensure length is the same
|
||||
if(byteArray1.length !== byteArray2.length){
|
||||
|
|
@ -1,52 +1,54 @@
|
|||
class BufferWriter {
|
||||
|
||||
buffer: number[];
|
||||
|
||||
constructor() {
|
||||
this.buffer = [];
|
||||
}
|
||||
|
||||
toBytes() {
|
||||
toBytes(): Uint8Array {
|
||||
return new Uint8Array(this.buffer);
|
||||
}
|
||||
|
||||
writeBytes(bytes) {
|
||||
writeBytes(bytes: ArrayLike<number>): void {
|
||||
this.buffer = [
|
||||
...this.buffer,
|
||||
...bytes,
|
||||
...Array.from(bytes),
|
||||
];
|
||||
}
|
||||
|
||||
writeByte(byte) {
|
||||
writeByte(byte: number): void {
|
||||
this.writeBytes([
|
||||
byte,
|
||||
]);
|
||||
}
|
||||
|
||||
writeUInt16LE(num) {
|
||||
writeUInt16LE(num: number): void {
|
||||
const bytes = new Uint8Array(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
view.setUint16(0, num, true);
|
||||
this.writeBytes(bytes);
|
||||
}
|
||||
|
||||
writeUInt32LE(num) {
|
||||
writeUInt32LE(num: number): void {
|
||||
const bytes = new Uint8Array(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
view.setUint32(0, num, true);
|
||||
this.writeBytes(bytes);
|
||||
}
|
||||
|
||||
writeInt32LE(num) {
|
||||
writeInt32LE(num: number): void {
|
||||
const bytes = new Uint8Array(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
view.setInt32(0, num, true);
|
||||
this.writeBytes(bytes);
|
||||
}
|
||||
|
||||
writeString(string) {
|
||||
writeString(string: string): void {
|
||||
this.writeBytes(new TextEncoder().encode(string));
|
||||
}
|
||||
|
||||
writeCString(string, maxLength) {
|
||||
writeCString(string: string, maxLength: number): void {
|
||||
|
||||
// create buffer of max length
|
||||
const bytes = new Uint8Array(new ArrayBuffer(maxLength));
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import BufferReader from "./buffer_reader.js";
|
||||
import type { TelemetryEntry } from "./types.js";
|
||||
|
||||
class CayenneLpp {
|
||||
|
||||
|
|
@ -30,10 +31,10 @@ class CayenneLpp {
|
|||
static LPP_SWITCH = 142; // 1 byte, 0/1
|
||||
static LPP_POLYLINE = 240; // 1 byte size, 1 byte delta factor, 3 byte lon/lat 0.0001° * factor, n (size-8) bytes deltas
|
||||
|
||||
static parse(bytes) {
|
||||
static parse(bytes: Uint8Array): TelemetryEntry[] {
|
||||
|
||||
const buffer = new BufferReader(bytes);
|
||||
const telemetry = [];
|
||||
const telemetry: TelemetryEntry[] = [];
|
||||
|
||||
while(buffer.getRemainingBytesCount() >= 2){ // need at least 2 more bytes to get channel and type
|
||||
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,15 +2,18 @@ import SerialConnection from "./serial_connection.js";
|
|||
|
||||
class NodeJSSerialConnection extends SerialConnection {
|
||||
|
||||
serialPortPath: string;
|
||||
serialPort: any;
|
||||
|
||||
/**
|
||||
* @param path serial port to connect to, e.g: "/dev/ttyACM0" or "/dev/cu.usbmodem14401"
|
||||
*/
|
||||
constructor(path) {
|
||||
constructor(path: string) {
|
||||
super();
|
||||
this.serialPortPath = path;
|
||||
}
|
||||
|
||||
async connect() {
|
||||
async connect(): Promise<void> {
|
||||
|
||||
// note: serialport module is only available in NodeJS, you shouldn't use NodeJSSerialConnection from a web browser
|
||||
const { SerialPort } = await import('serialport');
|
||||
|
|
@ -30,11 +33,11 @@ class NodeJSSerialConnection extends SerialConnection {
|
|||
this.onDisconnected();
|
||||
});
|
||||
|
||||
this.serialPort.on("error", function(err) {
|
||||
this.serialPort.on("error", function(err: Error) {
|
||||
console.log("SerialPort Error: ", err.message)
|
||||
});
|
||||
|
||||
this.serialPort.on("data", async (data) => {
|
||||
this.serialPort.on("data", async (data: Buffer) => {
|
||||
await this.onDataReceived(data);
|
||||
});
|
||||
|
||||
|
|
@ -43,7 +46,7 @@ class NodeJSSerialConnection extends SerialConnection {
|
|||
|
||||
}
|
||||
|
||||
async close() {
|
||||
async close(): Promise<void> {
|
||||
try {
|
||||
await this.serialPort.close();
|
||||
} catch(e) {
|
||||
|
|
@ -51,7 +54,7 @@ class NodeJSSerialConnection extends SerialConnection {
|
|||
}
|
||||
}
|
||||
|
||||
/* override */ async write(bytes) {
|
||||
/* override */ async write(bytes: Uint8Array): Promise<void> {
|
||||
this.serialPort.write(bytes);
|
||||
}
|
||||
|
||||
|
|
@ -5,6 +5,8 @@ import Connection from "./connection.js";
|
|||
|
||||
class SerialConnection extends Connection {
|
||||
|
||||
readBuffer: number[];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.readBuffer = [];
|
||||
|
|
@ -13,11 +15,11 @@ class SerialConnection extends Connection {
|
|||
}
|
||||
}
|
||||
|
||||
async write(bytes) {
|
||||
async write(bytes: Uint8Array): Promise<void> {
|
||||
throw new Error("Not Implemented: write must be implemented by SerialConnection sub class.");
|
||||
}
|
||||
|
||||
async writeFrame(frameType, frameData) {
|
||||
async writeFrame(frameType: number, frameData: Uint8Array): Promise<void> {
|
||||
|
||||
// create frame
|
||||
const frame = new BufferWriter();
|
||||
|
|
@ -34,18 +36,18 @@ class SerialConnection extends Connection {
|
|||
|
||||
}
|
||||
|
||||
async sendToRadioFrame(data) {
|
||||
async sendToRadioFrame(data: Uint8Array): Promise<void> {
|
||||
// write "app to radio" frame 0x3c "<"
|
||||
this.emit("tx", data);
|
||||
await this.writeFrame(0x3c, data);
|
||||
}
|
||||
|
||||
async onDataReceived(value) {
|
||||
async onDataReceived(value: ArrayLike<number>): Promise<void> {
|
||||
|
||||
// append received bytes to read buffer
|
||||
this.readBuffer = [
|
||||
...this.readBuffer,
|
||||
...value,
|
||||
...Array.from(value),
|
||||
];
|
||||
|
||||
// process read buffer while there is enough bytes for a frame header
|
||||
|
|
@ -84,7 +86,7 @@ class SerialConnection extends Connection {
|
|||
this.readBuffer = this.readBuffer.slice(requiredLength);
|
||||
|
||||
// handle received frame
|
||||
this.onFrameReceived(frameData);
|
||||
this.onFrameReceived(new Uint8Array(frameData));
|
||||
|
||||
} catch(e) {
|
||||
console.error("Failed to process frame", e);
|
||||
|
|
@ -5,14 +5,19 @@ import Connection from "./connection.js";
|
|||
|
||||
class TCPConnection extends Connection {
|
||||
|
||||
constructor(host, port) {
|
||||
host: string;
|
||||
port: number;
|
||||
readBuffer: number[];
|
||||
socket: any;
|
||||
|
||||
constructor(host: string, port: number) {
|
||||
super();
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.readBuffer = [];
|
||||
}
|
||||
|
||||
async connect() {
|
||||
async connect(): Promise<void> {
|
||||
|
||||
// note: net module is only available in NodeJS, you shouldn't use TCPConnection from a web browser
|
||||
const { Socket } = await import("net");
|
||||
|
|
@ -21,17 +26,17 @@ class TCPConnection extends Connection {
|
|||
this.socket = new Socket();
|
||||
|
||||
// handle received data
|
||||
this.socket.on('data', (data) => {
|
||||
this.socket.on('data', (data: Buffer) => {
|
||||
this.onSocketDataReceived(data);
|
||||
});
|
||||
|
||||
// handle errors
|
||||
this.socket.on('error', (error) => {
|
||||
this.socket.on('error', (error: Error) => {
|
||||
console.error('Connection Error', error);
|
||||
});
|
||||
|
||||
// handle socket close
|
||||
this.socket.on('close', (error) => {
|
||||
this.socket.on('close', (error: boolean) => {
|
||||
this.onDisconnected();
|
||||
});
|
||||
|
||||
|
|
@ -42,7 +47,7 @@ class TCPConnection extends Connection {
|
|||
|
||||
}
|
||||
|
||||
onSocketDataReceived(data) {
|
||||
onSocketDataReceived(data: Buffer): void {
|
||||
|
||||
// append received bytes to read buffer
|
||||
this.readBuffer = [
|
||||
|
|
@ -86,7 +91,7 @@ class TCPConnection extends Connection {
|
|||
this.readBuffer = this.readBuffer.slice(requiredLength);
|
||||
|
||||
// handle received frame
|
||||
this.onFrameReceived(frameData);
|
||||
this.onFrameReceived(new Uint8Array(frameData));
|
||||
|
||||
} catch(e) {
|
||||
console.error("Failed to process frame", e);
|
||||
|
|
@ -96,7 +101,7 @@ class TCPConnection extends Connection {
|
|||
|
||||
}
|
||||
|
||||
close() {
|
||||
async close(): Promise<void> {
|
||||
try {
|
||||
this.socket.destroy();
|
||||
} catch(e) {
|
||||
|
|
@ -104,11 +109,11 @@ class TCPConnection extends Connection {
|
|||
}
|
||||
}
|
||||
|
||||
async write(bytes) {
|
||||
async write(bytes: Uint8Array): Promise<void> {
|
||||
this.socket.write(new Uint8Array(bytes));
|
||||
}
|
||||
|
||||
async writeFrame(frameType, frameData) {
|
||||
async writeFrame(frameType: number, frameData: Uint8Array): Promise<void> {
|
||||
|
||||
// create frame
|
||||
const frame = new BufferWriter();
|
||||
|
|
@ -125,7 +130,7 @@ class TCPConnection extends Connection {
|
|||
|
||||
}
|
||||
|
||||
async sendToRadioFrame(data) {
|
||||
async sendToRadioFrame(data: Uint8Array): Promise<void> {
|
||||
// write "app to radio" frame 0x3c "<"
|
||||
this.emit("tx", data);
|
||||
await this.writeFrame(0x3c, data);
|
||||
|
|
@ -1,9 +1,20 @@
|
|||
import Constants from "../constants.js";
|
||||
import Connection from "./connection.js";
|
||||
|
||||
declare global {
|
||||
interface Navigator {
|
||||
bluetooth: any;
|
||||
}
|
||||
}
|
||||
|
||||
class WebBleConnection extends Connection {
|
||||
|
||||
constructor(bleDevice) {
|
||||
bleDevice: any;
|
||||
gattServer: any;
|
||||
rxCharacteristic: any;
|
||||
txCharacteristic: any;
|
||||
|
||||
constructor(bleDevice: any) {
|
||||
super();
|
||||
this.bleDevice = bleDevice;
|
||||
this.gattServer = null;
|
||||
|
|
@ -12,7 +23,7 @@ class WebBleConnection extends Connection {
|
|||
this.init();
|
||||
}
|
||||
|
||||
static async open() {
|
||||
static async open(): Promise<WebBleConnection | null | undefined> {
|
||||
|
||||
// ensure browser supports web bluetooth
|
||||
if(!navigator.bluetooth){
|
||||
|
|
@ -40,7 +51,7 @@ class WebBleConnection extends Connection {
|
|||
|
||||
}
|
||||
|
||||
async init() {
|
||||
async init(): Promise<void> {
|
||||
|
||||
// listen for ble disconnect
|
||||
this.bleDevice.addEventListener("gattserverdisconnected", () => {
|
||||
|
|
@ -55,18 +66,18 @@ class WebBleConnection extends Connection {
|
|||
const characteristics = await service.getCharacteristics();
|
||||
|
||||
// find rx characteristic (we write to this one, it's where the radio reads from)
|
||||
this.rxCharacteristic = characteristics.find((characteristic) => {
|
||||
this.rxCharacteristic = characteristics.find((characteristic: any) => {
|
||||
return characteristic.uuid.toLowerCase() === Constants.Ble.CharacteristicUuidRx.toLowerCase();
|
||||
});
|
||||
|
||||
// find tx characteristic (we read this one, it's where the radio writes to)
|
||||
this.txCharacteristic = characteristics.find((characteristic) => {
|
||||
this.txCharacteristic = characteristics.find((characteristic: any) => {
|
||||
return characteristic.uuid.toLowerCase() === Constants.Ble.CharacteristicUuidTx.toLowerCase();
|
||||
});
|
||||
|
||||
// listen for frames from transmitted to us from the ble device
|
||||
await this.txCharacteristic.startNotifications();
|
||||
this.txCharacteristic.addEventListener('characteristicvaluechanged', (event) => {
|
||||
this.txCharacteristic.addEventListener('characteristicvaluechanged', (event: any) => {
|
||||
const frame = new Uint8Array(event.target.value.buffer);
|
||||
this.onFrameReceived(frame);
|
||||
});
|
||||
|
|
@ -76,7 +87,7 @@ class WebBleConnection extends Connection {
|
|||
|
||||
}
|
||||
|
||||
async close() {
|
||||
async close(): Promise<void> {
|
||||
try {
|
||||
this.gattServer?.disconnect();
|
||||
this.gattServer = null;
|
||||
|
|
@ -85,7 +96,7 @@ class WebBleConnection extends Connection {
|
|||
}
|
||||
}
|
||||
|
||||
async write(bytes) {
|
||||
async write(bytes: Uint8Array): Promise<void> {
|
||||
try {
|
||||
// fixme: NetworkError: GATT operation already in progress.
|
||||
// todo: implement mutex to prevent multiple writes when another write is in progress
|
||||
|
|
@ -96,7 +107,7 @@ class WebBleConnection extends Connection {
|
|||
}
|
||||
}
|
||||
|
||||
async sendToRadioFrame(frame) {
|
||||
async sendToRadioFrame(frame: Uint8Array): Promise<void> {
|
||||
this.emit("tx", frame);
|
||||
await this.write(frame);
|
||||
}
|
||||
|
|
@ -1,8 +1,18 @@
|
|||
import SerialConnection from "./serial_connection.js";
|
||||
|
||||
declare global {
|
||||
interface Navigator {
|
||||
serial: any;
|
||||
}
|
||||
}
|
||||
|
||||
class WebSerialConnection extends SerialConnection {
|
||||
|
||||
constructor(serialPort) {
|
||||
serialPort: any;
|
||||
reader: any;
|
||||
writable: any;
|
||||
|
||||
constructor(serialPort: any) {
|
||||
|
||||
super();
|
||||
|
||||
|
|
@ -23,7 +33,7 @@ class WebSerialConnection extends SerialConnection {
|
|||
|
||||
}
|
||||
|
||||
static async open() {
|
||||
static async open(): Promise<WebSerialConnection | null> {
|
||||
|
||||
// ensure browser supports web serial
|
||||
if(!navigator.serial){
|
||||
|
|
@ -45,7 +55,7 @@ class WebSerialConnection extends SerialConnection {
|
|||
|
||||
}
|
||||
|
||||
async close() {
|
||||
async close(): Promise<void> {
|
||||
|
||||
// release reader lock
|
||||
try {
|
||||
|
|
@ -63,7 +73,7 @@ class WebSerialConnection extends SerialConnection {
|
|||
|
||||
}
|
||||
|
||||
/* override */ async write(bytes) {
|
||||
/* override */ async write(bytes: Uint8Array): Promise<void> {
|
||||
const writer = this.writable.getWriter();
|
||||
try {
|
||||
await writer.write(new Uint8Array(bytes));
|
||||
|
|
@ -72,7 +82,7 @@ class WebSerialConnection extends SerialConnection {
|
|||
}
|
||||
}
|
||||
|
||||
async readLoop() {
|
||||
async readLoop(): Promise<void> {
|
||||
try {
|
||||
while(true){
|
||||
|
||||
|
|
@ -1,19 +1,19 @@
|
|||
class Constants {
|
||||
|
||||
static SupportedCompanionProtocolVersion = 1;
|
||||
static readonly SupportedCompanionProtocolVersion = 1 as const;
|
||||
|
||||
static SerialFrameTypes = {
|
||||
static readonly SerialFrameTypes = {
|
||||
Incoming: 0x3e, // ">"
|
||||
Outgoing: 0x3c, // "<"
|
||||
}
|
||||
} as const;
|
||||
|
||||
static Ble = {
|
||||
static readonly Ble = {
|
||||
ServiceUuid: "6E400001-B5A3-F393-E0A9-E50E24DCCA9E",
|
||||
CharacteristicUuidRx: "6E400002-B5A3-F393-E0A9-E50E24DCCA9E",
|
||||
CharacteristicUuidTx: "6E400003-B5A3-F393-E0A9-E50E24DCCA9E",
|
||||
}
|
||||
} as const;
|
||||
|
||||
static CommandCodes = {
|
||||
static readonly CommandCodes = {
|
||||
AppStart: 1,
|
||||
SendTxtMsg: 2,
|
||||
SendChannelTxtMsg: 3,
|
||||
|
|
@ -52,9 +52,9 @@ class Constants {
|
|||
SendTelemetryReq: 39,
|
||||
|
||||
SendBinaryReq: 50,
|
||||
}
|
||||
} as const;
|
||||
|
||||
static ResponseCodes = {
|
||||
static readonly ResponseCodes = {
|
||||
Ok: 0, // todo
|
||||
Err: 1, // todo
|
||||
ContactsStart: 2,
|
||||
|
|
@ -74,9 +74,9 @@ class Constants {
|
|||
ChannelInfo: 18,
|
||||
SignStart: 19,
|
||||
Signature: 20,
|
||||
}
|
||||
} as const;
|
||||
|
||||
static PushCodes = {
|
||||
static readonly PushCodes = {
|
||||
Advert: 0x80, // when companion is set to auto add contacts
|
||||
PathUpdated: 0x81,
|
||||
SendConfirmed: 0x82,
|
||||
|
|
@ -90,41 +90,41 @@ class Constants {
|
|||
NewAdvert: 0x8A, // when companion is set to manually add contacts
|
||||
TelemetryResponse: 0x8B,
|
||||
BinaryResponse: 0x8C,
|
||||
}
|
||||
} as const;
|
||||
|
||||
static ErrorCodes = {
|
||||
static readonly ErrorCodes = {
|
||||
UnsupportedCmd: 1,
|
||||
NotFound: 2,
|
||||
TableFull: 3,
|
||||
BadState: 4,
|
||||
FileIoError: 5,
|
||||
IllegalArg: 6,
|
||||
}
|
||||
} as const;
|
||||
|
||||
static AdvType = {
|
||||
static readonly AdvType = {
|
||||
None: 0,
|
||||
Chat: 1,
|
||||
Repeater: 2,
|
||||
Room: 3,
|
||||
}
|
||||
} as const;
|
||||
|
||||
static SelfAdvertTypes = {
|
||||
static readonly SelfAdvertTypes = {
|
||||
ZeroHop: 0,
|
||||
Flood: 1,
|
||||
}
|
||||
} as const;
|
||||
|
||||
static TxtTypes = {
|
||||
static readonly TxtTypes = {
|
||||
Plain: 0,
|
||||
CliData: 1,
|
||||
SignedPlain: 2,
|
||||
}
|
||||
} as const;
|
||||
|
||||
static BinaryRequestTypes = {
|
||||
static readonly BinaryRequestTypes = {
|
||||
GetTelemetryData: 0x03, // #define REQ_TYPE_GET_TELEMETRY_DATA 0x03
|
||||
GetAvgMinMax: 0x04, // #define REQ_TYPE_GET_AVG_MIN_MAX 0x04
|
||||
GetAccessList: 0x05, // #define REQ_TYPE_GET_ACCESS_LIST 0x05
|
||||
GetNeighbours: 0x06, // #define REQ_TYPE_GET_NEIGHBOURS 0x06
|
||||
}
|
||||
} as const;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
class EventEmitter {
|
||||
|
||||
eventListenersMap: Map<string | number, ((...args: any[]) => void)[]>;
|
||||
|
||||
constructor() {
|
||||
this.eventListenersMap = new Map();
|
||||
}
|
||||
|
||||
on(event, callback) {
|
||||
on(event: string | number, callback: (...args: any[]) => void): void {
|
||||
|
||||
// create list of listeners for event if it doesn't exist
|
||||
if(!this.eventListenersMap.has(event)){
|
||||
|
|
@ -12,24 +14,24 @@ class EventEmitter {
|
|||
}
|
||||
|
||||
// add listener for event
|
||||
this.eventListenersMap.get(event).push(callback);
|
||||
this.eventListenersMap.get(event)!.push(callback);
|
||||
|
||||
}
|
||||
|
||||
off(event, callback) {
|
||||
off(event: string | number, callback: (...args: any[]) => void): void {
|
||||
|
||||
// remove callback from listeners for this event
|
||||
if(this.eventListenersMap.has(event)){
|
||||
const callbacks = this.eventListenersMap.get(event).filter(cb => cb !== callback);
|
||||
const callbacks = this.eventListenersMap.get(event)!.filter(cb => cb !== callback);
|
||||
this.eventListenersMap.set(event, callbacks);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
once(event, callback) {
|
||||
once(event: string | number, callback: (...args: any[]) => void): void {
|
||||
|
||||
// internal callback to handle the event
|
||||
const internalCallback = (...data) => {
|
||||
const internalCallback = (...data: any[]) => {
|
||||
|
||||
// we received an event, so lets remove the event listener
|
||||
this.off(event, internalCallback);
|
||||
|
|
@ -44,11 +46,11 @@ class EventEmitter {
|
|||
|
||||
}
|
||||
|
||||
emit(event, ...data) {
|
||||
emit(event: string | number, ...data: any[]): void {
|
||||
|
||||
// invoke each listener for this event
|
||||
if(this.eventListenersMap.has(event)){
|
||||
for(const eventListener of this.eventListenersMap.get(event)){
|
||||
for(const eventListener of this.eventListenersMap.get(event)!){
|
||||
setTimeout(() => eventListener(...data), 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,8 @@ import Constants from "./constants.js";
|
|||
import Advert from "./advert.js";
|
||||
import Packet from "./packet.js";
|
||||
import BufferUtils from "./buffer_utils.js";
|
||||
import BufferReader from "./buffer_reader.js";
|
||||
import BufferWriter from "./buffer_writer.js";
|
||||
import CayenneLpp from "./cayenne_lpp.js";
|
||||
|
||||
export {
|
||||
|
|
@ -21,5 +23,9 @@ export {
|
|||
Advert,
|
||||
Packet,
|
||||
BufferUtils,
|
||||
BufferReader,
|
||||
BufferWriter,
|
||||
CayenneLpp,
|
||||
};
|
||||
|
||||
export type * from "./types.js";
|
||||
|
|
@ -27,8 +27,19 @@ class Packet {
|
|||
static PAYLOAD_TYPE_TRACE = 0x09; // trace a path, collecting SNR for each hop
|
||||
static PAYLOAD_TYPE_RAW_CUSTOM = 0x0F; // custom packet as raw bytes, for applications with custom encryption, payloads, etc
|
||||
|
||||
constructor(header, path, payload, transportCode1, transportCode2) {
|
||||
header: number;
|
||||
path: Uint8Array;
|
||||
payload: Uint8Array;
|
||||
transportCode1: number | null;
|
||||
transportCode2: number | null;
|
||||
route_type: number;
|
||||
route_type_string: string | null;
|
||||
payload_type: number;
|
||||
payload_type_string: string | null;
|
||||
payload_version: number;
|
||||
is_marked_do_not_retransmit: boolean;
|
||||
|
||||
constructor(header: number, path: Uint8Array, payload: Uint8Array, transportCode1: number | null, transportCode2: number | null) {
|
||||
|
||||
this.header = header;
|
||||
this.path = path;
|
||||
|
|
@ -46,7 +57,7 @@ class Packet {
|
|||
|
||||
}
|
||||
|
||||
static fromBytes(bytes) {
|
||||
static fromBytes(bytes: Uint8Array): Packet {
|
||||
|
||||
const bufferReader = new BufferReader(bytes);
|
||||
const header = bufferReader.readByte();
|
||||
|
|
@ -56,8 +67,8 @@ class Packet {
|
|||
const hasTransportCodes = routeType === Packet.ROUTE_TYPE_TRANSPORT_FLOOD || routeType === Packet.ROUTE_TYPE_TRANSPORT_DIRECT;
|
||||
|
||||
// parse transport codes
|
||||
var transportCode1 = null;
|
||||
var transportCode2 = null;
|
||||
var transportCode1: number | null = null;
|
||||
var transportCode2: number | null = null;
|
||||
if(hasTransportCodes){
|
||||
transportCode1 = bufferReader.readUInt16LE();
|
||||
transportCode2 = bufferReader.readUInt16LE();
|
||||
|
|
@ -71,11 +82,11 @@ class Packet {
|
|||
|
||||
}
|
||||
|
||||
getRouteType() {
|
||||
getRouteType(): number {
|
||||
return this.header & Packet.PH_ROUTE_MASK;
|
||||
}
|
||||
|
||||
getRouteTypeString() {
|
||||
getRouteTypeString(): string | null {
|
||||
switch(this.getRouteType()){
|
||||
case Packet.ROUTE_TYPE_FLOOD: return "FLOOD";
|
||||
case Packet.ROUTE_TYPE_DIRECT: return "DIRECT";
|
||||
|
|
@ -85,19 +96,19 @@ class Packet {
|
|||
}
|
||||
}
|
||||
|
||||
isRouteFlood() {
|
||||
isRouteFlood(): boolean {
|
||||
return this.getRouteType() === Packet.ROUTE_TYPE_FLOOD;
|
||||
}
|
||||
|
||||
isRouteDirect() {
|
||||
isRouteDirect(): boolean {
|
||||
return this.getRouteType() === Packet.ROUTE_TYPE_DIRECT;
|
||||
}
|
||||
|
||||
getPayloadType() {
|
||||
getPayloadType(): number {
|
||||
return (this.header >> Packet.PH_TYPE_SHIFT) & Packet.PH_TYPE_MASK;
|
||||
}
|
||||
|
||||
getPayloadTypeString() {
|
||||
getPayloadTypeString(): string | null {
|
||||
switch(this.getPayloadType()){
|
||||
case Packet.PAYLOAD_TYPE_REQ: return "REQ";
|
||||
case Packet.PAYLOAD_TYPE_RESPONSE: return "RESPONSE";
|
||||
|
|
@ -114,19 +125,19 @@ class Packet {
|
|||
}
|
||||
}
|
||||
|
||||
getPayloadVer() {
|
||||
getPayloadVer(): number {
|
||||
return (this.header >> Packet.PH_VER_SHIFT) & Packet.PH_VER_MASK;
|
||||
}
|
||||
|
||||
markDoNotRetransmit() {
|
||||
markDoNotRetransmit(): void {
|
||||
this.header = 0xFF;
|
||||
}
|
||||
|
||||
isMarkedDoNotRetransmit() {
|
||||
isMarkedDoNotRetransmit(): boolean {
|
||||
return this.header === 0xFF;
|
||||
}
|
||||
|
||||
parsePayload() {
|
||||
parsePayload(): any {
|
||||
switch(this.getPayloadType()){
|
||||
case Packet.PAYLOAD_TYPE_PATH: return this.parsePayloadTypePath();
|
||||
case Packet.PAYLOAD_TYPE_REQ: return this.parsePayloadTypeReq();
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
class RandomUtils {
|
||||
|
||||
static getRandomInt(min, max) {
|
||||
static getRandomInt(min: number, max: number): number {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
263
src/types.ts
Normal file
263
src/types.ts
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
/** Information about the connected MeshCore device/node. */
|
||||
export interface SelfInfo {
|
||||
type: number;
|
||||
txPower: number;
|
||||
maxTxPower: number;
|
||||
publicKey: Uint8Array;
|
||||
advLat: number;
|
||||
advLon: number;
|
||||
reserved: Uint8Array;
|
||||
manualAddContacts: number;
|
||||
radioFreq: number;
|
||||
radioBw: number;
|
||||
radioSf: number;
|
||||
radioCr: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/** A contact stored on the device. */
|
||||
export interface Contact {
|
||||
publicKey: Uint8Array;
|
||||
type: number;
|
||||
flags: number;
|
||||
outPathLen: number;
|
||||
outPath: Uint8Array;
|
||||
advName: string;
|
||||
lastAdvert: number;
|
||||
advLat: number;
|
||||
advLon: number;
|
||||
lastMod: number;
|
||||
}
|
||||
|
||||
/** A channel stored on the device. */
|
||||
export interface ChannelInfo {
|
||||
channelIdx: number;
|
||||
name: string;
|
||||
secret: Uint8Array;
|
||||
}
|
||||
|
||||
/** Device info returned from a device query. */
|
||||
export interface DeviceInfo {
|
||||
firmwareVer: number;
|
||||
reserved: Uint8Array;
|
||||
firmware_build_date: string;
|
||||
manufacturerModel: string;
|
||||
}
|
||||
|
||||
/** Battery voltage response. */
|
||||
export interface BatteryVoltage {
|
||||
batteryMilliVolts: number;
|
||||
}
|
||||
|
||||
/** Current time response. */
|
||||
export interface CurrTime {
|
||||
epochSecs: number;
|
||||
}
|
||||
|
||||
/** Sent response from the device. */
|
||||
export interface SentResponse {
|
||||
result: number;
|
||||
expectedAckCrc: number;
|
||||
estTimeout: number;
|
||||
}
|
||||
|
||||
/** A contact message received from the device. */
|
||||
export interface ContactMessage {
|
||||
pubKeyPrefix: Uint8Array;
|
||||
pathLen: number;
|
||||
txtType: number;
|
||||
senderTimestamp: number;
|
||||
text: string;
|
||||
}
|
||||
|
||||
/** A channel message received from the device. */
|
||||
export interface ChannelMessage {
|
||||
channelIdx: number;
|
||||
pathLen: number;
|
||||
txtType: number;
|
||||
senderTimestamp: number;
|
||||
text: string;
|
||||
}
|
||||
|
||||
/** Exported contact response. */
|
||||
export interface ExportContactResponse {
|
||||
advertPacketBytes: Uint8Array;
|
||||
}
|
||||
|
||||
/** Private key response. */
|
||||
export interface PrivateKeyResponse {
|
||||
privateKey: Uint8Array;
|
||||
}
|
||||
|
||||
/** Sign start response. */
|
||||
export interface SignStartResponse {
|
||||
reserved: number;
|
||||
maxSignDataLen: number;
|
||||
}
|
||||
|
||||
/** Signature response. */
|
||||
export interface SignatureResponse {
|
||||
signature: Uint8Array;
|
||||
}
|
||||
|
||||
/** Error response. */
|
||||
export interface ErrResponse {
|
||||
errCode: number | null;
|
||||
}
|
||||
|
||||
/** Contacts start response. */
|
||||
export interface ContactsStartResponse {
|
||||
count: number;
|
||||
}
|
||||
|
||||
/** End of contacts response. */
|
||||
export interface EndOfContactsResponse {
|
||||
mostRecentLastmod: number;
|
||||
}
|
||||
|
||||
/** Advert push data. */
|
||||
export interface AdvertPush {
|
||||
publicKey: Uint8Array;
|
||||
}
|
||||
|
||||
/** Path updated push data. */
|
||||
export interface PathUpdatedPush {
|
||||
publicKey: Uint8Array;
|
||||
}
|
||||
|
||||
/** Send confirmed push data. */
|
||||
export interface SendConfirmedPush {
|
||||
ackCode: number;
|
||||
roundTrip: number;
|
||||
}
|
||||
|
||||
/** Raw data push. */
|
||||
export interface RawDataPush {
|
||||
lastSnr: number;
|
||||
lastRssi: number;
|
||||
reserved: number;
|
||||
payload: Uint8Array;
|
||||
}
|
||||
|
||||
/** Login success push. */
|
||||
export interface LoginSuccessPush {
|
||||
reserved: number;
|
||||
pubKeyPrefix: Uint8Array;
|
||||
}
|
||||
|
||||
/** Status response push. */
|
||||
export interface StatusResponsePush {
|
||||
reserved: number;
|
||||
pubKeyPrefix: Uint8Array;
|
||||
statusData: Uint8Array;
|
||||
}
|
||||
|
||||
/** Log RX data push. */
|
||||
export interface LogRxDataPush {
|
||||
lastSnr: number;
|
||||
lastRssi: number;
|
||||
raw: Uint8Array;
|
||||
}
|
||||
|
||||
/** Telemetry response push. */
|
||||
export interface TelemetryResponsePush {
|
||||
reserved: number;
|
||||
pubKeyPrefix: Uint8Array;
|
||||
lppSensorData: Uint8Array;
|
||||
}
|
||||
|
||||
/** Binary response push. */
|
||||
export interface BinaryResponsePush {
|
||||
reserved: number;
|
||||
tag: number;
|
||||
responseData: Uint8Array;
|
||||
}
|
||||
|
||||
/** Trace data push. */
|
||||
export interface TraceDataPush {
|
||||
reserved: number;
|
||||
pathLen: number;
|
||||
flags: number;
|
||||
tag: number;
|
||||
authCode: number;
|
||||
pathHashes: Uint8Array;
|
||||
pathSnrs: Uint8Array;
|
||||
lastSnr: number;
|
||||
}
|
||||
|
||||
/** New advert push data. */
|
||||
export interface NewAdvertPush {
|
||||
publicKey: Uint8Array;
|
||||
type: number;
|
||||
flags: number;
|
||||
outPathLen: number;
|
||||
outPath: Uint8Array;
|
||||
advName: string;
|
||||
lastAdvert: number;
|
||||
advLat: number;
|
||||
advLon: number;
|
||||
lastMod: number;
|
||||
}
|
||||
|
||||
/** Repeater stats from status response. */
|
||||
export interface RepeaterStats {
|
||||
batt_milli_volts: number;
|
||||
curr_tx_queue_len: number;
|
||||
noise_floor: number;
|
||||
last_rssi: number;
|
||||
n_packets_recv: number;
|
||||
n_packets_sent: number;
|
||||
total_air_time_secs: number;
|
||||
total_up_time_secs: number;
|
||||
n_sent_flood: number;
|
||||
n_sent_direct: number;
|
||||
n_recv_flood: number;
|
||||
n_recv_direct: number;
|
||||
err_events: number;
|
||||
last_snr: number;
|
||||
n_direct_dups: number;
|
||||
n_flood_dups: number;
|
||||
}
|
||||
|
||||
/** Synced message result. */
|
||||
export interface SyncedMessage {
|
||||
contactMessage?: ContactMessage;
|
||||
channelMessage?: ChannelMessage;
|
||||
}
|
||||
|
||||
/** Ping result. */
|
||||
export interface PingResult {
|
||||
rtt: number;
|
||||
snr: number;
|
||||
rssi: number;
|
||||
}
|
||||
|
||||
/** Neighbour info. */
|
||||
export interface Neighbour {
|
||||
publicKeyPrefix: Uint8Array;
|
||||
heardSecondsAgo: number;
|
||||
snr: number;
|
||||
}
|
||||
|
||||
/** Get neighbours result. */
|
||||
export interface GetNeighboursResult {
|
||||
totalNeighboursCount: number;
|
||||
neighbours: Neighbour[];
|
||||
}
|
||||
|
||||
/** Parsed advert app data. */
|
||||
export interface ParsedAdvertAppData {
|
||||
type: string | null;
|
||||
lat: number | null;
|
||||
lon: number | null;
|
||||
name: string | null;
|
||||
feat1: number | null;
|
||||
feat2: number | null;
|
||||
}
|
||||
|
||||
/** CayenneLPP telemetry entry. */
|
||||
export interface TelemetryEntry {
|
||||
channel: number;
|
||||
type: number;
|
||||
value: number | { latitude: number; longitude: number; altitude: number };
|
||||
}
|
||||
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"lib": ["ES2022", "DOM"],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue