From 44ad49aa90d6473bdcbeee5cb4d07fa2d581636a Mon Sep 17 00:00:00 2001 From: Piotr Machowski Date: Thu, 30 Dec 2021 04:06:43 +0100 Subject: [PATCH 01/12] Add GitHub actions --- .github/workflows/hacs.yaml | 29 +++++++++++++++++++++++++++++ .github/workflows/release.yaml | 27 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 .github/workflows/hacs.yaml create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/hacs.yaml b/.github/workflows/hacs.yaml new file mode 100644 index 0000000..a59c9a8 --- /dev/null +++ b/.github/workflows/hacs.yaml @@ -0,0 +1,29 @@ +name: Validate HACS +on: + push: + pull_request: +jobs: + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + name: Download repo + with: + fetch-depth: 0 + + - uses: actions/setup-python@v2 + name: Setup Python + with: + python-version: '3.8.x' + + - uses: actions/cache@v2 + name: Cache + with: + path: | + ~/.cache/pip + key: custom-component-ci + + - name: HACS Action + uses: hacs/action@main + with: + CATEGORY: integration \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..9d94005 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,27 @@ +name: Release + +on: + release: + types: [published] + +jobs: + release: + name: Prepare release + runs-on: ubuntu-latest + steps: + - name: Download repo + uses: actions/checkout@v1 + + - name: Zip xiaomi_cloud_map_extractor dir + run: | + cd /home/runner/work/Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor/Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor/custom_components/xiaomi_cloud_map_extractor + zip xiaomi_cloud_map_extractor.zip -r ./ + + - name: Upload zip to release + uses: svenstaro/upload-release-action@v1-release + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: /home/runner/work/Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor/Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor/custom_components/xiaomi_cloud_map_extractor/xiaomi_cloud_map_extractor.zip + asset_name: xiaomi_cloud_map_extractor.zip + tag: ${{ github.ref }} + overwrite: true \ No newline at end of file From ff44cf4d6f0d398f521e2ea9b89f7dabba870d01 Mon Sep 17 00:00:00 2001 From: Piotr Machowski Date: Mon, 3 Jan 2022 01:45:55 +0100 Subject: [PATCH 02/12] Add RC4 encryption --- .../common/vacuum_v2.py | 2 +- .../common/xiaomi_cloud_connector.py | 54 +++++++++++++++---- .../xiaomi_cloud_map_extractor/manifest.json | 3 +- .../xiaomi/vacuum.py | 2 +- 4 files changed, 47 insertions(+), 14 deletions(-) diff --git a/custom_components/xiaomi_cloud_map_extractor/common/vacuum_v2.py b/custom_components/xiaomi_cloud_map_extractor/common/vacuum_v2.py index f7bd5ed..9ef4973 100644 --- a/custom_components/xiaomi_cloud_map_extractor/common/vacuum_v2.py +++ b/custom_components/xiaomi_cloud_map_extractor/common/vacuum_v2.py @@ -11,7 +11,7 @@ def get_map_url(self, map_name): params = { "data": f'{{"obj_name":"{self._user_id}/{self._device_id}/{map_name}"}}' } - api_response = self._connector.execute_api_call(url, params) + api_response = self._connector.execute_api_call_encrypted(url, params) if api_response is None or "result" not in api_response or "url" not in api_response["result"]: return None return api_response["result"]["url"] diff --git a/custom_components/xiaomi_cloud_map_extractor/common/xiaomi_cloud_connector.py b/custom_components/xiaomi_cloud_map_extractor/common/xiaomi_cloud_connector.py index 4dc2a0e..1f2c92a 100644 --- a/custom_components/xiaomi_cloud_map_extractor/common/xiaomi_cloud_connector.py +++ b/custom_components/xiaomi_cloud_map_extractor/common/xiaomi_cloud_connector.py @@ -7,6 +7,7 @@ import random import time from typing import Optional +from Crypto.Cipher import ARC4 import requests @@ -149,14 +150,15 @@ def get_devices(self, country): params = { "data": '{"getVirtualModel":false,"getHuamiDevices":0}' } - return self.execute_api_call(url, params) + return self.execute_api_call_encrypted(url, params) - def execute_api_call(self, url, params): + def execute_api_call_encrypted(self, url, params): headers = { - "Accept-Encoding": "gzip", + "Accept-Encoding": "identity", "User-Agent": self._agent, "Content-Type": "application/x-www-form-urlencoded", - "x-xiaomi-protocal-flag-cli": "PROTOCAL-HTTP2" + "x-xiaomi-protocal-flag-cli": "PROTOCAL-HTTP2", + "MIOT-ENCRYPT-ALGORITHM": "ENCRYPT-RC4", } cookies = { "userId": str(self._userId), @@ -171,18 +173,15 @@ def execute_api_call(self, url, params): millis = round(time.time() * 1000) nonce = self.generate_nonce(millis) signed_nonce = self.signed_nonce(nonce) - signature = self.generate_signature(url.replace("/app", ""), signed_nonce, nonce, params) - fields = { - "signature": signature, - "_nonce": nonce, - "data": params["data"] - } + fields = self.generate_enc_params(url, "POST", signed_nonce, nonce, params, self._ssecurity) + try: response = self._session.post(url, headers=headers, cookies=cookies, params=fields, timeout=10) except: response = None if response is not None and response.status_code == 200: - return response.json() + decoded = self.decrypt_rc4(self.signed_nonce(fields["_nonce"]), response.text) + return json.loads(decoded) return None def get_api_url(self, country): @@ -215,6 +214,39 @@ def generate_signature(url, signed_nonce, nonce, params): signature = hmac.new(base64.b64decode(signed_nonce), msg=signature_string.encode(), digestmod=hashlib.sha256) return base64.b64encode(signature.digest()).decode() + @staticmethod + def generate_enc_signature(url, method, signed_nonce, params): + signature_params = [str(method).upper(), url.split("com")[1].replace("/app/", "/")] + for k, v in params.items(): + signature_params.append(f"{k}={v}") + signature_params.append(signed_nonce) + signature_string = "&".join(signature_params) + return base64.b64encode(hashlib.sha1(signature_string.encode('utf-8')).digest()).decode() + + @staticmethod + def generate_enc_params(url, method, signed_nonce, nonce, params, ssecurity): + params['rc4_hash__'] = XiaomiCloudConnector.generate_enc_signature(url, method, signed_nonce, params) + for k, v in params.items(): + params[k] = XiaomiCloudConnector.encrypt_rc4(signed_nonce, v) + params.update({ + 'signature': XiaomiCloudConnector.generate_enc_signature(url, method, signed_nonce, params), + 'ssecurity': ssecurity, + '_nonce': nonce, + }) + return params + @staticmethod def to_json(response_text): return json.loads(response_text.replace("&&&START&&&", "")) + + @staticmethod + def encrypt_rc4(password, payload): + r = ARC4.new(base64.b64decode(password)) + r.encrypt(bytes(1024)) + return base64.b64encode(r.encrypt(payload.encode())).decode() + + @staticmethod + def decrypt_rc4(password, payload): + r = ARC4.new(base64.b64decode(password)) + r.encrypt(bytes(1024)) + return r.encrypt(base64.b64decode(payload)) diff --git a/custom_components/xiaomi_cloud_map_extractor/manifest.json b/custom_components/xiaomi_cloud_map_extractor/manifest.json index 6089d1c..1128fb6 100644 --- a/custom_components/xiaomi_cloud_map_extractor/manifest.json +++ b/custom_components/xiaomi_cloud_map_extractor/manifest.json @@ -11,7 +11,8 @@ "pillow", "pybase64", "python-miio", - "requests" + "requests", + "pycryptodome" ], "version": "v2.1.1", "iot_class": "cloud_polling" diff --git a/custom_components/xiaomi_cloud_map_extractor/xiaomi/vacuum.py b/custom_components/xiaomi_cloud_map_extractor/xiaomi/vacuum.py index d1c2642..dc3128d 100644 --- a/custom_components/xiaomi_cloud_map_extractor/xiaomi/vacuum.py +++ b/custom_components/xiaomi_cloud_map_extractor/xiaomi/vacuum.py @@ -15,7 +15,7 @@ def get_map_url(self, map_name): params = { "data": '{"obj_name":"' + map_name + '"}' } - api_response = self._connector.execute_api_call(url, params) + api_response = self._connector.execute_api_call_encrypted(url, params) if api_response is None or \ "result" not in api_response or \ api_response["result"] is None or \ From 1e1d93f2e1c3cf13ae32ef3ec8696a8290001637 Mon Sep 17 00:00:00 2001 From: halfbakery <54818270+halfbakery@users.noreply.github.com> Date: Mon, 3 Jan 2022 02:01:30 +0100 Subject: [PATCH 03/12] Fix blueprint errors caused by manual run (#162) (#195) --- .../automation/disable_vacuum_camera_update_when_docked.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blueprints/automation/disable_vacuum_camera_update_when_docked.yaml b/blueprints/automation/disable_vacuum_camera_update_when_docked.yaml index aa72b6f..0d6d4c6 100644 --- a/blueprints/automation/disable_vacuum_camera_update_when_docked.yaml +++ b/blueprints/automation/disable_vacuum_camera_update_when_docked.yaml @@ -26,6 +26,8 @@ condition: value_template: '{{ trigger.to_state.state != trigger.from_state.state }}' action: + - condition: trigger + id: 0 - service: | {% if trigger.to_state.state in ["unavailable", "unknown", "docked"] %} camera.turn_off From 4fbe9413e375aeba7d10e414e1300f2fc500d7f0 Mon Sep 17 00:00:00 2001 From: "Ryan Kim(True-World)" <58506302+nukusinji@users.noreply.github.com> Date: Mon, 3 Jan 2022 10:02:42 +0900 Subject: [PATCH 04/12] fix for HA 2021.12 (#201) --- custom_components/xiaomi_cloud_map_extractor/camera.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/xiaomi_cloud_map_extractor/camera.py b/custom_components/xiaomi_cloud_map_extractor/camera.py index 1c421fb..d158d54 100644 --- a/custom_components/xiaomi_cloud_map_extractor/camera.py +++ b/custom_components/xiaomi_cloud_map_extractor/camera.py @@ -195,7 +195,7 @@ def supported_features(self): return SUPPORT_ON_OFF @property - def device_state_attributes(self): + def extra_state_attributes (self): attributes = {} if self._map_data is not None: rooms = [] From 4ef35edc658956cd952a54c24464b301f8cd124b Mon Sep 17 00:00:00 2001 From: Piotr Machowski Date: Mon, 3 Jan 2022 04:23:44 +0100 Subject: [PATCH 05/12] Fix comparison (#184) --- custom_components/xiaomi_cloud_map_extractor/common/map_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/xiaomi_cloud_map_extractor/common/map_data.py b/custom_components/xiaomi_cloud_map_extractor/common/map_data.py index 317cc58..8a36a36 100644 --- a/custom_components/xiaomi_cloud_map_extractor/common/map_data.py +++ b/custom_components/xiaomi_cloud_map_extractor/common/map_data.py @@ -19,7 +19,7 @@ def __str__(self): return f"({self.x}, {self.y}, a = {self.a})" def __eq__(self, other): - return self.x == other.x and self.y == other.y and self.a == other.a + return other is not None and self.x == other.x and self.y == other.y and self.a == other.a def as_dict(self): if self.a is None: From eec4b9a6e71d22690aa05e3d0a978d8d118890b5 Mon Sep 17 00:00:00 2001 From: Piotr Machowski Date: Mon, 3 Jan 2022 04:24:44 +0100 Subject: [PATCH 06/12] Fix Roidmi calibration (#193) --- .../xiaomi_cloud_map_extractor/roidmi/map_data_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/xiaomi_cloud_map_extractor/roidmi/map_data_parser.py b/custom_components/xiaomi_cloud_map_extractor/roidmi/map_data_parser.py index e3064fc..545379c 100644 --- a/custom_components/xiaomi_cloud_map_extractor/roidmi/map_data_parser.py +++ b/custom_components/xiaomi_cloud_map_extractor/roidmi/map_data_parser.py @@ -27,7 +27,7 @@ def parse(raw: bytes, colors, drawables, texts, sizes, image_config) -> MapData: resolution = map_info["resolution"] x_min_calc = x_min / resolution y_min_calc = y_min / resolution - map_data = MapData(0, 1) + map_data = MapData(0, 1000) map_data.rooms = MapDataParserRoidmi.parse_rooms(map_info) image = MapDataParserRoidmi.parse_image(map_image, width, height, x_min_calc, y_min_calc, resolution, colors, image_config, map_data.rooms) From 79472ce8a3435be550c448f00f1527ae180f6f8b Mon Sep 17 00:00:00 2001 From: Piotr Machowski Date: Mon, 3 Jan 2022 04:25:32 +0100 Subject: [PATCH 07/12] Fix Roidmi areas calculation (#158) --- .../xiaomi_cloud_map_extractor/roidmi/map_data_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/xiaomi_cloud_map_extractor/roidmi/map_data_parser.py b/custom_components/xiaomi_cloud_map_extractor/roidmi/map_data_parser.py index 545379c..54ad77d 100644 --- a/custom_components/xiaomi_cloud_map_extractor/roidmi/map_data_parser.py +++ b/custom_components/xiaomi_cloud_map_extractor/roidmi/map_data_parser.py @@ -112,7 +112,7 @@ def parse_rooms(map_info: dict) -> Dict[int, Room]: areas = [] if "autoArea" in map_info: areas = map_info["autoArea"] - elif "autoAreaValue" in map_info: + elif "autoAreaValue" in map_info and map_info["autoAreaValue"] is not None: areas = map_info["autoAreaValue"] for area in areas: id = area["id"] From a2762f8299460aa08631e877121f18355293d970 Mon Sep 17 00:00:00 2001 From: Piotr Machowski Date: Mon, 3 Jan 2022 04:26:05 +0100 Subject: [PATCH 08/12] Cleanup --- README.md | 53 +++++++++++++------ .../xiaomi_cloud_map_extractor/camera.py | 4 +- .../viomi/map_data_parser.py | 3 +- scripts/get_map.py | 2 +- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 6293910..8a1419c 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,29 @@ -[![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)](https://hacs.xyz/) -[![Community Forum](https://img.shields.io/badge/Community-Forum-41BDF5.svg?style=popout)](https://community.home-assistant.io/t/xiaomi-cloud-vacuum-map-extractor/231292) -[![buymeacoffee_badge](https://img.shields.io/badge/Donate-Buy%20Me%20a%20Coffee-ff813f?style=flat)](https://www.buymeacoffee.com/PiotrMachowski) -[![paypalme_badge](https://img.shields.io/badge/Donate-PayPal-0070ba?style=flat)](https://paypal.me/PiMachowski) -![GitHub All Releases](https://img.shields.io/github/downloads/Piotrmachowski/Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor/total) +[![HACS Default][hacs_shield]][hacs] +[![GitHub Latest Release][releases_shield]][latest_release] +[![GitHub All Releases][downloads_total_shield]][releases] +[![Community Forum][community_forum_shield]][community_forum] +[![Buy me a coffee][buy_me_a_coffee_shield]][buy_me_a_coffee] +[![PayPal.Me][paypal_me_shield]][paypal_me] + + +[hacs_shield]: https://img.shields.io/static/v1.svg?label=HACS&message=Default&style=popout&color=green&labelColor=41bdf5&logo=HomeAssistantCommunityStore&logoColor=white +[hacs]: https://hacs.xyz/docs/default_repositories + +[latest_release]: https://github.com/PiotrMachowski/Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor/releases/latest +[releases_shield]: https://img.shields.io/github/release/PiotrMachowski/Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor.svg?style=popout + +[releases]: https://github.com/PiotrMachowski/Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor/releases +[downloads_total_shield]: https://img.shields.io/github/downloads/PiotrMachowski/Home-Assistant-custom-components-Xiaomi-Cloud-Map-Extractor/total + +[community_forum_shield]: https://img.shields.io/static/v1.svg?label=%20&message=Forum&style=popout&color=41bdf5&logo=HomeAssistant&logoColor=white +[community_forum]: https://community.home-assistant.io/t/xiaomi-cloud-vacuum-map-extractor/231292 + +[buy_me_a_coffee_shield]: https://img.shields.io/static/v1.svg?label=%20&message=Buy%20me%20a%20coffee&color=6f4e37&logo=buy%20me%20a%20coffee&logoColor=white +[buy_me_a_coffee]: https://www.buymeacoffee.com/PiotrMachowski + +[paypal_me_shield]: https://img.shields.io/static/v1.svg?label=%20&message=PayPal.Me&logo=paypal +[paypal_me]: https://paypal.me/PiMachowski + # Xiaomi Cloud Map Extractor @@ -67,7 +88,7 @@ camera: password: !secret xiaomi_cloud_password draw: ['all'] attributes: - - calibration_points + - calibration_points ``` @@ -229,18 +250,18 @@ camera: | `attributes` | list | false | | List of desired entity attributes ([see below](#attributes-configuration)) | | `scan_interval` | interval | false | default: `5` seconds | Interval between map updates ([documentation](https://www.home-assistant.io/docs/configuration/platform_options/#scan-interval)) | | `auto_update` | boolean | false | default: `true` | Activation/deactivation of automatic map updates. ([see below](#updates)) | -| `store_map_raw` | boolean | false | default: `false` | Enables storing raw map data in `store_map_path` directory ([more info](#retrieving-map)). Xiaomi map can be opened with [RoboMapViewer](https://github.com/marcelrv/XiaomiRobotVacuumProtocol/tree/master/RRMapFile). | -| `store_map_image` | boolean | false | default: `false` | Enables storing map image in `store_map_path` path with name `map_image_.png` | -| `store_map_path` | string | false | default: `/tmp` | Storing map data directory | -| `force_api` | string | false | One of: `xiaomi`, `viomi`, `roidmi` | Forces usage of specific API. | +| `store_map_raw` | boolean | false | default: `false` | Enables storing raw map data in `store_map_path` directory ([more info](#retrieving-map)). Xiaomi map can be opened with [RoboMapViewer](https://github.com/marcelrv/XiaomiRobotVacuumProtocol/tree/master/RRMapFile). | +| `store_map_image` | boolean | false | default: `false` | Enables storing map image in `store_map_path` path with name `map_image_.png` | +| `store_map_path` | string | false | default: `/tmp` | Storing map data directory | +| `force_api` | string | false | One of: `xiaomi`, `viomi`, `roidmi` | Forces usage of specific API. | #### Colors configuration Each color is represented by a list of 3 or 4 parameters: `[red, green, blue]` or `[red, green, blue, alpha]`. - Each parameter is a number from a range 0-255 and can be also provided as a HEX value: [0x12, 0xAF, 0xC5] matches #12AFC5. - + Each parameter is a number from a range 0-255 and can be also provided as a HEX value: [0x12, 0xAF, 0xC5] matches #12AFC5. + - + | Color name | Description | | --- | --- | | `color_charger` | Charger position | @@ -274,7 +295,7 @@ camera: This section contains mapping between room numbers and colors. Each color is represented by a list of 3 or 4 parameters: `[red, green, blue]` or `[red, green, blue, alpha]`. - Each parameter is a number from a range 0-255 and can be also provided as a HEX value: [0x12, 0xAF, 0xC5] matches #12AFC5. + Each parameter is a number from a range 0-255 and can be also provided as a HEX value: [0x12, 0xAF, 0xC5] matches #12AFC5. #### Draw configuration @@ -401,7 +422,7 @@ This integration was tested on following vacuums: - Roidmi map format: - `roidmi.vacuum.v60` (Roidmi EVE Plus) - `viomi.vacuum.v18` (Viomi S9) - + ## Unsupported devices At this moment this integration is known to not work with following vacuums: @@ -412,7 +433,7 @@ At this moment this integration is known to not work with following vacuums: When `store_map_raw: true` is added to your config this integration will store a raw map file in `/tmp` directory. If you don't use Core installation ([installation types](https://www.home-assistant.io/installation/#compare-installation-methods)) you can retrieve this file in the following way: - In [SSH & Terminal add-on](https://github.com/hassio-addons/addon-ssh) enable protected access -- Open terminal and use the following command to copy file: +- Open terminal and use the following command to copy file: ``` docker exec homeassistant bash -c "mkdir -p /config/tmp/ && cp /tmp/map_* /config/tmp/" ``` diff --git a/custom_components/xiaomi_cloud_map_extractor/camera.py b/custom_components/xiaomi_cloud_map_extractor/camera.py index d158d54..d85a0e7 100644 --- a/custom_components/xiaomi_cloud_map_extractor/camera.py +++ b/custom_components/xiaomi_cloud_map_extractor/camera.py @@ -145,7 +145,7 @@ def __init__(self, entity_id, host, token, username, password, country, name, sh super().__init__() self.entity_id = entity_id self.content_type = CONTENT_TYPE - self._vacuum = miio.Vacuum(host, token) + self._vacuum = miio.RoborockVacuum(host, token) self._connector = XiaomiCloudConnector(username, password) self._status = CameraStatus.INITIALIZING self._device = None @@ -195,7 +195,7 @@ def supported_features(self): return SUPPORT_ON_OFF @property - def extra_state_attributes (self): + def extra_state_attributes(self): attributes = {} if self._map_data is not None: rooms = [] diff --git a/custom_components/xiaomi_cloud_map_extractor/viomi/map_data_parser.py b/custom_components/xiaomi_cloud_map_extractor/viomi/map_data_parser.py index 386da5f..b53be0a 100644 --- a/custom_components/xiaomi_cloud_map_extractor/viomi/map_data_parser.py +++ b/custom_components/xiaomi_cloud_map_extractor/viomi/map_data_parser.py @@ -311,7 +311,8 @@ def parse_section(buf: ParsingBuffer, name: str, map_id: int): magic = buf.get_uint32('magic') if magic != map_id: raise ValueError( - f"error parsing section {name} at offset {buf._offs - 4:#x}: magic check failed {magic:#x}") + f"error parsing section {name} at offset {buf._offs - 4:#x}: magic check failed." + + f" Magic: {magic:#x}, Map ID: {map_id:#x}") @staticmethod def parse_position(buf: ParsingBuffer, name: str) -> Optional[Point]: diff --git a/scripts/get_map.py b/scripts/get_map.py index 7fd3790..b691fb6 100644 --- a/scripts/get_map.py +++ b/scripts/get_map.py @@ -13,7 +13,7 @@ username = "" password = "" country = "" -map_name = "0" # if the vacuum_ip is empty, the map name query will be skipped (userful for Xiaomi Mi Robot Vacuum Mop Pro (STYJ02YM) / Viomi V2 Pro / etc.) and this value will be used +map_name = "0" # if the vacuum_ip is empty, the map name query will be skipped (useful for Xiaomi Mi Robot Vacuum Mop Pro (STYJ02YM) / Viomi V2 Pro / etc.) and this value will be used draw = [ "charger", From 3367cd67f7d45b8ba0973f0763ccc8954dacd047 Mon Sep 17 00:00:00 2001 From: Piotr Machowski Date: Mon, 3 Jan 2022 04:30:45 +0100 Subject: [PATCH 09/12] Add gitignore --- .gitignore | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c353f71 --- /dev/null +++ b/.gitignore @@ -0,0 +1,152 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ \ No newline at end of file From 1eabdddbe30c30d1712e1746be83e42464e10850 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Mon, 3 Jan 2022 04:32:44 +0100 Subject: [PATCH 10/12] GitHub Action to lint Python code (#179) * GitHub Action to lint Python code * Make bandit and codespell mandatory tests * Fix typo discovered by codespell * --ignore=B001,E241,E265,E302,E722,E731,F403,F405,F841,W504 --- .github/workflows/lint_python.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/workflows/lint_python.yml diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml new file mode 100644 index 0000000..6660125 --- /dev/null +++ b/.github/workflows/lint_python.yml @@ -0,0 +1,24 @@ +name: lint_python +on: [pull_request, push] +jobs: + lint_python: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - run: pip install --upgrade pip wheel + - run: pip install bandit black codespell flake8 flake8-2020 flake8-bugbear + flake8-comprehensions isort mypy pytest pyupgrade safety + - run: bandit --recursive --skip B105,B108,B303,B311 . + - run: black --check . || true + - run: codespell --ignore-words-list="hass" + - run: flake8 . --count --ignore=B001,E241,E265,E302,E722,E731,F403,F405,F841,W504 + --max-complexity=21 --max-line-length=184 --show-source --statistics + - run: isort --check-only --profile black . || true + - run: pip install -r requirements.txt || pip install --editable . || true + - run: mkdir --parents --verbose .mypy_cache + - run: mypy --ignore-missing-imports --install-types --non-interactive . || true + - run: pytest . || true + - run: pytest --doctest-modules . || true + - run: shopt -s globstar && pyupgrade --py36-plus **/*.py || true + - run: safety check From c7bd7428580d556f76b015908a0fb1979a845b79 Mon Sep 17 00:00:00 2001 From: Piotr Machowski Date: Mon, 3 Jan 2022 04:39:31 +0100 Subject: [PATCH 11/12] Skip bandit ARC4 failure --- .github/workflows/lint_python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml index 6660125..3ac6408 100644 --- a/.github/workflows/lint_python.yml +++ b/.github/workflows/lint_python.yml @@ -9,7 +9,7 @@ jobs: - run: pip install --upgrade pip wheel - run: pip install bandit black codespell flake8 flake8-2020 flake8-bugbear flake8-comprehensions isort mypy pytest pyupgrade safety - - run: bandit --recursive --skip B105,B108,B303,B311 . + - run: bandit --recursive --skip B105,B108,B303,B304,B311,B413 . - run: black --check . || true - run: codespell --ignore-words-list="hass" - run: flake8 . --count --ignore=B001,E241,E265,E302,E722,E731,F403,F405,F841,W504 From 162ef22dbaa43dd2247a4949e38fbbad0bab89a4 Mon Sep 17 00:00:00 2001 From: Piotr Machowski Date: Mon, 3 Jan 2022 04:43:22 +0100 Subject: [PATCH 12/12] Cleanup --- .../xiaomi_cloud_map_extractor/viomi/map_data_parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/xiaomi_cloud_map_extractor/viomi/map_data_parser.py b/custom_components/xiaomi_cloud_map_extractor/viomi/map_data_parser.py index b53be0a..0d3a7ee 100644 --- a/custom_components/xiaomi_cloud_map_extractor/viomi/map_data_parser.py +++ b/custom_components/xiaomi_cloud_map_extractor/viomi/map_data_parser.py @@ -311,8 +311,8 @@ def parse_section(buf: ParsingBuffer, name: str, map_id: int): magic = buf.get_uint32('magic') if magic != map_id: raise ValueError( - f"error parsing section {name} at offset {buf._offs - 4:#x}: magic check failed." - + f" Magic: {magic:#x}, Map ID: {map_id:#x}") + f"error parsing section {name} at offset {buf._offs - 4:#x}: magic check failed. " + + f"Magic: {magic:#x}, Map ID: {map_id:#x}") @staticmethod def parse_position(buf: ParsingBuffer, name: str) -> Optional[Point]: