nexalwarenexalwaredocs
Simulate

Simulate

Act as a real Nexalware device, or a master orchestrating sub-devices, from a plain PC process - no physical hardware required.

@nexalware/simulate (npm) and nexalware-simulate (PyPI) let a plain Node.js or Python process act as a real Nexalware device, or a master orchestrating sub-devices, with none of the embedded firmware or physical hardware MicroPython/Arduino devices normally need.

This isn't a special simulation mode

Nexalware's device connection is plain MQTT with username/password auth, nothing embedded-specific about it - "anything with a TCP stack and an MQTT client library can connect the same way" (see the MicroPython Reference). This package doesn't add a new capability to the platform, it just gives a PC process the same easy API the REST SDK already has for the HTTP side, instead of you hand-rolling raw MQTT.

Two things this is for

  • Simulating a device you haven't built yet. Design a circuit in a simulator like Proteus, write real Arduino sketch code implementing the Sub-Device Contract over Serial, bridge it into your PC over a real or virtual COM port, and it shows up on your dashboard exactly like a physical board would. Prove a project works, run a classroom demo, before anyone needs to buy or solder anything.
  • Running a genuine production master from a PC instead of an ESP32. A PC is strictly more capable than a microcontroller, and nothing about a master requires embedded hardware specifically - this is a real, supported way to run one, not just a testing shortcut.

Install

npm install @nexalware/simulate
pip install nexalware-simulate

Only if you're bridging a serial/COM port (the Proteus workflow, or any real USB-serial board):

npm install serialport
pip install nexalware-simulate[serial]

Only if you're using WebSocketTransport (see Remote & Multi-Machine Simulation) or the nexalware-serial-bridge CLI, which needs both:

npm install ws
pip install nexalware-simulate[websocket]      # WebSocketTransport only
pip install nexalware-simulate[serial-bridge]  # the CLI (needs pyserial too)

serialport/ws/pyserial are optional - installing the base package never pulls in native serial bindings or a WebSocket library you don't need.

A single simulated device

import { NexalwareDevice } from "@nexalware/simulate";

const device = new NexalwareDevice({
  deviceId: "dev_a1b2c3",
  mqttUsername: "d_a1b2c3d4",
  mqttPassword: "your-device-password",
});

device.on("command", (cmd) => {
  device.publishStatus({ relay: cmd === "ON" ? "ON" : "OFF" });
});

await device.connect();
device.startHeartbeat();
from nexalware_simulate import NexalwareDevice

device = NexalwareDevice("dev_a1b2c3", "d_a1b2c3d4", "your-device-password")
device.on_command = lambda cmd, params: device.publish_status(relay="ON" if cmd == "ON" else "OFF")
device.connect()
device.start_heartbeat()

mqttUsername/mqttPassword come from the dashboard's Credentials tab for a device you've registered, the exact same credentials the MicroPython/Arduino references use.

A master with simulated sub-devices

import { MasterDevice, SerialTransport } from "@nexalware/simulate";

const master = new MasterDevice({ deviceId: "dev_master1", mqttUsername: "d_master1x", mqttPassword: "..." });
await master.connect();

const transport = new SerialTransport({ path: "COM3", baudRate: 9600 });
await transport.attach(master); // every message your sketch sends over Serial becomes a tracked sub-device

master.startHeartbeat();

See the full Proteus walkthrough for the circuit + Arduino sketch side of this.

API reference

NexalwareDevice(options)

OptionTypeRequiredMeaning
deviceIdstringyesThis device's public id, e.g. "dev_a1b2c3".
mqttUsernamestringyesFrom the dashboard's Credentials tab.
mqttPasswordstringyesFrom the same tab - shown once, generate new credentials if lost.
mqttHoststringnoOverride for a self-hosted deployment. Defaults to mqtt.nexalware.com.
mqttPortnumbernoOverride for a self-hosted deployment. Defaults to 1883.
  • connect() - connects and subscribes to the command topic, resolves once confirmed.
  • disconnect() - stops the heartbeat and closes the connection.
  • publishStatus(partial) - merges partial (relay, state, telemetry, uptime) into the last published status and publishes it. device_id/ts are filled in automatically.
  • startHeartbeat(intervalMs?) / stopHeartbeat() - republishes the last status on a timer (default 20s), so the device stays "online" under the backend's 35s heartbeat-timeout window.
  • on("connected" | "disconnected" | "command" | "error", ...) (Python: on_connected/on_disconnected/on_command/on_error callback attributes).

MasterDevice(options) - everything above, plus:

  • receiveSubDeviceMessage(channelId, raw) (Python: receive_sub_device_message) - feed it one line of raw JSON from a sub-device on channelId (a stable id for that physical connection, e.g. a serial port's path). One connection = one sub-device: a sub-device's identify is the only message carrying its id, every later message on the same channelId is assumed to be from the same sub-device.
  • publishStatus - same as above, but sub_devices is built automatically from everything sub-devices have reported.
  • on("subDeviceSend", (channelId, message) => ...) (Python: on_sub_device_send callback) - the master wants to send message down to the sub-device on channelId, your transport writes the actual bytes.

SerialTransport

new SerialTransport({ path: "COM3", baudRate: 9600 })
SerialTransport("COM3", baud_rate=9600)

Opens the port and wires its read/write directly to a master's receiveSubDeviceMessage/subDeviceSend. .attach(master) to start, .close() to stop. Same-machine only, see WebSocketTransport below for sub-devices on another machine.

WebSocketTransport

new WebSocketTransport({ port: 8080, token: "a-shared-secret" })
WebSocketTransport(port=8080, token="a-shared-secret")

The network-capable transport: hosts a WebSocket server sub-devices connect in to (the same model the reference ESP32 master uses), instead of a local serial port. Any number of sub-devices can connect simultaneously, each connection becomes its own tracked sub-device automatically. .attach(master) to start listening, .close() to stop. token is optional but strongly recommended off-localhost, it's checked at the WebSocket handshake, before an unauthorized connection can inject a fake sub-device. See Remote & Multi-Machine Simulation for the full walkthrough, including a serial-bridge CLI (nexalware-serial-bridge) for the Proteus-on-a-different-machine case.

Why isn't this an MCP tool?

@nexalware/mcp's tools are request/response, an agent calls one and gets an answer back. A device or master needs a long-held, continuously-listening MQTT connection, reacting to commands in real time, a fundamentally different shape than a stateless tool call - so this stays a library you run in your own script/process, not a tool an agent calls.

Where to go from here

On this page