From 83ea9c8e3c962955e9e453c3d3dfed7c40aac092 Mon Sep 17 00:00:00 2001 From: liamcottle Date: Fri, 20 Jun 2025 16:17:02 +1200 Subject: [PATCH] add support for fetching telemetry from contacts --- examples/get_repeater_telemetry.js | 41 +++++++++++++++ src/connection/connection.js | 84 ++++++++++++++++++++++++++++++ src/constants.js | 2 + 3 files changed, 127 insertions(+) create mode 100644 examples/get_repeater_telemetry.js diff --git a/examples/get_repeater_telemetry.js b/examples/get_repeater_telemetry.js new file mode 100644 index 0000000..2a06510 --- /dev/null +++ b/examples/get_repeater_telemetry.js @@ -0,0 +1,41 @@ +import TCPConnection from "../src/connection/tcp_connection.js"; +import NodeJSSerialConnection from "../src/connection/nodejs_serial_connection.js"; + +// create connection +// const connection = new TCPConnection("10.1.0.226", 5000); +const connection = new NodeJSSerialConnection("/dev/cu.usbmodem14401"); + +// wait until connected +connection.on("connected", async () => { + + // we are now connected + console.log("Connected"); + + // find contact + // const contact = await connection.findContactByName("Liam's Solar Repeater"); + // const contact = await connection.findContactByPublicKeyPrefix([0x93, 0x5c, 0x6b, 0x69]); + // const contact = await connection.findContactByPublicKeyPrefix(Buffer.from("935c6b69", "hex")); + const contact = await connection.findContactByPublicKeyPrefix(Buffer.from("935c6b694200644710a374c250c76f7aed9ec2ff3e60261447d4eda7c246ce5d", "hex")); + if(!contact){ + console.log("Contact not found"); + await connection.close(); + return; + } + + // login to repeater (with empty guest password) + console.log("Logging in..."); + const res = await connection.login(contact.publicKey, ""); + console.log("res", res); + + // get repeater telemetry + console.log("Fetching telemetry..."); + const telemetry = await connection.getTelemetry(contact.publicKey); + console.log(telemetry); + + // disconnect + await connection.close(); + +}); + +// connect to meshcore device +await connection.connect(); diff --git a/src/connection/connection.js b/src/connection/connection.js index c492670..2d02459 100644 --- a/src/connection/connection.js +++ b/src/connection/connection.js @@ -242,6 +242,16 @@ class Connection extends EventEmitter { await this.sendToRadioFrame(data.toBytes()); } + async sendCommandSendTelemetryReq(publicKey) { + const data = new BufferWriter(); + data.writeByte(Constants.CommandCodes.SendTelemetryReq); + data.writeByte(0); // reserved + data.writeByte(0); // reserved + data.writeByte(0); // reserved + data.writeBytes(publicKey); // 32 bytes - id of destination node + await this.sendToRadioFrame(data.toBytes()); + } + async sendCommandGetChannel(channelIdx) { const data = new BufferWriter(); data.writeByte(Constants.CommandCodes.GetChannel); @@ -333,6 +343,8 @@ class Connection extends EventEmitter { this.onStatusResponsePush(bufferReader); } else if(responseCode === Constants.PushCodes.LogRxData){ this.onLogRxDataPush(bufferReader); + } else if(responseCode === Constants.PushCodes.TelemetryResponse){ + this.onTelemetryResponsePush(bufferReader); } else if(responseCode === Constants.PushCodes.TraceData){ this.onTraceDataPush(bufferReader); } else if(responseCode === Constants.PushCodes.NewAdvert){ @@ -400,6 +412,14 @@ class Connection extends EventEmitter { }); } + onTelemetryResponsePush(bufferReader) { + this.emit(Constants.PushCodes.TelemetryResponse, { + reserved: bufferReader.readByte(), // reserved + pubKeyPrefix: bufferReader.readBytes(6), // 6 bytes of public key this telemetry response is from + lppSensorData: bufferReader.readRemainingBytes(), + }); + } + onTraceDataPush(bufferReader) { const reserved = bufferReader.readByte(); const pathLen = bufferReader.readUInt8(); @@ -1544,6 +1564,70 @@ class Connection extends EventEmitter { }); } + getTelemetry(contactPublicKey, extraTimeoutMillis = 1000) { + return new Promise(async (resolve, reject) => { + try { + + // get public key prefix we expect in the telemetry response + const publicKeyPrefix = contactPublicKey.subarray(0, 6); + + // listen for sent response so we can get estimated timeout + var timeoutHandler = null; + const onSent = (response) => { + + // remove error listener since we received sent response + this.once(Constants.ResponseCodes.Err, onErr); + + // reject as timed out after estimated delay, plus a bit extra + const estTimeout = response.estTimeout + extraTimeoutMillis; + timeoutHandler = setTimeout(() => { + reject("timeout"); + }, estTimeout); + + } + + // resolve promise when we receive telemetry response push code + const onTelemetryResponsePush = (response) => { + + // make sure telemetry response is for this telemetry request + if(!BufferUtils.areBuffersEqual(publicKeyPrefix, response.pubKeyPrefix)){ + console.log("onTelemetryResponsePush is not for this telemetry request, ignoring..."); + return; + } + + // telemetry request successful + clearTimeout(timeoutHandler); + this.off(Constants.ResponseCodes.Err, onErr); + this.off(Constants.ResponseCodes.Sent, onSent); + this.off(Constants.PushCodes.TelemetryResponse, onTelemetryResponsePush); + + resolve(response); + + } + + // reject promise when we receive err + const onErr = () => { + clearTimeout(timeoutHandler); + this.off(Constants.ResponseCodes.Err, onErr); + this.off(Constants.ResponseCodes.Sent, onSent); + this.off(Constants.PushCodes.TelemetryResponse, onTelemetryResponsePush); + reject(); + } + + // listen for events + this.once(Constants.ResponseCodes.Err, onErr); + this.once(Constants.ResponseCodes.Sent, onSent); + this.once(Constants.PushCodes.TelemetryResponse, onTelemetryResponsePush); + + // request telemetry + await this.sendCommandSendTelemetryReq(contactPublicKey); + + } catch(e) { + reject(e); + } + }); + } + pingRepeaterZeroHop(contactPublicKey, timeoutMillis) { return new Promise(async (resolve, reject) => { try { diff --git a/src/constants.js b/src/constants.js index 1037ddd..43b2996 100644 --- a/src/constants.js +++ b/src/constants.js @@ -47,6 +47,7 @@ class Constants { SendTracePath: 36, // todo set device pin command SetOtherParams: 38, + SendTelemetryReq: 39, } static ResponseCodes = { @@ -81,6 +82,7 @@ class Constants { LogRxData: 0x88, TraceData: 0x89, NewAdvert: 0x8A, // when companion is set to manually add contacts + TelemetryResponse: 0x8B, } static ErrorCodes = {