Compare commits
	
		
			6 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 84000fb76c | |||
| 26528a7310 | |||
| 11988c73a6 | |||
| 7b51caecca | |||
| 38d09e2ef5 | |||
| 3c7ad3f395 | 
| @ -242,15 +242,20 @@ class HonAppliance: | ||||
|         self._attributes = await self.api.load_attributes(self) | ||||
|         for name, values in self._attributes.pop("shadow").get("parameters").items(): | ||||
|             self._attributes.setdefault("parameters", {})[name] = values["parNewVal"] | ||||
|         if self._extra: | ||||
|             self._attributes = self._extra.attributes(self._attributes) | ||||
|  | ||||
|     async def load_statistics(self): | ||||
|         self._statistics = await self.api.load_statistics(self) | ||||
|         self._statistics |= await self.api.load_maintenance(self) | ||||
|  | ||||
|     async def update(self): | ||||
|     async def update(self, force=False): | ||||
|         now = datetime.now() | ||||
|         if not self._last_update or self._last_update < now - timedelta( | ||||
|             seconds=self._MINIMAL_UPDATE_INTERVAL | ||||
|         if ( | ||||
|             force | ||||
|             or not self._last_update | ||||
|             or self._last_update | ||||
|             < now - timedelta(seconds=self._MINIMAL_UPDATE_INTERVAL) | ||||
|         ): | ||||
|             self._last_update = now | ||||
|             await self.load_attributes() | ||||
| @ -286,9 +291,8 @@ class HonAppliance: | ||||
|             "statistics": self.statistics, | ||||
|             "additional_data": self._additional_data, | ||||
|             **self.command_parameters, | ||||
|             **self.attributes, | ||||
|         } | ||||
|         if self._extra: | ||||
|             return self._extra.data(result) | ||||
|         return result | ||||
|  | ||||
|     def diagnose(self, whitespace="  ", command_only=False): | ||||
| @ -298,8 +302,6 @@ class HonAppliance: | ||||
|             "statistics": self.statistics, | ||||
|             "additional_data": self._additional_data, | ||||
|         } | ||||
|         if self._extra and data.get("attributes"): | ||||
|             data = self._extra.data(data) | ||||
|         if command_only: | ||||
|             data.pop("attributes") | ||||
|             data.pop("appliance") | ||||
| @ -320,6 +322,12 @@ class HonAppliance: | ||||
|         ) | ||||
|         return result.replace(self.mac_address, "xx-xx-xx-xx-xx-xx") | ||||
|  | ||||
|     def sync_to_params(self, command_name): | ||||
|         command: HonCommand = self.commands.get(command_name) | ||||
|         for key, value in self.attributes.get("parameters", {}).items(): | ||||
|             if isinstance(value, str) and (new := command.parameters.get(key)): | ||||
|                 self.attributes["parameters"][key] = str(new.intern_value) | ||||
|  | ||||
|     def sync_command(self, main, target=None) -> None: | ||||
|         base: HonCommand = self.commands.get(main) | ||||
|         for command, data in self.commands.items(): | ||||
| @ -345,6 +353,7 @@ class HonApplianceTest(HonAppliance): | ||||
|         super().__init__(None, {}) | ||||
|         self._name = name | ||||
|         self.load_commands() | ||||
|         self.load_attributes() | ||||
|         self._info = self._appliance_model | ||||
|  | ||||
|     def load_commands(self): | ||||
|  | ||||
| @ -2,12 +2,14 @@ class ApplianceBase: | ||||
|     def __init__(self, appliance): | ||||
|         self.parent = appliance | ||||
|  | ||||
|     def data(self, data): | ||||
|     def attributes(self, data): | ||||
|         program_name = "No Program" | ||||
|         if program := int(data["attributes"]["parameters"].get("prCode", "0")): | ||||
|             if ids := self.parent.settings["startProgram.program"].ids: | ||||
|                 program_name = ids.get(program, program_name) | ||||
|         if program := int(data["parameters"].get("prCode", "0")): | ||||
|             if start_cmd := self.parent.settings.get("startProgram.program"): | ||||
|                 if ids := start_cmd.ids: | ||||
|                     program_name = ids.get(program, program_name) | ||||
|         data["programName"] = program_name | ||||
|         return data | ||||
|  | ||||
|     def settings(self, settings): | ||||
|         return settings | ||||
|  | ||||
| @ -2,9 +2,9 @@ from pyhon.appliances.base import ApplianceBase | ||||
|  | ||||
|  | ||||
| class Appliance(ApplianceBase): | ||||
|     def data(self, data): | ||||
|         super().data(data) | ||||
|         if data["attributes"]["lastConnEvent"]["category"] == "DISCONNECTED": | ||||
|             data["attributes"]["parameters"]["machMode"] = "0" | ||||
|         data["active"] = bool(data.get("attributes", {}).get("activity")) | ||||
|     def attributes(self, data): | ||||
|         data = super().attributes(data) | ||||
|         if data["lastConnEvent"]["category"] == "DISCONNECTED": | ||||
|             data["parameters"]["machMode"] = "0" | ||||
|         data["active"] = bool(data.get("activity")) | ||||
|         return data | ||||
|  | ||||
| @ -2,17 +2,17 @@ from pyhon.appliances.base import ApplianceBase | ||||
|  | ||||
|  | ||||
| class Appliance(ApplianceBase): | ||||
|     def data(self, data): | ||||
|         super().data(data) | ||||
|         if data["attributes"]["lastConnEvent"]["category"] == "DISCONNECTED": | ||||
|             data["attributes"]["parameters"]["temp"] = "0" | ||||
|             data["attributes"]["parameters"]["onOffStatus"] = "0" | ||||
|             data["attributes"]["parameters"]["remoteCtrValid"] = "0" | ||||
|             data["attributes"]["parameters"]["remainingTimeMM"] = "0" | ||||
|     def attributes(self, data): | ||||
|         data = super().attributes(data) | ||||
|         if data["lastConnEvent"]["category"] == "DISCONNECTED": | ||||
|             data["parameters"]["temp"] = "0" | ||||
|             data["parameters"]["onOffStatus"] = "0" | ||||
|             data["parameters"]["remoteCtrValid"] = "0" | ||||
|             data["parameters"]["remainingTimeMM"] = "0" | ||||
|  | ||||
|         data["active"] = data["attributes"]["parameters"]["onOffStatus"] == "1" | ||||
|         data["active"] = data["parameters"]["onOffStatus"] == "1" | ||||
|  | ||||
|         if program := int(data["attributes"]["parameters"]["prCode"]): | ||||
|         if program := int(data["parameters"]["prCode"]): | ||||
|             ids = self.parent.settings["startProgram.program"].ids | ||||
|             data["programName"] = ids.get(program, "") | ||||
|  | ||||
|  | ||||
| @ -2,20 +2,20 @@ from pyhon.appliances.base import ApplianceBase | ||||
|  | ||||
|  | ||||
| class Appliance(ApplianceBase): | ||||
|     def data(self, data): | ||||
|         super().data(data) | ||||
|         if data["attributes"]["parameters"]["holidayMode"] == "1": | ||||
|     def attributes(self, data): | ||||
|         data = super().attributes(data) | ||||
|         if data["parameters"]["holidayMode"] == "1": | ||||
|             data["modeZ1"] = "holiday" | ||||
|         elif data["attributes"]["parameters"]["intelligenceMode"] == "1": | ||||
|         elif data["parameters"]["intelligenceMode"] == "1": | ||||
|             data["modeZ1"] = "auto_set" | ||||
|         elif data["attributes"]["parameters"]["quickModeZ1"] == "1": | ||||
|         elif data["parameters"]["quickModeZ1"] == "1": | ||||
|             data["modeZ1"] = "super_cool" | ||||
|         else: | ||||
|             data["modeZ1"] = "no_mode" | ||||
|  | ||||
|         if data["attributes"]["parameters"]["quickModeZ2"] == "1": | ||||
|         if data["parameters"]["quickModeZ2"] == "1": | ||||
|             data["modeZ2"] = "super_freeze" | ||||
|         elif data["attributes"]["parameters"]["intelligenceMode"] == "1": | ||||
|         elif data["parameters"]["intelligenceMode"] == "1": | ||||
|             data["modeZ2"] = "auto_set" | ||||
|         else: | ||||
|             data["modeZ2"] = "no_mode" | ||||
|  | ||||
| @ -3,12 +3,12 @@ from pyhon.parameter.fixed import HonParameterFixed | ||||
|  | ||||
|  | ||||
| class Appliance(ApplianceBase): | ||||
|     def data(self, data): | ||||
|         super().data(data) | ||||
|         if data["attributes"]["lastConnEvent"]["category"] == "DISCONNECTED": | ||||
|             data["attributes"]["parameters"]["machMode"] = "0" | ||||
|         data["active"] = bool(data.get("attributes", {}).get("activity")) | ||||
|         data["pause"] = data["attributes"]["parameters"]["machMode"] == "3" | ||||
|     def attributes(self, data): | ||||
|         data = super().attributes(data) | ||||
|         if data["lastConnEvent"]["category"] == "DISCONNECTED": | ||||
|             data["parameters"]["machMode"] = "0" | ||||
|         data["active"] = bool(data.get("activity")) | ||||
|         data["pause"] = data["parameters"]["machMode"] == "3" | ||||
|         return data | ||||
|  | ||||
|     def settings(self, settings): | ||||
|  | ||||
							
								
								
									
										5
									
								
								pyhon/appliances/wc.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								pyhon/appliances/wc.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| from pyhon.appliances.base import ApplianceBase | ||||
|  | ||||
|  | ||||
| class Appliance(ApplianceBase): | ||||
|     pass | ||||
| @ -2,12 +2,12 @@ from pyhon.appliances.base import ApplianceBase | ||||
|  | ||||
|  | ||||
| class Appliance(ApplianceBase): | ||||
|     def data(self, data): | ||||
|         super().data(data) | ||||
|         if data["attributes"]["lastConnEvent"]["category"] == "DISCONNECTED": | ||||
|             data["attributes"]["parameters"]["machMode"] = "0" | ||||
|         data["active"] = bool(data.get("attributes", {}).get("activity")) | ||||
|         data["pause"] = data["attributes"]["parameters"]["machMode"] == "3" | ||||
|     def attributes(self, data): | ||||
|         data = super().attributes(data) | ||||
|         if data["lastConnEvent"]["category"] == "DISCONNECTED": | ||||
|             data["parameters"]["machMode"] = "0" | ||||
|         data["active"] = bool(data.get("activity")) | ||||
|         data["pause"] = data["parameters"]["machMode"] == "3" | ||||
|         return data | ||||
|  | ||||
|     def settings(self, settings): | ||||
|  | ||||
| @ -2,12 +2,12 @@ from pyhon.appliances.base import ApplianceBase | ||||
|  | ||||
|  | ||||
| class Appliance(ApplianceBase): | ||||
|     def data(self, data): | ||||
|         super().data(data) | ||||
|         if data["attributes"]["lastConnEvent"]["category"] == "DISCONNECTED": | ||||
|             data["attributes"]["parameters"]["machMode"] = "0" | ||||
|         data["active"] = bool(data.get("attributes", {}).get("activity")) | ||||
|         data["pause"] = data["attributes"]["parameters"]["machMode"] == "3" | ||||
|     def attributes(self, data): | ||||
|         data = super().attributes(data) | ||||
|         if data["lastConnEvent"]["category"] == "DISCONNECTED": | ||||
|             data["parameters"]["machMode"] = "0" | ||||
|         data["active"] = bool(data.get("activity")) | ||||
|         data["pause"] = data["parameters"]["machMode"] == "3" | ||||
|         return data | ||||
|  | ||||
|     def settings(self, settings): | ||||
|  | ||||
| @ -2,6 +2,7 @@ import logging | ||||
| from typing import Optional, Dict, Any, List, TYPE_CHECKING, Union | ||||
|  | ||||
| from pyhon import exceptions | ||||
| from pyhon.exceptions import ApiError | ||||
| from pyhon.parameter.base import HonParameter | ||||
| from pyhon.parameter.enum import HonParameterEnum | ||||
| from pyhon.parameter.fixed import HonParameterFixed | ||||
| @ -110,9 +111,14 @@ class HonCommand: | ||||
|     async def send(self) -> bool: | ||||
|         params = self.parameter_groups.get("parameters", {}) | ||||
|         ancillary_params = self.parameter_groups.get("ancillaryParameters", {}) | ||||
|         return await self.api.send_command( | ||||
|         ancillary_params.pop("programRules", None) | ||||
|         self.appliance.sync_to_params(self.name) | ||||
|         result = await self.api.send_command( | ||||
|             self._appliance, self._name, params, ancillary_params | ||||
|         ) | ||||
|         if not result: | ||||
|             raise ApiError("Can't send command") | ||||
|         return result | ||||
|  | ||||
|     @property | ||||
|     def categories(self) -> Dict[str, "HonCommand"]: | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| import json | ||||
| import logging | ||||
| from datetime import datetime | ||||
| from pprint import pformat | ||||
| from typing import Dict, Optional | ||||
|  | ||||
| from aiohttp import ClientSession | ||||
| @ -188,6 +189,7 @@ class HonAPI: | ||||
|             if json_data.get("payload", {}).get("resultCode") == "0": | ||||
|                 return True | ||||
|             _LOGGER.error(await response.text()) | ||||
|             _LOGGER.error("%s - Payload:\n%s", url, pformat(data)) | ||||
|         return False | ||||
|  | ||||
|     async def appliance_configuration(self) -> Dict: | ||||
|  | ||||
| @ -134,9 +134,7 @@ class HonAuth: | ||||
|                 fw_uid, loaded_str = context[0] | ||||
|                 self._login_data.fw_uid = fw_uid | ||||
|                 self._login_data.loaded = json.loads(loaded_str) | ||||
|                 self._login_data.url = login_url.replace( | ||||
|                     "/".join(const.AUTH_API.split("/")[:-1]), "" | ||||
|                 ) | ||||
|                 self._login_data.url = login_url.replace(const.AUTH_API, "") | ||||
|                 return True | ||||
|             await self._error_logger(response) | ||||
|         return False | ||||
| @ -149,8 +147,8 @@ class HonAuth: | ||||
|             "descriptor": "apex://LightningLoginCustomController/ACTION$login", | ||||
|             "callingDescriptor": "markup://c:loginForm", | ||||
|             "params": { | ||||
|                 "username": quote(self._login_data.email), | ||||
|                 "password": quote(self._login_data.password), | ||||
|                 "username": self._login_data.email, | ||||
|                 "password": self._login_data.password, | ||||
|                 "startUrl": start_url, | ||||
|             }, | ||||
|         } | ||||
| @ -172,7 +170,7 @@ class HonAuth: | ||||
|         async with self._request.post( | ||||
|             const.AUTH_API + "/s/sfsites/aura", | ||||
|             headers={"Content-Type": "application/x-www-form-urlencoded"}, | ||||
|             data="&".join(f"{k}={json.dumps(v)}" for k, v in data.items()), | ||||
|             data="&".join(f"{k}={quote(json.dumps(v))}" for k, v in data.items()), | ||||
|             params=params, | ||||
|         ) as response: | ||||
|             if response.status == 200: | ||||
| @ -210,7 +208,7 @@ class HonAuth: | ||||
|                 url_search = re.findall( | ||||
|                     "href\\s*=\\s*[\"'](.*?)[\"']", await response.text() | ||||
|                 ) | ||||
|         url = "/".join(const.AUTH_API.split("/")[:-1]) + url_search[0] | ||||
|         url = const.AUTH_API + url_search[0] | ||||
|         async with self._request.get(url) as response: | ||||
|             if response.status != 200: | ||||
|                 await self._error_logger(response) | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| AUTH_API = "https://he-accounts.force.com/SmartHome" | ||||
| AUTH_API = "https://account2.hon-smarthome.com" | ||||
| API_URL = "https://api-iot.he.services" | ||||
| API_KEY = "GRCqFhC6Gk@ikWXm1RmnSmX1cm,MxY-configuration" | ||||
| APP = "hon" | ||||
|  | ||||
| @ -12,3 +12,7 @@ class NoSessionException(Exception): | ||||
|  | ||||
| class NoAuthenticationException(Exception): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class ApiError(Exception): | ||||
|     pass | ||||
|  | ||||
| @ -28,8 +28,8 @@ class HonParameter: | ||||
|         self.check_trigger(value) | ||||
|  | ||||
|     @property | ||||
|     def intern_value(self) -> str | float: | ||||
|         return str(self._value) if self._value is not None else "" | ||||
|     def intern_value(self) -> str: | ||||
|         return str(self.value) | ||||
|  | ||||
|     @property | ||||
|     def values(self) -> List[str]: | ||||
|  | ||||
| @ -27,6 +27,10 @@ class HonParameterEnum(HonParameter): | ||||
|     def values(self, values) -> None: | ||||
|         self._values = values | ||||
|  | ||||
|     @property | ||||
|     def intern_value(self) -> str: | ||||
|         return str(self._value) if self._value is not None else str(self.values[0]) | ||||
|  | ||||
|     @property | ||||
|     def value(self) -> str | float: | ||||
|         return clean_value(self._value) if self._value is not None else self.values[0] | ||||
|  | ||||
| @ -16,27 +16,27 @@ class HonParameterRange(HonParameter): | ||||
|         self._min: float = str_to_float(attributes["minimumValue"]) | ||||
|         self._max: float = str_to_float(attributes["maximumValue"]) | ||||
|         self._step: float = str_to_float(attributes["incrementValue"]) | ||||
|         self._default: float = str_to_float(attributes.get("defaultValue", self._min)) | ||||
|         self._default: float = str_to_float(attributes.get("defaultValue", self.min)) | ||||
|         self._value: float = self._default | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return f"{self.__class__} (<{self.key}> [{self._min} - {self._max}])" | ||||
|         return f"{self.__class__} (<{self.key}> [{self.min} - {self.max}])" | ||||
|  | ||||
|     @property | ||||
|     def min(self) -> float: | ||||
|         return self._min | ||||
|  | ||||
|     @min.setter | ||||
|     def min(self, min: float) -> None: | ||||
|         self._min = min | ||||
|     def min(self, mini: float) -> None: | ||||
|         self._min = mini | ||||
|  | ||||
|     @property | ||||
|     def max(self) -> float: | ||||
|         return self._max | ||||
|  | ||||
|     @max.setter | ||||
|     def max(self, max: float) -> None: | ||||
|         self._max = max | ||||
|     def max(self, maxi: float) -> None: | ||||
|         self._max = maxi | ||||
|  | ||||
|     @property | ||||
|     def step(self) -> float: | ||||
| @ -50,18 +50,16 @@ class HonParameterRange(HonParameter): | ||||
|  | ||||
|     @property | ||||
|     def value(self) -> str | float: | ||||
|         return self._value if self._value is not None else self._min | ||||
|         return self._value if self._value is not None else self.min | ||||
|  | ||||
|     @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) % self.step: | ||||
|             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}") | ||||
|  | ||||
|     @property | ||||
|     def values(self) -> List[str]: | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	