Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A guide how to communicate with MIOT devices -> Plugin? #901

Closed
SpeedFire0 opened this issue Jan 6, 2021 · 30 comments · Fixed by #1581
Closed

A guide how to communicate with MIOT devices -> Plugin? #901

SpeedFire0 opened this issue Jan 6, 2021 · 30 comments · Fixed by #1581

Comments

@SpeedFire0
Copy link

I was able to get the data from an unsupported device (HeatCold Heating Floor Controller - cubee.airrtc.th123e) and to set the required parameters from a Terminal on Mac. Can someone help with converting this into a working plugin for Home Assistant and then HomeKit?

Here is my step-by-step guide for all newcomers who want to play with their devices:

  1. Install python-miio on Mac. Enter this in Terminal app on Mac:
    sudo pip3 install python-miio

  2. Get model name of your device ("cubee.airrtc.th123e" in my case), IP address (like "192.168.1.152") and Token (like "b110204d86732b019d3d6axxxxb9ad3a"). The easiest way for me was to use this integration for Home Assistant - https://github.com/AlexxIT/XiaomiGateway3

  3. Check that your device is accessible by entering the following in a Terminal app on Mac:
    miiocli -d device --ip 192.168.1.152 --token b110204d86732b019d3d6axxxxb9ad3a info
    Note 1: You need to put your device's IP and Token here.
    Note 2: "-d" is for Debug and can be excluded for a shorter output.
    In my case I got something like this:
    "Model: cubee.airrtc.th123e
    Hardware version: esp32
    Firmware version: 2.1.7"
    Device is responding! Nice! Going to the next step.

  4. Find your device in the list of MIOT devices:
    http://miot-spec.org/miot-spec-v2/instances?status=all
    I searched for "cubee.airrtc.th123e" and found the following line:
    {"status":"released","model":"cubee.airrtc.th123e","version":1,"type":**"urn:miot-spec-v2:device:thermostat:0000A031:cubee-th123e:1"**},

  5. Copy the "urn" part after this url:
    https://miot-spec.org/miot-spec-v2/instance?type=
    in my case: https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:thermostat:0000A031:cubee-th123e:1
    You will see an unformatted JSON text of the device's specs

  6. Put this JSON text to any JSON Formatter to make it more human readable. For example here: http://json.parser.online.fr/

  7. You will see a hierarchy with a list of Services (later used as "siid"), each of which has a list of Properties ("piid"):

miot_json

  1. You need to find some property in the list which you want to get from the device and which is readable
    (in my case siid 1 didn't return the value, so you may want to check other services like "siid 2" and other properties)
    I wanted to receive the current "Target Temperature". This is "siid":2 and "piid":5:

miot_json_temp

  1. Try to get a response from your device by entering this in Terminal:
    miiocli -d device --ip 192.168.1.152 --token b110204d86732b019d3d6axxxxb9ad3a raw_command get_properties "[{'did': 'MYDID', 'siid': 2, 'piid': 5 }]"
    Note: replace IP, token, siid and piid with your values
    I got a response the last line of which was:
    "[{'did': 'MYDID', 'siid': 2, 'piid': 5, 'code': 0, 'value': 29}]"
    This was an actual Target Temperature set on my device: 29 degrees celsius! It works!

  2. Now let's try to change some properties. Make sure it has a "write" access (look at the the field "access" for a particular piid in the JSON file mentioned above).
    I want to set 28 degrees and here is the command for that:
    miiocli -d device --ip 192.168.1.152 --token b110204d86732b019d3d6axxxxb9ad3a raw_command set_properties "[{'did': 'MYDID', 'siid': 2, 'piid': 5, 'value' : 28 }]"
    The new temperature was set on my device and was also updated in MiHome app!

  3. Check that the value / setting was set on an actual device and redo step 9 to confirm that the new value is successfully returned by the device.

That's all! Now you can retrieve and set values from a Terminal.
This was my part. I would be glad if someone describes how to further make a Home Assistant plugin based on this knowledge :)

Originally posted by @SpeedFire0 in #543 (comment)

@gudvinr
Copy link

gudvinr commented Jan 7, 2021

What is 'did': 'MYDID' though?

@SpeedFire0
Copy link
Author

What is 'did': 'MYDID' though?

I'm not sure, to be honest. In my case the word "MYDID" did the job (so I haven't replaced it with any other word or number).

@rytilahti
Copy link
Owner

The 'did' (device id, most likely) is something that gets returned back by the device for responses, but you can put any value to it. The current miot implementations are using that as a key to a user-friendly property name.

Thanks for the guide, the current documentation on that front is definitely lacking. There is a tool under devtools directory, which allows downloading the relevant json file based on the URN (that could be improved to allow downloading by the model):

$ python miottemplate.py download urn:miot-spec-v2:device:thermostat:0000A031:cubee-th123e:1
Saving data to urn:miot-spec-v2:device:thermostat:0000A031:cubee-th123e:1.json

And printing out the information:

$ python miottemplate.py print urn:miot-spec-v2:device:thermostat:0000A031:cubee-th123e:1.json
Device 'urn:miot-spec-v2:device:thermostat:0000A031:cubee-th123e:1': Thermostat with 3 services

* Service siid 1: (Device Information): 4 props, 0 actions

        ## Properties ##
                siid 1: piid: 1 (Device Manufacturer): (string, unit: None) (acc: ['read'])
                siid 1: piid: 2 (Device Model): (string, unit: None) (acc: ['read'])
                siid 1: piid: 3 (Device Serial Number): (string, unit: None) (acc: ['read'])
                siid 1: piid: 4 (Current Firmware Version): (string, unit: None) (acc: ['read'])

* Service siid 2: (Thermostat): 6 props, 0 actions

        ## Properties ##
                siid 2: piid: 1 (Switch Status): (bool, unit: None) (acc: ['read', 'write', 'notify'])
                siid 2: piid: 2 (Status): (uint8, unit: None) (acc: ['read', 'notify'])
                        {'value': 0, 'description': '未加热状态'}
                        {'value': 1, 'description': '加热中'}
                siid 2: piid: 3 (Device Fault): (uint8, unit: None) (acc: ['read', 'notify'])
                        {'value': 1, 'description': '传感器错误'}
                        {'value': 0, 'description': '无错误'}
                        {'value': 2, 'description': '高温保护'}
                        {'value': 3, 'description': '低温保护'}
                siid 2: piid: 4 (Mode): (uint8, unit: none) (acc: ['read', 'write', 'notify'])
                        {'value': 0, 'description': 'Manual'}
                        {'value': 1, 'description': 'Home'}
                        {'value': 2, 'description': 'Away'}
                        {'value': 3, 'description': 'Smart'}
                        {'value': 4, 'description': 'Sleep'}
                siid 2: piid: 5 (Target Temperature): (float, unit: celsius) (acc: ['read', 'write', 'notify'])
                        Range: [0, 90, 1]
                siid 2: piid: 7 (Temperature): (float, unit: celsius) (acc: ['read', 'notify'])
                        Range: [-30, 100, 1]

* Service siid 4: (): 7 props, 0 actions

        ## Properties ##
                siid 4: piid: 1 (): (bool, unit: None) (acc: ['read', 'write', 'notify'])
                siid 4: piid: 2 (): (uint8, unit: None) (acc: ['read', 'notify', 'write'])
                        {'value': 0, 'description': '内置传感器'}
                        {'value': 1, 'description': '外置传感器'}
                        {'value': 2, 'description': '内外置传感器'}
                siid 4: piid: 3 (): (uint8, unit: None) (acc: ['read', 'notify', 'write'])
                        Range: [1, 9, 1]
                siid 4: piid: 4 (): (int8, unit: None) (acc: ['read', 'notify', 'write'])
                        Range: [-9, 9, 1]
                siid 4: piid: 5 (): (int8, unit: None) (acc: ['read', 'notify'])
                        Range: [0, 100, 1]
                siid 4: piid: 6 (): (uint8, unit: celsius) (acc: ['read', 'notify'])
                        Range: [35, 90, 1]
                siid 4: piid: 7 (): (uint8, unit: celsius) (acc: ['read', 'notify'])
                        Range: [0, 30, 1]

@rytilahti
Copy link
Owner

I just modified the miottemplate to download the mapping file, if not available, to allow using download with only the model as input. python miottemplate.py download <some model> followed by python miottemplate.py print <json file> for the downloaded json file should do the trick and be much simpler than it used to be.

@rytilahti
Copy link
Owner

rytilahti commented Jan 7, 2021

If you don't mind, please check out the linked PR and try miiocli miotdevice get_property_by and set_property_by commands. I cannot test these as I have no test devices anymore.

get_property_by [siid] [piid] and set_property_by [siid] [piid] [value] with an optional [value_type] (if the value needs to be other than a string).

@gudvinr
Copy link

gudvinr commented Jan 8, 2021

@rytilahti get_property_by works but I'm getting only 'hello' string for everything under siid: 1 (Device Information):

$ miiocli miotdevice ... get_property_by 1 1
[{'did': '1-1', 'siid': 1, 'piid': 1, 'code': 0, 'value': 'hello'}]

set_property_by sort of works but miiocli sends wrong payload.
I tested with my pet waterer from #897.

When I turn indicator light on using set_property_by 4 1 'True' 'bool' it turns on. It was off before I started.
If I turn it off using set_property_by 4 1 'False' 'bool' it doesn't turn off.

When I run miiocli -d miotdevice ... set_property_by 4 1 'False' 'bool' (while indicator light is on):

DEBUG:miio.miioprotocol:192.168.xxx.xxx:xxxxx >>: {'id': 1, 'method': 'set_properties', 'params': [{'did': 'set-4-1', 'siid': 4, 'piid': 1, 'value': True}]}

If I use set_property_by 4 1 'True' 'bool' while indicator is on it doesn't turn it off.

Also there's number of "actions" in miot specs but I don't see how can I use them using miiocli.

@SpeedFire0
Copy link
Author

I have found and modified an example of a simple switch for Home Assistant.
Here is the repo for an example in the guide: https://github.com/SpeedFire0/xiaomi_miot_heatcold
Device is successfully switches on/off both in Home Assistant and HomeKit.

@rytilahti
Copy link
Owner

@rytilahti get_property_by works but I'm getting only 'hello' string for everything under siid: 1 (Device Information):

Great! Yes, the siid1 (which is there for all miot devices, miot_info command is still missing to output that) was also hard-coded on the test device I had at one point (#672 (comment)). That could be the sdk default and vendors haven't changed it for a reason or another.

If I turn it off using set_property_by 4 1 'False' 'bool' it doesn't turn off.

Try set_property_by 4 1 False bool or set_property_by 4 1 0 bool. I think it detects 'False' as a string -> which is true when converted to bool.

I'll try add a way to call an action, thanks for noticing & testing!

@rytilahti
Copy link
Owner

@SpeedFire0 I think xiaomi_raw from @syssi is the proper way for quick, stop-gap solutions. That is, as soon as it gets miot support. You can follow this issue for that: syssi/xiaomi_raw#6

The problem with custom components is that they deem to break when the official integration gets updated to use APIs that are not available for the python-miio versions pinned by custom integrations. In turn, the less custom components one has, the easier it is to do the manual adjustments to make it work again.

@gudvinr
Copy link

gudvinr commented Jan 8, 2021

Try set_property_by 4 1 False bool or set_property_by 4 1 0 bool. I think it detects 'False' as a string -> which is true when converted to bool.

Both didn't work

@rytilahti
Copy link
Owner

rytilahti commented Jan 8, 2021

Ah, indeed. bool('0') equates to True (as the input itself is a string). I'll try to find a solution for that, at worst bools will need to be inputed as 0/1 in the command line, and it'll be converted using bool(int(value)).

@gudvinr
Copy link

gudvinr commented Jan 8, 2021

@rytilahti there's method for that in distutils

@SpeedFire0
Copy link
Author

I tried to get commands from a Xiaomi camera and it returned the following error:
Error: {'code': -10000, 'message': 'user undefined error'}
Probably this is some sort of protection from unauthorised access. Is it possible to define my user credentials (in MiHome app?) and send it to the device?

@rezmus
Copy link

rezmus commented Jan 11, 2021

which camera? most cams don't use miot, only a few latest models.

@SpeedFire0
Copy link
Author

which camera? most cams don't use miot, only a few latest models.
Outdoor camera - "chuangmi.camera.ipc020"
Indoor camera - "chuangmi.camera.ipc021"

@rezmus
Copy link

rezmus commented Jan 12, 2021

these models talk miio only (unless something changed in latest fw). all devices have miot specs, but older devices never really deploy it in firmware and they still use human readable miio.

@SpeedFire0
Copy link
Author

SpeedFire0 commented Jan 12, 2021

these models talk miio only (unless something changed in latest fw). all devices have miot specs, but older devices never really deploy it in firmware and they still use human readable miio.

I didn't find any way how to control them (even the simplest function like on/off). Is it possible?

This is an example of "info" response:
DEBUG:miio.miioprotocol:192.168.1.92:54321 >>: {'id': 1, 'method': 'miIO.info', 'params': []} DEBUG:miio.miioprotocol:192.168.1.92:54321 (ts: 1970-02-20 21:26:43, id: 1) << {'id': 1, 'result': {'life': 4397203, 'model': 'chuangmi.camera.ipc016', 'token': '396a67493638696a6552324f6356664e', 'ipflag': 1, 'miio_ver': '0.0.8', 'mac': '78:8B:2A:9F:EA:02', 'fw_ver': '16.4.0.9_0213', 'hw_ver': 'Linux', 'bootloader_ver': 'a97a650aea304f0d2b08da11cab16439', 'miio_client_ver': '4.0.10', 'VmPeak': 6820, 'VmRSS': 1588, 'MemFree': 1988, 'ap': {'ssid': 'PointerDacha', 'bssid': '52:FF:20:5E:75:C0', 'rssi': '-53', 'freq': 2457}, 'netif': {'localIp': '192.168.1.92', 'mask': '255.255.255.0', 'gw': '192.168.1.1'}, 'miio_times': [4397197, 7, 5486, 4391660]}, 'exe_time': 2} Model: chuangmi.camera.ipc016 Hardware version: Linux Firmware version: 16.4.0.9_0213

@rezmus
Copy link

rezmus commented Jan 12, 2021

@SpeedFire0
Copy link
Author

https://github.com/rytilahti/python-miio/blob/master/miio/chuangmi_camera.py

OK. But I feel myself as a complete idiot because I can't figure out how to use this in Home Assistant :(
Maybe someone will help with a guide in the future.

@rytilahti
Copy link
Owner

@rytilahti there's method for that in distutils

It's weird to add an import for this single function, but I have now fixed this by simply checking of the value is either 'true' or '1'. Feel free to test it again, the newest commit also adds the ability to call actions using call_action_by.

@gudvinr
Copy link

gudvinr commented Jan 17, 2021

@rytilahti it works now. As I mentioned earlier, I use "Indicator light" property to test and set_property_by 4 1 false bool now works fine for this case.
call_action_by 6 1 also works fo me (it is an action to reset time-to-clean).

@rytilahti
Copy link
Owner

Thanks for testing @gudvinr! I'll write some tests for the new functionality when I'll find some free time again, and then it can be merged.

@ha0y
Copy link
Contributor

ha0y commented Jan 17, 2021

@SpeedFire0 For the plugin you may try https://github.com/ha0y/xiaomi_miot_raw. The author of xiaomi_raw didn't implement it for so long time, so I did it myself.

@syssi
Copy link
Collaborator

syssi commented Jan 17, 2021

@ha0y You could help to improve xiaomi_raw. I'm happy about any contribution.

@liangxrbiu
Copy link

i got this:
DEBUG:miio.miioprotocol:192.168.154.160:54321 (ts: 1970-01-01 01:40:03, id: 1) << {'id': 1, 'error': {'code': -9999, 'message': 'user ack timeout'}, 'exe_time': 4010}
DEBUG:miio.click_common:Exception: {'code': -9999, 'message': 'user ack timeout'}
...
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\miio\miioprotocol.py", line 214, in send
self._handle_error(payload["error"])
File "c:\users\lenovo\appdata\local\programs\python\python37\lib\site-packages\miio\miioprotocol.py", line 274, in _handle_error
raise DeviceError(error)
miio.exceptions.DeviceError: {'code': -9999, 'message': 'user ack timeout'}
Error: {'code': -9999, 'message': 'user ack timeout'}
i dont know how to solve it,and i run the miioprotocal.py,the error is No module named 'main.exceptions'.

@AlexKalopsia
Copy link

@rytilahti I am trying to use miottemplate.py as you suggested, but it's complaining that it can't find the module requests. Any idea how to fix this?

@rytilahti
Copy link
Owner

@AlexKalopsia pip install requests will install the required lib.

@scarycky
Copy link

hi, I'm reading this thread, I can't do what needs to be done so that the thermostat starts displaying data, please tell me

@doradoradoradoradoradora

@rytilahti it works now. As I mentioned earlier, I use "Indicator light" property to test and set_property_by 4 1 false bool now works fine for this case. call_action_by 6 1 also works fo me (it is an action to reset time-to-clean).

Can i ask a question?what's the action you want to do about the command?i have a smart socket,the mode is cuco.plug.v3,i want to turn on and turn off,do you have try on this action on you plug?

@rytilahti
Copy link
Owner

I will lock this issue now as it's old and it contains a lot of outdated information, thanks to everyone who contributed and helped!

The instructions on how to use genericmiot integration supporting devices based on the schema files can be found in the README: https://github.com/rytilahti/python-miio/#controlling-modern-miot-devicesNote: this is currently only available on git master branch until 0.6.0 is released!

To see the full list of available commands, use miiocli genericmiot --help:

❯ miiocli genericmiot --help
Usage: miiocli genericmiot [OPTIONS] COMMAND [ARGS]...

Options:
  --ip TEXT     [required]
  --token TEXT  [required]
  --model TEXT
  --help        Show this message and exit.

Commands:
  actions                   Return device actions.
  call                      Call action by name.
  call_action_by            Call an action.
  call_action_from_mapping  Call an action by a name in the mapping.
  descriptors               Return a collection containing all...
  get_property_by           Get a single property (siid/piid).
  info                      Get (and cache) miIO protocol information...
  raw_command               Send a raw command to the device.
  sensors                   Return read-only properties.
  set                       Change setting value.
  set_property_by           Set a single property (siid/piid) to given...
  settings                  Return settable properties.
  status                    Return status based on the miot model.

Repository owner locked as resolved and limited conversation to collaborators Dec 5, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants