mirror of
				https://github.com/Andre0512/pyhOn.git
				synced 2025-11-04 10:34:52 +00:00 
			
		
		
		
	Clean up attribute structure
This commit is contained in:
		
							parent
							
								
									43d61ab853
								
							
						
					
					
						commit
						5acc81acc3
					
				
							
								
								
									
										33
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								README.md
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -19,17 +19,28 @@ To get an idea of what is possible, use the commandline-tool `pyhOn`. This comma
 | 
			
		|||
```commandline
 | 
			
		||||
$ pyhOn --user example@mail.com --password pass123
 | 
			
		||||
========== WM - Waschmaschine ==========
 | 
			
		||||
commands:
 | 
			
		||||
  pauseProgram: pauseProgram command
 | 
			
		||||
  resumeProgram: resumeProgram command
 | 
			
		||||
  startProgram: startProgram command
 | 
			
		||||
  stopProgram: stopProgram command
 | 
			
		||||
data:
 | 
			
		||||
  actualWeight: 0
 | 
			
		||||
  airWashTempLevel: 0
 | 
			
		||||
  airWashTime: 0
 | 
			
		||||
  antiAllergyStatus: 0
 | 
			
		||||
...
 | 
			
		||||
  attributes:
 | 
			
		||||
    parameters:
 | 
			
		||||
      ...
 | 
			
		||||
      texture: 1
 | 
			
		||||
      totalElectricityUsed: 28.71
 | 
			
		||||
      totalWashCycle: 35
 | 
			
		||||
      totalWaterUsed: 2494
 | 
			
		||||
      transMode: 0
 | 
			
		||||
      ...
 | 
			
		||||
settings:
 | 
			
		||||
  startProgram:
 | 
			
		||||
    rinseIterations:
 | 
			
		||||
      max: 5
 | 
			
		||||
      min: 3
 | 
			
		||||
      step: 1
 | 
			
		||||
    spinSpeed:
 | 
			
		||||
      - 0
 | 
			
		||||
      - 400
 | 
			
		||||
      - 600
 | 
			
		||||
      - 800
 | 
			
		||||
      ...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Python-API
 | 
			
		||||
| 
						 | 
				
			
			@ -55,8 +66,6 @@ async with HonConnection(USER, PASSWORD) as hon:
 | 
			
		|||
```
 | 
			
		||||
 | 
			
		||||
### Set command parameter
 | 
			
		||||
Use `device.settings` to get all variable parameters.  
 | 
			
		||||
Use `device.parmeters` to get also fixed parameters. 
 | 
			
		||||
```python
 | 
			
		||||
async with HonConnection(USER, PASSWORD) as hon:
 | 
			
		||||
    washing_machine = hon.devices[0]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,9 @@ def get_arguments():
 | 
			
		|||
    parser = argparse.ArgumentParser(description="pyhOn: Command Line Utility")
 | 
			
		||||
    parser.add_argument("-u", "--user", help="user for haier hOn account")
 | 
			
		||||
    parser.add_argument("-p", "--password", help="password for haier hOn account")
 | 
			
		||||
    subparser = parser.add_subparsers(title="commands", metavar="COMMAND")
 | 
			
		||||
    keys = subparser.add_parser("keys", help="print as key format")
 | 
			
		||||
    keys.add_argument("keys", help="print as key format", action="store_true")
 | 
			
		||||
    return vars(parser.parse_args())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -47,15 +50,33 @@ def pretty_print(data, key="", intend=0, is_list=False):
 | 
			
		|||
        print(f"{'  ' * intend}{'- ' if is_list else ''}{key}{': ' if key else ''}{data}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_command(commands):
 | 
			
		||||
def key_print(data, key="", start=True):
 | 
			
		||||
    if type(data) is list:
 | 
			
		||||
        for i, value in enumerate(data):
 | 
			
		||||
            key_print(value, key=f"{key}.{i}", start=False)
 | 
			
		||||
    elif type(data) is dict:
 | 
			
		||||
        for k, value in sorted(data.items()):
 | 
			
		||||
            key_print(value, key=k if start else f"{key}.{k}", start=False)
 | 
			
		||||
    else:
 | 
			
		||||
        print(f"{key}: {data}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_command(commands, concat=False):
 | 
			
		||||
    result = {}
 | 
			
		||||
    for name, command in commands.items():
 | 
			
		||||
        result[name] = {}
 | 
			
		||||
        if not concat:
 | 
			
		||||
            result[name] = {}
 | 
			
		||||
        for parameter, data in command.parameters.items():
 | 
			
		||||
            if data.typology == "enum":
 | 
			
		||||
                result[name][parameter] = data.values
 | 
			
		||||
            if data.typology == "range":
 | 
			
		||||
                result[name][parameter] = {"min": data.min, "max": data.max, "step": data.step}
 | 
			
		||||
                value = data.values
 | 
			
		||||
            elif data.typology == "range":
 | 
			
		||||
                value = {"min": data.min, "max": data.max, "step": data.step}
 | 
			
		||||
            else:
 | 
			
		||||
                continue
 | 
			
		||||
            if not concat:
 | 
			
		||||
                result[name][parameter] = value
 | 
			
		||||
            else:
 | 
			
		||||
                result[f"{name}.{parameter}"] = value
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -67,9 +88,15 @@ async def main():
 | 
			
		|||
        password = getpass("Password for hOn account: ")
 | 
			
		||||
    async with HonConnection(user, password) as hon:
 | 
			
		||||
        for device in hon.devices:
 | 
			
		||||
            print("=" * 10, device.appliance_type_name, "-", device.nick_name, "=" * 10)
 | 
			
		||||
            pretty_print({"commands": device.commands})
 | 
			
		||||
            pretty_print({"data": device.data})
 | 
			
		||||
            print("=" * 10, device.appliance_type, "-", device.nick_name, "=" * 10)
 | 
			
		||||
            if args.get("keys"):
 | 
			
		||||
                key_print(device.data["attributes"]["parameters"])
 | 
			
		||||
                key_print(device.data["appliance"])
 | 
			
		||||
                key_print(device.data)
 | 
			
		||||
                pretty_print(create_command(device.commands, concat=True))
 | 
			
		||||
            else:
 | 
			
		||||
                pretty_print({"data": device.data})
 | 
			
		||||
                pretty_print({"settings": create_command(device.commands)})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def start():
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								pyhon/api.py
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								pyhon/api.py
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -67,15 +67,15 @@ class HonConnection:
 | 
			
		|||
 | 
			
		||||
    async def load_commands(self, device: HonDevice):
 | 
			
		||||
        params = {
 | 
			
		||||
            "applianceType": device.appliance_type_name,
 | 
			
		||||
            "code": device.code,
 | 
			
		||||
            "applianceType": device.appliance_type,
 | 
			
		||||
            "code": device.appliance["code"],
 | 
			
		||||
            "applianceModelId": device.appliance_model_id,
 | 
			
		||||
            "firmwareId": "41",
 | 
			
		||||
            "macAddress": device.mac_address,
 | 
			
		||||
            "fwVersion": device.fw_version,
 | 
			
		||||
            "fwVersion": device.appliance["fwVersion"],
 | 
			
		||||
            "os": const.OS,
 | 
			
		||||
            "appVersion": const.APP_VERSION,
 | 
			
		||||
            "series": device.series,
 | 
			
		||||
            "series": device.appliance["series"],
 | 
			
		||||
        }
 | 
			
		||||
        url = f"{const.API_URL}/commands/v1/retrieve"
 | 
			
		||||
        async with self._session.get(url, params=params, headers=await self._headers) as response:
 | 
			
		||||
| 
						 | 
				
			
			@ -87,13 +87,13 @@ class HonConnection:
 | 
			
		|||
    async def load_attributes(self, device: HonDevice, loop=False):
 | 
			
		||||
        params = {
 | 
			
		||||
            "macAddress": device.mac_address,
 | 
			
		||||
            "applianceType": device.appliance_type_name,
 | 
			
		||||
            "applianceType": device.appliance_type,
 | 
			
		||||
            "category": "CYCLE"
 | 
			
		||||
        }
 | 
			
		||||
        url = f"{const.API_URL}/commands/v1/context"
 | 
			
		||||
        async with self._session.get(url, params=params, headers=await self._headers) as response:
 | 
			
		||||
            if response.status >= 400 and not loop:
 | 
			
		||||
                _LOGGER.error("%s - Error %s - %s", url, response.status, await response.text)
 | 
			
		||||
                _LOGGER.error("%s - Error %s - %s", url, response.status, await response.text())
 | 
			
		||||
                await self.setup()
 | 
			
		||||
                return await self.load_attributes(device, loop=True)
 | 
			
		||||
            return (await response.json()).get("payload", {})
 | 
			
		||||
| 
						 | 
				
			
			@ -101,7 +101,7 @@ class HonConnection:
 | 
			
		|||
    async def load_statistics(self, device: HonDevice):
 | 
			
		||||
        params = {
 | 
			
		||||
            "macAddress": device.mac_address,
 | 
			
		||||
            "applianceType": device.appliance_type_name
 | 
			
		||||
            "applianceType": device.appliance_type
 | 
			
		||||
        }
 | 
			
		||||
        url = f"{const.API_URL}/commands/v1/statistics"
 | 
			
		||||
        async with self._session.get(url, params=params, headers=await self._headers) as response:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,6 @@ class Appliance:
 | 
			
		|||
        self._data = data
 | 
			
		||||
 | 
			
		||||
    def get(self):
 | 
			
		||||
        if self._data["lastConnEvent.category"] == "DISCONNECTED":
 | 
			
		||||
            self._data["machMode"] = "0"
 | 
			
		||||
        if self._data["attributes"]["lastConnEvent"]["category"] == "DISCONNECTED":
 | 
			
		||||
            self._data["attributes"]["parameters"]["machMode"] = "0"
 | 
			
		||||
        return self._data
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										120
									
								
								pyhon/device.py
									
									
									
									
									
								
							
							
						
						
									
										120
									
								
								pyhon/device.py
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -1,14 +1,12 @@
 | 
			
		|||
import importlib
 | 
			
		||||
from pprint import pprint
 | 
			
		||||
 | 
			
		||||
from pyhon.commands import HonCommand
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HonDevice:
 | 
			
		||||
    def __init__(self, connector, appliance):
 | 
			
		||||
        appliance["attributes"] = {v["parName"]: v["parValue"] for v in appliance["attributes"]}
 | 
			
		||||
        self._appliance = appliance
 | 
			
		||||
        for values in self._appliance.pop("attributes"):
 | 
			
		||||
            self._appliance[values["parName"]] = values["parValue"]
 | 
			
		||||
        self._connector = connector
 | 
			
		||||
        self._appliance_model = {}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -16,74 +14,33 @@ class HonDevice:
 | 
			
		|||
        self._statistics = {}
 | 
			
		||||
        self._attributes = {}
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def appliance_id(self):
 | 
			
		||||
        return self._appliance.get("applianceId")
 | 
			
		||||
        try:
 | 
			
		||||
            self._extra = importlib.import_module(f'pyhon.appliances.{self.appliance_type.lower()}')
 | 
			
		||||
        except ModuleNotFoundError:
 | 
			
		||||
            self._extra = None
 | 
			
		||||
 | 
			
		||||
    def __getitem__(self, item):
 | 
			
		||||
        if "." in item:
 | 
			
		||||
            result = self.data
 | 
			
		||||
            for key in item.split("."):
 | 
			
		||||
                if all([k in "0123456789" for k in key]):
 | 
			
		||||
                    result = result[int(key)]
 | 
			
		||||
                else:
 | 
			
		||||
                    result = result[key]
 | 
			
		||||
            return result
 | 
			
		||||
        else:
 | 
			
		||||
            if item in self.data:
 | 
			
		||||
                return self.data[item]
 | 
			
		||||
            return self.attributes["parameters"].get(item, self.appliance[item])
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def appliance_model_id(self):
 | 
			
		||||
        return self._appliance.get("applianceModelId")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def appliance_status(self):
 | 
			
		||||
        return self._appliance.get("applianceStatus")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def appliance_type_id(self):
 | 
			
		||||
        return self._appliance.get("applianceTypeId")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def appliance_type_name(self):
 | 
			
		||||
    def appliance_type(self):
 | 
			
		||||
        return self._appliance.get("applianceTypeName")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def brand(self):
 | 
			
		||||
        return self._appliance.get("brand")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def code(self):
 | 
			
		||||
        return self._appliance.get("code")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def connectivity(self):
 | 
			
		||||
        return self._appliance.get("connectivity")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def coords(self):
 | 
			
		||||
        return self._appliance.get("coords")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def eeprom_id(self):
 | 
			
		||||
        return self._appliance.get("eepromId")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def eeprom_name(self):
 | 
			
		||||
        return self._appliance.get("eepromName")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def enrollment_date(self):
 | 
			
		||||
        return self._appliance.get("enrollmentDate")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def first_enrollment(self):
 | 
			
		||||
        return self._appliance.get("firstEnrollment")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def first_enrollment_tbc(self):
 | 
			
		||||
        return self._appliance.get("firstEnrollmentTBC")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def fw_version(self):
 | 
			
		||||
        return self._appliance.get("fwVersion")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def id(self):
 | 
			
		||||
        return self._appliance.get("id")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def last_update(self):
 | 
			
		||||
        return self._appliance.get("lastUpdate")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def mac_address(self):
 | 
			
		||||
        return self._appliance.get("macAddress")
 | 
			
		||||
| 
						 | 
				
			
			@ -96,22 +53,6 @@ class HonDevice:
 | 
			
		|||
    def nick_name(self):
 | 
			
		||||
        return self._appliance.get("nickName")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def purchase_date(self):
 | 
			
		||||
        return self._appliance.get("purchaseDate")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def serial_number(self):
 | 
			
		||||
        return self._appliance.get("serialNumber")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def series(self):
 | 
			
		||||
        return self._appliance.get("series")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def water_hard(self):
 | 
			
		||||
        return self._appliance.get("waterHard")
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def commands_options(self):
 | 
			
		||||
        return self._appliance_model.get("options")
 | 
			
		||||
| 
						 | 
				
			
			@ -162,15 +103,13 @@ class HonDevice:
 | 
			
		|||
        result = {}
 | 
			
		||||
        for name, command in self._commands.items():
 | 
			
		||||
            for key, parameter in command.parameters.items():
 | 
			
		||||
                result[f"{name}.{key}"] = parameter.value
 | 
			
		||||
                result.setdefault(name, {})[key] = parameter.value
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    async def load_attributes(self):
 | 
			
		||||
        data = await self._connector.load_attributes(self)
 | 
			
		||||
        for name, values in data.get("shadow").get("parameters").items():
 | 
			
		||||
            self._attributes[name] = values["parNewVal"]
 | 
			
		||||
        for name, value in data.get("lastConnEvent").items():
 | 
			
		||||
            self._attributes[f"lastConnEvent.{name}"] = value
 | 
			
		||||
        self._attributes = await self._connector.load_attributes(self)
 | 
			
		||||
        for name, values in self._attributes.pop("shadow").get("parameters").items():
 | 
			
		||||
            self._attributes.setdefault("parameters", {})[name] = values["parNewVal"]
 | 
			
		||||
 | 
			
		||||
    async def load_statistics(self):
 | 
			
		||||
        self._statistics = await self._connector.load_statistics(self)
 | 
			
		||||
| 
						 | 
				
			
			@ -180,9 +119,8 @@ class HonDevice:
 | 
			
		|||
 | 
			
		||||
    @property
 | 
			
		||||
    def data(self):
 | 
			
		||||
        result = self.attributes | self.parameters | self.appliance | self._statistics
 | 
			
		||||
        try:
 | 
			
		||||
            extra = importlib.import_module(f'appliances.{self.appliance_type_name.lower()}')
 | 
			
		||||
            return result | extra.Appliance(result).get()
 | 
			
		||||
        except ModuleNotFoundError:
 | 
			
		||||
            return result
 | 
			
		||||
        result = {"attributes": self.attributes, "appliance": self.appliance, "statistics": self.statistics,
 | 
			
		||||
                  "commands": self.parameters}
 | 
			
		||||
        if self._extra:
 | 
			
		||||
            return result | self._extra.Appliance(result).get()
 | 
			
		||||
        return result
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue