Device Simulators
This section describes how to use and develop device simulators that can be useful when developing either this library and its CLI tool, as well as when developing applications communicating with MiIO/MiOT devices, like the Home Assistant integration, even when you have no access to real devices.
MiIO Simulator
The miiocli devtools miio-simulator
command can be used to simulate devices.
You can command the simulated devices using the miiocli
tool or any other implementation
that talks the MiIO proocol, like Home Assistant.
Behind the scenes, the simulator uses the push server
to
handle the low-level protocol handling.
To make it easy to simulate devices, it uses YAML-based device description files
to describe information like models and exposed properties to simulate.
Note
The simulator currently supports only devices whose properties are queried using get_prop
method,
and whose properties are set using a single setter method (e.g., set_fan_speed
) accepting the new value.
Usage
You start the simulator like this:
miiocli devtools miio-simulator --file miio/integrations/zhimi/fan/zhimi_fan.yaml
The mandatory --file
option takes a path to a device description file file
that defines information about the device to be simulated.
Note
You can define --model
to define which model string you want to expose to the clients.
The MAC address of the device is generated from the model string, making them unique for
downstream use cases, e.g., to make them distinguishable to Home Assistant.
After the simulator has started, you can communicate with it using the miiocli
:
$ export MIIO_FAN_TOKEN=00000000000000000000000000000000
$ miiocli fan --host 127.0.0.1 info
Model: zhimi.fan.sa1
Hardware version: MW300
Firmware version: 1.2.4_16
$ miiocli fan --ip 127.0.0.1 status
Power: on
Battery: None %
AC power: True
Temperature: None °C
Humidity: None %
LED: None
LED brightness: LedBrightness.Bright
Buzzer: False
Child lock: False
Speed: 277
Natural speed: 2
Direct speed: 1
Oscillate: False
Power-off time: 12
Angle: 120
Note
The default token is hardcoded to full of zeros (00000000000000000000000000000000
).
We defined MIIO_FAN_TOKEN
to avoid repeating --token
for each command.
Note
Although Home Assistant uses MAC address as a unique ID to identify the device, the model information is stored in the configuration entry which is used to initialize the integration.
Therefore, if you are testing multiple simulated devices in Home Assistant, you want to disable other simulated integrations inside Home Assistant to avoid them being updated against a wrong simulated device.
Device Descriptions
The simulator uses YAML files that describe information about the device, including supported models and the available properties.
Required Information
The file begins with a definition of models supported by the file:
models:
- name: Name of the device, if known
model: model.string.v2
- model: model.string.v3
You need to have model
for each model the description file wants to support.
The name is not required, but recommended.
This information is currently used to set the model information for the simulated
device when not overridden using the --model
option.
The description file can define a list of properties the device supports for get_prop
queries.
You need to define several mappings for each property:
name
defines the name used for fetching using theget_prop
request
type
defines the type of the property, e.g.,bool
,int
, orstr
value
is the value which is returned forget_prop
requests
setter
defines the method that allows changing thevalue
models
list, if the property is only available on some of the supported models
Note
The schema might change in the future to accommodate other potential uses, e.g., allowing definition of new files using pure YAML without a need for Python implementation. Refer Example Description File for a complete, working example.
Alternatively, you can define methods with their responses by defining methods
, which is necessary to simulate
devices that use other ways to obtain the status information (e.g., on Roborock vacuums).
You can either use result
or result_json
to define the response for the given method:
methods:
- name: get_status
result:
- some_variable: 1
another_variable: "foo"
- name: get_timezone
result_json: '["UTC"]'
Calling method get_status
will return [{"some_variable": 1, "another_variable": "foo"}]
,
the result_json
will be parsed and serialized to ["UTC"]
when sent to the client.
A full working example can be found in Example Description File Using Methods.
Minimal Working Example
The following YAML file defines a very minimal device having a single model with two properties,
and exposing also a custom method (reboot
):
models:
- name: Some Fan
model: some.fan.model
properties:
- name: speed
type: int
value: 33
setter: set_speed
- name: is_on
type: bool
value: false
methods:
- name: reboot
result_json: '["ok"]'
In this case, the get_prop
method call with parameters ['speed', 'is_on']
will return [33, 0]
.
The speed
property can be changed by calling the set_speed
method.
See Example Description File for a more complete example.
Example Description File
The following description file shows a complete,
concrete example for a device using get_prop
for accessing the properties (zhimi_fan.yaml
):
models:
- model: zhimi.fan.sa1
- model: zhimi.fan.za1
- model: zhimi.fan.za3
- model: zhimi.fan.za4
- model: zhimi.fan.v3
- model: zhimi.fan.v2
type: fan
properties:
- name: angle
value: 120
type: int
min: 0
max: 120
setter: set_angle
- name: speed
value: 277
type: int
setter: set_speed_level
min: 0
max: 100
- name: poweroff_time
value: 12
type: int
setter: set_poweroff_time
- name: power
value: 'on'
type: str_bool
setter: set_power
- name: ac_power
value: 'on'
type: str_bool
- name: angle_enable
value: 'off'
setter: set_angle_enable
type: str_bool
- name: speed_level
value: 1
type: int
min: 0
max: 100
setter: set_speed_level
- name: natural_level
value: 2
type: int
setter: set_natural_level
- name: child_lock
value: 'off'
type: str_bool
setter: set_child_lock
- name: buzzer
value: 0
type: int
setter: set_buzzer
- name: led_b
value: 0
type: int
setter: set_led_b
- name: use_time
value: 2318
type: int
# V2 & V3 only
- name: temp_dec
value: 232
type: float
models:
- zhimi.fan.v3
- zhimi.fan.v2
- name: humidity
value: 46
type: int
models:
- zhimi.fan.v3
- zhimi.fan.v2
- name: battery
type: int
value: 98
models:
- zhimi.fan.v3
- zhimi.fan.v2
- name: bat_charge
value: "complete"
type: str
models:
- zhimi.fan.v3
- zhimi.fan.v2
- name: button_pressed
type: str
value: speed
models:
- zhimi.fan.v3
- zhimi.fan.v2
# V2 only properties
- name: led
type: str
value: null
models:
- zhimi.fan.v2
- name: bat_state
type: str
value: "unknown state"
models:
- zhimi.fan.v2
Example Description File Using Methods
The following description file (simulated_roborock.yaml
) shows a complete,
concrete example for a device using custom method names for obtaining the status.
models:
- model: roborock.vacuum.a15
type: vacuum
methods:
- name: get_status
result:
- _model: roborock.vacuum.a15 # internal note where this status came from
adbumper_status:
- 0
- 0
- 0
auto_dust_collection: 1
battery: 87
clean_area: 35545000
clean_time: 2311
debug_mode: 0
dnd_enabled: 0
dock_type: 0
dust_collection_status: 0
error_code: 0
fan_power: 102
in_cleaning: 0
in_fresh_state: 1
in_returning: 0
is_locating: 0
lab_status: 3
lock_status: 0
map_present: 1
map_status: 3
mop_forbidden_enable: 0
mop_mode: 300
msg_seq: 1839
msg_ver: 2
state: 8
water_box_carriage_status: 0
water_box_mode: 202
water_box_status: 1
water_shortage_status: 0
- name: get_consumable
result:
- filter_work_time: 32454
sensor_dirty_time: 3798
side_brush_work_time: 32454
main_brush_work_time: 32454
- name: get_clean_summary
result_json: '[ 174145, 2410150000, 82, [ 1488240000, 1488153600, 1488067200, 1487980800, 1487894400, 1487808000, 1487548800 ] ]'
- name: get_timer
result_json: '[["1488667794112", "off", ["49 22 * * 6", ["start_clean", ""]], ["1488667777661", "off", ["49 21 * * 3,4,5,6", ["start_clean", ""]]]]]'
- name: get_timezone
result_json: '["UTC"]'
- name: get_dnd_timer
result:
- enabled: 1
start_minute: 0
end_minute: 0
start_hour: 22
end_hour: 8
- name: get_clean_record
result:
- begin: 1488347071
end: 1488347123
duration: 16
area: 0
error: 0
complete: 0
MiOT Simulator
The miiocli devtools miot-simulator
command can be used to simulate MiOT devices for a given description file.
You can command the simulated devices using the miiocli
tool or any other implementation that supports the device.
Behind the scenes, the simulator uses the push server
to
handle the low-level protocol handling.
The simulator implements the following methods:
miIO.info
returns the device information
get_properties
returns randomized (leveraging the schema limits) values for the givensiid
andpiid
set_properties
allows setting the property for the givensiid
andpiid
combination
action
to call actions that simply respond that the action succeeded
Furthermore, two custom methods are implemented help with development:
dump_services
returns the list of available services
dump_properties
returns the available properties and their values the givensiid
Usage
You start the simulator like this:
miiocli devtools miot-simulator --file some.vacuum.model.json --model some.vacuum.model
The mandatory --file
option takes a path to a MiOT description file, while --model
defines the model
the simulator should report in its miIO.info
response.
Note
The default token is hardcoded to full of zeros (00000000000000000000000000000000
).
Dump Service Information
dump_services
method that returns a JSON dictionary keyed with the siid
containing the simulated services:
$ miiocli device --ip 127.0.0.1 --token 00000000000000000000000000000000 raw_command dump_services
Running command raw_command
{'services': {'1': {'siid': 1, 'description': 'Device Information'}, '2': {'siid': 2, 'description': 'Heater'}, '3': {'siid': 3, 'description': 'Countdown'}, '4': {'siid': 4, 'description': 'Environment'}, '5': {'siid': 5, 'description': 'Physical Control Locked'}, '6': {'siid': 6, 'description': 'Alarm'}, '7': {'siid': 7, 'description': 'Indicator Light'}, '8': {'siid': 8, 'description': '私有服务'}}, 'id': 2}
Dump Service Properties
dump_properties
method can be used to return the current state of the device on service-basis:
$ miiocli device --ip 127.0.0.1 --token 00000000000000000000000000000000 raw_command dump_properties '{"siid": 2}'
Running command raw_command
[{'siid': 2, 'piid': 1, 'prop': 'Switch Status', 'value': False}, {'siid': 2, 'piid': 2, 'prop': 'Device Fault', 'value': 167}, {'siid': 2, 'piid': 5, 'prop': 'Target Temperature', 'value': 28}]