Compare commits
	
		
			20 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 715d8a96fc | |||
| ec358034e1 | |||
| 6cdac99ce8 | |||
| 0c1670733f | |||
| 744094dc07 | |||
| bf2015fcb9 | |||
| bc7e8994c9 | |||
| 8ca40d7ad0 | |||
| 9a6a07fd46 | |||
| f1818bbc5d | |||
| 3d5c8405ea | |||
| e234ef3bbb | |||
| e00e147ecd | |||
| 26bc35c8a6 | |||
| 17d73cdeb8 | |||
| a10ab4423e | |||
| 0553e6c17d | |||
| 44f40c531e | |||
| 4e88bc7a9f | |||
| b5d8a90d79 | 
							
								
								
									
										5
									
								
								mypy.ini
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								mypy.ini
									
									
									
									
									
								
							| @ -2,3 +2,8 @@ | ||||
| check_untyped_defs = True | ||||
| disallow_any_generics = True | ||||
| disallow_untyped_defs = True | ||||
| disallow_any_unimported = True | ||||
| no_implicit_optional = True | ||||
| warn_return_any = True | ||||
| show_error_codes = True | ||||
| warn_unused_ignores = True | ||||
|  | ||||
| @ -99,7 +99,7 @@ async def main() -> None: | ||||
|                 print(printer.key_print(data)) | ||||
|                 print( | ||||
|                     printer.pretty_print( | ||||
|                         printer.create_command(device.commands, concat=True) | ||||
|                         printer.create_commands(device.commands, concat=True) | ||||
|                     ) | ||||
|                 ) | ||||
|             else: | ||||
|  | ||||
| @ -6,10 +6,12 @@ from pathlib import Path | ||||
| from typing import Optional, Dict, Any, TYPE_CHECKING, List | ||||
|  | ||||
| from pyhon import diagnose, exceptions | ||||
| from pyhon.appliances.base import ApplianceBase | ||||
| from pyhon.attributes import HonAttribute | ||||
| from pyhon.command_loader import HonCommandLoader | ||||
| from pyhon.commands import HonCommand | ||||
| from pyhon.parameter.base import HonParameter | ||||
| from pyhon.parameter.enum import HonParameterEnum | ||||
| from pyhon.parameter.range import HonParameterRange | ||||
| from pyhon.typedefs import Parameter | ||||
|  | ||||
| @ -40,7 +42,7 @@ class HonAppliance: | ||||
|         self._default_setting = HonParameter("", {}, "") | ||||
|  | ||||
|         try: | ||||
|             self._extra = importlib.import_module( | ||||
|             self._extra: Optional[ApplianceBase] = importlib.import_module( | ||||
|                 f"pyhon.appliances.{self.appliance_type.lower()}" | ||||
|             ).Appliance(self) | ||||
|         except ModuleNotFoundError: | ||||
| @ -71,7 +73,8 @@ class HonAppliance: | ||||
|  | ||||
|     def _check_name_zone(self, name: str, frontend: bool = True) -> str: | ||||
|         zone = " Z" if frontend else "_z" | ||||
|         if (attribute := self._info.get(name, "")) and self._zone: | ||||
|         attribute: str = self._info.get(name, "") | ||||
|         if attribute and self._zone: | ||||
|             return f"{attribute}{zone}{self._zone}" | ||||
|         return attribute | ||||
|  | ||||
| @ -101,20 +104,22 @@ class HonAppliance: | ||||
|  | ||||
|     @property | ||||
|     def brand(self) -> str: | ||||
|         return self._check_name_zone("brand") | ||||
|         brand = self._check_name_zone("brand") | ||||
|         return brand[0].upper() + brand[1:] | ||||
|  | ||||
|     @property | ||||
|     def nick_name(self) -> str: | ||||
|         result = self._check_name_zone("nickName") | ||||
|         if not result or re.findall("^[xX\s]+$", result): | ||||
|         if not result or re.findall("^[xX1\\s-]+$", result): | ||||
|             return self.model_name | ||||
|         return result | ||||
|  | ||||
|     @property | ||||
|     def code(self) -> str: | ||||
|         if code := self.info.get("code"): | ||||
|         code: str = self.info.get("code", "") | ||||
|         if code: | ||||
|             return code | ||||
|         serial_number = self.info.get("serialNumber", "") | ||||
|         serial_number: str = self.info.get("serialNumber", "") | ||||
|         return serial_number[:8] if len(serial_number) < 18 else serial_number[:11] | ||||
|  | ||||
|     @property | ||||
| @ -162,16 +167,18 @@ class HonAppliance: | ||||
|         self._commands = command_loader.commands | ||||
|         self._additional_data = command_loader.additional_data | ||||
|         self._appliance_model = command_loader.appliance_data | ||||
|         self.sync_params_to_command("settings") | ||||
|  | ||||
|     async def load_attributes(self) -> None: | ||||
|         self._attributes = await self.api.load_attributes(self) | ||||
|         for name, values in self._attributes.pop("shadow").get("parameters").items(): | ||||
|         attributes = await self.api.load_attributes(self) | ||||
|         for name, values in attributes.pop("shadow", {}).get("parameters", {}).items(): | ||||
|             if name in self._attributes.get("parameters", {}): | ||||
|                 self._attributes["parameters"][name].update(values) | ||||
|             else: | ||||
|                 self._attributes.setdefault("parameters", {})[name] = HonAttribute( | ||||
|                     values | ||||
|                 ) | ||||
|         self._attributes |= attributes | ||||
|         if self._extra: | ||||
|             self._attributes = self._extra.attributes(self._attributes) | ||||
|  | ||||
| @ -189,6 +196,7 @@ class HonAppliance: | ||||
|         ): | ||||
|             self._last_update = now | ||||
|             await self.load_attributes() | ||||
|             self.sync_params_to_command("settings") | ||||
|  | ||||
|     @property | ||||
|     def command_parameters(self) -> Dict[str, Dict[str, str | float]]: | ||||
| @ -196,7 +204,7 @@ class HonAppliance: | ||||
|  | ||||
|     @property | ||||
|     def settings(self) -> Dict[str, Parameter]: | ||||
|         result = {} | ||||
|         result: Dict[str, Parameter] = {} | ||||
|         for name, command in self._commands.items(): | ||||
|             for key in command.setting_keys: | ||||
|                 setting = command.settings.get(key, self._default_setting) | ||||
| @ -232,16 +240,34 @@ class HonAppliance: | ||||
|     async def data_archive(self, path: Path) -> str: | ||||
|         return await diagnose.zip_archive(self, path, anonymous=True) | ||||
|  | ||||
|     def sync_to_params(self, command_name: str) -> None: | ||||
|     def sync_command_to_params(self, command_name: str) -> None: | ||||
|         if not (command := self.commands.get(command_name)): | ||||
|             return | ||||
|         for key, value in self.attributes.get("parameters", {}).items(): | ||||
|             if isinstance(value, str) and (new := command.parameters.get(key)): | ||||
|         for key in self.attributes.get("parameters", {}): | ||||
|             if new := command.parameters.get(key): | ||||
|                 self.attributes["parameters"][key].update( | ||||
|                     str(new.intern_value), shield=True | ||||
|                 ) | ||||
|  | ||||
|     def sync_command(self, main: str, target: Optional[List[str]] = None) -> None: | ||||
|     def sync_params_to_command(self, command_name: str) -> None: | ||||
|         if not (command := self.commands.get(command_name)): | ||||
|             return | ||||
|         for key in command.setting_keys: | ||||
|             if ( | ||||
|                 new := self.attributes.get("parameters", {}).get(key) | ||||
|             ) is None or new.value == "": | ||||
|                 continue | ||||
|             setting = command.settings[key] | ||||
|             try: | ||||
|                 if not isinstance(setting, HonParameterRange): | ||||
|                     command.settings[key].value = str(new.value) | ||||
|                 else: | ||||
|                     command.settings[key].value = float(new.value) | ||||
|             except ValueError as error: | ||||
|                 _LOGGER.info("Can't set %s - %s", key, error) | ||||
|                 continue | ||||
|  | ||||
|     def sync_command(self, main: str, target: Optional[List[str] | str] = None) -> None: | ||||
|         base: Optional[HonCommand] = self.commands.get(main) | ||||
|         if not base: | ||||
|             return | ||||
| @ -260,4 +286,6 @@ class HonAppliance: | ||||
|                         parameter.max = int(base_value.value) | ||||
|                         parameter.min = int(base_value.value) | ||||
|                         parameter.step = 1 | ||||
|                     elif isinstance(parameter, HonParameterEnum): | ||||
|                         parameter.values = base_value.values | ||||
|                     parameter.value = base_value.value | ||||
|  | ||||
| @ -14,11 +14,4 @@ class Appliance(ApplianceBase): | ||||
|             data["parameters"]["remainingTimeMM"].value = "0" | ||||
|  | ||||
|         data["active"] = data["parameters"]["onOffStatus"] == "1" | ||||
|  | ||||
|         if program := int(data["parameters"]["prCode"]): | ||||
|             if (setting := self.parent.settings["startProgram.program"]) and isinstance( | ||||
|                 setting, HonParameterProgram | ||||
|             ): | ||||
|                 data["programName"] = setting.ids.get(program, "") | ||||
|  | ||||
|         return data | ||||
|  | ||||
| @ -55,7 +55,7 @@ class HonCommandLoader: | ||||
|     async def load_commands(self) -> None: | ||||
|         """Trigger loading of command data""" | ||||
|         await self._load_data() | ||||
|         self._appliance_data = self._api_commands.pop("applianceModel") | ||||
|         self._appliance_data = self._api_commands.pop("applianceModel", {}) | ||||
|         self._get_commands() | ||||
|         self._add_favourites() | ||||
|         self._recover_last_command_states() | ||||
| @ -187,18 +187,19 @@ class HonCommandLoader: | ||||
|             command = favourite.get("command", {}) | ||||
|             command_name = command.get("commandName", "") | ||||
|             program_name = self._clean_name(command.get("programName", "")) | ||||
|             base: HonCommand = copy( | ||||
|                 self.commands[command_name].categories[program_name] | ||||
|             ) | ||||
|             if not (base := self.commands[command_name].categories.get(program_name)): | ||||
|                 continue | ||||
|             base_command: HonCommand = copy(base) | ||||
|             for data in command.values(): | ||||
|                 if isinstance(data, str): | ||||
|                     continue | ||||
|                 for key, value in data.items(): | ||||
|                     if parameter := base.parameters.get(key): | ||||
|                     if parameter := base_command.parameters.get(key): | ||||
|                         with suppress(ValueError): | ||||
|                             parameter.value = value | ||||
|             extra_param = HonParameterFixed("favourite", {"fixedValue": "1"}, "custom") | ||||
|             base.parameters.update(favourite=extra_param) | ||||
|             if isinstance(program := base.parameters["program"], HonParameterProgram): | ||||
|             base_command.parameters.update(favourite=extra_param) | ||||
|             program = base_command.parameters["program"] | ||||
|             if isinstance(program, HonParameterProgram): | ||||
|                 program.set_value(name) | ||||
|             self.commands[command_name].categories[name] = base | ||||
|             self.commands[command_name].categories[name] = base_command | ||||
|  | ||||
| @ -115,7 +115,7 @@ class HonCommand: | ||||
|         params = self.parameter_groups.get("parameters", {}) | ||||
|         ancillary_params = self.parameter_groups.get("ancillaryParameters", {}) | ||||
|         ancillary_params.pop("programRules", None) | ||||
|         self.appliance.sync_to_params(self.name) | ||||
|         self.appliance.sync_command_to_params(self.name) | ||||
|         try: | ||||
|             result = await self.api.send_command( | ||||
|                 self._appliance, self._name, params, ancillary_params | ||||
|  | ||||
| @ -75,8 +75,12 @@ class HonAPI: | ||||
|  | ||||
|     async def load_appliances(self) -> List[Dict[str, Any]]: | ||||
|         async with self._hon.get(f"{const.API_URL}/commands/v1/appliance") as resp: | ||||
|             if result := await resp.json(): | ||||
|                 return result.get("payload", {}).get("appliances", {}) | ||||
|             result = await resp.json() | ||||
|         if result: | ||||
|             appliances: List[Dict[str, Any]] = result.get("payload", {}).get( | ||||
|                 "appliances", {} | ||||
|             ) | ||||
|             return appliances | ||||
|         return [] | ||||
|  | ||||
|     async def load_commands(self, appliance: HonAppliance) -> Dict[str, Any]: | ||||
| @ -110,9 +114,10 @@ class HonAPI: | ||||
|         ) | ||||
|         async with self._hon.get(url) as response: | ||||
|             result: Dict[str, Any] = await response.json() | ||||
|             if not result or not result.get("payload"): | ||||
|                 return [] | ||||
|             return result["payload"]["history"] | ||||
|         if not result or not result.get("payload"): | ||||
|             return [] | ||||
|         command_history: List[Dict[str, Any]] = result["payload"]["history"] | ||||
|         return command_history | ||||
|  | ||||
|     async def load_favourites(self, appliance: HonAppliance) -> List[Dict[str, Any]]: | ||||
|         url: str = ( | ||||
| @ -120,17 +125,20 @@ class HonAPI: | ||||
|         ) | ||||
|         async with self._hon.get(url) as response: | ||||
|             result: Dict[str, Any] = await response.json() | ||||
|             if not result or not result.get("payload"): | ||||
|                 return [] | ||||
|             return result["payload"]["favourites"] | ||||
|         if not result or not result.get("payload"): | ||||
|             return [] | ||||
|         favourites: List[Dict[str, Any]] = result["payload"]["favourites"] | ||||
|         return favourites | ||||
|  | ||||
|     async def load_last_activity(self, appliance: HonAppliance) -> Dict[str, Any]: | ||||
|         url: str = f"{const.API_URL}/commands/v1/retrieve-last-activity" | ||||
|         params: Dict[str, str] = {"macAddress": appliance.mac_address} | ||||
|         async with self._hon.get(url, params=params) as response: | ||||
|             result: Dict[str, Any] = await response.json() | ||||
|             if result and (activity := result.get("attributes")): | ||||
|                 return activity | ||||
|             if result: | ||||
|                 activity: Dict[str, Any] = result.get("attributes", "") | ||||
|                 if activity: | ||||
|                     return activity | ||||
|         return {} | ||||
|  | ||||
|     async def load_appliance_data(self, appliance: HonAppliance) -> Dict[str, Any]: | ||||
| @ -142,7 +150,10 @@ class HonAPI: | ||||
|         async with self._hon.get(url, params=params) as response: | ||||
|             result: Dict[str, Any] = await response.json() | ||||
|             if result: | ||||
|                 return result.get("payload", {}).get("applianceModel", {}) | ||||
|                 appliance_data: Dict[str, Any] = result.get("payload", {}).get( | ||||
|                     "applianceModel", {} | ||||
|                 ) | ||||
|                 return appliance_data | ||||
|         return {} | ||||
|  | ||||
|     async def load_attributes(self, appliance: HonAppliance) -> Dict[str, Any]: | ||||
| @ -153,7 +164,8 @@ class HonAPI: | ||||
|         } | ||||
|         url: str = f"{const.API_URL}/commands/v1/context" | ||||
|         async with self._hon.get(url, params=params) as response: | ||||
|             return (await response.json()).get("payload", {}) | ||||
|             attributes: Dict[str, Any] = (await response.json()).get("payload", {}) | ||||
|         return attributes | ||||
|  | ||||
|     async def load_statistics(self, appliance: HonAppliance) -> Dict[str, Any]: | ||||
|         params: Dict[str, str] = { | ||||
| @ -162,13 +174,15 @@ class HonAPI: | ||||
|         } | ||||
|         url: str = f"{const.API_URL}/commands/v1/statistics" | ||||
|         async with self._hon.get(url, params=params) as response: | ||||
|             return (await response.json()).get("payload", {}) | ||||
|             statistics: Dict[str, Any] = (await response.json()).get("payload", {}) | ||||
|         return statistics | ||||
|  | ||||
|     async def load_maintenance(self, appliance: HonAppliance) -> Dict[str, Any]: | ||||
|         url = f"{const.API_URL}/commands/v1/maintenance-cycle" | ||||
|         params = {"macAddress": appliance.mac_address} | ||||
|         async with self._hon.get(url, params=params) as response: | ||||
|             return (await response.json()).get("payload", {}) | ||||
|             maintenance: Dict[str, Any] = (await response.json()).get("payload", {}) | ||||
|         return maintenance | ||||
|  | ||||
|     async def send_command( | ||||
|         self, | ||||
| @ -207,9 +221,8 @@ class HonAPI: | ||||
|         url: str = f"{const.API_URL}/config/v1/program-list-rules" | ||||
|         async with self._hon_anonymous.get(url) as response: | ||||
|             result: Dict[str, Any] = await response.json() | ||||
|             if result and (data := result.get("payload")): | ||||
|                 return data | ||||
|         return {} | ||||
|         data: Dict[str, Any] = result.get("payload", {}) | ||||
|         return data | ||||
|  | ||||
|     async def app_config( | ||||
|         self, language: str = "en", beta: bool = True | ||||
| @ -223,17 +236,17 @@ class HonAPI: | ||||
|         } | ||||
|         payload: str = json.dumps(payload_data, separators=(",", ":")) | ||||
|         async with self._hon_anonymous.post(url, data=payload) as response: | ||||
|             if (result := await response.json()) and (data := result.get("payload")): | ||||
|                 return data | ||||
|         return {} | ||||
|             result = await response.json() | ||||
|         data: Dict[str, Any] = result.get("payload", {}) | ||||
|         return data | ||||
|  | ||||
|     async def translation_keys(self, language: str = "en") -> Dict[str, Any]: | ||||
|         config = await self.app_config(language=language) | ||||
|         if url := config.get("language", {}).get("jsonPath"): | ||||
|             async with self._hon_anonymous.get(url) as response: | ||||
|                 if result := await response.json(): | ||||
|                     return result | ||||
|         return {} | ||||
|         if not (url := config.get("language", {}).get("jsonPath")): | ||||
|             return {} | ||||
|         async with self._hon_anonymous.get(url) as response: | ||||
|             result: Dict[str, Any] = await response.json() | ||||
|         return result | ||||
|  | ||||
|     async def close(self) -> None: | ||||
|         if self._hon_handler is not None: | ||||
| @ -250,9 +263,17 @@ class TestAPI(HonAPI): | ||||
|  | ||||
|     def _load_json(self, appliance: HonAppliance, file: str) -> Dict[str, Any]: | ||||
|         directory = f"{appliance.appliance_type}_{appliance.appliance_model_id}".lower() | ||||
|         path = f"{self._path}/{directory}/{file}.json" | ||||
|         if not (path := self._path / directory / f"{file}.json").exists(): | ||||
|             _LOGGER.warning("Can't open %s", str(path)) | ||||
|             return {} | ||||
|         with open(path, "r", encoding="utf-8") as json_file: | ||||
|             return json.loads(json_file.read()) | ||||
|             text = json_file.read() | ||||
|         try: | ||||
|             data: Dict[str, Any] = json.loads(text) | ||||
|             return data | ||||
|         except json.decoder.JSONDecodeError as error: | ||||
|             _LOGGER.error("%s - %s", str(path), error) | ||||
|             return {} | ||||
|  | ||||
|     async def load_appliances(self) -> List[Dict[str, Any]]: | ||||
|         result = [] | ||||
|  | ||||
| @ -6,7 +6,7 @@ import urllib | ||||
| from contextlib import suppress | ||||
| from dataclasses import dataclass | ||||
| from datetime import datetime, timedelta | ||||
| from typing import Dict, Optional, Any | ||||
| from typing import Dict, Optional, Any, List | ||||
| from urllib import parse | ||||
| from urllib.parse import quote | ||||
|  | ||||
| @ -115,7 +115,8 @@ class HonAuth: | ||||
|         async with self._request.get(url) as response: | ||||
|             text = await response.text() | ||||
|             self._expires = datetime.utcnow() | ||||
|             if not (login_url := re.findall("url = '(.+?)'", text)): | ||||
|             login_url: List[str] = re.findall("url = '(.+?)'", text) | ||||
|             if not login_url: | ||||
|                 if "oauth/done#access_token=" in text: | ||||
|                     self._parse_token_data(text) | ||||
|                     raise exceptions.HonNoAuthenticationNeeded() | ||||
| @ -184,7 +185,8 @@ class HonAuth: | ||||
|             if response.status == 200: | ||||
|                 with suppress(json.JSONDecodeError, KeyError): | ||||
|                     result = await response.json() | ||||
|                     return result["events"][0]["attributes"]["values"]["url"] | ||||
|                     url: str = result["events"][0]["attributes"]["values"]["url"] | ||||
|                     return url | ||||
|             await self._error_logger(response) | ||||
|             return "" | ||||
|  | ||||
|  | ||||
| @ -4,6 +4,7 @@ from contextlib import asynccontextmanager | ||||
| from typing import Dict, Any | ||||
|  | ||||
| import aiohttp | ||||
| from yarl import URL | ||||
|  | ||||
| from pyhon import const | ||||
| from pyhon.connection.handler.base import ConnectionHandler | ||||
| @ -17,10 +18,10 @@ class HonAnonymousConnectionHandler(ConnectionHandler): | ||||
|  | ||||
|     @asynccontextmanager | ||||
|     async def _intercept( | ||||
|         self, method: Callback, *args: Any, **kwargs: Any | ||||
|         self, method: Callback, url: str | URL, *args: Any, **kwargs: Dict[str, Any] | ||||
|     ) -> AsyncIterator[aiohttp.ClientResponse]: | ||||
|         kwargs["headers"] = kwargs.pop("headers", {}) | self._HEADERS | ||||
|         async with method(*args, **kwargs) as response: | ||||
|         async with method(url, *args, **kwargs) as response: | ||||
|             if response.status == 403: | ||||
|                 _LOGGER.error("Can't authenticate anymore") | ||||
|             yield response | ||||
|  | ||||
| @ -1,9 +1,10 @@ | ||||
| import logging | ||||
| from collections.abc import AsyncIterator | ||||
| from contextlib import asynccontextmanager | ||||
| from typing import Optional, List, Tuple, Any | ||||
| from typing import Optional, List, Tuple, Any, Dict | ||||
|  | ||||
| import aiohttp | ||||
| from yarl import URL | ||||
|  | ||||
| from pyhon import const | ||||
| from pyhon.connection.handler.base import ConnectionHandler | ||||
| @ -29,9 +30,9 @@ class HonAuthConnectionHandler(ConnectionHandler): | ||||
|  | ||||
|     @asynccontextmanager | ||||
|     async def _intercept( | ||||
|         self, method: Callback, *args: Any, **kwargs: Any | ||||
|         self, method: Callback, url: str | URL, *args: Any, **kwargs: Dict[str, Any] | ||||
|     ) -> AsyncIterator[aiohttp.ClientResponse]: | ||||
|         kwargs["headers"] = kwargs.pop("headers", {}) | self._HEADERS | ||||
|         async with method(*args, **kwargs) as response: | ||||
|         async with method(url, *args, **kwargs) as response: | ||||
|             self._called_urls.append((response.status, str(response.request_info.url))) | ||||
|             yield response | ||||
|  | ||||
| @ -6,6 +6,7 @@ from typing import Optional, Dict, Type, Any, Protocol | ||||
|  | ||||
| import aiohttp | ||||
| from typing_extensions import Self | ||||
| from yarl import URL | ||||
|  | ||||
| from pyhon import const, exceptions | ||||
| from pyhon.typedefs import Callback | ||||
| @ -47,7 +48,7 @@ class ConnectionHandler: | ||||
|  | ||||
|     @asynccontextmanager | ||||
|     def _intercept( | ||||
|         self, method: Callback, *args: Any, loop: int = 0, **kwargs: Any | ||||
|         self, method: Callback, url: str | URL, *args: Any, **kwargs: Dict[str, Any] | ||||
|     ) -> AsyncIterator[aiohttp.ClientResponse]: | ||||
|         raise NotImplementedError | ||||
|  | ||||
|  | ||||
| @ -6,6 +6,7 @@ from typing import Optional, Dict, Any | ||||
|  | ||||
| import aiohttp | ||||
| from typing_extensions import Self | ||||
| from yarl import URL | ||||
|  | ||||
| from pyhon.connection.auth import HonAuth | ||||
| from pyhon.connection.device import HonDevice | ||||
| @ -54,16 +55,19 @@ class HonConnectionHandler(ConnectionHandler): | ||||
|  | ||||
|     @asynccontextmanager | ||||
|     async def _intercept( | ||||
|         self, method: Callback, *args: Any, loop: int = 0, **kwargs: Dict[str, str] | ||||
|         self, method: Callback, url: str | URL, *args: Any, **kwargs: Any | ||||
|     ) -> AsyncIterator[aiohttp.ClientResponse]: | ||||
|         loop: int = kwargs.pop("loop", 0) | ||||
|         kwargs["headers"] = await self._check_headers(kwargs.get("headers", {})) | ||||
|         async with method(args[0], *args[1:], **kwargs) as response: | ||||
|         async with method(url, *args, **kwargs) as response: | ||||
|             if ( | ||||
|                 self.auth.token_expires_soon or response.status in [401, 403] | ||||
|             ) and loop == 0: | ||||
|                 _LOGGER.info("Try refreshing token...") | ||||
|                 await self.auth.refresh() | ||||
|                 async with self._intercept(method, loop=loop + 1, **kwargs) as result: | ||||
|                 async with self._intercept( | ||||
|                     method, url, *args, loop=loop + 1, **kwargs | ||||
|                 ) as result: | ||||
|                     yield result | ||||
|             elif ( | ||||
|                 self.auth.token_is_expired or response.status in [401, 403] | ||||
| @ -75,7 +79,9 @@ class HonConnectionHandler(ConnectionHandler): | ||||
|                     await response.text(), | ||||
|                 ) | ||||
|                 await self.create() | ||||
|                 async with self._intercept(method, loop=loop + 1, **kwargs) as result: | ||||
|                 async with self._intercept( | ||||
|                     method, url, *args, loop=loop + 1, **kwargs | ||||
|                 ) as result: | ||||
|                     yield result | ||||
|             elif loop >= 2: | ||||
|                 _LOGGER.error( | ||||
|  | ||||
| @ -4,7 +4,7 @@ API_KEY = "GRCqFhC6Gk@ikWXm1RmnSmX1cm,MxY-configuration" | ||||
| APP = "hon" | ||||
| # All seen id's (different accounts, different devices) are the same, so I guess this hash is static | ||||
| CLIENT_ID = "3MVG9QDx8IX8nP5T2Ha8ofvlmjLZl5L_gvfbT9.HJvpHGKoAS_dcMN8LYpTSYeVFCraUnV.2Ag1Ki7m4znVO6" | ||||
| APP_VERSION = "2.0.10" | ||||
| APP_VERSION = "2.1.2" | ||||
| OS_VERSION = 31 | ||||
| OS = "android" | ||||
| DEVICE_MODEL = "exynos9820" | ||||
|  | ||||
| @ -70,7 +70,7 @@ async def zip_archive( | ||||
| ) -> str: | ||||
|     data = await appliance_data(appliance, path, anonymous) | ||||
|     archive = data[0].parent | ||||
|     shutil.make_archive(str(archive.parent), "zip", archive) | ||||
|     shutil.make_archive(str(archive), "zip", archive) | ||||
|     shutil.rmtree(archive) | ||||
|     return f"{archive.stem}.zip" | ||||
|  | ||||
| @ -89,12 +89,11 @@ def yaml_export(appliance: "HonAppliance", anonymous: bool = False) -> str: | ||||
|     if anonymous: | ||||
|         for sensible in ["serialNumber", "coords"]: | ||||
|             data.get("appliance", {}).pop(sensible, None) | ||||
|     data = { | ||||
|         "data": data, | ||||
|         "commands": printer.create_command(appliance.commands), | ||||
|         "rules": printer.create_rules(appliance.commands), | ||||
|     } | ||||
|     result = printer.pretty_print(data) | ||||
|     result = printer.pretty_print({"data": data}) | ||||
|     if commands := printer.create_commands(appliance.commands): | ||||
|         result += printer.pretty_print({"commands": commands}) | ||||
|     if rules := printer.create_rules(appliance.commands): | ||||
|         result += printer.pretty_print({"rules": rules}) | ||||
|     if anonymous: | ||||
|         result = anonymize_data(result) | ||||
|     return result | ||||
|  | ||||
| @ -41,4 +41,4 @@ class HonParameterEnum(HonParameter): | ||||
|             self._value = value | ||||
|             self.check_trigger(value) | ||||
|         else: | ||||
|             raise ValueError(f"Allowed values {self._values}") | ||||
|             raise ValueError(f"Allowed values: {self._values} But was: {value}") | ||||
|  | ||||
| @ -28,7 +28,7 @@ class HonParameterProgram(HonParameterEnum): | ||||
|         if value in self.values: | ||||
|             self._command.category = value | ||||
|         else: | ||||
|             raise ValueError(f"Allowed values {self.values}") | ||||
|             raise ValueError(f"Allowed values: {self.values} But was: {value}") | ||||
|  | ||||
|     @property | ||||
|     def values(self) -> List[str]: | ||||
|  | ||||
| @ -49,11 +49,15 @@ class HonParameterRange(HonParameter): | ||||
|     @value.setter | ||||
|     def value(self, value: str | float) -> None: | ||||
|         value = str_to_float(value) | ||||
|         if self.min <= value <= self.max and not (value - self.min) % self.step: | ||||
|         if self.min <= value <= self.max and not ((value - self.min) * 100) % ( | ||||
|             self.step * 100 | ||||
|         ): | ||||
|             self._value = value | ||||
|             self.check_trigger(value) | ||||
|         else: | ||||
|             raise ValueError(f"Allowed: min {self.min} max {self.max} step {self.step}") | ||||
|             raise ValueError( | ||||
|                 f"Allowed: min {self.min} max {self.max} step {self.step} But was: {value}" | ||||
|             ) | ||||
|  | ||||
|     @property | ||||
|     def values(self) -> List[str]: | ||||
|  | ||||
| @ -59,7 +59,7 @@ def pretty_print( | ||||
|     return result | ||||
|  | ||||
|  | ||||
| def create_command( | ||||
| def create_commands( | ||||
|     commands: Dict[str, "HonCommand"], concat: bool = False | ||||
| ) -> Dict[str, Any]: | ||||
|     result: Dict[str, Any] = {} | ||||
|  | ||||
| @ -1,2 +1,3 @@ | ||||
| aiohttp==3.8.4 | ||||
| yarl==1.8.2 | ||||
| aiohttp~=3.8.5 | ||||
| yarl~=1.9.2 | ||||
| typing-extensions~=4.7.1 | ||||
|  | ||||
							
								
								
									
										4
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								setup.py
									
									
									
									
									
								
							| @ -7,7 +7,7 @@ with open("README.md", "r") as f: | ||||
|  | ||||
| setup( | ||||
|     name="pyhOn", | ||||
|     version="0.14.3", | ||||
|     version="0.14.12", | ||||
|     author="Andre Basche", | ||||
|     description="Control hOn devices with python", | ||||
|     long_description=long_description, | ||||
| @ -21,7 +21,7 @@ setup( | ||||
|     packages=find_packages(), | ||||
|     include_package_data=True, | ||||
|     python_requires=">=3.10", | ||||
|     install_requires=["aiohttp"], | ||||
|     install_requires=["aiohttp~=3.8.5", "typing-extensions~=4.7.1", "yarl~=1.9.2"], | ||||
|     classifiers=[ | ||||
|         "Development Status :: 4 - Beta", | ||||
|         "Environment :: Console", | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	