diff --git a/packages/sdk-react/README.md b/packages/sdk-react/README.md index a9b3d7dd..c8bf2458 100644 --- a/packages/sdk-react/README.md +++ b/packages/sdk-react/README.md @@ -1,20 +1,20 @@ # @meshtastic/sdk-react -React hooks and provider for `@meshtastic/sdk`. - -## Install +React hooks for [`@meshtastic/sdk`](https://www.npmjs.com/package/@meshtastic/sdk). Wraps the SDK's +signal-based state in hooks that only re-render when the data they touch changes. ```sh pnpm add @meshtastic/sdk @meshtastic/sdk-react @meshtastic/transport-web-serial ``` -## Quickstart +## One device + +If your app talks to a single radio at a time, wrap your tree in `MeshProvider` and you're done: ```tsx -import { MeshClient } from "@meshtastic/sdk"; +import { MeshClient, ChannelNumber } from "@meshtastic/sdk"; import { MeshProvider, useDevice, useChat } from "@meshtastic/sdk-react"; import { TransportWebSerial } from "@meshtastic/transport-web-serial"; -import { ChannelNumber } from "@meshtastic/sdk"; const transport = await TransportWebSerial.create({ baudRate: 115200 }); const client = new MeshClient({ transport }); @@ -31,11 +31,101 @@ function App() { function Status() { const { status, myNodeNum } = useDevice(); const { messages, send } = useChat(ChannelNumber.Primary); - return
{status} / {myNodeNum} / {messages.length} msgs
; + return ( +
+ {status} · node #{myNodeNum} · {messages.length} msgs + +
+ ); } ``` -All hooks are read-only against a single `MeshClient` instance supplied through context. Commands are returned as stable functions. +## Several devices + +For apps that hold multiple connections at once — say, a desktop dashboard with a USB device plus a +Bluetooth one — use the registry provider instead. Hooks default to the "active" client, and you can +target a specific one by id when you need to. + +```tsx +import { MeshRegistry } from "@meshtastic/sdk"; +import { MeshRegistryProvider, useNodes } from "@meshtastic/sdk-react"; + +const registry = new MeshRegistry(); +registry.register("usb-1", usbClient); +registry.register("ble-1", bleClient); +registry.setActive("usb-1"); + +function App() { + return ( + + + + ); +} + +function Nodes() { + const nodes = useNodes(); // active client + return
{nodes.length} nodes
; +} +``` + +## Why this is a separate package + +The SDK used to live in `@meshtastic/core`, and its React conventions (effects, refs, a few hook +helpers) were baked right into protocol code. That made it impossible to ship the SDK to non-React +consumers — Node CLIs, Deno scripts, native shells — without dragging React along. + +When we split that up, the React bits moved here. Now the layering is straightforward: + +- `@meshtastic/sdk` — protocol, state, transports. No React, no DOM. +- `@meshtastic/sdk-react` — this. Hooks and providers, nothing else. +- `@meshtastic/transport-*` — per-runtime byte transports. + +If you're not using React, you don't need this package at all. Read `@meshtastic/sdk` directly. + +## Hooks + +Most components only need two or three of these. The full list is here for reference: + +**Plumbing** + +- `useClient()` — the client from `MeshProvider` +- `useActiveClient()` — the active client from a `MeshRegistryProvider` +- `useClientById(id)` — a specific client from the registry +- `useMeshRegistry()` / `useOptionalMeshRegistry()` — the registry itself +- `useSignal(signal)` / `useSignalValue(signal)` — bridge any SDK signal into React state + +**Connection** + +- `useConnection()` — status + lifecycle helpers +- `useConnectionProgress()` — live "configuring" progress with per-section counters (handy for + building a connecting overlay) +- `useMeshDevice()` — convenience wrapper around the legacy `MeshDevice` shim + +**Device & node state** + +- `useDevice()` +- `useNodes()` / `useNode(num)` +- `useNodeError(num)` / `useNodeErrors()` / `useHasNodeError(num)` + +**Messaging** + +- `useChat(channel)` — channel messages +- `useDirectChat(peer)` — DMs +- `useDraft(...)` — draft messages + +**Config** + +- `useChannels()` / `useChannel(idx)` +- `useConfig()` / `useModuleConfig()` / `useIsRegionUnset()` +- `useConfigEditor()` — staged edits with dirty tracking + +Each hook reads from a single signal, so updates only re-render components that actually use that +data. + +## Source / issues + + ## License diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 1727ffc1..bc7f3093 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -1,16 +1,14 @@ # @meshtastic/sdk -Domain-driven SDK for Meshtastic devices. Feature slices with signals-backed reactive state. - -Replaces `@meshtastic/core`. During migration a shim layer re-exports the legacy `MeshDevice` class so existing consumers keep building — see `src/shim/`. - -## Install +TypeScript SDK for talking to [Meshtastic](https://meshtastic.org) radios. Works in the browser, in +Node, and in Deno. Pick a transport, hand it a `MeshClient`, and you've got reactive access to the +device's chat, nodes, channels, config, and telemetry. ```sh pnpm add @meshtastic/sdk @meshtastic/transport-web-serial ``` -## Quickstart +## Quick example ```ts import { MeshClient } from "@meshtastic/sdk"; @@ -20,20 +18,90 @@ const transport = await TransportWebSerial.create({ baudRate: 115200 }); const client = new MeshClient({ transport }); await client.connect(); -client.chat.send({ text: "hello mesh" }); -console.log(client.nodes.list.value); +client.device.status.subscribe((s) => console.log("status:", s)); +console.log("my node:", client.device.myNodeNum.value); +console.log("nodes:", client.nodes.list.value); + +await client.chat.send({ text: "hello mesh" }); + +client.events.onConfigComplete.subscribe(() => console.log("configured")); +client.events.onRebooted.subscribe(() => console.log("device rebooted")); ``` -See the repo root [README](../../README.md) for architecture and feature slice layout. +State is exposed as signals. Subscribe to them, or read `.value` synchronously. Commands are async and +resolve when the device acks. -## Layout +## Why this is a new package +If you've used Meshtastic on the web before, you've probably seen `@meshtastic/core`. That package +mixed protocol, transports, and consumer state in one class. It worked, but it bled assumptions: +browser apps still pulled the Node serial driver, slice state had a few React-isms baked in, and there +wasn't a clean seam for unit testing or non-browser runtimes. + +We split it into three pieces: + +- `@meshtastic/sdk` (this one) owns the protocol, the queue, the codec, and the reactive state. No + React. No DOM. No Node builtins. It runs the same everywhere. +- `@meshtastic/transport-*` packages are thin byte adapters. Install only the ones you need. The SDK + talks to them through a tiny `Transport` interface (`fromDevice` / `toDevice` streams plus + `disconnect()`), so you can write your own without forking anything. +- `@meshtastic/sdk-react` lives separately for the React hooks. Skip it if you're not using React. + +The old `MeshDevice` class is still exported from `src/shim/` so existing `@meshtastic/core` apps can +upgrade without a full rewrite. The shim will be removed once the web client finishes its migration — +new code should use `MeshClient` directly. + +## What's in the client + +`MeshClient` is just a small composition root. The interesting bits are the feature clients hanging +off it: + +- `client.device` — identity, status, metadata, reboot / shutdown / factory-reset +- `client.chat` — channel + direct messages, drafts, unread tracking +- `client.nodes` — live node list, per-node lookups, error states +- `client.channels` — channel config +- `client.config` — device + module config, plus a staged-edit `ConfigEditor` +- `client.telemetry` — environment / device / power metrics +- `client.position` — position broadcast + node positions +- `client.traceroute` — mesh route discovery +- `client.files` — XModem get/put + +Each one can be used on its own; `MeshClient` just hands them a shared transport, queue, and event +bus. + +## Subpath exports + +| Import path | What's there | +| --- | --- | +| `@meshtastic/sdk` | `MeshClient`, feature clients, signal types | +| `@meshtastic/sdk/transport` | The `Transport` interface and related types — use this if you're writing a custom adapter | +| `@meshtastic/sdk/protobuf` | Re-export of `@meshtastic/protobufs` for raw packet construction | +| `@meshtastic/sdk/testing` | `createFakeTransport()` for unit tests | + +## Testing without a radio + +```ts +import { MeshClient } from "@meshtastic/sdk"; +import { createFakeTransport } from "@meshtastic/sdk/testing"; + +const { transport, respond } = createFakeTransport(); +const client = new MeshClient({ transport }); + +respond.withMyNodeInfo({ myNodeNum: 42 }); +respond.withConfigCompleteId(1); + +// client.device, client.nodes, etc. are now populated. ``` -src/ - core/ # shared kernel: client, transport, event-bus, queue, xmodem, signals, logging, packet-codec - features/ # DDD feature slices (device, chat, nodes, channels, config, telemetry, position, traceroute, files) - shim/ # legacy MeshDevice compatibility exports (removed in Phase C) -``` + +The fake transport is the same one the SDK's own integration tests use. + +## React? + +[`@meshtastic/sdk-react`](https://www.npmjs.com/package/@meshtastic/sdk-react). + +## Source / issues + + ## License