feat(protobufs): sync to firmware-current and consume workspace package (#1097)
* feat(protobufs): sync to firmware-current and consume workspace package Sync the vendored .proto sources to firmware-current (v2.7.25+48), regenerate the v2 TS bindings, and consume the workspace @meshtastic/protobufs (workspace:*) in place of the stale JSR 2.7.20 — finishing the monorepo migration (core was already workspace:*). Includes the one required breaking-change fix: admin nodedb_reset changed int32 to bool, so resetNodes() now sends value: true. * build(protobufs): vendor generated bindings for workspace consumers The package is consumed via workspace:* — its exports point at the TS source, which imports ./dist/meshtastic/*_pb.ts — so the generated output must exist at build time. CI builds web/core with no codegen step and the runners have no buf CLI, so the bindings are vendored here (kept gitignored; lint/format skip them). Regenerate with: pnpm --filter @meshtastic/protobufs gen * fix(protobufs): clean script removes the actual generated output dir buf writes bindings to packages/ts/dist, but clean was removing a non-existent root dist — so it never cleaned stale output. Addresses Copilot review feedback. * refactor: move web app packages/web -> apps/web Aligns the web app with the apps/web layout (matching the Vercel web-test Root Directory and the SDK-migration direction). Pure directory move plus root config: pnpm-workspace (adds apps/*), vitest projects, root tsconfig reference, and the pr/release-web/nightly workflows. vercel.json moved with the app. Build + 36 validation tests green. * feat: config fields, module pages, key verification, telemetry capture Incorporates the firmware-current feature work onto the protobuf foundation: new config fields (Display message bubbles; LoRa fem_lna_mode + serial_hal_only; Telemetry air_quality_screen_enabled); 4 new ModuleConfig pages (TrafficManagement, StatusMessage, TAK, RemoteHardware); the manual Key Verification flow (sendKeyVerification + ClientNotificationDialog stages + Verify Key button); live telemetry capture (nodeDB addDeviceMetrics) and admin hardening (toggleMutedNode, graceful PortNum default); plus the sdk-preview ConfigEditor demo and store/config tests. Build + lint + format + 131 tests green. * chore: drop #1062 (unsaved-change-detection) to match upstream revert #1062 was merged to main by accident (per @danditomaso) and is being reverted. Reverse-applied its diff here via 3-way so #1097 stays consistent with where main is headed, while keeping the feature changes layered on the same files (deviceStore/changeRegistry). Build + 131 tests + lint + format green. * fix(nodes): clean up SNR display in node table and map popup SNR is a ratio measured in dB, not dBm (which is absolute power); the node table and map popup both mislabeled it and crammed three values together: '0dBm/50%/50raw'. The trailing '%/raw' pair was the same heuristic ((snr+10)*5) shown twice — once clamped, once not. Render SNR in dB rounded to one decimal, color-coded by a 0-100% signal-quality heuristic (green/yellow/red), with the quality percentage as a muted secondary. Drop the redundant raw value. Adds unit.db; this matches the existing SNRTooltip, which already renders dB.
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
import PacketToMessageDTO from "@core/dto/PacketToMessageDTO.ts";
|
||||
import { useNewNodeNum } from "@core/hooks/useNewNodeNum";
|
||||
import { type Device, type MessageStore, MessageType, type NodeDB } from "@core/stores";
|
||||
import { type MeshDevice, Protobuf } from "@meshtastic/core";
|
||||
export const subscribeAll = (
|
||||
device: Device,
|
||||
connection: MeshDevice,
|
||||
messageStore: MessageStore,
|
||||
nodeDB: NodeDB,
|
||||
) => {
|
||||
let myNodeNum = 0;
|
||||
|
||||
connection.events.onDeviceMetadataPacket.subscribe((metadataPacket) => {
|
||||
device.addMetadata(metadataPacket.from, metadataPacket.data);
|
||||
});
|
||||
|
||||
connection.events.onRoutingPacket.subscribe((routingPacket) => {
|
||||
switch (routingPacket.data.variant.case) {
|
||||
case "errorReason": {
|
||||
if (routingPacket.data.variant.value === Protobuf.Mesh.Routing_Error.NONE) {
|
||||
return;
|
||||
}
|
||||
console.info(`Routing Error: ${routingPacket.data.variant.value}`);
|
||||
break;
|
||||
}
|
||||
case "routeReply": {
|
||||
console.info(`Route Reply: ${routingPacket.data.variant.value}`);
|
||||
break;
|
||||
}
|
||||
case "routeRequest": {
|
||||
console.info(`Route Request: ${routingPacket.data.variant.value}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connection.events.onTelemetryPacket.subscribe((telemetryPacket) => {
|
||||
// Fold live device-metrics telemetry into the node so battery / channel
|
||||
// utilisation / voltage stay current between NodeInfo broadcasts.
|
||||
if (telemetryPacket.data.variant.case === "deviceMetrics") {
|
||||
nodeDB.addDeviceMetrics({
|
||||
...telemetryPacket,
|
||||
data: telemetryPacket.data.variant.value,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
connection.events.onDeviceStatus.subscribe((status) => {
|
||||
device.setStatus(status);
|
||||
});
|
||||
|
||||
connection.events.onWaypointPacket.subscribe((waypoint) => {
|
||||
const { data, channel, from, rxTime } = waypoint;
|
||||
device.addWaypoint(data, channel, from, rxTime);
|
||||
});
|
||||
|
||||
connection.events.onMyNodeInfo.subscribe((nodeInfo) => {
|
||||
useNewNodeNum(device.id, nodeInfo);
|
||||
myNodeNum = nodeInfo.myNodeNum;
|
||||
});
|
||||
|
||||
connection.events.onUserPacket.subscribe((user) => {
|
||||
nodeDB.addUser(user);
|
||||
});
|
||||
|
||||
connection.events.onPositionPacket.subscribe((position) => {
|
||||
nodeDB.addPosition(position);
|
||||
});
|
||||
|
||||
// NOTE: Node handling is managed by the nodeDB
|
||||
// Nodes are added via subscriptions.ts and stored in nodeDB
|
||||
// Configuration is handled directly by meshDevice.configure() in useConnections
|
||||
connection.events.onNodeInfoPacket.subscribe((nodeInfo) => {
|
||||
nodeDB.addNode(nodeInfo);
|
||||
});
|
||||
|
||||
connection.events.onChannelPacket.subscribe((channel) => {
|
||||
device.addChannel(channel);
|
||||
});
|
||||
connection.events.onConfigPacket.subscribe((config) => {
|
||||
device.setConfig(config);
|
||||
});
|
||||
connection.events.onModuleConfigPacket.subscribe((moduleConfig) => {
|
||||
device.setModuleConfig(moduleConfig);
|
||||
});
|
||||
|
||||
connection.events.onMessagePacket.subscribe((messagePacket) => {
|
||||
// incoming and outgoing messages are handled by this event listener
|
||||
const dto = new PacketToMessageDTO(messagePacket, myNodeNum);
|
||||
const message = dto.toMessage();
|
||||
messageStore.saveMessage(message);
|
||||
|
||||
if (message.type === MessageType.Direct) {
|
||||
if (message.to === myNodeNum) {
|
||||
device.incrementUnread(messagePacket.from);
|
||||
}
|
||||
} else if (message.type === MessageType.Broadcast) {
|
||||
if (message.from !== myNodeNum) {
|
||||
device.incrementUnread(message.channel);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connection.events.onTraceRoutePacket.subscribe((traceRoutePacket) => {
|
||||
device.addTraceRoute({
|
||||
...traceRoutePacket,
|
||||
});
|
||||
});
|
||||
|
||||
connection.events.onPendingSettingsChange.subscribe((state) => {
|
||||
device.setPendingSettingsChanges(state);
|
||||
});
|
||||
|
||||
connection.events.onMeshPacket.subscribe((meshPacket) => {
|
||||
nodeDB.processPacket({
|
||||
from: meshPacket.from,
|
||||
snr: meshPacket.rxSnr,
|
||||
time: meshPacket.rxTime,
|
||||
});
|
||||
});
|
||||
|
||||
connection.events.onClientNotificationPacket.subscribe((clientNotificationPacket) => {
|
||||
device.addClientNotification(clientNotificationPacket);
|
||||
device.setDialogOpen("clientNotification", true);
|
||||
});
|
||||
|
||||
connection.events.onNeighborInfoPacket.subscribe((neighborInfo) => {
|
||||
device.addNeighborInfo(neighborInfo.from, neighborInfo.data);
|
||||
});
|
||||
|
||||
connection.events.onRoutingPacket.subscribe((routingPacket) => {
|
||||
if (routingPacket.data.variant.case === "errorReason") {
|
||||
switch (routingPacket.data.variant.value) {
|
||||
case Protobuf.Mesh.Routing_Error.MAX_RETRANSMIT:
|
||||
console.error(`Routing Error: ${routingPacket.data.variant.value}`);
|
||||
break;
|
||||
case Protobuf.Mesh.Routing_Error.NO_CHANNEL:
|
||||
console.error(`Routing Error: ${routingPacket.data.variant.value}`);
|
||||
nodeDB.setNodeError(routingPacket.from, routingPacket?.data?.variant?.value);
|
||||
device.setDialogOpen("refreshKeys", true);
|
||||
break;
|
||||
case Protobuf.Mesh.Routing_Error.PKI_UNKNOWN_PUBKEY:
|
||||
console.error(`Routing Error: ${routingPacket.data.variant.value}`);
|
||||
nodeDB.setNodeError(routingPacket.from, routingPacket?.data?.variant?.value);
|
||||
device.setDialogOpen("refreshKeys", true);
|
||||
break;
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user