Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor
Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor copied to clipboard
Request new Vacuum Xiaomi s12
Checklist
- [X] I have updated the integration to the latest version available
- [X] I have checked if the vacuum/platform is already requested
- [ ] I have sent raw map file to
piotr.machowski.dev [at] gmail.com
(Retrieving map; please provide your GitHub username in the email)
What vacuum model do you want to be supported?
xiaomi.vacuum.b106eu
What is its name?
Available APIs
- [ ] xiaomi
- [ ] viomi
- [ ] roidmi
- [ ] dreame
Errors shown in the HA logs (if applicable)
2023-08-19 05:08:13.598 ERROR (MainThread) [homeassistant.helpers.entity] Update for camera.xiaomi_cloud_map_extractor fails
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 699, in async_update_ha_state
await self.async_device_update()
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 940, in async_device_update
await hass.async_add_executor_job(self.update)
File "/usr/local/lib/python3.11/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/xiaomi_cloud_map_extractor/camera.py", line 278, in update
self._handle_map_data(map_name)
File "/config/custom_components/xiaomi_cloud_map_extractor/camera.py", line 335, in _handle_map_data
map_data, map_stored = self._device.get_map(map_name, self._colors, self._drawables, self._texts,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/xiaomi_cloud_map_extractor/common/vacuum.py", line 27, in get_map
response = self.get_raw_map_data(map_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/xiaomi_cloud_map_extractor/common/vacuum.py", line 45, in get_raw_map_data
map_url = self.get_map_url(map_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/config/custom_components/xiaomi_cloud_map_extractor/common/vacuum_v2.py", line 18, in get_map_url
if api_response is None or "result" not in api_response or "url" not in api_response["result"]:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: argument of type 'NoneType' is not iterable
Other info
No response
I'm joining the add request!
@PiotrMachowski, please tell me if there is any reason why support for this vacuum cleaner is not yet available? (s12/s10) Is it a matter of time, or do mijia servers not provide a card for it?
@4ronos it is not a simple process "just download a map image from a URL". API has to be found and map has to be parsed from a binary file that has an unknown structure. And I have a lot of other repositories to maintain. If you can find an already existing implementation (even not in python) then it would be much much easier for me.
I need that too. Thanks.
I need that too. Thanks.
Have the same model. Anything i can do to help to speed up the fix?
Hi, same vacuum for me.
It's the same model as Xiaomi Robot Vacuum S10 B106GL, i don't think they changed something.
Yeah, it would be great to have that integration available
I have the same vacuum and I would like to help! I'm very interested supporting this model
I`ve done some research and succesfully downloaded map from xiaomi.vacuum.c103
I guess that would work with some newest models as well
The url for obtaining map file is https://api.io.mi.com/app/v2/home/get_interim_file_url_pro
Object name is {user_id}/{device_id}/0
I only have one map on my vacuum, later I will try to create another one and check if it possible to download map by {user_id}/{device_id}/{map_name}
It seems like map file is not in zlib/gz format, maybe it is encrypted. At that point I cannot say it exactly
I could hep too, I just received a xiaomi.vacuum.b106eu
@maksp86 wich is the format of the payload? The URL is correct? I get {"code":0,"message":"auth err"}
JSON response (without autentication 😓). How do you authenticate? Shall we add the {user_id}/{device_id}/{map_name}
endpoint?
Update: I've got map decrypted by reverse engineering the mihome plugin for xiaomi.vacuum.c103 (i guess that would work not only for my vacuum). I will post some code later. Now I have only one thing (some weird serial number) that I cant get from api
Some summary: encryption algorithm is a AES (ECB mode), pkcs7 padding Key is generated from serial_num+owner_id+device_id
After decryption I got hex string, that represents some zlib inflated file, so I think map is in viomi format
This is a reverse-engeneered map encryption algorithm
from Crypto.Cipher import AES
from Crypto.Hash import MD5
from Crypto.Util.Padding import pad, unpad
import base64
isEncryptKeyTypeHex = True
def aesEncrypted(data, key: str):
cipher = AES.new(key.encode("utf-8"), AES.MODE_ECB)
encryptedData = cipher.encrypt(
pad(data.encode("utf-8"), AES.block_size, 'pkcs7'))
encryptedBase64Str = base64.b64encode(encryptedData).decode("utf-8")
return encryptedBase64Str
def aesDecrypted(data, key: str):
parsedKey = key.encode("utf-8")
if isEncryptKeyTypeHex:
parsedKey = bytes.fromhex(key)
cipher = AES.new(parsedKey, AES.MODE_ECB)
decryptedBytes = cipher.decrypt(base64.b64decode(data))
decryptedData = unpad(decryptedBytes, AES.block_size, 'pkcs7')
decryptedStr = decryptedData.decode("utf-8")
return decryptedStr
def md5key(string: str, model: str, device_mac: str):
pjstr = "".join(device_mac.lower().split(":"))
tempModel = model.split('.')[-1]
if len(tempModel) == 2:
tempModel = "00" + tempModel
elif len(tempModel) == 3:
tempModel = "0" + tempModel
tempKey = pjstr + tempModel
aeskey = aesEncrypted(string, tempKey)
temp = MD5.new(aeskey.encode('utf-8')).hexdigest()
if isEncryptKeyTypeHex:
return temp
else:
return temp[8:-8].upper()
def genMD5key(wifi_info_sn: str, owner_id: str, device_id: str, model: str, device_mac: str):
arr = [wifi_info_sn, owner_id, device_id]
tempString = '+'.join(arr)
return md5key(tempString, model, device_mac)
def unGzipCommon(data: bytes, wifi_info_sn: str, owner_id: str, device_id: str, model: str, device_mac: str):
tempKey = genMD5key(wifi_info_sn, owner_id, device_id, model, device_mac)
base64map = base64.b64encode(data)
tempString = aesDecrypted(base64map, tempKey)
return tempString
P.S functions naming was taken from original code Usage:
map_file = some_func_to_download_map() -> bytes
wifi_info_sn = "Weirdo looking string, that I got from attributes section of vacuum entity in Xiaomi Miot Auto"
unGzipCommon(map_file, wifi_info_sn, owner_id, device_id, model, device_mac)
@PiotrMachowski Think my research would help ;-)
vacuum_map_parser_dreame raised Unsupported frame type error vacuum_map_parser_viomi parsed without errors but it seems like nothing is parsed Also it prints in console "223371 bytes remained in the buffer"
parsed_map output
vacuum_map_parser_roborock raised IndexError: index out of range error somewhere in parsing code
I can send you a decrypted map file if you want
I m also very interested for this integration for the S12 Did you send the raw map? (It is listed in the first post as « unticked ») Thank you very much for your hard work! @PiotrMachowski @maksp86
Could you please attach or publish somewhere then decrypted map file (before using any parser / parsing obviously) but already decrypted, to save time ? Thanks in advance
Could you please attach or publish somewhere then decrypted map file (before using any parser / parsing obviously) but already decrypted, to save time ? Thanks in advance
Here you go decrypted_encrypted_map_test.tar.gz
Also there is all files needed to download map from miot-based vacuum (3C Enchanced, S10/S12, ijai.vacuum.*) map-download-kit-for-miot-vacuums.tar.gz I`ve also added patch files for two components
@
vacuum_map_parser_dreame raised Unsupported frame type error vacuum_map_parser_viomi parsed without errors but it seems like nothing is parsed Also it prints in console "223371 bytes remained in the buffer"
parsed_map output
vacuum_map_parser_roborock raised IndexError: index out of range error somewhere in parsing code
I can send you a decrypted map file if you want
Maybe it is a dumb question but ... did you try the xiaomi map parser? what is the result ?
Maybe it is a dumb question but ... did you try the xiaomi map parser? what is the result ?
It fails on parsing.. at least for me
on line 55 of xiaomi_cloud_map_extractor\xiaomi\map_data_parser.py
block_type = MapDataParserXiaomi.get_int16(header, 0x00)
For information, the vacuum does not allow connection if the PC is on another subnet.... i connected in the same subnet, and now got a different error ...
now it fails here :
if wifi_info_sn == None:
raise Exception("Get wifi_info_sn failed")
it is strange as in your code, you seem to be working with a config option ? WIFI_INFO_SN
Traceback (most recent call last): File "C:\Users\XXXX\Downloads\xiaomimapextractor\master\map_downloader.py", line 59, in <module> main() File "C:\Users\XXXX\Downloads\xiaomimapextractor\master\map_downloader.py", line 35, in main raise Exception("Get wifi_info_sn failed") Exception: Get wifi_info_sn failed
Are you on latest version of python-miio? You should use lib from their master git branch
yes i am. I have a xiaomi.vacuum.b106eu, maybe there are small adaptation to make, i don't know. but i don't have the wierd looking wifi string.... mine looks like this :
device.get_property_by(7, 45)[0]["value"]
'value': '[0,3,1,3,2,0,-3600,15,12.0,0,"fr_FR","xx23xxxxxxx46","en_US",0,-1,0,0,1,2,1]'
--> i did replace the numbers with some xxx
The user_id is NOT present in this sentence, nor there is any semi column in the text. (i saw you are looking for semicolumn)
version of my robot is 4.3.3_0016
Here you go decrypted_encrypted_map_test.tar.gz
Thanks! The decrypted version definitely looks like zlib-compressed data to me. ... managed to decompress it 0_decrypted_uncompressed.tar.gz
Ok I managed to get this from your file:
The map starts at offset 0x75 in plain 8bit "grayscale" format. I couldn't figure out where the dimensions are stored. And the dimensions are definitely not fixed as I extracted my own map which is 800x800
Is there any news on this issue?
Is there any news on this issue?
I'm currently spending free time trying to decompile mi home plugin code for data extraction algorithm, but it much harder for understanding to me than code for decryption