Skip to main content

Device Connect Edge — lightweight edge device runtime with Zenoh/NATS messaging and D2D communication

Project description

device-connect-edge

Lightweight Python SDK for enabling physical devices to work with Device Connect. You write the device logic; the runtime handles registration, heartbeats, and command routing.

Contents

Where This Fits

  device-connect-edge        device-connect-server       device-connect-agent-tools
  (edge runtime — this)      (server runtime)            (agent tools)
        │                          │                           │
        └──────────────── Device Connect Mesh ─────────────────┘
  • device-connect-edge — runs on physical devices (Raspberry Pi, robots, cameras, sensors)
  • device-connect-server — runs on servers. Adds registry, security, state, and CLIs
  • device-connect-agent-tools — connects AI agents (Strands, LangChain, MCP) to the device mesh

Install

pip install device-connect-edge

Decorators

Decorator Purpose
@rpc() Expose a method as a remotely-callable function
@emit() Declare an event that can be published to subscribers
@periodic(interval=N) Run a method every N seconds in the background
@on(device_type=..., event_name=...) Subscribe to events from other devices (D2D)
@before_emit("event_name") Intercept an event before it's published

Quick Start

After installing device-connect-edge, write a driver and run it.

1. Write a driver

from device_connect_edge.drivers import DeviceDriver, rpc, emit, periodic
from device_connect_edge.types import DeviceIdentity, DeviceStatus

class SensorDriver(DeviceDriver):
    device_type = "sensor"

    @property
    def identity(self) -> DeviceIdentity:
        return DeviceIdentity(device_type="sensor", manufacturer="Acme", model="TH-100")

    @property
    def status(self) -> DeviceStatus:
        return DeviceStatus(availability="available")

    @rpc()
    async def get_reading(self) -> dict:
        """Return the current sensor reading."""
        return {"temperature": 22.5, "humidity": 45}

    @emit()
    async def alert(self, level: str, message: str):
        """Emit an alert event."""
        pass

    @periodic(interval=10.0)
    async def poll_sensor(self):
        reading = await self.get_reading()
        if reading["temperature"] > 30:
            await self.alert(level="warning", message="High temperature")

    async def connect(self) -> None:
        pass  # initialize hardware

    async def disconnect(self) -> None:
        pass  # cleanup hardware

2. Connect to the mesh

import asyncio
from device_connect_edge import DeviceRuntime

async def main():
    device = DeviceRuntime(
        driver=SensorDriver(),
        device_id="sensor-001",
        messaging_urls=["tcp/localhost:7447"],
        # Or use NATS:
        # messaging_urls=["nats://localhost:4222"],
    )
    await device.run()

asyncio.run(main())

3. Run the simulator

Save the code above to my_sensor.py and run it:

# Zenoh (default) — or omit messaging_urls entirely for D2D mode
DEVICE_CONNECT_ALLOW_INSECURE=true python my_sensor.py

# Or NATS
# DEVICE_CONNECT_ALLOW_INSECURE=true NATS_URL=nats://localhost:4222 python my_sensor.py

4. More examples

Example Description
examples/number_generator/ Simulated random number generator with on-demand and periodic emission
examples/string_generator/ Simulated random word fragment generator with mood themes
examples/dht22_sensor/ Real DHT22 temperature/humidity sensor on Raspberry Pi

Real hardware drivers run as a Python process on the physical device and require credentials provisioned by device-connect-server.

# Real hardware (on the device)
NATS_CREDENTIALS_FILE=~/.device-connect/credentials/dht22-001.creds.json python examples/dht22_sensor/device_driver.py

Device-to-Device Mode (No Infrastructure)

Devices can discover each other directly on the LAN without any infrastructure (no broker, no etcd, no device registry). This uses Zenoh's built-in multicast scouting.

D2D mode is the default when no broker endpoint URLs are configured:

device = DeviceRuntime(
    driver=SensorDriver(),
    device_id="sensor-001",
    allow_insecure=True,
    # No messaging_urls → Zenoh peer mode with multicast discovery
)
await device.run()

Or via environment variables:

DEVICE_CONNECT_ALLOW_INSECURE=true python my_device.py

To force D2D mode even when a router URL is set (e.g., router available but no registry):

DEVICE_CONNECT_DISCOVERY_MODE=d2d ZENOH_CONNECT=tcp/localhost:7447 DEVICE_CONNECT_ALLOW_INSECURE=true python my_device.py

How it works: Each device announces its presence (capabilities, identity, status) via device-connect.{tenant}.{device_id}.presence messages. Other devices subscribe to a wildcard and maintain an in-memory peer table. Device-to-device RPC works identically to infrastructure mode.

Trade-offs vs full infrastructure:

Full Infrastructure D2D Mode
Device state Persistent (etcd) Ephemeral (in-memory)
Offline tracking Registry remembers devices Gone when device stops
Cross-network Zenoh router bridges LANs LAN only (multicast)
Scale 1000s of devices ~50-100 devices

Credentials

Credentials are generated server-side using device-connect-server's provisioning tools. See device-connect-server — Device Commissioning.

The credentials file is JSON with JWT and NKey seed:

{
  "device_id": "sensor-001",
  "auth_type": "jwt",
  "tenant": "default",
  "nats": {
    "urls": ["nats://nats-jwt:4222"],
    "jwt": "<NATS user JWT>",
    "nkey_seed": "<NKey seed>"
  }
}

Pass the file path via environment variable or constructor parameter:

# Via environment variable
NATS_CREDENTIALS_FILE=~/.device-connect/credentials/sensor-001.creds.json \
  NATS_URL=nats://localhost:4222 python my_device.py
# Via constructor
device = DeviceRuntime(
    driver=SensorDriver(),
    device_id="sensor-001",
    nats_credentials_file="~/.device-connect/credentials/sensor-001.creds.json",
    messaging_urls=["nats://localhost:4222"],
)

For development without auth, set DEVICE_CONNECT_ALLOW_INSECURE=true or pass allow_insecure=True to DeviceRuntime.

Testing

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/ -v --timeout=30

Unit tests run without external services. Integration tests are in tests/.

Contributing

We welcome contributions! Please open an issue to report bugs or suggest features, or submit a pull request directly.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

device_connect_edge-0.2.1.tar.gz (98.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

device_connect_edge-0.2.1-py3-none-any.whl (84.3 kB view details)

Uploaded Python 3

File details

Details for the file device_connect_edge-0.2.1.tar.gz.

File metadata

  • Download URL: device_connect_edge-0.2.1.tar.gz
  • Upload date:
  • Size: 98.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for device_connect_edge-0.2.1.tar.gz
Algorithm Hash digest
SHA256 7294bb482d22a5dc1f58ca2dc302a7b57e17db2de360e0855754a59c279e261f
MD5 3c4453b95fe442f5dd8b8526a309318e
BLAKE2b-256 e00f2fd5cca58bfd5620cd6de1119a05b65fa255098931d0d64b54ea756b2eac

See more details on using hashes here.

File details

Details for the file device_connect_edge-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for device_connect_edge-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 39f1774bd0e3cede2a962c1d90b80bfabf3923af89c52bb4b266777f497e0354
MD5 bfeda9483de0f8e4a8416954b0bc3eaf
BLAKE2b-256 eca9d9061b3e60ccf79b951c26b03c8674b10f578c014dc63dd7fe48a740a0e9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page