Skip to content

Commit

Permalink
Merge branch 'main' into pre-commit-ci-update-config
Browse files Browse the repository at this point in the history
  • Loading branch information
chemelli74 authored Aug 31, 2024
2 parents 34c5bf3 + 8610f1e commit 830f1f3
Show file tree
Hide file tree
Showing 31 changed files with 1,486 additions and 135 deletions.
17 changes: 17 additions & 0 deletions .commitlint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { RuleConfigSeverity } from "@commitlint/types";

const Configuration = {
/*
* Resolve and load @commitlint/config-conventional from node_modules.
* Referenced packages must be installed
*/
extends: ["@commitlint/config-conventional"],
/*
* Any rules defined here will override rules from @commitlint/config-conventional
*/
rules: {
"body-max-line-length": [RuleConfigSeverity.Error, "always", 300],
},
};

export default Configuration;
3 changes: 3 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# These are supported funding model platforms

github: [rokam, chemelli74, wuwentao, Necroneco]
2 changes: 2 additions & 0 deletions .github/workflows/lint-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
label-pr:
runs-on: ubuntu-latest
if: "!contains( github.event.pull_request.labels.*.name, 'autorelease: pending') && !contains( github.event.pull_request.labels.*.name, 'autorelease: tagged')"
permissions:
contents: read
pull-requests: write
steps:
- uses: grafana/[email protected]
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/python-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: wagoid/[email protected]
- uses: wagoid/[email protected]
with:
configFile: .commitlint.config.mjs
build:
needs:
- commitlint
Expand Down
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ repos:
- id: no-commit-to-branch
args: ["--branch", "main"]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.4
rev: v0.6.2
hooks:
- id: ruff
args:
- --fix
- id: ruff-format
- repo: https://github.com/commitizen-tools/commitizen
rev: v3.28.0
rev: v3.29.0
hooks:
- id: commitizen
stages: [commit-msg]
Expand All @@ -41,7 +41,7 @@ repos:
- id: prettier
args: ["--tab-width", "2"]
- repo: https://github.com/asottile/pyupgrade
rev: v3.16.0
rev: v3.17.0
hooks:
- id: pyupgrade
args: [--py37-plus]
Expand Down
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,68 @@
# Changelog

## [2.7.0](https://github.com/rokam/midea-local/compare/v2.6.3...v2.7.0) (2024-08-21)


### Features

* **cli:** use of preset account if cloud info missing ([#278](https://github.com/rokam/midea-local/issues/278)) ([84293bf](https://github.com/rokam/midea-local/commit/84293bfd86b9bb55f59b6897ff5d356df51f7fdb))


### Bug Fixes

* **cloud:** meiju cloud download_lua appliance_type error ([#281](https://github.com/rokam/midea-local/issues/281)) ([54f1bf4](https://github.com/rokam/midea-local/commit/54f1bf4a812c44590d9e01e9cd91c4c0f1768948))

## [2.6.3](https://github.com/rokam/midea-local/compare/v2.6.2...v2.6.3) (2024-08-13)


### Bug Fixes

* body_type default value is zero and not None ([#271](https://github.com/rokam/midea-local/issues/271)) ([bf6b4f0](https://github.com/rokam/midea-local/commit/bf6b4f0d0548bf495339cf793acd30673634f6d1))
* **c3:** silent level as string ([#270](https://github.com/rokam/midea-local/issues/270)) ([c851e33](https://github.com/rokam/midea-local/commit/c851e33dc9e8f7bb5a2f7d49f2e7c557d3a7151f))

## [2.6.2](https://github.com/rokam/midea-local/compare/v2.6.1...v2.6.2) (2024-08-10)


### Bug Fixes

* **cli:** discover must return a list ([#266](https://github.com/rokam/midea-local/issues/266)) ([345794b](https://github.com/rokam/midea-local/commit/345794b1241c149c172c117412ed48707daff6b9))

## [2.6.1](https://github.com/rokam/midea-local/compare/v2.6.0...v2.6.1) (2024-08-09)


### Bug Fixes

* **c3:** silent typo ([#265](https://github.com/rokam/midea-local/issues/265)) ([97defcd](https://github.com/rokam/midea-local/commit/97defcdc100424992c0f2bc7d4797cbbb6825076))
* **cli:** authenticate on discover v3 device ([#263](https://github.com/rokam/midea-local/issues/263)) ([05b0b11](https://github.com/rokam/midea-local/commit/05b0b11d98a3435373742c2cf50142f618700ed1))

## [2.6.0](https://github.com/rokam/midea-local/compare/v2.5.0...v2.6.0) (2024-08-02)


### Features

* parse all the response items from MeijuCloud for get_device_inf ([#231](https://github.com/rokam/midea-local/issues/231)) ([1976eb6](https://github.com/rokam/midea-local/commit/1976eb63578698d2b9a745a92c9c61a887219e0d))


### Bug Fixes

* **cli:** authenticate to get keys ([#256](https://github.com/rokam/midea-local/issues/256)) ([a017e7a](https://github.com/rokam/midea-local/commit/a017e7af46130266226f6c9a100b05703dd6cb5c))
* tank is always seen as full ([#255](https://github.com/rokam/midea-local/issues/255)) ([3524405](https://github.com/rokam/midea-local/commit/3524405fd36131deb83c9ded236eb686860f58c2))

## [2.5.0](https://github.com/rokam/midea-local/compare/v2.4.0...v2.5.0) (2024-07-30)


### Features

* **40:** add `precision_halves` customization ([#248](https://github.com/rokam/midea-local/issues/248)) ([8f5ec79](https://github.com/rokam/midea-local/commit/8f5ec79fe859ad88519360353cd3b6e159200625))
* **b8:** first implementation ([#225](https://github.com/rokam/midea-local/issues/225)) ([259e4f2](https://github.com/rokam/midea-local/commit/259e4f2f715b38a280789530941acc53d98beca4))


### Bug Fixes

* `break` the loop when connected ([#244](https://github.com/rokam/midea-local/issues/244)) ([536f975](https://github.com/rokam/midea-local/commit/536f975b93a3d68466a1bfa5a7c152570121531e))
* **ac:** correct attributes based on msg type ([#251](https://github.com/rokam/midea-local/issues/251)) ([fada9bc](https://github.com/rokam/midea-local/commit/fada9bc5fcc4158a6315566b8e9372c8805e21b4))
* **cloud:** fix email obfuscation ([#245](https://github.com/rokam/midea-local/issues/245)) ([ad9f278](https://github.com/rokam/midea-local/commit/ad9f278c7284e7e80285d66adc8320bb944f1162))

## [2.4.0](https://github.com/rokam/midea-local/compare/v2.3.0...v2.4.0) (2024-07-24)


Expand Down
76 changes: 53 additions & 23 deletions midealocal/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@
import platformdirs
from colorlog import ColoredFormatter

from midealocal.cloud import SUPPORTED_CLOUDS, MideaCloud, get_midea_cloud
from midealocal.device import MideaDevice, ProtocolVersion
from midealocal.cloud import (
SUPPORTED_CLOUDS,
MideaCloud,
get_midea_cloud,
get_preset_account_cloud,
)
from midealocal.device import AuthException, MideaDevice, ProtocolVersion, RefreshFailed
from midealocal.devices import device_selector
from midealocal.discover import discover
from midealocal.exceptions import ElementMissing
from midealocal.exceptions import SocketException
from midealocal.version import __version__

_LOGGER = logging.getLogger("cli")
Expand All @@ -36,15 +41,22 @@ class MideaCLI:

async def _get_cloud(self) -> MideaCloud:
"""Get cloud instance."""
if not hasattr(self, "session"):
self.session = aiohttp.ClientSession()

if (
not self.namespace.cloud_name
or not self.namespace.username
or not self.namespace.password
):
raise ElementMissing("Missing required parameters for cloud request.")

if not hasattr(self, "session"):
self.session = aiohttp.ClientSession()
default_cloud = get_preset_account_cloud()
_LOGGER.info("Using preset account.")
return get_midea_cloud(
cloud_name=default_cloud["cloud_name"],
session=self.session,
account=default_cloud["username"],
password=default_cloud["password"],
)

return get_midea_cloud(
cloud_name=self.namespace.cloud_name,
Expand All @@ -55,17 +67,24 @@ async def _get_cloud(self) -> MideaCloud:

async def _get_keys(self, device_id: int) -> dict[int, dict[str, Any]]:
cloud = await self._get_cloud()
cloud_keys = await cloud.get_cloud_keys(device_id)
default_keys = await cloud.get_default_keys()
if not await cloud.login():
_LOGGER.warning(
"Failed to authenticate to the cloud. Using only default keys.",
)
return default_keys
cloud_keys = await cloud.get_cloud_keys(device_id)

return {**cloud_keys, **default_keys}

async def discover(self) -> MideaDevice | None:
async def discover(self) -> list[MideaDevice]:
"""Discover device information."""
devices = discover(ip_address=self.namespace.host)

device_list: list[MideaDevice] = []
if len(devices) == 0:
_LOGGER.error("No devices found.")
return None
return device_list

# Dump only basic device info from the base class
_LOGGER.info("Found %d devices.", len(devices))
Expand All @@ -90,13 +109,24 @@ async def discover(self) -> MideaDevice | None:
subtype=0,
customize="",
)
_LOGGER.debug("Trying to connect with key: %s", key)
_LOGGER.debug("Opening socket for device.")
if dev.connect():
_LOGGER.info("Found device:\n%s", dev.attributes)
return dev

_LOGGER.debug("Unable to connect with key: %s", key)
return None
try:
if device["protocol"] == ProtocolVersion.V3:
_LOGGER.debug("Trying to connect with key: %s", key)
dev.authenticate()
_LOGGER.debug("Trying to retrieve device attributes.")
dev.refresh_status(True)
except AuthException:
_LOGGER.debug("Unable to connect with key: %s", key)
except SocketException:
_LOGGER.exception("Device socket closed.")
except RefreshFailed:
_LOGGER.exception("Unable to retrieve device attributes.")
else:
_LOGGER.info("Found device:\n%s", dev.attributes)
device_list.append(dev)
return device_list

def message(self) -> None:
"""Load message into device."""
Expand Down Expand Up @@ -162,23 +192,23 @@ async def download(self) -> None:

async def set_attribute(self) -> None:
"""Set attribute for device."""
device = await self.discover()
if device is None:
device_list = await self.discover()
if len(device_list) != 1:
return

_LOGGER.info(
"Setting attribute %s for %s [%s]",
self.namespace.attribute,
device.device_id,
device.device_type,
device_list[0].device_id,
device_list[0].device_type,
)
device.set_attribute(
device_list[0].set_attribute(
self.namespace.attribute,
self._cast_attr_value(),
)
await asyncio.sleep(2)
device.refresh_status(True)
_LOGGER.info("New device status:\n%s", device.attributes)
device_list[0].refresh_status(True)
_LOGGER.info("New device status:\n%s", device_list[0].attributes)

def _cast_attr_value(self) -> int | bool | str:
if self.namespace.attr_type == "bool":
Expand Down
58 changes: 51 additions & 7 deletions midealocal/cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from typing import Any, cast

import aiofiles
from aiohttp import ClientConnectionError, ClientSession
from aiohttp import ClientConnectionError, ClientSession, ClientTimeout
from commonregex import CommonRegex

from midealocal.exceptions import ElementMissing
Expand Down Expand Up @@ -126,8 +126,13 @@ def _redact_data(data: str) -> str:
+ getattr(cr, "street_addresses", [])
)
for token in token_list:
m = len(token)
elm = r"\b" + token + r"\b"
item = token
if len(item) > 0 and item[0] == "'":
item = item[1:]
if len(item) == 0:
break
m = len(item)
elm = r"\b" + item + r"\b"
data = re.sub(elm, block * m, data)
return data

Expand Down Expand Up @@ -200,7 +205,7 @@ async def _api_request(
url,
headers=header,
data=dump_data,
timeout=10,
timeout=ClientTimeout(10),
)
raw = await r.read()
_LOGGER.debug(
Expand Down Expand Up @@ -430,7 +435,25 @@ async def list_appliances(
return None

async def get_device_info(self, device_id: int) -> dict[str, Any] | None:
"""Get device information."""
"""Get device information.
API url: https://mp-prod.smartmidea.net/mas/v5/app/proxy?alias=/v1/appliance/info/get
header:
input: {'applianceCode': 21000***830**18,
'reqId': 'b11bb9083be6d77906fe1c9f019cdea0', 'stamp': '20240710092728'}
response: b'{"code":0,"msg":null,"data":{"id":null,
"applianceCode":21000***830**18,
"sn":"7105f17f36a6afcce272f8053e2be60fd74b1a4baca120afaad83011bb50e8d5f3678bf88e32ea11885394e1a32c9c0e",
"onlineStatus":1,"type":"0xDB","modelNumber":"12877",
"name":"device_name_bytearray",
"des":null,"activeStatus":1,"activeTime":"2024-06-12 10:45:45",
"masterId":null,"wifiVersion":"059009012205","enterprise":"0000",
"isOtherEquipment":null,"attrs":null,"roomName":null,
"btMac":"54B8740FA801","btToken":null,"hotspotName":null,
"isBluetooth":0,"bindType":null,"ability":null,"nameChanged":null,
"sn8":"38127874","supportWot":false,"templateOfTSL":null,
"shadowLevel":null,"smartProductId":10004256,"brand":null}}'
"""
data = {"applianceCode": device_id}
if response := await self._api_request(
endpoint="/v1/appliance/info/get",
Expand All @@ -450,6 +473,27 @@ async def get_device_info(self, device_id: int) -> dict[str, Any] | None:
"manufacturer_code": response.get("enterpriseCode", "0000"),
"model": response.get("productModel"),
"online": response.get("onlineStatus") == "1",
"des": response.get("des", None),
"active_status": response.get("activeStatus", None),
"active_time": response.get("activeTime", None),
"master_id": response.get("masterId", None),
"wifi_version": response.get("wifiVersion", None),
"enterprise": response.get("enterprise", None),
"is_other_equipment": response.get("isOtherEquipment", None),
"attrs": response.get("attrs", None),
"room_name": response.get("roomName", None),
"bt_mac": response.get("btMac", None),
"bt_token": response.get("btToken", None),
"hotspot_name": response.get("hotspotName", None),
"is_bluetooth": response.get("isBluetooth", None),
"bind_type": response.get("bindType", None),
"ability": response.get("ability", None),
"name_changed": response.get("nameChanged", None),
"support_wot": response.get("supportWot", None),
"template_of_tsl": response.get("templateOfTSL", None),
"shadow_level": response.get("shadowLevel", None),
"smart_product_id": response.get("smartProductId", None),
"brand": response.get("brand", None),
}
sn8 = device_info.get("sn8")
if sn8 is None or len(sn8) == 0:
Expand All @@ -471,7 +515,7 @@ async def download_lua(
"""Download lua integration."""
data = {
"applianceSn": sn,
"applianceType": f".{f'x{device_type:02x}'}",
"applianceType": hex(device_type),
"applianceMFCode": manufacturer_code,
"version": "0",
"iotAppId": self._app_id,
Expand Down Expand Up @@ -752,7 +796,7 @@ async def _api_request(
url,
headers=header,
data=data,
timeout=10,
timeout=ClientTimeout(10),
)
raw = await r.read()
_LOGGER.debug(
Expand Down
Loading

0 comments on commit 830f1f3

Please sign in to comment.