mock-devices is a simulation engine that manages and runs simulated devices that connect to an Azure Iot Hub. When hosted in the Azure IoT Edge runtime, the engine will simulate Edge modules too. The simulated devices and modules implement D2C/C2D scenarios i.e telemetry, twin and commands as supported by the Azure IoT Device SDK
Each configured device/module acts independently of other devices/modules running within the engine. Each has its own model (capabilities), configuration and connection details. Devices/modules running on the same simulation engine can be a mix of connection strings, DPS, SaS, Edge modules. The engine has additional scenarios like cloning, bulk, templates and acknowledgements. See internal help
The Docker edition is a Docker container of the running simulation engine. It exposes a REST endpoint for control and data plane operations. Use this edition to run the Edge modules configured in a mock-devices state file (and deploy as an Edge module) It is also useful where a headless simulation experience per state file is required. It is part of a suite of mock-devices tools
Other editions
-
The mock-devices edition of the tool is the single install UX + engine experience. It provides an interactive experience for running and controlling the simulation as well as managing the devices/modules. It can be used to create a state file which can be used to run any mock-devices engine of the same version
-
The mock-devices-edge edition is a Docker container configured as an Edge module that can be used to manage basic operations for running instances of mock-devices-de within the same Edge runtime. Clients can interact with the simulation engine using Twin Desired and Direct Commands making it an alternative to doing REST
-
The mdux edition is a Docker container build of the desktop edition. It is a fully functional UX + simulation engine mock-devices instance and is useful for dynamic module scenarios. It is feature limited to run inside containers with no access to file system. It can also be used as a "terminal" UX experience to see other running mock-devices engines connecting via IP or DNS
See the desktop edition for details about the state file
Install Docker and execute the following commands. Default port is 9000. The following example remaps to 8989
docker pull codetunez/mock-devices-de
docker run -p 8989:9000 codetunez/mock-devices-de
Clone this repo and from the command line
Node
Install and Build
npm ci && npm run build
node _dist/start.js
Docker
Building the container
docker build -t <namespace>/mock-devices-de .
Run the container (changing port)
docker run -p 8989:9000 -d <namespace>/mock-devices-de
Use a state file
Once the container is running, load a state file by using one generated by the desktop edition or by using APIs below
See running containers
docker ps
Load your state (or reload new state)
curl -d "@<state file>" -H "Content-Type: application/json" -X POST http://127.0.0.1:8989/api/state
Start all devices
curl http://127.0.0.1:8989/api/devices/start
See the console
docker logs -f <container id>
Stop all devices
curl http://127.0.0.1:8989/api/devices/stop
These docs do not have the complete updates for Plan mode, Edge modules, SSE, Simulation and changes to response payloads. Doc will incrementally update
mock-devices DE has several resource endpoints to manage the simulation
The following endpoints deal with the core of creating, running and updating devices as well as adding capabilities to send and receive data.
- /api/device - CRUD for a device including start/stopping the device
- /api/devices - Gets the list of current devices and starts/stops all devices
The following endpoints deal with the service state and the misc simulation variables
- /api/state - Gets or sets the current state machine and simulation config
- /api/simulation - Gets or sets the simulation configuration. All devices restart required
The following endpoint deals with reference data
- /api/sensors - Gets a list of fake sensors that can be added to a device to send simulated data
property | type | usage |
---|---|---|
devices | array | The list of device objects in the state machine |
simulation | object | The current configuration for the simulation |
EXAMPLE
{
"devices" : []
"simulation" : {}
}
POST
state object
<devices> curl -d "<state>" -H "Content-Type: application/json" -X POST http://127.0.0.1:8989/api/state
only simulation object
<devices> curl -d "<simulation>" -H "Content-Type: application/json" -X POST http://127.0.0.1:8989/api/simulation
GET
state object
<state> curl http://127.0.0.1:8989/api/state
only devices array
<devices> curl http://127.0.0.1:8989/api/devices
only simulation object
<simulation> curl http://127.0.0.1:8989/api/simulation
Transport - All Devices
GET
<devices> curl http://127.0.0.1:8989/api/devices/start
<devices> curl http://127.0.0.1:8989/api/devices/stop
The device object defines all of the device's or module's inputs/outputs and it's connection configuration.
property | type | usage |
---|---|---|
_id | string | Device id in IoT Hub or DeviceId + ModuleId |
comms | array | The list of comm objects |
configuration | object | The connection information of a device |
plan | array | The list of plan objects (TBD) |
To represent a module, format the id using <deviceId>moduleId
Example for device
{
"_id: "a-device",
"configuration" : { _type: 'dps'}
"comms" : [],
"plan" : []
}
Example for module
{
"_id: "<a-device>a-module",
"configuration" : { _type: 'module'},
"comms" : [],
"plan" : []
}
POST
<device> curl -d "<configuration>" -H "Content-Type: application/json" -X POST http://127.0.0.1:8989/api/device/new
DELETE
<devices> curl -X DELETE http://127.0.0.1:8989/api/device/<device id>
PUT
can only update configuration
<device> curl -d "<configuration>" -H "Content-Type: application/json" -X POST http://127.0.0.1:8989/api/<device id>
GET
<device> curl http://127.0.0.1:8989/api/device/<device id>
Transport - This device only
GET
<device> curl http://127.0.0.1:8989/api/device/<device id>/start
<device> curl http://127.0.0.1:8989/api/device/<device id>/stop
Defines the connection configuration of the device and the initial create profile. Changes here require device restart
property | type | usage |
---|---|---|
_kind | string | 'dps' | 'template' | 'hub' | 'module | 'edge' |
deviceId | string | Device id in IoT Hub or DeviceId + ModuleId |
scopeId | string | DPS scope ID |
dpsPayload | object | JSON payload as supported by DPS/Hub build |
sasKey | string | Device SaS key |
isMasterKey | boolean | 'true' |'false'. If true, device id is ignored and issued during DPS registration |
mockDeviceName | string | Friendly name used by the GUI |
mockDeviceCount | string? | Device number to start from in bulk create |
mockDeviceCountMax | string? | Device number to end on in bulk create |
connectionString | string? | Uses Azure IoT Hub connection string if _kind is hub |
planMode | boolean | Switch this device to use plan mode |
modules | array | The list of _ids that are modules for the Edge device |
{
"_kind": "dps"
"deviceId" : "any-device-id"
"scopeId": "00SED0045",
"dpsPayload": {},
"sasKey": "3249cxvnkj43579fdsg+=DFS90832",
"isMasterKey": false,
"mockDeviceName": "My Device",
"mockDeviceCount": 1,
"mockDeviceCountMax": 1,
}
This object can only be updated with the device object POST and PUT API
A communication or comm object is used to define the type of input/output the device will do. There are 3 kinds of comm objects. Reported (send data), Desired (receive data) and Method (execute). The device does not have to be stopped to add or remove comm objects except methods
property | type | usage |
---|---|---|
_id | string | A unique id for this property. Needs to be unique for the simulation i.e. a GUID |
_type | string | 'property' |'method'. Msg/Events and Twins are properties. Primarily used by the GUI |
color | string? | Only used by GUI |
enabled | boolean | Only honored when device is in a runloop. Updating a device will send value immediately |
interface | string | Name the comm property. Not supported |
string | boolean | true for values to be treated as strings i.e. text and dates. false for all other types i.e. object, arrays and booleans |
value | any | The current value of the property. Update this to send a new value (PUT) for the device. Supports AUTO_* in a runloop |
sdk | string | Determine how to send the device data. msg for SDK event/message API or twin for SDK Device Twin API |
version | number | Readonly. Lifecycle count of Twin updates for this device (only for Desired fields) |
mock | object? | See later |
propertyObject | object | See later |
type | object | See later |
runloop | object? | See later |
To use a mock sensor, assign a <sensor> to the mock property and change the mock value in the type object
GET
<sensors> curl http://127.0.0.1:8989/api/sensors
<sensor> curl http://127.0.0.1:8989/api/sensors/<type>
The propertyObject defines if the value field is literal or a JSON object. The latter is the second property in this object should must be stringified. Supports AUTO_*
property | type | usage |
---|---|---|
type | string | 'default' |'templated' |
templated | string | JSON object (optional) |
The type defines how to comm object binds to the SDK
property | type | usage |
---|---|---|
mock | any | use false to switch off mock sensor. Value is a sensor when mock sensor is on |
direction | string | 'd2c' |'c2d'. Used to bind the comm object to the SDK. Honors sdk type and sets up the property to be read/desired or send/reported |
Use the runloop object to send the value on a timed loop.
property | type | usage |
---|---|---|
include | boolean | false to not include this property in a runloop |
unit | string | 'secs' |'mins' |
value | integer | Honors unit |
EXAMPLE
{
"_id": "b07f4260-df7d-4d79-8f2d-02cf9d8c5b1f",
"_type": "property",
"name": "d2cProperty_1d57",
"color": "#333333",
"enabled": false,
"interface": "(single interface only)",
"string": false,
"value": 0,
"sdk": "msg",
"propertyObject": {
"type": "default"
},
"version": 0,
"type": {
"mock": false,
"direction": "d2c"
},
"runloop": {
"include": false,
"unit": "secs",
"value": 15
}
}
POST
create the comm with defaults
<device> curl -d "{type:'d2c'}" -H "Content-Type: application/json" -X POST http://127.0.0.1:8989/api/device/<device id>/property/new
create the default mock sensor and assign to comm
<device> curl -X POST http://127.0.0.1:8989/api/device/<device id>/property/<property id>/mock/new
PUT
<device> curl -d "{comm}" -H "Content-Type: application/json" -X POST http://127.0.0.1:8989/api/device/<device id>/property/<property id>
DELETE
<devices> curl -X DELETE http://127.0.0.1:8989/api/device/<device id>/property/<property id>
GET
<devices> curl http://127.0.0.1:8989/api/device/<device id>/property/<property id>
runloop not required
EXAMPLE
{
"_id": "61cdc84c-20e8-4066-9e39-22a3868719bf",
"_type": "property",
"enabled": true,
"name": "c2dProperty_a8e3",
"color": "#383a1e",
"interface": "(single interface only)",
"string": false,
"value": 0,
"sdk": "twin",
"propertyObject": {
"type": "default"
},
"version": 0,
"type": {
"mock": false,
"direction": "c2d"
}
}
POST
create the comm with defaults
<device> curl -d "{type:'c2d'}" -H "Content-Type: application/json" -X POST http://127.0.0.1:8989/api/device/<device id>/property/new
\
PUT
update comm and not value
<device> curl -d "{comm}" -H "Content-Type: application/json" -X POST http://127.0.0.1:8989/api/device/<device id>/property/<property id>
update comm and value
<device> curl -d "{comm}" -H "Content-Type: application/json" -X POST http://127.0.0.1:8989/api/device/<device id>/property/<property id>/value
DELETE
<devices> curl -X DELETE http://127.0.0.1:8989/api/device/<device id>/property/<property id>
GET
<devices> curl http://127.0.0.1:8989/api/device/<device id>/property/<property id>
property | type | usage |
---|---|---|
_id | string | A unique id for this property. Needs to be unique for the simulation i.e. a GUID |
_type | string | 'property' |'method'. Msg/Events and Twins are properties. Primarily used by the GUI |
color | string? | Only used by GUI |
enabled | boolean | Only honored when device is in a runloop. Updating a device will send value immediately |
interface | string | Name the comm property. Not supported |
status | integer | HTTP status code |
receivedParams | string | The parameters sent to the device from the caller |
asProperty | boolean | Send the result as a reported Twin reported as well |
payload | string | The result JSON |
EXAMPLE
{
"_id": "27dfec16-128a-4cf8-bbba-89263c51f4d3",
"_type": "method",
"enabled": true,
"name": "method47a2",
"color": "#3a1e1e",
"interface": "(single interface only)",
"status": 200,
"receivedParams": null,
"asProperty": false,
"payload": "{\n \"result\": \"OK\"\n}"
}
POST
create the comm with defaults
<device> curl -X POST http://127.0.0.1:8989/api/device/<device id>/method/new
PUT
<device> curl -d "{comm}" -H "Content-Type: application/json" -X POST http://127.0.0.1:8989/api/device/<device id>/method/<method id>
DELETE
<devices> curl -X DELETE http://127.0.0.1:8989/api/device/<device id>/method/<method id>
GET
<devices> curl http://127.0.0.1:8989/api/device/<device id>/<method>/<method id>
{
"comms": [
{
"_id": "b07f4260-df7d-4d79-8f2d-02cf9d8c5b1f",
"_type": "property",
"name": "d2cProperty_1d57",
"color": "#333333",
"enabled": false,
"interface": "(single interface only)",
"string": false,
"value": 0,
"sdk": "msg",
"propertyObject": {
"type": "default"
},
"version": 0,
"type": {
"mock": true,
"direction": "d2c"
},
"runloop": {
"include": true,
"unit": "secs",
"value": 15
},
"mock": {
"_id": "2400337d-46dd-4885-857f-306bf4761e75",
"_hasState": false,
"_type": "random",
"_value": 0,
"variance": 3,
"init": 0,
"_resx": {
"init": "Initial",
"variance": "Length"
}
}
},
{
"_id": "61cdc84c-20e8-4066-9e39-22a3868719bf",
"_type": "property",
"enabled": true,
"name": "c2dProperty_a8e3",
"color": "#383a1e",
"interface": "(single interface only)",
"string": false,
"value": 0,
"sdk": "twin",
"propertyObject": {
"type": "default"
},
"version": 0,
"type": {
"mock": false,
"direction": "c2d"
}
},
{
"_id": "27dfec16-128a-4cf8-bbba-89263c51f4d3",
"_type": "method",
"enabled": true,
"name": "method47a2",
"color": "#3a1e1e",
"interface": "(single interface only)",
"status": 200,
"receivedParams": null,
"asProperty": false,
"payload": "{\n \"result\": \"OK\"\n}"
}
],
"configuration": {
"_kind": "dps",
"deviceId": "any-device-id",
"scopeId": "00SED0045",
"dpsPayload": {},
"sasKey": "3249cxvnkj43579fdsg+=DFS90832",
"isMasterKey": false,
"mockDeviceName": "My Device",
},
"_id": "any-device-id",
"running": false
}
AUTO values are macros that are replaced with real (random) values when the device sends its data. They can be used in value fields and complex payloads (for sending random data on the leaf nodes) Replace AUTO string with a static value if the feature is not required.
Complex values must be authored using JSON so use the macro as a string (see examples). When using AUTOs, the property's type will be replaced. See the following.
- AUTO_STRING - Use a random word from 'random-words' library
- AUTO_BOOLEAN - Use a random true or false
- AUTO_INTEGER - Use a random number
- AUTO_LONG - Use a random number
- AUTO_DOUBLE - Use a random number with precision
- AUTO_FLOAT - Use a random number with precision
- AUTO_DATE - Use now() ISO 8601 format
- AUTO_DATETIME - Use now() ISO 8601 format
- AUTO_TIME - Use now() ISO 8601 format
- AUTO_DURATION - Use now() ISO 8601 format
- AUTO_GEOPOINT - Use a random lat/long object
- AUTO_VECTOR - Use a random x,y,z
- AUTO_MAP - Use empty HashMap
- AUTO_ENUM/* - Use a random Enum value (See below)
- AUTO_VALUE - Use the last user supplied or mock sensor value. Honors String setting.
Enum support is possible by extending the macro to include the list of values. Values can only be integers or strings. Enums use the JavaScript style arrays i.e. [1,0] or ['foo','bar'] Append this to the end of an Enum AUTO like ...
AUTO_ENUM/['foo','bar']
The default geo location is London, UK. This can be changed in the Simulation object
Complex Examples A JSON with AUTO values and statics
{
"complexObject": {
"nestedObj": {
"nestedObj": {
"param3": "AUTO_BOOLEAN"
},
"param1": "AUTO_INTEGER",
"param2": "Hello World!"
},
"param1": "AUTO_VALUE",
"param2": "AUTO_STRING",
"param3": true
}
}
An X,Y,Z object
{
"x": "AUTO_INTEGER",
"y": "AUTO_INTEGER",
"z": 150
}
An Array
['AUTO_INTEGER', 5, 'AUTO_DOUBLE', 999]
A Map - Needs static keys (Beta)
{
"map": {
"mapKey1": "AUTO_STRING",
"mapKey2": "AUTO_STRING",
"mapKey3": "AUTO_STRING"
}
}