add support for fetching telemetry from contacts

This commit is contained in:
liamcottle 2025-06-20 16:17:02 +12:00
parent 7acd0eefa0
commit 83ea9c8e3c
3 changed files with 127 additions and 0 deletions

View file

@ -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();

View file

@ -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 {

View file

@ -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 = {