Telemetry stream: GET /api/telemetry + MCP get_telemetry (#16)
CI / build + unit (push) Successful in 21s
CI / build + unit (push) Successful in 21s
This commit was merged in pull request #16.
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
moduleConfigSectionFrame,
|
||||
nodeInfoFrame,
|
||||
startBridge,
|
||||
telemetryPacketFrame,
|
||||
waitFor,
|
||||
} from './fake-bridge.js';
|
||||
import { MeshService } from './mesh.service.js';
|
||||
@@ -560,6 +561,121 @@ describe('MeshService message history', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('MeshService telemetry', () => {
|
||||
it('captures device metrics and environment variants with from/kind/data', async () => {
|
||||
const bridge = await startBridge();
|
||||
process.env.MESH_HOST = '127.0.0.1';
|
||||
process.env.MESH_PORT = String(bridge.port);
|
||||
const svc = new MeshService();
|
||||
try {
|
||||
svc.onModuleInit();
|
||||
await waitFor(
|
||||
() => bridge.sockets.length === 1 && svc.status().connected,
|
||||
);
|
||||
|
||||
expect(svc.telemetry()).toEqual([]); // nothing before the first packet
|
||||
|
||||
bridge.sockets[0].write(
|
||||
telemetryPacketFrame(KAMIL_NUM, {
|
||||
case: 'deviceMetrics',
|
||||
value: { batteryLevel: 87, voltage: 4.1, uptimeSeconds: 3600 },
|
||||
}),
|
||||
);
|
||||
bridge.sockets[0].write(
|
||||
telemetryPacketFrame(987654321, {
|
||||
case: 'environmentMetrics',
|
||||
value: { temperature: 21.5, relativeHumidity: 48 },
|
||||
}),
|
||||
);
|
||||
|
||||
await waitFor(() => svc.telemetry().length === 2);
|
||||
expect(svc.telemetry()).toEqual([
|
||||
{
|
||||
from: 987654321, // newest first
|
||||
at: expect.any(Number),
|
||||
kind: 'environmentMetrics',
|
||||
// the deprecated repeated one_wire_temperature field decodes as an empty list even when unset
|
||||
data: {
|
||||
temperature: 21.5,
|
||||
relativeHumidity: 48,
|
||||
oneWireTemperature: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
from: KAMIL_NUM,
|
||||
at: expect.any(Number),
|
||||
kind: 'deviceMetrics',
|
||||
// proto float is IEEE-754 single precision — 4.1 widens to this exact double
|
||||
data: {
|
||||
batteryLevel: 87,
|
||||
voltage: 4.099999904632568,
|
||||
uptimeSeconds: 3600,
|
||||
},
|
||||
},
|
||||
]);
|
||||
} finally {
|
||||
svc.onModuleDestroy();
|
||||
bridge.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('caps telemetry at 100 records, dropping the oldest', async () => {
|
||||
const bridge = await startBridge();
|
||||
process.env.MESH_HOST = '127.0.0.1';
|
||||
process.env.MESH_PORT = String(bridge.port);
|
||||
const svc = new MeshService();
|
||||
try {
|
||||
svc.onModuleInit();
|
||||
await waitFor(
|
||||
() => bridge.sockets.length === 1 && svc.status().connected,
|
||||
);
|
||||
|
||||
for (let i = 0; i < 105; i++)
|
||||
bridge.sockets[0].write(
|
||||
telemetryPacketFrame(42, {
|
||||
case: 'deviceMetrics',
|
||||
value: { batteryLevel: i },
|
||||
}),
|
||||
);
|
||||
|
||||
await waitFor(() => svc.telemetry().length === 100, 10000);
|
||||
expect(svc.telemetry()[0]?.data).toEqual({ batteryLevel: 104 }); // newest first
|
||||
expect(svc.telemetry()[99]?.data).toEqual({ batteryLevel: 5 }); // i=0..4 dropped
|
||||
} finally {
|
||||
svc.onModuleDestroy();
|
||||
bridge.close();
|
||||
}
|
||||
});
|
||||
|
||||
it('resets telemetry on reconnect (no stale data)', async () => {
|
||||
const bridge = await startBridge();
|
||||
process.env.MESH_HOST = '127.0.0.1';
|
||||
process.env.MESH_PORT = String(bridge.port);
|
||||
process.env.MESH_RECONNECT_MS = '50';
|
||||
const svc = new MeshService();
|
||||
try {
|
||||
svc.onModuleInit();
|
||||
await waitFor(
|
||||
() => bridge.sockets.length === 1 && svc.status().connected,
|
||||
);
|
||||
|
||||
bridge.sockets[0].write(
|
||||
telemetryPacketFrame(KAMIL_NUM, { case: 'deviceMetrics', value: {} }),
|
||||
);
|
||||
await waitFor(() => svc.telemetry().length === 1);
|
||||
|
||||
// drop the connection; bot reconnects and must start with an empty history
|
||||
bridge.sockets[0].destroy();
|
||||
await waitFor(() => bridge.sockets.length >= 2 && svc.status().connected);
|
||||
await waitFor(() => svc.telemetry().length === 0);
|
||||
} finally {
|
||||
delete process.env.MESH_RECONNECT_MS;
|
||||
svc.onModuleDestroy();
|
||||
bridge.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('MeshService resolveDest', () => {
|
||||
it('resolves broadcast sentinels, numbers and names to node numbers', async () => {
|
||||
const bridge = await startBridge();
|
||||
|
||||
Reference in New Issue
Block a user