39 lines
5.5 KiB
Markdown
39 lines
5.5 KiB
Markdown
# Changelog
|
||
|
||
## v0.3.0 — 2026-09-24 14:08
|
||
|
||
- Message history with delivery tracking, exposed as `GET /api/messages` (newest first): inbound TEXT_MESSAGE_APP packets (`dir:'rx'`, with `from`) and our sends (`dir:'tx'`, with `to` + `status`). tx records start `pending` and move to `delivered`/`failed` when the routing ACK/NACK arrives — ROUTING_APP packets carry a serialized `meshtastic.Routing` in `Data.payload` whose oneof variant is `errorReason` (`Routing_Error`: 0 NONE = delivered, anything else failed with a human-readable `ackError`, e.g. NO_ROUTE/GOT_NAK/TIMEOUT, fallback `reason <n>`), matched to our sends via `Data.requestId`. Broadcasts stay `pending` because firmware strips want_ack pre-TX (documented behavior, no timer). History is a bounded in-memory buffer of the last 100 records (newest kept) and resets on every bridge reconnect alongside channels/nodes.
|
||
- Codec: new `PORT_ROUTING_APP = 5` constant (verified against the installed `@meshtastic/protobufs` PortNum enum), `decodeRouting()` for ROUTING_APP payloads, and `describeRoutingError()` which maps a Routing.Error code to its name from the installed package's `Mesh.Routing_Error` enum.
|
||
- Swagger: `GET /api/messages` documented with the pending→delivered/failed lifecycle, the broadcast caveat, and an example matching the record shape; README + AGENTS.md endpoint lists updated.
|
||
|
||
## v0.2.0 — 2026-09-24 13:08
|
||
|
||
- Node database collected during the initial sync (`FromRadio.nodeInfo` frames, upserted by node number) and kept fresh by live `POSITION_APP` packets (the Position is serialized in `Data.payload`; unknown senders get a stub entry). The map resets on every bridge reconnect alongside channels.
|
||
- New endpoints: `GET /api/nodes` — lean summaries sorted by node number as `{ num, longName?, shortName?, hwModel?, batteryLevel?, lastHeard (epoch secs | null), position? { lat, lon, alt? } }[]`; names/hwModel/battery appear only when the device reported them, `hwModel` is the enum's string name (UNSET omitted), and `position` only when both coordinates are known. `GET /api/nodes/:id` — lookup by exact node number (numbers/numeric strings), then case-insensitive exact long/short name, else unique case-insensitive substring; 404 with a clear message when nothing or more than one node matches.
|
||
- Codec: new `PORT_POSITION_APP = 3` constant (verified against the installed `@meshtastic/protobufs` PortNum enum), `decodePosition()` for POSITION_APP payloads, and `loadHwModelNames()` reverse lookup of the HardwareModel enum.
|
||
- Swagger docs made genuinely useful: `Mesh` tag on the controller (description via `DocumentBuilder.addTag` — v12's `ApiTagOptions` has no description field) plus `@ApiOperation` summary/description and `@ApiResponse` examples on all endpoints, existing and new.
|
||
- Unit tests build real framed FromRadio bytes for nodeInfo/POSITION_APP: sync collection + summary shape (lat/lon = value × 1e-7, battery), stub entry from an unknown POSITION_APP sender, lookup ladder (exact num / numeric string / exact name / unique substring) with ambiguous and missing → null, and reconnect reset.
|
||
|
||
## v0.1.4 — 2026-09-23 20:33
|
||
|
||
- Send a `ToRadio` with `want_config_id` (random non-zero uint32 nonce) on every bridge connect, so the firmware streams its initial sync data (`myInfo`, channels, `config_complete_id`) — previously nothing was written on connect, which is why `/api/channels` stayed empty and `myNodeNum` stayed null in production. New codec helper `buildWantConfigToRadio`; unit test captures the outbound frame on the fake bridge, asserts the `wantConfigId` oneof case with a non-zero value, then feeds back `myInfo` + `configCompleteId` frames and checks service state.
|
||
|
||
## v0.1.3 — 2026-09-23 19:58
|
||
|
||
- Sends are now pinned to the test channel `testowy` (channel index 2) instead of the hardcoded primary channel (`channel: 0`). The target channel is configurable via `MESH_SEND_CHANNEL` (default `2`) and parsed once at service construction; `buildTextToRadio` takes the channel index as a parameter. Unit tests now decode the outbound ToRadio frame on the fake bridge and assert `packet.channel === 2` on the default send path.
|
||
|
||
## v0.1.2 — 2026-09-23 19:51
|
||
|
||
- Added a Gitea Actions CI pipeline (`.gitea/workflows/ci.yml`), mirroring flight's runner conventions: on push/PR to `main` (plus manual dispatch) one job runs `npm ci`, `nest build`, the vitest unit suite, and a production Docker image build — with retry loops for the runner's flaky uplink. No app code changed.
|
||
|
||
## v0.1.1 — 2026-09-23 19:38
|
||
|
||
- Fixed the default bridge port to `4403`, the meshtastic-serial-bridge's actual TCP port (the old value pointed at a closed port). Updated the `MESH_PORT` default in code, the docker-compose environment, and docs.
|
||
|
||
## v0.1.0 — 2026-09-23 18:54
|
||
|
||
- New `GET /api/channels` endpoint listing the device's Meshtastic channels as `{ index, name, uplinkEnabled, downlinkEnabled }`; psk/keys are redacted and never exposed over HTTP. Channels are collected from `FromRadio.channel` frames streamed during initial sync (before `config_complete_id`) and reset on every bridge reconnect so stale data is dropped after a disconnect.
|
||
- All endpoints moved under the `/api` namespace via `app.setGlobalPrefix("api")`: `GET /status` → `GET /api/status`, `POST /send` → `POST /api/send`.
|
||
- Scalar API docs served at `/` (OpenAPI JSON at `/docs-json`) using `@nestjs/swagger` + `@scalar/nestjs-api-reference`; added light swagger decorators (`ApiProperty` on the send body, `ApiOperation` summaries) so the docs are useful.
|
||
- Reconnect interval is now configurable via `MESH_RECONNECT_MS` (default 5000 ms).
|