mqtt-scenario-sim
v1.0.2
Published
Configurable MQTT sensor simulator with built-in test scenarios. Publish synthetic sensor data over MQTT — as JSON or protobuf — without real hardware.
Maintainers
Readme
mqtt-scenario-sim
A configurable MQTT sensor simulator with built-in test scenarios. Publish synthetic sensor data over MQTT — as JSON or protobuf — without real hardware.
Define your sources with any labels you want. Wire up any MQTT topic template. Switch between test scenarios over HTTP to drive your alert and pipeline logic.
Maintenance: This project is maintained on a best-effort basis. Bug reports and PRs are welcome — response times may vary.
Quick start
npx mqtt-scenario-sim --config examples/minimal.yamlRequires a running MQTT broker (e.g. docker run -p 1883:1883 eclipse-mosquitto).
Scenarios
The real differentiator. Switch your entire simulator into a controlled test state at any time — no restarts.
| ID | Key | What it does |
|---|---|---|
| 0 | normal | Each metric runs its configured mode |
| 1 | out_of_range | All metrics 30% above max — tests breach detection |
| 2 | trending_to_breach | Rising toward max — tests early-warning alerts |
| 3 | stable_healthy | Held at midpoint ideal — tests steady-state |
| 4 | recovery | Starts out-of-range, decays to ideal over ~5 min |
| 5 | oscillating | ±10% swing — tests flapping suppression |
# Activate a scenario
curl -X POST http://localhost:4000/scenario/1
# With auto-revert after 60 seconds
curl -X POST "http://localhost:4000/scenario/4?durationSeconds=60"
# Target a single source by its labels key
curl -X POST "http://localhost:4000/scenario/2?sourceKey=%7B%22device%22%3A%22node-1%22%7D"
# Check current scenario
curl http://localhost:4000/scenario
# Back to normal
curl -X POST http://localhost:4000/scenario/0YAML config
mqtt:
host: localhost
port: 1883
publishIntervalMs: 5000
encoding:
type: json # or "protobuf" (see Protobuf section)
sources:
- labels: # fully freeform — any keys you want
building: hq
floor: "3"
zone: east
topic: "{building}/{floor}/{zone}/metrics" # template using label keys
metrics:
- name: temperature
units: "°C"
mode: sinusoidal # sinusoidal | drift | normal | spike
range: { low: 18, high: 28 }
periodSeconds: 3600 # optional
- name: humidity
units: "%"
mode: drift
range: { low: 30, high: 70 }
effects: # optional — external things that bias metric readings
- name: hvac
effects:
temperature: -0.40 # fraction of range span, negative = reduce
humidity: -0.20Sensor modes
| Mode | Description |
|---|---|
| sinusoidal | Smooth oscillation over periodSeconds |
| normal | Gaussian noise around baseline |
| drift | Slow random walk that bounces at range edges |
| spike | Normal noise with random anomaly spikes |
Effects
Effects let you simulate external influences on metric readings. Send a command to {topic}/cmd:
{ "effect": "hvac", "state": true }Each effect's effects map specifies bias as a fraction of the metric's span. -0.40 on temperature with a span of 10°C = -4°C bias.
Protobuf
encoding:
type: protobuf
protoFile: ./my.proto
messageType: myapp.SensorReading
fieldMap: # optional — remap internal fields to proto field names
metric: sensor_name
value: reading_value
timestamp: recorded_atInternal fields available for mapping: metric, value, units, timestamp, plus any label key (e.g. building, floor).
HTTP API
| Method | Path | Description |
|---|---|---|
| GET | /stream | SSE stream — one event per metric publish |
| GET | /status | Full snapshot: uptime, scenario, all readings, all effects |
| GET | /health | Status, uptime, source/metric counts, active scenario |
| GET | /scenario | Current scenario name + description |
| POST | /scenario/:id | Activate scenario (0–5 or name). Query: durationSeconds, sourceKey |
| GET | /state | Last published value per metric |
| GET | /effects | Current effect state per source |
Live tailing with /stream
/stream is a Server-Sent Events endpoint. It pushes one JSON event per metric publish and stays open until you disconnect.
curl -N http://localhost:4000/streamdata: {"labels":{"building":"hq","floor":"3"},"topic":"hq/3/east/metrics","metric":"temperature","units":"°C","value":22.45,"scenario":"normal","timestamp":1747613001234}
data: {"labels":{"building":"hq","floor":"3"},"topic":"hq/3/east/metrics","metric":"humidity","units":"%","value":58.1,"scenario":"normal","timestamp":1747613001235}Multiple clients can connect simultaneously. The scenario field reflects the active scenario at publish time.
Full snapshot with /status
curl http://localhost:4000/status{
"status": "ok",
"uptime": 142,
"sources": 1,
"metrics": 2,
"scenario": {
"id": "normal",
"label": "0 / n — Normal (uses each metric's configured mode)",
"detail": "Each metric runs its configured mode: sinusoidal, drift, normal, or spike."
},
"readings": [
{ "labels": { "building": "hq", "floor": "3" }, "metric": "temperature", "units": "°C", "value": 22.45, "lastPublishedAt": 1747613001234 },
{ "labels": { "building": "hq", "floor": "3" }, "metric": "humidity", "units": "%", "value": 58.1, "lastPublishedAt": 1747613001235 }
],
"effects": {
"hq/3/east/metrics": { "hvac": false }
}
}CLI args
| Argument | Description |
|---|---|
| --config <path> | Path to YAML config file |
| --log-level <level> | silent | error | warn | info | debug (default: info) |
| -h, --help | Print help and exit |
# Verbose — see every metric publish
npx mqtt-scenario-sim --config my.yaml --log-level debug
# Quiet — errors only
npx mqtt-scenario-sim --config my.yaml --log-level error
# Help
npx mqtt-scenario-sim --helpEnv vars
| Variable | Default | Description |
|---|---|---|
| MQTT_HOST | localhost | MQTT broker host |
| MQTT_PORT | 1883 | MQTT broker port |
| PUBLISH_INTERVAL_MS | 5000 | Default publish interval |
| ENCODING | json | json or protobuf |
| PROTO_FILE | — | Path to .proto file |
| PROTO_MESSAGE_TYPE | — | Fully-qualified proto message type |
| PROTO_FIELD_MAP | — | JSON string field map |
| CONFIG_PATH | examples/minimal.yaml | Path to YAML config |
| PORT | 4000 | HTTP control plane port |
| LOG_LEVEL | info | silent | error | warn | info | debug |
CLI args take precedence over env vars.
Examples
examples/minimal.yaml— 1 source, 1 metric, JSONexamples/greenhouse.yaml— multi-source, multi-metric IoT exampleexamples/custom-proto.yaml— protobuf with fieldMap
Docker
A Dockerfile is included. Build and run with your own config:
docker build -t mqtt-scenario-sim .
docker run --rm -e MQTT_HOST=host.docker.internal mqtt-scenario-simTo use a custom config, mount it at runtime:
docker run --rm \
-v $(pwd)/my-config.yaml:/app/my-config.yaml \
-e CONFIG_PATH=/app/my-config.yaml \
-e MQTT_HOST=host.docker.internal \
mqtt-scenario-simLicense
MIT
