@dpup/meshcore-ts
v0.1.1
Published
Typed, ergonomic TypeScript wrapper around @liamcottle/meshcore.js for talking to MeshCore Companion Radio devices over TCP or USB serial.
Downloads
56
Maintainers
Readme
meshcore-ts
A typed, ergonomic TypeScript client for MeshCore Companion Radio devices.
meshcore-ts is a thin, typed wrapper over the official
@liamcottle/meshcore.js — it does
not reimplement the protocol. That library does the wire work; this one
delegates to it and adds a first-class TypeScript surface: typed methods, named
events, normalized values, and proper errors. So you talk to a MeshCore node over
WiFi or USB without wrangling raw numeric byte codes and Uint8Arrays — and you
get upstream protocol updates for free.
import { MeshCoreClient } from "@dpup/meshcore-ts";
const client = MeshCoreClient.tcp("192.168.1.50", 5000);
client.on("contactMessage", (msg) => console.log(`${msg.pubKeyPrefix}: ${msg.text}`));
await client.connect();
const contacts = await client.getContacts();Features
- 🧩 Fully typed — typed methods, events, and data models (
Contact,Channel,SelfInfo,RepeaterStats, …). - 📡 Named events —
client.on("contactMessage", …)instead ofconnection.on(0x83, …). - 🔤 Normalized data — hex-string keys & paths,
Datetimestamps, realbooleanflags. - 🛡️ Safe by default — typed errors (
MeshCoreError/…DeviceError/…TimeoutError) and request timeouts so a silent device never hangs your program. - 🔌 Node transports — TCP/WiFi and USB serial, with simple factories.
- 🪝 Escape hatch — the raw
meshcore.jsconnection stays reachable viaclient.raw.
Install
npm install @dpup/meshcore-ts # or: bun add @dpup/meshcore-ts / pnpm add @dpup/meshcore-tsESM-only, Node.js ≥ 18. The @liamcottle/meshcore.js dependency is installed automatically.
Quick start
import { MeshCoreClient, TxtType } from "@dpup/meshcore-ts";
// Connect over TCP/WiFi …
const client = MeshCoreClient.tcp("192.168.1.50", 5000);
// … or USB serial:
// const client = MeshCoreClient.serial("/dev/ttyACM0");
await client.connect();
const self = await client.getSelfInfo();
console.log(`Connected to "${self.name}" — ${self.publicKey}`);
const alice = await client.findContactByName("alice");
if (alice) {
await client.sendTextMessage(alice, "hello from typescript", TxtType.Plain);
}
await client.close();Any method that takes a contact accepts a Contact, a hex public-key string, or
raw Uint8Array bytes.
Events
By default the client auto-drains the device's message queue on msgWaiting and
emits typed events. Set { autoSync: false } to pull manually with
getWaitingMessages().
| Event | Payload | When |
| --- | --- | --- |
| connected / disconnected | — | connection lifecycle |
| contactMessage / channelMessage | ContactMessage / ChannelMessage | an incoming message |
| channelData | ChannelData | a channel datagram |
| advert / newAdvert | Advert / NewAdvert | a node advertised |
| pathUpdated | { publicKey } | a contact's path changed |
| rawData / logRxData | RawData / LogRxData | received packets (with SNR/RSSI) |
| traceData | TraceData | a path trace returned |
| telemetryResponse / statusResponse / binaryResponse | typed payloads | server responses |
| sendConfirmed / loginSuccess | typed payloads | send ack / login |
| error | Error | a surfaced async failure (e.g. a failed auto-sync) |
const client = MeshCoreClient.tcp(host, port, {
requestTimeoutMs: 10_000, // timeout for requests a device might never answer
autoSync: true, // auto-drain + emit on msgWaiting
});API
MeshCoreClient provides typed wrappers across the full companion API. All are
fully typed — your editor's autocomplete is the canonical reference.
- Device
getSelfInfo()getDeviceTime(),setDeviceTime(),syncDeviceTime()getBatteryVoltage(),deviceQuery(),reboot()exportPrivateKey(),importPrivateKey()
- Contacts
getContacts(),findContactByName(),findContactByPublicKeyPrefix()importContact(),exportContact(),shareContact(),removeContact()addOrUpdateContact(),setContactPath(),resetPath()setAutoAddContacts(),setManualAddContacts()
- Messaging
sendTextMessage(),sendChannelTextMessage()syncNextMessage(),getWaitingMessages()
- Channels
getChannel(),getChannels()setChannel(),deleteChannel()findChannelByName(),findChannelBySecret()
- Radio & adverts
sendAdvert(),sendFloodAdvert(),sendZeroHopAdvert()setAdvertName(),setAdvertLatLong()setTxPower(),setRadioParams()
- Remote nodes
login(),getStatus(),getTelemetry()getNeighbours(),sendBinaryRequest()
- Stats, signing & tracing
getStats(),getStatsCore(),getStatsRadio(),getStatsPackets()sign(),tracePath()
Examples
examples/list-contacts.ts— connect and print self info + contacts.examples/monitor.ts— a live, color-coded traffic monitor.
bun examples/monitor.ts 172.16.0.23 5000 # monitor until Ctrl-C
bun examples/monitor.ts 172.16.0.23 5000 30 # …for 30 secondsDesign & notes
- A wrapper, not a reimplementation. All protocol logic lives in
@liamcottle/meshcore.js; this package delegates to it and layers on types, normalization, and safety — so it tracks upstream automatically. The raw connection is always reachable viaclient.rawfor anything unwrapped. - Node-focused, ESM-only. Transports are TCP/WiFi and USB serial. ESM-only
mirrors
meshcore.jsand keeps theserialportdependency out of browser bundles; browser BLE/WebSerial transports are intentionally not exposed. - Values are normalized. Keys/paths/secrets are hex strings and timestamps are
Dates; the wrapper converts back when sending.
Development
bun install
bun run typecheck # tsc --noEmit (strict)
bun run test # vitest unit tests (no hardware needed)
bun run build # emit dist/ (ESM + .d.ts)See AGENTS.md for architecture and contribution notes.
License
MIT © Dan Pupius
