mirror of
https://github.com/meshcore-dev/meshcore.js.git
synced 2026-04-20 22:13:49 +00:00
Replace hand-written .d.ts with JSDoc + tsc auto-generation
- Delete hand-written index.d.ts that drifted from source - Add JSDoc type annotations to all source files - Create src/types.js with shared typedefs (SelfInfo, Contact, ContactMessage, ChannelInfo, RepeaterStats, etc.) - Add tsconfig.json for declaration generation - Add build:types script and prepublishOnly hook - Add GitHub Actions CI workflow for type checking - Use Uint8Array everywhere (no Buffer references) - Add semantic type aliases (EpochSeconds, Milliseconds, MilliVolts) - Add typed event overloads on Connection (on/once/off) - All 11 PR #15 review comments addressed
This commit is contained in:
parent
de31939a28
commit
2d4cb35e51
23 changed files with 988 additions and 212 deletions
17
.github/workflows/typecheck.yml
vendored
Normal file
17
.github/workflows/typecheck.yml
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
name: Type Check
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
typecheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
- run: npm ci
|
||||
- run: npm run build:types
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
|||
/.idea
|
||||
/node_modules
|
||||
/dist
|
||||
|
|
|
|||
185
index.d.ts
vendored
185
index.d.ts
vendored
|
|
@ -1,185 +0,0 @@
|
|||
/**
|
||||
* TypeScript declarations for @liamcottle/meshcore.js
|
||||
*/
|
||||
|
||||
declare module '@liamcottle/meshcore.js' {
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
export class Connection extends EventEmitter {
|
||||
connect(): Promise<void>;
|
||||
close(): Promise<void>;
|
||||
|
||||
// High-level API methods
|
||||
getSelfInfo(timeout?: number): Promise<SelfInfo>;
|
||||
getWaitingMessages(): Promise<Message[]>;
|
||||
getChannels(): Promise<Channel[]>;
|
||||
getContacts(since?: number): Promise<Contact[]>;
|
||||
syncNextMessage(): Promise<void>;
|
||||
|
||||
// Message sending
|
||||
sendTextMessage(publicKey: Buffer, text: string): Promise<void>;
|
||||
sendChannelTextMessage(channelIdx: number, text: string): Promise<void>;
|
||||
|
||||
// Contact lookup
|
||||
findContactByPublicKeyPrefix(prefix: Buffer): Promise<Contact | null>;
|
||||
|
||||
// Frame handling (can be overridden in subclasses)
|
||||
onFrameReceived(frame: Buffer): void;
|
||||
|
||||
// Events
|
||||
on(event: 'connected', listener: () => void): this;
|
||||
on(event: 'disconnected', listener: () => void): this;
|
||||
on(event: number, listener: (data: any) => void): this;
|
||||
on(event: string | number, listener: (...args: any[]) => void): this;
|
||||
|
||||
emit(event: string | number, ...args: any[]): boolean;
|
||||
}
|
||||
|
||||
export class NodeJSSerialConnection extends Connection {
|
||||
constructor(port: string);
|
||||
}
|
||||
|
||||
export class WebSerialConnection extends Connection {
|
||||
constructor();
|
||||
}
|
||||
|
||||
export class TCPConnection extends Connection {
|
||||
constructor(host: string, port: number);
|
||||
}
|
||||
|
||||
export class WebBleConnection extends Connection {
|
||||
constructor();
|
||||
}
|
||||
|
||||
export class SerialConnection extends Connection {
|
||||
constructor();
|
||||
}
|
||||
|
||||
// Type definitions
|
||||
export interface SelfInfo {
|
||||
publicKey: Buffer;
|
||||
name?: string;
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
pubKeyPrefix: Buffer;
|
||||
pathLen: number;
|
||||
txtType: number;
|
||||
senderTimestamp: number;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface Channel {
|
||||
channelIdx: number;
|
||||
name: string;
|
||||
secret: Buffer;
|
||||
}
|
||||
|
||||
export interface Contact {
|
||||
publicKey: Buffer;
|
||||
name?: string;
|
||||
lastSeen?: number;
|
||||
}
|
||||
|
||||
// Constants
|
||||
export class Constants {
|
||||
static readonly SupportedCompanionProtocolVersion: number;
|
||||
|
||||
static readonly ResponseCodes: {
|
||||
ContactMsgRecv: number;
|
||||
ChannelMsgRecv: number;
|
||||
[key: string]: number;
|
||||
};
|
||||
|
||||
static readonly PushCodes: {
|
||||
MsgWaiting: number;
|
||||
NewAdvert: number;
|
||||
[key: string]: number;
|
||||
};
|
||||
|
||||
static readonly CommandCodes: {
|
||||
AppStart: number;
|
||||
SendTxtMsg: number;
|
||||
SendChannelTxtMsg: number;
|
||||
GetContacts: number;
|
||||
GetDeviceTime: number;
|
||||
SetDeviceTime: number;
|
||||
SendSelfAdvert: number;
|
||||
SetAdvertName: number;
|
||||
[key: string]: number;
|
||||
};
|
||||
}
|
||||
|
||||
export class Advert {
|
||||
constructor(data: Buffer);
|
||||
publicKey: Buffer;
|
||||
advName?: string;
|
||||
}
|
||||
|
||||
export class Packet {
|
||||
constructor(data: Buffer);
|
||||
}
|
||||
|
||||
export class BufferUtils {
|
||||
static xor(a: Buffer, b: Buffer): Buffer;
|
||||
static concat(...buffers: Buffer[]): Buffer;
|
||||
}
|
||||
|
||||
export class CayenneLpp {
|
||||
constructor();
|
||||
}
|
||||
}
|
||||
|
||||
// Type declarations for submodules
|
||||
declare module '@liamcottle/meshcore.js/src/constants.js' {
|
||||
const Constants: {
|
||||
SupportedCompanionProtocolVersion: number;
|
||||
ResponseCodes: {
|
||||
ContactMsgRecv: number;
|
||||
ChannelMsgRecv: number;
|
||||
[key: string]: number;
|
||||
};
|
||||
PushCodes: {
|
||||
MsgWaiting: number;
|
||||
NewAdvert: number;
|
||||
[key: string]: number;
|
||||
};
|
||||
CommandCodes: {
|
||||
AppStart: number;
|
||||
SendTxtMsg: number;
|
||||
SendChannelTxtMsg: number;
|
||||
GetContacts: number;
|
||||
GetDeviceTime: number;
|
||||
SetDeviceTime: number;
|
||||
SendSelfAdvert: number;
|
||||
SetAdvertName: number;
|
||||
[key: string]: number;
|
||||
};
|
||||
};
|
||||
export default Constants;
|
||||
}
|
||||
|
||||
declare module '@liamcottle/meshcore.js/src/buffer_reader.js' {
|
||||
export default class BufferReader {
|
||||
constructor(buffer: Buffer);
|
||||
readByte(): number;
|
||||
readInt8(): number;
|
||||
readUInt16LE(): number;
|
||||
readUInt32LE(): number;
|
||||
readBytes(length: number): Buffer;
|
||||
readString(): string;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@liamcottle/meshcore.js/src/buffer_writer.js' {
|
||||
export default class BufferWriter {
|
||||
constructor();
|
||||
writeByte(value: number): void;
|
||||
writeInt8(value: number): void;
|
||||
writeUInt16LE(value: number): void;
|
||||
writeUInt32LE(value: number): void;
|
||||
writeBytes(buffer: Buffer | Uint8Array): void;
|
||||
writeString(str: string): void;
|
||||
toBytes(): Buffer;
|
||||
}
|
||||
}
|
||||
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": "^25.2.3",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/curves": {
|
||||
|
|
@ -227,6 +231,16 @@
|
|||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz",
|
||||
"integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.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": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@
|
|||
"version": "1.11.0",
|
||||
"description": "",
|
||||
"main": "src/index.js",
|
||||
"types": "index.d.ts",
|
||||
"types": "dist/index.d.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build:types": "tsc",
|
||||
"prepublishOnly": "npm run build:types",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Liam Cottle <liam@liamcottle.com>",
|
||||
|
|
@ -13,5 +15,9 @@
|
|||
"dependencies": {
|
||||
"@noble/curves": "^1.8.1",
|
||||
"serialport": "^13.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^25.2.3",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import BufferReader from "./buffer_reader.js";
|
||||
import BufferWriter from "./buffer_writer.js";
|
||||
|
||||
/** @typedef {import("./types.js").AdvertParsedData} AdvertParsedData */
|
||||
|
||||
class Advert {
|
||||
|
||||
static ADV_TYPE_NONE = 0;
|
||||
|
|
@ -13,14 +15,25 @@ class Advert {
|
|||
static ADV_FEAT2_MASK = 0x40;
|
||||
static ADV_NAME_MASK = 0x80;
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} publicKey
|
||||
* @param {number} timestamp
|
||||
* @param {Uint8Array} signature
|
||||
* @param {Uint8Array} appData
|
||||
*/
|
||||
constructor(publicKey, timestamp, signature, appData) {
|
||||
this.publicKey = publicKey;
|
||||
this.timestamp = timestamp;
|
||||
this.signature = signature;
|
||||
this.appData = appData;
|
||||
/** @type {AdvertParsedData} */
|
||||
this.parsed = this.parseAppData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} bytes
|
||||
* @returns {Advert}
|
||||
*/
|
||||
static fromBytes(bytes) {
|
||||
|
||||
// read bytes
|
||||
|
|
@ -34,15 +47,18 @@ class Advert {
|
|||
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
getFlags() {
|
||||
return this.appData[0];
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
getType() {
|
||||
const flags = this.getFlags();
|
||||
return flags & 0x0F;
|
||||
}
|
||||
|
||||
/** @returns {string | null} */
|
||||
getTypeString() {
|
||||
const type = this.getType();
|
||||
if(type === Advert.ADV_TYPE_NONE) return "NONE";
|
||||
|
|
@ -52,6 +68,7 @@ class Advert {
|
|||
return null;
|
||||
}
|
||||
|
||||
/** @returns {Promise<boolean>} */
|
||||
async isVerified() {
|
||||
|
||||
const { ed25519 } = await import("@noble/curves/ed25519");
|
||||
|
|
@ -67,6 +84,7 @@ class Advert {
|
|||
|
||||
}
|
||||
|
||||
/** @returns {AdvertParsedData} */
|
||||
parseAppData() {
|
||||
|
||||
// read app data
|
||||
|
|
|
|||
|
|
@ -1,32 +1,49 @@
|
|||
class BufferReader {
|
||||
|
||||
/**
|
||||
* @param {Uint8Array | ArrayLike<number>} data
|
||||
*/
|
||||
constructor(data) {
|
||||
/** @type {number} */
|
||||
this.pointer = 0;
|
||||
/** @type {Uint8Array} */
|
||||
this.buffer = new Uint8Array(data);
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
getRemainingBytesCount() {
|
||||
return this.buffer.length - this.pointer;
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
readByte() {
|
||||
return this.readBytes(1)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} count
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
readBytes(count) {
|
||||
const data = this.buffer.slice(this.pointer, this.pointer + count);
|
||||
this.pointer += count;
|
||||
return data;
|
||||
}
|
||||
|
||||
/** @returns {Uint8Array} */
|
||||
readRemainingBytes() {
|
||||
return this.readBytes(this.getRemainingBytesCount());
|
||||
}
|
||||
|
||||
/** @returns {string} */
|
||||
readString() {
|
||||
return new TextDecoder().decode(this.readRemainingBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} maxLength
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
readCString(maxLength) {
|
||||
const value = [];
|
||||
const bytes = this.readBytes(maxLength);
|
||||
|
|
@ -42,60 +59,70 @@ class BufferReader {
|
|||
}
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
readInt8() {
|
||||
const bytes = this.readBytes(1);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt8(0);
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
readUInt8() {
|
||||
const bytes = this.readBytes(1);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint8(0);
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
readUInt16LE() {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint16(0, true);
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
readUInt16BE() {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint16(0, false);
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
readUInt32LE() {
|
||||
const bytes = this.readBytes(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint32(0, true);
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
readUInt32BE() {
|
||||
const bytes = this.readBytes(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getUint32(0, false);
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
readInt16LE() {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt16(0, true);
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
readInt16BE() {
|
||||
const bytes = this.readBytes(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt16(0, false);
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
readInt32LE() {
|
||||
const bytes = this.readBytes(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
return view.getInt32(0, true);
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
readInt24BE() {
|
||||
|
||||
// read 24-bit (3 bytes) big endian integer
|
||||
|
|
|
|||
|
|
@ -1,21 +1,38 @@
|
|||
class BufferUtils {
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} uint8Array
|
||||
* @returns {string}
|
||||
*/
|
||||
static bytesToHex(uint8Array) {
|
||||
return Array.from(uint8Array).map(byte => {
|
||||
return byte.toString(16).padStart(2, '0');
|
||||
}).join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} hex
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
static hexToBytes(hex) {
|
||||
return Uint8Array.from(hex.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} base64
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
static base64ToBytes(base64) {
|
||||
return Uint8Array.from(atob(base64), (c) => {
|
||||
return c.charCodeAt(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} byteArray1
|
||||
* @param {Uint8Array} byteArray2
|
||||
* @returns {boolean}
|
||||
*/
|
||||
static areBuffersEqual(byteArray1, byteArray2) {
|
||||
|
||||
// ensure length is the same
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
class BufferWriter {
|
||||
|
||||
constructor() {
|
||||
/** @type {number[]} */
|
||||
this.buffer = [];
|
||||
}
|
||||
|
||||
/** @returns {Uint8Array} */
|
||||
toBytes() {
|
||||
return new Uint8Array(this.buffer);
|
||||
}
|
||||
|
||||
/** @param {Uint8Array | number[]} bytes */
|
||||
writeBytes(bytes) {
|
||||
this.buffer = [
|
||||
...this.buffer,
|
||||
|
|
@ -15,12 +18,14 @@ class BufferWriter {
|
|||
];
|
||||
}
|
||||
|
||||
/** @param {number} byte */
|
||||
writeByte(byte) {
|
||||
this.writeBytes([
|
||||
byte,
|
||||
]);
|
||||
}
|
||||
|
||||
/** @param {number} num */
|
||||
writeUInt16LE(num) {
|
||||
const bytes = new Uint8Array(2);
|
||||
const view = new DataView(bytes.buffer);
|
||||
|
|
@ -28,6 +33,7 @@ class BufferWriter {
|
|||
this.writeBytes(bytes);
|
||||
}
|
||||
|
||||
/** @param {number} num */
|
||||
writeUInt32LE(num) {
|
||||
const bytes = new Uint8Array(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
|
|
@ -35,6 +41,7 @@ class BufferWriter {
|
|||
this.writeBytes(bytes);
|
||||
}
|
||||
|
||||
/** @param {number} num */
|
||||
writeInt32LE(num) {
|
||||
const bytes = new Uint8Array(4);
|
||||
const view = new DataView(bytes.buffer);
|
||||
|
|
@ -42,10 +49,15 @@ class BufferWriter {
|
|||
this.writeBytes(bytes);
|
||||
}
|
||||
|
||||
/** @param {string} string */
|
||||
writeString(string) {
|
||||
this.writeBytes(new TextEncoder().encode(string));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} string
|
||||
* @param {number} maxLength
|
||||
*/
|
||||
writeCString(string, maxLength) {
|
||||
|
||||
// create buffer of max length
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import BufferReader from "./buffer_reader.js";
|
||||
|
||||
/** @typedef {import("./types.js").CayenneTelemetryEntry} CayenneTelemetryEntry */
|
||||
|
||||
class CayenneLpp {
|
||||
|
||||
static LPP_DIGITAL_INPUT = 0; // 1 byte
|
||||
|
|
@ -30,9 +32,14 @@ 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
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} bytes
|
||||
* @returns {CayenneTelemetryEntry[]}
|
||||
*/
|
||||
static parse(bytes) {
|
||||
|
||||
const buffer = new BufferReader(bytes);
|
||||
/** @type {CayenneTelemetryEntry[]} */
|
||||
const telemetry = [];
|
||||
|
||||
while(buffer.getRemainingBytesCount() >= 2){ // need at least 2 more bytes to get channel and type
|
||||
|
|
@ -48,7 +55,6 @@ class CayenneLpp {
|
|||
switch(type){
|
||||
case this.LPP_GENERIC_SENSOR: {
|
||||
const value = buffer.readUInt32BE();
|
||||
// console.log(`[CayenneLpp] parsed LPP_GENERIC_SENSOR=${value}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
|
|
@ -58,7 +64,6 @@ class CayenneLpp {
|
|||
}
|
||||
case this.LPP_LUMINOSITY: {
|
||||
const lux = buffer.readInt16BE();
|
||||
// console.log(`[CayenneLpp] parsed LPP_LUMINOSITY=${lux}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
|
|
@ -68,7 +73,6 @@ class CayenneLpp {
|
|||
}
|
||||
case this.LPP_PRESENCE: {
|
||||
const presence = buffer.readUInt8();
|
||||
// console.log(`[CayenneLpp] parsed LPP_PRESENCE=${presence}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
|
|
@ -78,7 +82,6 @@ class CayenneLpp {
|
|||
}
|
||||
case this.LPP_TEMPERATURE: {
|
||||
const temperature = buffer.readInt16BE() / 10;
|
||||
// console.log(`[CayenneLpp] parsed LPP_TEMPERATURE=${temperature}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
|
|
@ -88,7 +91,6 @@ class CayenneLpp {
|
|||
}
|
||||
case this.LPP_RELATIVE_HUMIDITY: {
|
||||
const relativeHumidity = buffer.readUInt8() / 2;
|
||||
// console.log(`[CayenneLpp] parsed LPP_RELATIVE_HUMIDITY=${relativeHumidity}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
|
|
@ -98,7 +100,6 @@ class CayenneLpp {
|
|||
}
|
||||
case this.LPP_BAROMETRIC_PRESSURE: {
|
||||
const barometricPressure = buffer.readUInt16BE() / 10;
|
||||
// console.log(`[CayenneLpp] parsed LPP_BAROMETRIC_PRESSURE=${barometricPressure}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
|
|
@ -111,7 +112,6 @@ class CayenneLpp {
|
|||
// int16: -327.67v to +327.67v
|
||||
// should be readUInt16BE, but I'm using readInt16BE to allow for negative voltage
|
||||
const voltage = buffer.readInt16BE() / 100;
|
||||
// console.log(`[CayenneLpp] parsed LPP_VOLTAGE=${voltage}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
|
|
@ -124,7 +124,6 @@ class CayenneLpp {
|
|||
// int16: -327.67A to +327.67A
|
||||
// should be readUInt16BE, but I'm using readInt16BE to allow for negative current
|
||||
const current = buffer.readInt16BE() / 1000;
|
||||
// console.log(`[CayenneLpp] parsed LPP_CURRENT=${current}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
|
|
@ -134,7 +133,6 @@ class CayenneLpp {
|
|||
}
|
||||
case this.LPP_PERCENTAGE: {
|
||||
const percentage = buffer.readUInt8();
|
||||
// console.log(`[CayenneLpp] parsed LPP_PERCENTAGE=${percentage}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
|
|
@ -144,7 +142,6 @@ class CayenneLpp {
|
|||
}
|
||||
case this.LPP_CONCENTRATION: {
|
||||
const concentration = buffer.readUInt16BE();
|
||||
// console.log(`[CayenneLpp] parsed LPP_CONCENTRATION=${concentration}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
|
|
@ -154,7 +151,6 @@ class CayenneLpp {
|
|||
}
|
||||
case this.LPP_POWER: {
|
||||
const power = buffer.readUInt16BE();
|
||||
// console.log(`[CayenneLpp] parsed LPP_POWER=${power}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
|
|
@ -166,7 +162,6 @@ class CayenneLpp {
|
|||
const latitude = buffer.readInt24BE() / 10000;
|
||||
const longitude = buffer.readInt24BE() / 10000;
|
||||
const altitude = buffer.readInt24BE() / 100;
|
||||
// console.log(`[CayenneLpp] parsed LPP_GPS=${latitude},${longitude},${altitude}`);
|
||||
telemetry.push({
|
||||
"channel": channel,
|
||||
"type": type,
|
||||
|
|
@ -180,7 +175,6 @@ class CayenneLpp {
|
|||
}
|
||||
// todo support all telemetry types, otherwise if an unknown is given, we can't read other telemetry after it
|
||||
default: {
|
||||
// console.log(`[CayenneLpp] unsupported type: ${type}`);
|
||||
return telemetry;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -3,13 +3,15 @@ import SerialConnection from "./serial_connection.js";
|
|||
class NodeJSSerialConnection extends SerialConnection {
|
||||
|
||||
/**
|
||||
* @param path serial port to connect to, e.g: "/dev/ttyACM0" or "/dev/cu.usbmodem14401"
|
||||
* @param {string} path serial port to connect to, e.g: "/dev/ttyACM0" or "/dev/cu.usbmodem14401"
|
||||
*/
|
||||
constructor(path) {
|
||||
super();
|
||||
/** @type {string} */
|
||||
this.serialPortPath = path;
|
||||
}
|
||||
|
||||
/** @returns {Promise<void>} */
|
||||
async connect() {
|
||||
|
||||
// note: serialport module is only available in NodeJS, you shouldn't use NodeJSSerialConnection from a web browser
|
||||
|
|
@ -43,6 +45,7 @@ class NodeJSSerialConnection extends SerialConnection {
|
|||
|
||||
}
|
||||
|
||||
/** @returns {Promise<void>} */
|
||||
async close() {
|
||||
try {
|
||||
await this.serialPort.close();
|
||||
|
|
@ -51,6 +54,10 @@ class NodeJSSerialConnection extends SerialConnection {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} bytes
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
/* override */ async write(bytes) {
|
||||
this.serialPort.write(bytes);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,16 +7,26 @@ class SerialConnection extends Connection {
|
|||
|
||||
constructor() {
|
||||
super();
|
||||
/** @type {number[]} */
|
||||
this.readBuffer = [];
|
||||
if(this.constructor === SerialConnection){
|
||||
throw new Error("SerialConnection is an abstract class and can't be instantiated.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} bytes
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async write(bytes) {
|
||||
throw new Error("Not Implemented: write must be implemented by SerialConnection sub class.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} frameType
|
||||
* @param {Uint8Array} frameData
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async writeFrame(frameType, frameData) {
|
||||
|
||||
// create frame
|
||||
|
|
@ -34,12 +44,20 @@ class SerialConnection extends Connection {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} data
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async sendToRadioFrame(data) {
|
||||
// write "app to radio" frame 0x3c "<"
|
||||
this.emit("tx", data);
|
||||
await this.writeFrame(0x3c, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array | number[]} value
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async onDataReceived(value) {
|
||||
|
||||
// append received bytes to read buffer
|
||||
|
|
|
|||
|
|
@ -5,13 +5,19 @@ import Connection from "./connection.js";
|
|||
|
||||
class TCPConnection extends Connection {
|
||||
|
||||
/**
|
||||
* @param {string} host
|
||||
* @param {number} port
|
||||
*/
|
||||
constructor(host, port) {
|
||||
super();
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
/** @type {number[]} */
|
||||
this.readBuffer = [];
|
||||
}
|
||||
|
||||
/** @returns {Promise<void>} */
|
||||
async connect() {
|
||||
|
||||
// note: net module is only available in NodeJS, you shouldn't use TCPConnection from a web browser
|
||||
|
|
@ -21,7 +27,7 @@ class TCPConnection extends Connection {
|
|||
this.socket = new Socket();
|
||||
|
||||
// handle received data
|
||||
this.socket.on('data', (data) => {
|
||||
this.socket.on('data', (/** @type {Uint8Array} */ data) => {
|
||||
this.onSocketDataReceived(data);
|
||||
});
|
||||
|
||||
|
|
@ -42,6 +48,7 @@ class TCPConnection extends Connection {
|
|||
|
||||
}
|
||||
|
||||
/** @param {Uint8Array | number[]} data */
|
||||
onSocketDataReceived(data) {
|
||||
|
||||
// append received bytes to read buffer
|
||||
|
|
@ -96,7 +103,7 @@ class TCPConnection extends Connection {
|
|||
|
||||
}
|
||||
|
||||
close() {
|
||||
async close() {
|
||||
try {
|
||||
this.socket.destroy();
|
||||
} catch(e) {
|
||||
|
|
@ -104,10 +111,19 @@ class TCPConnection extends Connection {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} bytes
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async write(bytes) {
|
||||
this.socket.write(new Uint8Array(bytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} frameType
|
||||
* @param {Uint8Array} frameData
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async writeFrame(frameType, frameData) {
|
||||
|
||||
// create frame
|
||||
|
|
@ -125,6 +141,10 @@ class TCPConnection extends Connection {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} data
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async sendToRadioFrame(data) {
|
||||
// write "app to radio" frame 0x3c "<"
|
||||
this.emit("tx", data);
|
||||
|
|
|
|||
|
|
@ -3,24 +3,31 @@ import Connection from "./connection.js";
|
|||
|
||||
class WebBleConnection extends Connection {
|
||||
|
||||
/** @param {any} bleDevice */
|
||||
constructor(bleDevice) {
|
||||
super();
|
||||
this.bleDevice = bleDevice;
|
||||
/** @type {any} */
|
||||
this.gattServer = null;
|
||||
/** @type {any} */
|
||||
this.rxCharacteristic = null;
|
||||
/** @type {any} */
|
||||
this.txCharacteristic = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
/** @returns {Promise<WebBleConnection | null | undefined>} */
|
||||
static async open() {
|
||||
|
||||
// ensure browser supports web bluetooth
|
||||
// @ts-ignore - Web Bluetooth API
|
||||
if(!navigator.bluetooth){
|
||||
alert("Web Bluetooth is not supported in this browser");
|
||||
return;
|
||||
}
|
||||
|
||||
// ask user to select device
|
||||
// @ts-ignore - Web Bluetooth API
|
||||
const device = await navigator.bluetooth.requestDevice({
|
||||
filters: [
|
||||
{
|
||||
|
|
@ -76,6 +83,7 @@ class WebBleConnection extends Connection {
|
|||
|
||||
}
|
||||
|
||||
/** @returns {Promise<void>} */
|
||||
async close() {
|
||||
try {
|
||||
this.gattServer?.disconnect();
|
||||
|
|
@ -85,6 +93,10 @@ class WebBleConnection extends Connection {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} bytes
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async write(bytes) {
|
||||
try {
|
||||
// fixme: NetworkError: GATT operation already in progress.
|
||||
|
|
@ -96,6 +108,10 @@ class WebBleConnection extends Connection {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} frame
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async sendToRadioFrame(frame) {
|
||||
this.emit("tx", frame);
|
||||
await this.write(frame);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import SerialConnection from "./serial_connection.js";
|
|||
|
||||
class WebSerialConnection extends SerialConnection {
|
||||
|
||||
/** @param {any} serialPort */
|
||||
constructor(serialPort) {
|
||||
|
||||
super();
|
||||
|
|
@ -23,15 +24,18 @@ class WebSerialConnection extends SerialConnection {
|
|||
|
||||
}
|
||||
|
||||
/** @returns {Promise<WebSerialConnection | null>} */
|
||||
static async open() {
|
||||
|
||||
// ensure browser supports web serial
|
||||
// @ts-ignore - Web Serial API
|
||||
if(!navigator.serial){
|
||||
alert("Web Serial is not supported in this browser");
|
||||
return null;
|
||||
}
|
||||
|
||||
// ask user to select device
|
||||
// @ts-ignore - Web Serial API
|
||||
const serialPort = await navigator.serial.requestPort({
|
||||
filters: [],
|
||||
});
|
||||
|
|
@ -45,6 +49,7 @@ class WebSerialConnection extends SerialConnection {
|
|||
|
||||
}
|
||||
|
||||
/** @returns {Promise<void>} */
|
||||
async close() {
|
||||
|
||||
// release reader lock
|
||||
|
|
@ -63,6 +68,10 @@ class WebSerialConnection extends SerialConnection {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} bytes
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
/* override */ async write(bytes) {
|
||||
const writer = this.writable.getWriter();
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/** Constants used by the MeshCore companion protocol. */
|
||||
class Constants {
|
||||
|
||||
static SupportedCompanionProtocolVersion = 1;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
class EventEmitter {
|
||||
|
||||
constructor() {
|
||||
/** @type {Map<string | number, Function[]>} */
|
||||
this.eventListenersMap = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | number} event
|
||||
* @param {Function} callback
|
||||
*/
|
||||
on(event, callback) {
|
||||
|
||||
// create list of listeners for event if it doesn't exist
|
||||
|
|
@ -16,6 +21,10 @@ class EventEmitter {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | number} event
|
||||
* @param {Function} callback
|
||||
*/
|
||||
off(event, callback) {
|
||||
|
||||
// remove callback from listeners for this event
|
||||
|
|
@ -26,6 +35,10 @@ class EventEmitter {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | number} event
|
||||
* @param {Function} callback
|
||||
*/
|
||||
once(event, callback) {
|
||||
|
||||
// internal callback to handle the event
|
||||
|
|
@ -44,6 +57,10 @@ class EventEmitter {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string | number} event
|
||||
* @param {...any} data
|
||||
*/
|
||||
emit(event, ...data) {
|
||||
|
||||
// invoke each listener for this event
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import Packet from "./packet.js";
|
|||
import BufferUtils from "./buffer_utils.js";
|
||||
import CayenneLpp from "./cayenne_lpp.js";
|
||||
|
||||
export * from "./types.js";
|
||||
|
||||
export {
|
||||
Connection,
|
||||
WebBleConnection,
|
||||
|
|
|
|||
|
|
@ -27,9 +27,15 @@ 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
|
||||
|
||||
/**
|
||||
* @param {number} header
|
||||
* @param {Uint8Array} path
|
||||
* @param {Uint8Array} payload
|
||||
* @param {number | null} transportCode1
|
||||
* @param {number | null} transportCode2
|
||||
*/
|
||||
constructor(header, path, payload, transportCode1, transportCode2) {
|
||||
|
||||
|
||||
this.header = header;
|
||||
this.path = path;
|
||||
this.payload = payload;
|
||||
|
|
@ -46,6 +52,10 @@ class Packet {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} bytes
|
||||
* @returns {Packet}
|
||||
*/
|
||||
static fromBytes(bytes) {
|
||||
|
||||
const bufferReader = new BufferReader(bytes);
|
||||
|
|
@ -71,10 +81,12 @@ class Packet {
|
|||
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
getRouteType() {
|
||||
return this.header & Packet.PH_ROUTE_MASK;
|
||||
}
|
||||
|
||||
/** @returns {string | null} */
|
||||
getRouteTypeString() {
|
||||
switch(this.getRouteType()){
|
||||
case Packet.ROUTE_TYPE_FLOOD: return "FLOOD";
|
||||
|
|
@ -85,18 +97,22 @@ class Packet {
|
|||
}
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
isRouteFlood() {
|
||||
return this.getRouteType() === Packet.ROUTE_TYPE_FLOOD;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
isRouteDirect() {
|
||||
return this.getRouteType() === Packet.ROUTE_TYPE_DIRECT;
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
getPayloadType() {
|
||||
return (this.header >> Packet.PH_TYPE_SHIFT) & Packet.PH_TYPE_MASK;
|
||||
}
|
||||
|
||||
/** @returns {string | null} */
|
||||
getPayloadTypeString() {
|
||||
switch(this.getPayloadType()){
|
||||
case Packet.PAYLOAD_TYPE_REQ: return "REQ";
|
||||
|
|
@ -114,6 +130,7 @@ class Packet {
|
|||
}
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
getPayloadVer() {
|
||||
return (this.header >> Packet.PH_VER_SHIFT) & Packet.PH_VER_MASK;
|
||||
}
|
||||
|
|
@ -122,6 +139,7 @@ class Packet {
|
|||
this.header = 0xFF;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
isMarkedDoNotRetransmit() {
|
||||
return this.header === 0xFF;
|
||||
}
|
||||
|
|
@ -139,6 +157,7 @@ class Packet {
|
|||
}
|
||||
}
|
||||
|
||||
/** @returns {{ src: number, dest: number }} */
|
||||
parsePayloadTypePath() {
|
||||
|
||||
// parse bytes
|
||||
|
|
@ -154,6 +173,7 @@ class Packet {
|
|||
|
||||
}
|
||||
|
||||
/** @returns {{ src: number, dest: number, encrypted: Uint8Array }} */
|
||||
parsePayloadTypeReq() {
|
||||
|
||||
// parse bytes
|
||||
|
|
@ -170,6 +190,7 @@ class Packet {
|
|||
|
||||
}
|
||||
|
||||
/** @returns {{ src: number, dest: number }} */
|
||||
parsePayloadTypeResponse() {
|
||||
|
||||
// parse bytes
|
||||
|
|
@ -185,6 +206,7 @@ class Packet {
|
|||
|
||||
}
|
||||
|
||||
/** @returns {{ src: number, dest: number }} */
|
||||
parsePayloadTypeTxtMsg() {
|
||||
|
||||
// parse bytes
|
||||
|
|
@ -200,12 +222,14 @@ class Packet {
|
|||
|
||||
}
|
||||
|
||||
/** @returns {{ ack_code: Uint8Array }} */
|
||||
parsePayloadTypeAck() {
|
||||
return {
|
||||
ack_code: this.payload,
|
||||
};
|
||||
}
|
||||
|
||||
/** @returns {{ public_key: Uint8Array, timestamp: number, app_data: import("./types.js").AdvertParsedData }} */
|
||||
parsePayloadTypeAdvert() {
|
||||
const advert = Advert.fromBytes(this.payload);
|
||||
return {
|
||||
|
|
@ -215,6 +239,7 @@ class Packet {
|
|||
};
|
||||
}
|
||||
|
||||
/** @returns {{ src: Uint8Array, dest: number }} */
|
||||
parsePayloadTypeAnonReq() {
|
||||
|
||||
// parse bytes
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
class RandomUtils {
|
||||
|
||||
/**
|
||||
* @param {number} min
|
||||
* @param {number} max
|
||||
* @returns {number}
|
||||
*/
|
||||
static getRandomInt(min, max) {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
|
|
|
|||
233
src/types.js
Normal file
233
src/types.js
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
/**
|
||||
* @typedef {number} EpochSeconds Unix timestamp in seconds
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {number} Milliseconds Duration in milliseconds
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {number} MilliVolts Voltage in millivolts
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {number} TxtType Text message type: 0=Plain, 1=CliData, 2=SignedPlain
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} SelfInfo
|
||||
* @property {number} type
|
||||
* @property {number} txPower
|
||||
* @property {number} maxTxPower
|
||||
* @property {Uint8Array} publicKey
|
||||
* @property {number} advLat
|
||||
* @property {number} advLon
|
||||
* @property {Uint8Array} reserved
|
||||
* @property {number} manualAddContacts
|
||||
* @property {number} radioFreq
|
||||
* @property {number} radioBw
|
||||
* @property {number} radioSf
|
||||
* @property {number} radioCr
|
||||
* @property {string} name
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} Contact
|
||||
* @property {Uint8Array} publicKey
|
||||
* @property {number} type
|
||||
* @property {number} flags
|
||||
* @property {number} outPathLen
|
||||
* @property {Uint8Array} outPath
|
||||
* @property {string} advName
|
||||
* @property {EpochSeconds} lastAdvert
|
||||
* @property {number} advLat
|
||||
* @property {number} advLon
|
||||
* @property {EpochSeconds} lastMod
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ContactMessage
|
||||
* @property {Uint8Array} pubKeyPrefix
|
||||
* @property {number} pathLen
|
||||
* @property {TxtType} txtType
|
||||
* @property {EpochSeconds} senderTimestamp
|
||||
* @property {string} text
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ChannelMessage
|
||||
* @property {number} channelIdx
|
||||
* @property {number} pathLen
|
||||
* @property {TxtType} txtType
|
||||
* @property {EpochSeconds} senderTimestamp
|
||||
* @property {string} text
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ChannelInfo
|
||||
* @property {number} channelIdx
|
||||
* @property {string} name
|
||||
* @property {Uint8Array} secret
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} SentResponse
|
||||
* @property {number} result
|
||||
* @property {number} expectedAckCrc
|
||||
* @property {Milliseconds} estTimeout
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} DeviceInfo
|
||||
* @property {number} firmwareVer
|
||||
* @property {Uint8Array} reserved
|
||||
* @property {string} firmware_build_date
|
||||
* @property {string} manufacturerModel
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} BatteryVoltageResponse
|
||||
* @property {MilliVolts} batteryMilliVolts
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} ExportContactResponse
|
||||
* @property {Uint8Array} advertPacketBytes
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PrivateKeyResponse
|
||||
* @property {Uint8Array} privateKey
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RepeaterStats
|
||||
* @property {MilliVolts} batt_milli_volts
|
||||
* @property {number} curr_tx_queue_len
|
||||
* @property {number} noise_floor
|
||||
* @property {number} last_rssi
|
||||
* @property {number} n_packets_recv
|
||||
* @property {number} n_packets_sent
|
||||
* @property {number} total_air_time_secs
|
||||
* @property {number} total_up_time_secs
|
||||
* @property {number} n_sent_flood
|
||||
* @property {number} n_sent_direct
|
||||
* @property {number} n_recv_flood
|
||||
* @property {number} n_recv_direct
|
||||
* @property {number} err_events
|
||||
* @property {number} last_snr
|
||||
* @property {number} n_direct_dups
|
||||
* @property {number} n_flood_dups
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} SyncMessageResult
|
||||
* @property {ContactMessage} [contactMessage]
|
||||
* @property {ChannelMessage} [channelMessage]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} Neighbour
|
||||
* @property {Uint8Array} publicKeyPrefix
|
||||
* @property {number} heardSecondsAgo
|
||||
* @property {number} snr
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} NeighboursResult
|
||||
* @property {number} totalNeighboursCount
|
||||
* @property {Neighbour[]} neighbours
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} TraceDataResult
|
||||
* @property {number} reserved
|
||||
* @property {number} pathLen
|
||||
* @property {number} flags
|
||||
* @property {number} tag
|
||||
* @property {number} authCode
|
||||
* @property {Uint8Array} pathHashes
|
||||
* @property {Uint8Array} pathSnrs
|
||||
* @property {number} lastSnr
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} AdvertParsedData
|
||||
* @property {string | null} type
|
||||
* @property {number | null} lat
|
||||
* @property {number | null} lon
|
||||
* @property {string | null} name
|
||||
* @property {number | null} feat1
|
||||
* @property {number | null} feat2
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} CayenneTelemetryEntry
|
||||
* @property {number} channel
|
||||
* @property {number} type
|
||||
* @property {number | {latitude: number, longitude: number, altitude: number}} value
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} LoginSuccessPush
|
||||
* @property {number} reserved
|
||||
* @property {Uint8Array} pubKeyPrefix
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} StatusResponsePush
|
||||
* @property {number} reserved
|
||||
* @property {Uint8Array} pubKeyPrefix
|
||||
* @property {Uint8Array} statusData
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} RawDataPush
|
||||
* @property {number} lastSnr
|
||||
* @property {number} lastRssi
|
||||
* @property {number} reserved
|
||||
* @property {Uint8Array} payload
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} SendConfirmedPush
|
||||
* @property {number} ackCode
|
||||
* @property {Milliseconds} roundTrip
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} LogRxDataPush
|
||||
* @property {number} lastSnr
|
||||
* @property {number} lastRssi
|
||||
* @property {Uint8Array} raw
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} TelemetryResponsePush
|
||||
* @property {number} reserved
|
||||
* @property {Uint8Array} pubKeyPrefix
|
||||
* @property {Uint8Array} lppSensorData
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} BinaryResponsePush
|
||||
* @property {number} reserved
|
||||
* @property {number} tag
|
||||
* @property {Uint8Array} responseData
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} NewAdvertPush
|
||||
* @property {Uint8Array} publicKey
|
||||
* @property {number} type
|
||||
* @property {number} flags
|
||||
* @property {number} outPathLen
|
||||
* @property {Uint8Array} outPath
|
||||
* @property {string} advName
|
||||
* @property {EpochSeconds} lastAdvert
|
||||
* @property {number} advLat
|
||||
* @property {number} advLon
|
||||
* @property {EpochSeconds} lastMod
|
||||
*/
|
||||
|
||||
export {};
|
||||
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"include": ["src/**/*.js"],
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"declarationMap": true,
|
||||
"outDir": "dist",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"target": "ESNext",
|
||||
"strict": false,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue