npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@edgez/react-native-sdk

v0.1.32

Published

React Native SDK for EdgeZ HaLow mesh devices

Readme

EdgeZ React Native SDK

React Native/Expo SDK for EdgeZ HaLow mesh devices, ported from the EdgeZ Flutter SDK. Native transports are included for Android, macOS, and Windows; iOS is not yet implemented.

Included

  • BLE scan, connect, disconnect, EdgeZ framing, and event delivery
  • HaLow mesh initialization and license credentials
  • protobuf-compatible status, settings, beacon, topology, and message packets
  • X25519 + AES-256-GCM encrypted conversations
  • chunked encrypted voice-message protocol
  • native Opus/AMR voice-message recording and playback
  • device provisioning settings and Lua driver transfer
  • UI-independent ESP32, nRF54, and H7608 provisioning over ESP-IDF BLE and SoftAP transports, including upstream Wi-Fi setup
  • BLE firmware OTA with acknowledged writes, progress, and cancellation
  • Android USB/IP host support for remote ESP32, J-Link, and OpenOCD flashing
  • verified Android React Native bundle updates through the firmware OTA proxy, with native-runtime compatibility checks and automatic crash rollback
  • Android BLE foreground service and message/call notification channels
  • best-known Android location lookup for shared beacons
  • native Organic Maps view with mesh-node markers, offline map downloads, themes, 2D/3D perspective, and optional satellite tiles on Android
  • identity, BLE preference, and installed-driver persistence
  • stateful EdgezMeshSession and React useEdgezMesh hooks
  • an Expo SDK 57 development-client example
  • a shared-UI React Native desktop example for macOS and Windows

Live voice-call audio still needs to be ported from the Flutter native plugin. Its public methods report not_available rather than silently behaving incorrectly. Text and recorded voice messages are supported.

Install

npm install @edgez/react-native-sdk \
  @react-native-async-storage/async-storage

The SDK contains native modules, so rebuild the native application after installing it; Expo Go cannot load them.

Android app bundle updates

An Android host app can load a release JS bundle from private app storage while keeping the bundle embedded in the APK as a safe fallback. Configure the host's MainApplication to pass EdgezBundleUpdateManager.resolveBundleFile(applicationContext, BuildConfig.DEBUG, runtimeVersion) as the jsBundleFilePath used to create its React host. The runtime version must be changed whenever native modules or other native app compatibility changes.

After React mounts, mark the selected bundle healthy and check the same constrained OTA release proxy used by device firmware:

import {
  checkAndInstallAppBundleUpdate,
  markAppBundleUpdateHealthy,
} from '@edgez/react-native-sdk';

await markAppBundleUpdateHealthy();
const result = await checkAndInstallAppBundleUpdate({
  repositoryUrl: 'https://github.com/example/mobile-app',
});
// result.state === 'installed' means the verified update is used next launch.

The release must contain live-stocking-update.json and the bundle asset named by that manifest. The SDK verifies the manifest against the installed APK signing certificate, then checks the SHA-256 and runtime version before staging it. If the first launch of a staged bundle exits before it is marked healthy, the following launch automatically restores the APK bundle. Bundle updates that trigger this rollback are not installed repeatedly. Bundle updates may change JavaScript only; adding native dependencies still requires a new APK.

On macOS the SDK autolinks through CocoaPods. On Windows it autolinks the C++ React Native Windows project and the consuming app must declare the Bluetooth device capability.

Basic use

import React, {useEffect, useMemo} from 'react';
import {EdgezIdentityStore, EdgezMeshSession, useEdgezMesh} from '@edgez/react-native-sdk';

export function MeshScreen() {
  const session = useMemo(() => new EdgezMeshSession(), []);
  const state = useEdgezMesh(session);

  useEffect(() => () => session.dispose(), [session]);

  async function initialize() {
    const identity = await new EdgezIdentityStore().getOrCreate();
    await session.initializeMesh({
      identity,
      countryCode: 'SE',
      meshId: 'edgez',
      passphrase: '',
      maxHop: 4,
      beacon: {marker: 'blue'},
    });
  }

  // Render state.bleDevices, state.nodes, state.conversations, etc.
  return null;
}

Typical connection sequence:

await session.startBleScan();
await session.connectBle(device.id);
// When state.bleReady becomes true:
await session.initializeMesh(config);
await session.sendTextMessage(node.nodeNum, 'Hello mesh');

Applications that use another state architecture can construct EdgezMeshSdk directly. Tests can inject an EdgezPlatformTransport without Android or BLE hardware.

Device provisioning

EdgezProvisioningManager owns discovery, permissions, connections, protocol details, upstream Wi-Fi scanning, configuration ordering, persistence checks, and cleanup. The application is responsible only for presenting and styling the returned devices and collecting configuration fields:

import {EdgezProvisioningManager} from '@edgez/react-native-sdk';

const provisioning = new EdgezProvisioningManager();
const {devices, warnings} = await provisioning.scan();
const device = devices[0];

await provisioning.connect(device, 'abcd1234');
const networks = device.supportsUpstreamWifi
  ? await provisioning.scanUpstreamWifi(device)
  : [];

await provisioning.configure(device, {
  clientId,
  username: device.serial,
  password,
  projectId,
  channel,
  meshId,
  passphrase,
  country,
  halowChannel,
  wifiUpstream: true,
  deviceName: userEnteredName,
  softapPassword: userEnteredDeviceWifiPassword,
  useDeviceGps,
}, {ssid: networks[0].ssid, passphrase: upstreamPassword});

await provisioning.disconnect(device);

ESP32 BLE and H7608 SoftAP discovery, connection, Wi-Fi scanning, and upstream Wi-Fi provisioning all use @orbital-systems/react-native-esp-idf-provisioning. The H7608 implements the standard ESP-IDF Security 0 prov-session, prov-scan, and prov-config endpoints; the SDK calls its custom mqtt-config endpoint before completing upstream Wi-Fi provisioning. A configuration result is accepted only when the gateway returns both ok and persisted. After the gateway applies the configuration, its AP uses deviceName and the user-selected softapPassword. Device-specific payload fields—including the H7608 SoftAP SSID and nRF54 HaLow frequency and BLE-safe name—are derived inside the SDK. UI code can use each device's capability and presentation metadata such as requiresProofOfPossession, requiresDeviceName, supportsUpstreamWifi, supportsDeviceGps, deviceNameMaxLength, category, and firmwareTarget.

Expo applications should add only @edgez/react-native-sdk to their plugins configuration. The SDK plugin configures its BLE and ESP-IDF provisioning dependencies, including both BLE and SoftAP transport permissions.

Remote USB flashing (Android)

The Android SDK can export USB devices attached to the phone through the same userspace USB/IP implementation used by EdgeZ Android DevTools. The phone owns the USB connection; a separate flash server runs esptool, SEGGER J-Link tools, or OpenOCD and reaches the SDK's abstract socket through the application's authenticated tunnel.

For ESP32, the app can use the system document picker and the managed high-level flow. pickUsbFirmware calculates the size and SHA-256 on Android without loading the complete image into JavaScript. discoverUsbDevices starts USB Host mode, asks for Android USB permission, and returns selectable bus IDs.

const sdk = new EdgezMeshSdk();
const firmware = await sdk.pickUsbFirmware();
if (!firmware) return; // The user closed the picker.

const [device] = await sdk.discoverUsbDevices();
if (!device) throw new Error('Connect an ESP32 over USB-C');

const result = await sdk.flashEsp32Firmware({
  endpoint: 'https://appwrite.edgez.ai/v1',
  projectId: appwriteProjectId,
  teamId: organizationId,
  jwt: await account.createJWT().then(value => value.jwt),
  busId: device.busId,
  chip: 'esp32s3', // also: esp32, esp32c3
  firmwareUri: firmware.firmwareUri,
  onProgress: status => {
    const percent = status.size ? Math.round(100 * (status.received ?? 0) / status.size) : undefined;
    console.log(status.state, percent, status.message);
  },
});
console.log(`Flashed ${result.size} bytes`);

The built-in ESP32 profiles write one full/merged image at address 0x0. Select a merged flash image produced for the exact chip and board. An ESP-IDF OTA application image such as *-ota.bin is not a full device image and must not be used with this flow.

flashEsp32Firmware waits for the WebSocket, uploads and verifies the image, waits for esptool to finish, and closes the USB export afterward. Pass a stable jobId if the UI needs a Cancel button, then call cancelUsbFlash(jobId).

For a versioned GitHub release, keep the firmware off the phone and let the runtime download it directly. Pass the SHA-256 published by GitHub's release asset metadata:

await sdk.flashEsp32ReleaseFirmware({
  projectId: appwriteProjectId,
  teamId: organizationId,
  jwt: await account.createJWT().then(value => value.jwt),
  busId: device.busId,
  chip: 'esp32s3',
  baudRate: 460800,
  ackWindow: 5,
  flashTimeoutMs: 30 * 60_000,
  esptoolConfig: 'high-latency',
  firmwareUrl: 'https://github.com/edgez-ai/example/releases/download/v1.0.0/firmware.bin',
  sha256: '<GitHub release asset SHA-256>',
  onProgress: status => console.log(status.state, status.message),
});

This sends only the URL and digest over the control channel. The runtime downloads the image, enforces its size limit, verifies SHA-256, and then flashes it through the phone's USB/IP connection.

An nRF54L15 + HT-HC01 release HEX can use the same managed flow with the server-controlled SEGGER profile:

await sdk.flashNrf54JLinkReleaseFirmware({
  projectId: appwriteProjectId,
  teamId: organizationId,
  jwt: await account.createJWT().then(value => value.jwt),
  busId: device.busId,
  firmwareUrl: 'https://github.com/edgez-ai/example/releases/download/v1.0.0/live-stocking-hc01.hex',
  sha256: '<GitHub release asset SHA-256>',
  onProgress: status => console.log(status.state, status.message),
});

The client selects only the fixed nrf54l15-jlink profile. Device name, SWD speed, reset sequence, and J-Link command script remain operator-controlled in the runtime profile file.

An XIAO nRF54L15 exposed through its CMSIS-DAP probe uses the fixed OpenOCD profile instead:

await sdk.flashNrf54OpenOcdReleaseFirmware({
  projectId: appwriteProjectId,
  teamId: organizationId,
  jwt: await account.createJWT().then(value => value.jwt),
  busId: device.busId,
  firmwareUrl: 'https://github.com/edgez-ai/example/releases/download/v1.0.0/live-stocking-nrf54l15-sense.hex',
  sha256: '<GitHub release asset SHA-256>',
  onProgress: status => console.log(status.state, status.message),
});

The client can select only nrf54l15-openocd. The backend remains responsible for authentication, profile selection, release download, and SHA-256 verification. For runtimes configured with the Android CMSIS-DAP accelerator, the verified image is streamed to the SDK and its latency-sensitive SWD flash algorithm runs next to the USB probe. Individual applications do not contain target-specific flashing code.

The lower-level API remains available when an application needs to keep a tunnel open or manage several operations itself:

const sdk = new EdgezMeshSdk();
const status = await sdk.startManagedUsbFlashTunnel({
  endpoint: 'https://appwrite.edgez.ai/v1',
  projectId: appwriteProjectId,
  teamId: organizationId,
  jwt: await account.createJWT().then(result => result.jwt),
  busId: '1-2',
});

await sdk.flashUsbFirmware({
  jobId: 'flash-20260925-1',
  profile: 'esp32s3',
  baudRate: 460800,
  ackWindow: 5,
  timeoutSeconds: 1800,
  esptoolConfig: 'high-latency',
  firmwareUri: 'content://com.example.files/firmware.bin',
  size: 1048576,
  sha256: '<64 lowercase hex characters>',
});

// Optional cancellation; progress and tool logs arrive as usbTunnelMessage
// events containing flash.status / flash.log JSON.
await sdk.cancelUsbFlash('flash-20260925-1');

console.log(status.socketName, status.routePort, status.devices);

const unsubscribe = sdk.subscribe(event => {
  if (event.type === 'usb') console.log(event.usbEvent);
});

// Stop exporting USB when flashing is finished.
await sdk.stopUsbFlashTunnel();
unsubscribe();

socketName names an Android abstract Unix socket and is deliberately not a public TCP listener. The app must pass it to its secure flash-server tunnel. USB/IP route port 3240 is provided for tunnel routing. Android asks the user for USB-host permission for each attached device. The transport supports CP210x-style ESP32 serial flashing, SEGGER J-Link, and generic USB control/bulk/interrupt transfers used by OpenOCD-compatible probes. The flash server remains responsible for selecting firmware, invoking the flashing tool, reporting progress, and authenticating the operation.

startManagedUsbFlashTunnel creates a five-minute, organization-scoped flash session through the authenticated Appwrite API. The JWT is used only for that HTTPS exchange and is not passed to the native tunnel. Applications that already obtain a flash session can call startUsbFlashTunnel with its url and token directly.

The tunnel requires wss://, sends its short-lived credential as a Bearer token, identifies the selected USB device with X-EdgeZ-USB-Bus-ID, and sends versioned binary frames that distinguish USB/IP from firmware chunks. The runtime grants upload credits only after chunks are persisted. Text frames control the flash job and are surfaced as USB tunnel events for status and progress messages. Only one tunnel can run in an SDK instance at a time, and a dropped connection is not silently resumed during a flash.

Organic Maps (Android)

The Android SDK includes EdgeZ Organic Maps 0.0.8. Android API 26 or newer is required. The package supplies its native dependencies and R8 rules; an Expo prebuild also needs the EdgeZ Ivy repository and Java core-library desugaring. The example's withOrganicMaps config plugin applies those settings automatically.

import React, {useRef} from 'react';
import {
  EdgezOrganicMap,
  type EdgezOrganicMapRef,
} from '@edgez/react-native-sdk';

export function MapScreen() {
  const map = useRef<EdgezOrganicMapRef>(null);

  return <EdgezOrganicMap
    ref={map}
    style={{flex: 1}}
    nodes={[{
      id: 'node-1',
      label: 'Sheep tag',
      latitude: 59.3293,
      longitude: 18.0686,
      marker: 'blue',
      icon: 'sheep',
    }]}
    lines={[{
      id: 'pasture-boundary',
      points: [
        {latitude: 59.3293, longitude: 18.0686},
        {latitude: 59.3300, longitude: 18.0700},
        {latitude: 59.3285, longitude: 18.0710},
        {latitude: 59.3293, longitude: 18.0686},
      ],
      color: '#e88d29',
    }]}
    zoom={9}
    enableMapDownloads
    onMapRegionAvailable={regionId => map.current?.downloadRegion(regionId)}
    onMapError={console.warn}
  />;
}

The ref also supports camera reads/updates, day/night themes, 2D/3D perspective, remote XYZ satellite tiles, and a bundled MBTiles satellite asset. The component renders an availability message on non-Android platforms. Lines are drawn by the native map renderer. Repeat the first point at the end to close a geofence boundary. Use edgezMapIcons for the supported icon names. icon and marker are independent, so the same icon can use any supported marker color.

Expo example

The example follows the managed Expo structure used by template-iot-prov: Expo SDK 57, expo/AppEntry.js, app.config.js, and a development-client/EAS profile.

cd example
npm install
npm run android   # creates and installs the native Expo development build
npm run start     # later JavaScript-only iterations

The example includes BLE connection, mesh status, encrypted text and recorded voice messages, notification permission, OTA readiness, and an Organic Maps tab with geolocated mesh-node markers and offline downloads. Its Flutter-aligned Nodes tab groups discovered devices by HaLow channel, manages the five public talkgroups, shows routes and node details, and exposes channel selection. The Settings tab includes user identity/location, country/bandwidth/channel setup, and device GPS, geofence, sensor, upstream network, and sleep controls.

Android example releases

The Build Android example release GitHub Actions workflow runs manually or when a v* tag is pushed. Configure these repository secrets before running it:

  • ANDROID_KEYSTORE_BASE64: base64-encoded Android release keystore
  • ANDROID_KEYSTORE_PASSWORD: keystore and key password

The keystore must contain the edgez-android-release alias. The workflow builds the Expo native project once, preserves its unsigned APK, directly signs that APK, signs the app bundle, verifies both signatures, and uploads all three packages. Tag runs also attach the packages to the matching GitHub Release.

The Build Windows example release workflow uses the same manual and v* tag triggers. It generates the ignored React Native Windows host, builds an x64 Release without deploying it, and uploads the complete Windows AppPackages directory as edgez-react-native-example-windows-x64.zip. Keeping the package, dependency files, and installation script together makes the artifact usable as generated by the React Native Windows toolchain. Tag runs attach the ZIP to the matching GitHub Release. This is the template's developer/test package; production Windows code signing requires a trusted PFX certificate and matching publisher identity, which are not currently configured in this repository.

Desktop example

The desktop-example reuses the same Nodes, Messages, and Settings source as the Expo example. The macOS host uses React Native 0.81.6 with React Native macOS 0.81.9. Its windows-app uses the actively supported React Native 0.84.1 and React Native Windows 0.84.0. The hosts are separate because the current desktop platform versions do not share a compatible React Native minor.

cd desktop-example
npm install

# macOS
npx pod-install macos
npm run macos

# Windows (run on Windows once before the first build)
cd windows-app
npm install
npm run windows:init
npm run windows

Desktop currently supports BLE scan/connect, framing, mesh setup, Nodes, Settings, and encrypted text messages. OTA, OS notifications/location, and recorded voice are still Android-only and fail explicitly on desktop.

Development

npm install
npm run typecheck
npm test -- --runInBand
npm run build

The wire schema is committed at protos/edgez_mesh.proto. Protocol tests cover initialization fields, 64-bit IDs, topology reports, and the Flutter-compatible 220-byte driver upload chunks.

SDK release credential

The source currently carries the signed compatibility credential from the Flutter 0.1.0 reference so it can target the same firmware compatibility range. Before publishing an official React Native release, replace it with a React-Native-specific credential signed by the EdgeZ SDK release process.