Compare commits
	
		
			22 Commits
		
	
	
		
			v0.4.0
			...
			v0.6.0-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 891ae51832 | |||
| 102a05ffcd | |||
| 380cde5a71 | |||
| 5f9dbef4fc | |||
| 2aa1d3df01 | |||
| e2f7f15a5f | |||
| d91b3edb40 | |||
| 83c5e3479e | |||
| b0cd020941 | |||
| 593842144a | |||
| 9a6e1155f9 | |||
| 365a3af171 | |||
| 6de6ff375c | |||
| 89d2fd4af1 | |||
| 92add01a59 | |||
| 4a685e67e0 | |||
| 6303843116 | |||
| 907bc44533 | |||
| 4901be4050 | |||
| c8189414b8 | |||
| 799ac67d94 | |||
| 7e9202ef38 | 
							
								
								
									
										38
									
								
								.github/workflows/python_check.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								.github/workflows/python_check.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | name: Python check | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     branches: [ "main" ] | ||||||
|  |   pull_request: | ||||||
|  |     branches: [ "main" ] | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   build: | ||||||
|  |  | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     strategy: | ||||||
|  |       fail-fast: false | ||||||
|  |       matrix: | ||||||
|  |         python-version: ["3.10", "3.11"] | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |     - uses: actions/checkout@v3 | ||||||
|  |     - name: Set up Python ${{ matrix.python-version }} | ||||||
|  |       uses: actions/setup-python@v3 | ||||||
|  |       with: | ||||||
|  |         python-version: ${{ matrix.python-version }} | ||||||
|  |     - name: Install dependencies | ||||||
|  |       run: | | ||||||
|  |         python -m pip install --upgrade pip | ||||||
|  |         python -m pip install flake8 pylint black | ||||||
|  |     - name: Lint with flake8 | ||||||
|  |       run: | | ||||||
|  |         # stop the build if there are Python syntax errors or undefined names | ||||||
|  |         flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics | ||||||
|  |         flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 --statistics | ||||||
|  |     # - name: Analysing the code with pylint | ||||||
|  |     #   run: | | ||||||
|  |     #     pylint --max-line-length 88 $(git ls-files '*.py') | ||||||
|  |     - name: Check black style | ||||||
|  |       run: | | ||||||
|  |         black . --check | ||||||
							
								
								
									
										25
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,25 @@ | |||||||
|  | name: Release | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   release: | ||||||
|  |     types: [published] | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   release-zip: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v2 | ||||||
|  |  | ||||||
|  |       - name: ZIP Component Dir | ||||||
|  |         run: | | ||||||
|  |           cd ${{ github.workspace }}/custom_components/hon | ||||||
|  |           zip -r haier_hon.zip ./ | ||||||
|  |  | ||||||
|  |       - name: Upload zip to release | ||||||
|  |         uses: svenstaro/upload-release-action@v2 | ||||||
|  |         with: | ||||||
|  |           repo_token: ${{ secrets.GITHUB_TOKEN }} | ||||||
|  |           file: ${{ github.workspace }}/custom_components/hon/haier_hon.zip | ||||||
|  |           asset_name: haier_hon.zip | ||||||
|  |           tag: ${{ github.ref }} | ||||||
|  |           overwrite: true | ||||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,3 +1,3 @@ | |||||||
| __pycache__/ | __pycache__/ | ||||||
| test.py | scripts/test.py | ||||||
| .idea/ | .idea/ | ||||||
|  | |||||||
							
								
								
									
										217
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										217
									
								
								README.md
									
									
									
									
									
								
							| @ -1,13 +1,17 @@ | |||||||
| # Haier hOn | # Haier hOn | ||||||
| [](https://github.com/hacs/integration) | [](https://hacs.xyz) | ||||||
| [](https://github.com/Andre0512/hon/releases/latest) | [](https://github.com/Andre0512/hon/releases/latest) | ||||||
| [](https://analytics.home-assistant.io/)   |  | ||||||
|  | [](https://analytics.home-assistant.io/)   | ||||||
| Home Assistant integration for Haier hOn: support for Haier/Candy/Hoover home appliances like washing machines. | Home Assistant integration for Haier hOn: support for Haier/Candy/Hoover home appliances like washing machines. | ||||||
|  |  | ||||||
| ## Supported Appliances | ## Supported Appliances | ||||||
| - Tumble Dryer | - [Washing Machine](https://github.com/Andre0512/hon#washing-machine) | ||||||
| - Washer Dryer | - [Tumble Dryer](https://github.com/Andre0512/hon#tumble-dryer) | ||||||
| - Washing Machine | - [Washer Dryer](https://github.com/Andre0512/hon#washer-dryer) | ||||||
| - Oven | - [Oven](https://github.com/Andre0512/hon#oven) | ||||||
|  | - [Hob](https://github.com/Andre0512/hon#hob) | ||||||
|  | - [Dish Washer](https://github.com/Andre0512/hon#dish-washer) | ||||||
|  |  | ||||||
| ## Installation | ## Installation | ||||||
| **Method 1:** [](https://my.home-assistant.io/redirect/hacs_repository/?owner=Andre0512&repository=hon&category=integration) | **Method 1:** [](https://my.home-assistant.io/redirect/hacs_repository/?owner=Andre0512&repository=hon&category=integration) | ||||||
| @ -16,6 +20,8 @@ Home Assistant integration for Haier hOn: support for Haier/Candy/Hoover home ap | |||||||
|  |  | ||||||
| **Method 3:** Manually copy `hon` folder from [latest release](https://github.com/Andre0512/hon/releases/latest) to `config/custom_components` folder. | **Method 3:** Manually copy `hon` folder from [latest release](https://github.com/Andre0512/hon/releases/latest) to `config/custom_components` folder. | ||||||
|  |  | ||||||
|  | _Restart Home Assistant_ | ||||||
|  |  | ||||||
| ## Configuration | ## Configuration | ||||||
|  |  | ||||||
| **Method 1**: [](https://my.home-assistant.io/redirect/config_flow_start/?domain=hon) | **Method 1**: [](https://my.home-assistant.io/redirect/config_flow_start/?domain=hon) | ||||||
| @ -25,7 +31,25 @@ _If the integration is not in the list, you need to clear the browser cache._ | |||||||
|  |  | ||||||
| ## Contribute | ## Contribute | ||||||
| Any kind of contribution is welcome! | Any kind of contribution is welcome! | ||||||
| #### Add appliances or additional attributes | ### Read out device data | ||||||
|  | If you want to make a request for adding new appliances or additional attributes and don't want to use the command line, here is how you can read out your device data. | ||||||
|  | For every device exists a hidden button which can be used to log all info of your appliance. | ||||||
|  | 1. Enable the "Log Device Info" button   | ||||||
|  |    _This button can be found in the diagnostic section of your device or in the entity overview if "show disabled entities" is enabled._ | ||||||
|  | 2. Press the button | ||||||
|  | 3. Go to Settings > System > Logs, click _load full logs_ and scroll down   | ||||||
|  |    _The formatting is messy if you not load full logs_ | ||||||
|  | 4. Here you can find all data which can be read out via the api | ||||||
|  |    ```yaml | ||||||
|  |    data: | ||||||
|  |      appliance: | ||||||
|  |        applianceId: 12-34-56-78-90-ab#2022-10-25T19:47:11Z | ||||||
|  |        applianceModelId: 1569  | ||||||
|  |        ... | ||||||
|  |    ``` | ||||||
|  | 5. Copy this data and create a [new issue](https://github.com/Andre0512/hon/issues/new) with your request | ||||||
|  |  | ||||||
|  | ### Add appliances or additional attributes | ||||||
| 1. Install [pyhOn](https://github.com/Andre0512/pyhOn) | 1. Install [pyhOn](https://github.com/Andre0512/pyhOn) | ||||||
|    ```commandline |    ```commandline | ||||||
|     $ pip install pyhOn |     $ pip install pyhOn | ||||||
| @ -65,8 +89,9 @@ Any kind of contribution is welcome! | |||||||
| 5. Create a [pull request](https://github.com/Andre0512/hon/pulls) | 5. Create a [pull request](https://github.com/Andre0512/hon/pulls) | ||||||
|  |  | ||||||
| #### Tips and Tricks | #### Tips and Tricks | ||||||
| - If you want to have some states humanreadable, have a look at the `translation_key` parameter of the `EntityDescription` | - If you want to have some states humanreadable, have a look at the `translation_key` parameter of the `EntityDescription`. | ||||||
| - If you need to implement some more logic, create a pull request to the underlying library. There we collect special requirements in the `appliances` directory. | - If you need to implement some more logic, create a pull request to the underlying library. There we collect special requirements in the `appliances` directory. | ||||||
|  | - Use [pyhOn's translate command](https://github.com/Andre0512/pyhOn#translation) to read out the official translations  | ||||||
|  |  | ||||||
| ## Tested Devices | ## Tested Devices | ||||||
| - Haier WD90-B14TEAM5 | - Haier WD90-B14TEAM5 | ||||||
| @ -78,3 +103,177 @@ Any kind of contribution is welcome! | |||||||
| The existing integrations missed some features from the app I liked to have in HomeAssistant. | The existing integrations missed some features from the app I liked to have in HomeAssistant. | ||||||
| I tried to create a pull request, but in the structures of these existing repos, I find it hard to fit in my needs, so I basically rewrote everything.  | I tried to create a pull request, but in the structures of these existing repos, I find it hard to fit in my needs, so I basically rewrote everything.  | ||||||
| I moved the api related stuff into the package [pyhOn](https://github.com/Andre0512/pyhOn). | I moved the api related stuff into the package [pyhOn](https://github.com/Andre0512/pyhOn). | ||||||
|  |  | ||||||
|  | ## Appliance Features | ||||||
|  | ### Dish washer | ||||||
|  | #### Controls | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Dish Washer | `mdi:dishwasher` | `switch` | `active` | | ||||||
|  | #### Configs | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Add Dish | `mdi:silverware-fork-knife` | `switch` | `startProgram.addDish` | | ||||||
|  | | Delay time | `mdi:timer-plus` | `number` | `startProgram.delayTime` | | ||||||
|  | | Eco Express | `mdi:sprout` | `switch` | `startProgram.ecoExpress` | | ||||||
|  | | Eco Index | `mdi:sprout` | `sensor` | `startProgram.ecoIndex` | | ||||||
|  | | Energy Label | `mdi:lightning-bolt-circle` | `sensor` | `startProgram.energyLabel` | | ||||||
|  | | Extra Dry | `mdi:hair-dryer` | `switch` | `startProgram.extraDry` | | ||||||
|  | | Half Load | `mdi:fraction-one-half` | `switch` | `startProgram.halfLoad` | | ||||||
|  | | Open Door | `mdi:door-open` | `switch` | `startProgram.openDoor` | | ||||||
|  | | Program |  | `select` | `startProgram.program` | | ||||||
|  | | Temperature | `mdi:thermometer` | `sensor` | `startProgram.temp` | | ||||||
|  | | Three in One | `mdi:numeric-3-box-outline` | `switch` | `startProgram.threeInOne` | | ||||||
|  | | Time | `mdi:timer` | `sensor` | `startProgram.remainingTime` | | ||||||
|  | | Water Efficiency | `mdi:water` | `sensor` | `startProgram.waterEfficiency` | | ||||||
|  | | Water Saving | `mdi:water-percent` | `sensor` | `startProgram.waterSaving` | | ||||||
|  | | Water hard | `mdi:water` | `number` | `startProgram.waterHard` | | ||||||
|  | #### Sensors | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Connection |  | `binary_sensor` | `attributes.lastConnEvent.category` | | ||||||
|  | | Door |  | `binary_sensor` | `doorStatus` | | ||||||
|  | | Error | `mdi:math-log` | `sensor` | `errors` | | ||||||
|  | | Machine Status | `mdi:information` | `sensor` | `machMode` | | ||||||
|  | | Remaining Time | `mdi:timer` | `sensor` | `remainingTimeMM` | | ||||||
|  | | Rinse Aid | `mdi:spray-bottle` | `binary_sensor` | `rinseAidStatus` | | ||||||
|  | | Salt | `mdi:shaker-outline` | `binary_sensor` | `saltStatus` | | ||||||
|  | ### Hob | ||||||
|  | #### Controls | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Start Program | `mdi:pot-steam` | `button` | `startProgram` | | ||||||
|  | #### Configs | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Power Management | `mdi:timelapse` | `number` | `startProgram.powerManagement` | | ||||||
|  | | Program |  | `select` | `startProgram.program` | | ||||||
|  | | Temperature | `mdi:thermometer` | `number` | `startProgram.temp` | | ||||||
|  | #### Sensors | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Connection | `mdi:wifi` | `binary_sensor` | `attributes.lastConnEvent.category` | | ||||||
|  | | Error | `mdi:math-log` | `sensor` | `errors` | | ||||||
|  | | Hob Lock |  | `binary_sensor` | `hobLockStatus` | | ||||||
|  | | Hot Status |  | `binary_sensor` | `hotStatus` | | ||||||
|  | | On | `mdi:power-cycle` | `binary_sensor` | `attributes.parameters.onOffStatus` | | ||||||
|  | | Remaining Time | `mdi:timer` | `sensor` | `remainingTimeMM` | | ||||||
|  | | Remote Control | `mdi:remote` | `binary_sensor` | `attributes.parameters.remoteCtrValid` | | ||||||
|  | | Temperature | `mdi:thermometer` | `sensor` | `temp` | | ||||||
|  | ### Oven | ||||||
|  | #### Controls | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Start Program | `mdi:power-cycle` | `button` | `startProgram` | | ||||||
|  | | Stop Program | `mdi:power-off` | `button` | `stopProgram` | | ||||||
|  | #### Configs | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Delay time | `mdi:timer-plus` | `number` | `startProgram.delayTime` | | ||||||
|  | | Preheat |  | `select` | `startProgram.preheatStatus` | | ||||||
|  | | Program |  | `select` | `startProgram.program` | | ||||||
|  | | Program Duration | `mdi:timelapse` | `number` | `startProgram.prTime` | | ||||||
|  | | Target Temperature | `mdi:thermometer` | `number` | `startProgram.tempSel` | | ||||||
|  | #### Sensors | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Connection | `mdi:wifi` | `binary_sensor` | `attributes.lastConnEvent.category` | | ||||||
|  | | On | `mdi:power-cycle` | `binary_sensor` | `attributes.parameters.onOffStatus` | | ||||||
|  | | Remaining Time | `mdi:timer` | `sensor` | `remainingTimeMM` | | ||||||
|  | | Remote Control | `mdi:remote` | `binary_sensor` | `attributes.parameters.remoteCtrValid` | | ||||||
|  | | Start Time | `mdi:clock-start` | `sensor` | `delayTime` | | ||||||
|  | | Temperature | `mdi:thermometer` | `sensor` | `temp` | | ||||||
|  | | Temperature Selected | `mdi:thermometer` | `sensor` | `tempSel` | | ||||||
|  | ### Tumble dryer | ||||||
|  | #### Controls | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Pause Tumble Dryer | `mdi:pause` | `switch` | `pause` | | ||||||
|  | | Tumble Dryer | `mdi:tumble-dryer` | `switch` | `active` | | ||||||
|  | #### Configs | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Anti-Crease time | `mdi:timer` | `number` | `startProgram.antiCreaseTime` | | ||||||
|  | | Delay time | `mdi:timer-plus` | `number` | `startProgram.delayTime` | | ||||||
|  | | Dry level | `mdi:hair-dryer` | `number` | `startProgram.dryLevel` | | ||||||
|  | | Program |  | `select` | `startProgram.program` | | ||||||
|  | | Sterilization status | `mdi:clock-start` | `number` | `startProgram.sterilizationStatus` | | ||||||
|  | | Temperature level | `mdi:thermometer` | `number` | `startProgram.tempLevel` | | ||||||
|  | | Time | `mdi:timer` | `select` | `startProgram.dryTimeMM` | | ||||||
|  | #### Sensors | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Connection |  | `binary_sensor` | `attributes.lastConnEvent.category` | | ||||||
|  | | Door |  | `binary_sensor` | `doorStatus` | | ||||||
|  | | Dry level | `mdi:hair-dryer` | `sensor` | `dryLevel` | | ||||||
|  | | Error | `mdi:math-log` | `sensor` | `errors` | | ||||||
|  | | Machine Status | `mdi:information` | `sensor` | `machMode` | | ||||||
|  | | Program | `mdi:tumble-dryer` | `sensor` | `prCode` | | ||||||
|  | | Program Phase | `mdi:tumble-dryer` | `sensor` | `prPhase` | | ||||||
|  | | Remaining Time | `mdi:timer` | `sensor` | `remainingTimeMM` | | ||||||
|  | | Start Time | `mdi:clock-start` | `sensor` | `delayTime` | | ||||||
|  | | Temperature level | `mdi:thermometer` | `sensor` | `tempLevel` | | ||||||
|  | ### Washer dryer | ||||||
|  | #### Controls | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Pause Washing Machine | `mdi:pause` | `switch` | `pause` | | ||||||
|  | | Washing Machine | `mdi:washing-machine` | `switch` | `active` | | ||||||
|  | #### Configs | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Delay Time | `mdi:timer-plus` | `number` | `startProgram.delayTime` | | ||||||
|  | | Program |  | `select` | `startProgram.program` | | ||||||
|  | #### Sensors | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Acqua Plus |  | `binary_sensor` | `acquaplus` | | ||||||
|  | | Anti-Crease |  | `binary_sensor` | `anticrease` | | ||||||
|  | | Current Program | `mdi:tumble-dryer` | `sensor` | `prCode` | | ||||||
|  | | Current Temperature | `mdi:thermometer` | `sensor` | `temp` | | ||||||
|  | | Dirt level | `mdi:liquid-spot` | `sensor` | `dirtyLevel` | | ||||||
|  | | Dry level | `mdi:hair-dryer` | `sensor` | `dryLevel` | | ||||||
|  | | Extra Rinse 1 |  | `binary_sensor` | `extraRinse1` | | ||||||
|  | | Extra Rinse 2 |  | `binary_sensor` | `extraRinse2` | | ||||||
|  | | Extra Rinse 3 |  | `binary_sensor` | `extraRinse3` | | ||||||
|  | | Good Night Mode |  | `binary_sensor` | `goodNight` | | ||||||
|  | | Machine Status | `mdi:information` | `sensor` | `machMode` | | ||||||
|  | | Pre Wash |  | `binary_sensor` | `startProgram.prewash` | | ||||||
|  | | Program Phase | `mdi:tumble-dryer` | `sensor` | `prPhase` | | ||||||
|  | | Remaining Time | `mdi:timer` | `sensor` | `remainingTimeMM` | | ||||||
|  | | Remote Control | `mdi:remote` | `binary_sensor` | `attributes.lastConnEvent.category` | | ||||||
|  | | Spin Speed | `mdi:fast-forward-outline` | `sensor` | `spinSpeed` | | ||||||
|  | | Steam level | `mdi:smoke` | `sensor` | `steamLevel` | | ||||||
|  | ### Washing machine | ||||||
|  | #### Controls | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Pause Washing Machine | `mdi:pause` | `switch` | `pause` | | ||||||
|  | | Washing Machine | `mdi:washing-machine` | `switch` | `active` | | ||||||
|  | #### Configs | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Delay Status | `mdi:timer-check` | `switch` | `startProgram.delayStatus` | | ||||||
|  | | Delay Time | `mdi:timer-plus` | `number` | `startProgram.delayTime` | | ||||||
|  | | Main Wash Time | `mdi:clock-start` | `number` | `startProgram.mainWashTime` | | ||||||
|  | | Program |  | `select` | `startProgram.program` | | ||||||
|  | | Rinse Iterations | `mdi:rotate-right` | `number` | `startProgram.rinseIterations` | | ||||||
|  | | Soak Prewash Selection | `mdi:tshirt-crew` | `switch` | `startProgram.haier_SoakPrewashSelection` | | ||||||
|  | | Spin speed | `mdi:numeric` | `select` | `startProgram.spinSpeed` | | ||||||
|  | | Suggested weight | `mdi:weight-kilogram` | `sensor` | `startProgram.weight` | | ||||||
|  | | Temperature | `mdi:thermometer` | `select` | `startProgram.temp` | | ||||||
|  | #### Sensors | ||||||
|  | | Name | Icon | Entity | Key | | ||||||
|  | | --- | --- | --- | --- | | ||||||
|  | | Current Electricity Used | `mdi:lightning-bolt` | `sensor` | `currentElectricityUsed` | | ||||||
|  | | Current Water Used | `mdi:water` | `sensor` | `currentWaterUsed` | | ||||||
|  | | Door |  | `binary_sensor` | `doorStatus` | | ||||||
|  | | Door Lock |  | `binary_sensor` | `doorLockStatus` | | ||||||
|  | | Error | `mdi:math-log` | `sensor` | `errors` | | ||||||
|  | | Machine Status | `mdi:information` | `sensor` | `machMode` | | ||||||
|  | | Remaining Time | `mdi:timer` | `sensor` | `remainingTimeMM` | | ||||||
|  | | Remote Control | `mdi:remote` | `binary_sensor` | `attributes.lastConnEvent.category` | | ||||||
|  | | Spin Speed | `mdi:speedometer` | `sensor` | `spinSpeed` | | ||||||
|  | | Total Power |  | `sensor` | `totalElectricityUsed` | | ||||||
|  | | Total Wash Cycle | `mdi:counter` | `sensor` | `totalWashCycle` | | ||||||
|  | | Total Water |  | `sensor` | `totalWaterUsed` | | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| import logging | import logging | ||||||
|  |  | ||||||
| import voluptuous as vol | import voluptuous as vol | ||||||
| from pyhon import HonConnection | from pyhon import Hon | ||||||
|  |  | ||||||
| from homeassistant.config_entries import ConfigEntry | from homeassistant.config_entries import ConfigEntry | ||||||
| from homeassistant.const import CONF_EMAIL, CONF_PASSWORD | from homeassistant.const import CONF_EMAIL, CONF_PASSWORD | ||||||
| @ -28,8 +28,9 @@ CONFIG_SCHEMA = vol.Schema( | |||||||
|  |  | ||||||
| async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): | async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): | ||||||
|     session = aiohttp_client.async_get_clientsession(hass) |     session = aiohttp_client.async_get_clientsession(hass) | ||||||
|     hon = HonConnection(entry.data["email"], entry.data["password"], session) |     hon = await Hon( | ||||||
|     await hon.setup() |         entry.data["email"], entry.data["password"], session=session | ||||||
|  |     ).create() | ||||||
|     hass.data.setdefault(DOMAIN, {}) |     hass.data.setdefault(DOMAIN, {}) | ||||||
|     hass.data[DOMAIN][entry.unique_id] = hon |     hass.data[DOMAIN][entry.unique_id] = hon | ||||||
|     hass.data[DOMAIN]["coordinators"] = {} |     hass.data[DOMAIN]["coordinators"] = {} | ||||||
|  | |||||||
| @ -1,10 +1,13 @@ | |||||||
| import logging | import logging | ||||||
| from dataclasses import dataclass | from dataclasses import dataclass | ||||||
|  |  | ||||||
| from pyhon import HonConnection | from pyhon import Hon | ||||||
|  |  | ||||||
| from homeassistant.components.binary_sensor import BinarySensorEntityDescription, BinarySensorDeviceClass, \ | from homeassistant.components.binary_sensor import ( | ||||||
|     BinarySensorEntity |     BinarySensorEntityDescription, | ||||||
|  |     BinarySensorDeviceClass, | ||||||
|  |     BinarySensorEntity, | ||||||
|  | ) | ||||||
| from homeassistant.config_entries import ConfigEntry | from homeassistant.config_entries import ConfigEntry | ||||||
| from homeassistant.core import callback | from homeassistant.core import callback | ||||||
| from .const import DOMAIN | from .const import DOMAIN | ||||||
| @ -19,7 +22,9 @@ class HonBinarySensorEntityDescriptionMixin: | |||||||
|  |  | ||||||
|  |  | ||||||
| @dataclass | @dataclass | ||||||
| class HonBinarySensorEntityDescription(HonBinarySensorEntityDescriptionMixin, BinarySensorEntityDescription): | class HonBinarySensorEntityDescription( | ||||||
|  |     HonBinarySensorEntityDescriptionMixin, BinarySensorEntityDescription | ||||||
|  | ): | ||||||
|     pass |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -30,7 +35,7 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = { | |||||||
|             name="Remote Control", |             name="Remote Control", | ||||||
|             device_class=BinarySensorDeviceClass.CONNECTIVITY, |             device_class=BinarySensorDeviceClass.CONNECTIVITY, | ||||||
|             on_value="CONNECTED", |             on_value="CONNECTED", | ||||||
|             icon="mdi:remote" |             icon="mdi:remote", | ||||||
|         ), |         ), | ||||||
|         HonBinarySensorEntityDescription( |         HonBinarySensorEntityDescription( | ||||||
|             key="doorLockStatus", |             key="doorLockStatus", | ||||||
| @ -65,7 +70,7 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = { | |||||||
|             name="Remote Control", |             name="Remote Control", | ||||||
|             device_class=BinarySensorDeviceClass.CONNECTIVITY, |             device_class=BinarySensorDeviceClass.CONNECTIVITY, | ||||||
|             on_value="CONNECTED", |             on_value="CONNECTED", | ||||||
|             icon="mdi:remote" |             icon="mdi:remote", | ||||||
|         ), |         ), | ||||||
|         HonBinarySensorEntityDescription( |         HonBinarySensorEntityDescription( | ||||||
|             key="startProgram.prewash", |             key="startProgram.prewash", | ||||||
| @ -99,48 +104,117 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = { | |||||||
|     "OV": ( |     "OV": ( | ||||||
|         HonBinarySensorEntityDescription( |         HonBinarySensorEntityDescription( | ||||||
|             key="attributes.lastConnEvent.category", |             key="attributes.lastConnEvent.category", | ||||||
|             name="Online", |             name="Connection", | ||||||
|             device_class=BinarySensorDeviceClass.CONNECTIVITY, |             device_class=BinarySensorDeviceClass.CONNECTIVITY, | ||||||
|             on_value="CONNECTED", |             on_value="CONNECTED", | ||||||
|             icon="mdi:wifi" |             icon="mdi:wifi", | ||||||
|         ), |         ), | ||||||
|         HonBinarySensorEntityDescription( |         HonBinarySensorEntityDescription( | ||||||
|             key="attributes.parameters.remoteCtrValid", |             key="attributes.parameters.remoteCtrValid", | ||||||
|             name="On", |             name="Remote Control", | ||||||
|             device_class=BinarySensorDeviceClass.CONNECTIVITY, |             device_class=BinarySensorDeviceClass.CONNECTIVITY, | ||||||
|             on_value="1", |             on_value="1", | ||||||
|             icon="mdi:remote" |             icon="mdi:remote", | ||||||
|         ), |         ), | ||||||
|         HonBinarySensorEntityDescription( |         HonBinarySensorEntityDescription( | ||||||
|             key="attributes.parameters.onOffStatus", |             key="attributes.parameters.onOffStatus", | ||||||
|             name="On", |             name="On", | ||||||
|             device_class=BinarySensorDeviceClass.RUNNING, |             device_class=BinarySensorDeviceClass.RUNNING, | ||||||
|             on_value="1", |             on_value="1", | ||||||
|             icon="mdi:power-cycle" |             icon="mdi:power-cycle", | ||||||
|  |         ), | ||||||
|  |     ), | ||||||
|  |     "IH": ( | ||||||
|  |         HonBinarySensorEntityDescription( | ||||||
|  |             key="attributes.lastConnEvent.category", | ||||||
|  |             name="Connection", | ||||||
|  |             device_class=BinarySensorDeviceClass.CONNECTIVITY, | ||||||
|  |             on_value="CONNECTED", | ||||||
|  |             icon="mdi:wifi", | ||||||
|  |         ), | ||||||
|  |         HonBinarySensorEntityDescription( | ||||||
|  |             key="attributes.parameters.remoteCtrValid", | ||||||
|  |             name="Remote Control", | ||||||
|  |             device_class=BinarySensorDeviceClass.CONNECTIVITY, | ||||||
|  |             on_value="1", | ||||||
|  |             icon="mdi:remote", | ||||||
|  |         ), | ||||||
|  |         HonBinarySensorEntityDescription( | ||||||
|  |             key="attributes.parameters.onOffStatus", | ||||||
|  |             name="On", | ||||||
|  |             device_class=BinarySensorDeviceClass.RUNNING, | ||||||
|  |             on_value="1", | ||||||
|  |             icon="mdi:power-cycle", | ||||||
|  |         ), | ||||||
|  |         HonBinarySensorEntityDescription( | ||||||
|  |             key="hotStatus", | ||||||
|  |             name="Hot Status", | ||||||
|  |             device_class=BinarySensorDeviceClass.HEAT, | ||||||
|  |             on_value="1", | ||||||
|  |         ), | ||||||
|  |         HonBinarySensorEntityDescription( | ||||||
|  |             key="hobLockStatus", | ||||||
|  |             name="Hob Lock", | ||||||
|  |             device_class=BinarySensorDeviceClass.LOCK, | ||||||
|  |             on_value="0", | ||||||
|  |         ), | ||||||
|  |     ), | ||||||
|  |     "DW": ( | ||||||
|  |         HonBinarySensorEntityDescription( | ||||||
|  |             key="saltStatus", | ||||||
|  |             name="Salt", | ||||||
|  |             device_class=BinarySensorDeviceClass.PROBLEM, | ||||||
|  |             on_value="1", | ||||||
|  |             icon="mdi:shaker-outline", | ||||||
|  |         ), | ||||||
|  |         HonBinarySensorEntityDescription( | ||||||
|  |             key="rinseAidStatus", | ||||||
|  |             name="Rinse Aid", | ||||||
|  |             device_class=BinarySensorDeviceClass.PROBLEM, | ||||||
|  |             on_value="1", | ||||||
|  |             icon="mdi:spray-bottle", | ||||||
|  |         ), | ||||||
|  |         HonBinarySensorEntityDescription( | ||||||
|  |             key="attributes.lastConnEvent.category", | ||||||
|  |             name="Connection", | ||||||
|  |             device_class=BinarySensorDeviceClass.CONNECTIVITY, | ||||||
|  |             on_value="CONNECTED", | ||||||
|  |         ), | ||||||
|  |         HonBinarySensorEntityDescription( | ||||||
|  |             key="doorStatus", | ||||||
|  |             name="Door", | ||||||
|  |             device_class=BinarySensorDeviceClass.DOOR, | ||||||
|  |             on_value="1", | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: | async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: | ||||||
|     hon: HonConnection = hass.data[DOMAIN][entry.unique_id] |     hon: Hon = hass.data[DOMAIN][entry.unique_id] | ||||||
|     coordinators = hass.data[DOMAIN]["coordinators"] |     coordinators = hass.data[DOMAIN]["coordinators"] | ||||||
|     appliances = [] |     appliances = [] | ||||||
|     for device in hon.devices: |     for device in hon.appliances: | ||||||
|         if device.mac_address in coordinators: |         if device.unique_id in coordinators: | ||||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] |             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||||
|         else: |         else: | ||||||
|             coordinator = HonCoordinator(hass, device) |             coordinator = HonCoordinator(hass, device) | ||||||
|             hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator |             hass.data[DOMAIN]["coordinators"][device.unique_id] = coordinator | ||||||
|         await coordinator.async_config_entry_first_refresh() |         await coordinator.async_config_entry_first_refresh() | ||||||
|  |  | ||||||
|         if descriptions := BINARY_SENSORS.get(device.appliance_type): |         if descriptions := BINARY_SENSORS.get(device.appliance_type): | ||||||
|             for description in descriptions: |             for description in descriptions: | ||||||
|                 if not device.get(description.key): |                 if not device.get(description.key): | ||||||
|                     _LOGGER.warning("[%s] Can't setup %s", device.appliance_type, description.key) |                     _LOGGER.warning( | ||||||
|  |                         "[%s] Can't setup %s", device.appliance_type, description.key | ||||||
|  |                     ) | ||||||
|                     continue |                     continue | ||||||
|                 appliances.extend([ |                 appliances.extend( | ||||||
|                     HonBinarySensorEntity(hass, coordinator, entry, device, description)] |                     [ | ||||||
|  |                         HonBinarySensorEntity( | ||||||
|  |                             hass, coordinator, entry, device, description | ||||||
|  |                         ) | ||||||
|  |                     ] | ||||||
|                 ) |                 ) | ||||||
|  |  | ||||||
|     async_add_entities(appliances) |     async_add_entities(appliances) | ||||||
| @ -159,9 +233,15 @@ class HonBinarySensorEntity(HonEntity, BinarySensorEntity): | |||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def is_on(self) -> bool: |     def is_on(self) -> bool: | ||||||
|         return self._device.get(self.entity_description.key, "") == self.entity_description.on_value |         return ( | ||||||
|  |             self._device.get(self.entity_description.key, "") | ||||||
|  |             == self.entity_description.on_value | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     @callback |     @callback | ||||||
|     def _handle_coordinator_update(self): |     def _handle_coordinator_update(self): | ||||||
|         self._attr_native_value = self._device.get(self.entity_description.key, "") == self.entity_description.on_value |         self._attr_native_value = ( | ||||||
|  |             self._device.get(self.entity_description.key, "") | ||||||
|  |             == self.entity_description.on_value | ||||||
|  |         ) | ||||||
|         self.async_write_ha_state() |         self.async_write_ha_state() | ||||||
|  | |||||||
| @ -1,11 +1,18 @@ | |||||||
|  | import logging | ||||||
|  | import urllib | ||||||
|  | from urllib.parse import quote | ||||||
|  |  | ||||||
| from homeassistant.components.button import ButtonEntityDescription, ButtonEntity | from homeassistant.components.button import ButtonEntityDescription, ButtonEntity | ||||||
| from homeassistant.config_entries import ConfigEntry | from homeassistant.config_entries import ConfigEntry | ||||||
| from pyhon import HonConnection | from pyhon import Hon | ||||||
| from pyhon.device import HonDevice | from pyhon.appliance import HonAppliance | ||||||
|  |  | ||||||
|  | from homeassistant.const import EntityCategory | ||||||
| from .const import DOMAIN | from .const import DOMAIN | ||||||
| from .hon import HonCoordinator, HonEntity | from .hon import HonCoordinator, HonEntity | ||||||
|  |  | ||||||
|  | _LOGGER = logging.getLogger(__name__) | ||||||
|  |  | ||||||
| BUTTONS: dict[str, tuple[ButtonEntityDescription, ...]] = { | BUTTONS: dict[str, tuple[ButtonEntityDescription, ...]] = { | ||||||
|     "OV": ( |     "OV": ( | ||||||
|         ButtonEntityDescription( |         ButtonEntityDescription( | ||||||
| @ -18,35 +25,45 @@ BUTTONS: dict[str, tuple[ButtonEntityDescription, ...]] = { | |||||||
|             name="Stop Program", |             name="Stop Program", | ||||||
|             icon="mdi:power-off", |             icon="mdi:power-off", | ||||||
|         ), |         ), | ||||||
|     ) |     ), | ||||||
|  |     "IH": ( | ||||||
|  |         ButtonEntityDescription( | ||||||
|  |             key="startProgram", | ||||||
|  |             name="Start Program", | ||||||
|  |             icon="mdi:pot-steam", | ||||||
|  |         ), | ||||||
|  |     ), | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: | async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: | ||||||
|     hon: HonConnection = hass.data[DOMAIN][entry.unique_id] |     hon: Hon = hass.data[DOMAIN][entry.unique_id] | ||||||
|     coordinators = hass.data[DOMAIN]["coordinators"] |     coordinators = hass.data[DOMAIN]["coordinators"] | ||||||
|     appliances = [] |     appliances = [] | ||||||
|     for device in hon.devices: |     for device in hon.appliances: | ||||||
|         if device.mac_address in coordinators: |         if device.unique_id in coordinators: | ||||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] |             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||||
|         else: |         else: | ||||||
|             coordinator = HonCoordinator(hass, device) |             coordinator = HonCoordinator(hass, device) | ||||||
|             hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator |             hass.data[DOMAIN]["coordinators"][device.unique_id] = coordinator | ||||||
|         await coordinator.async_config_entry_first_refresh() |         await coordinator.async_config_entry_first_refresh() | ||||||
|  |  | ||||||
|         if descriptions := BUTTONS.get(device.appliance_type): |         if descriptions := BUTTONS.get(device.appliance_type): | ||||||
|             for description in descriptions: |             for description in descriptions: | ||||||
|                 if not device.commands.get(description.key): |                 if not device.commands.get(description.key): | ||||||
|                     continue |                     continue | ||||||
|                 appliances.extend([ |                 appliances.extend( | ||||||
|                     HonButtonEntity(hass, coordinator, entry, device, description)] |                     [HonButtonEntity(hass, coordinator, entry, device, description)] | ||||||
|                 ) |                 ) | ||||||
|  |         appliances.extend([HonFeatureRequestButton(hass, coordinator, entry, device)]) | ||||||
|  |  | ||||||
|     async_add_entities(appliances) |     async_add_entities(appliances) | ||||||
|  |  | ||||||
|  |  | ||||||
| class HonButtonEntity(HonEntity, ButtonEntity): | class HonButtonEntity(HonEntity, ButtonEntity): | ||||||
|     def __init__(self, hass, coordinator, entry, device: HonDevice, description) -> None: |     def __init__( | ||||||
|  |         self, hass, coordinator, entry, device: HonAppliance, description | ||||||
|  |     ) -> None: | ||||||
|         super().__init__(hass, entry, coordinator, device) |         super().__init__(hass, entry, coordinator, device) | ||||||
|  |  | ||||||
|         self._coordinator = coordinator |         self._coordinator = coordinator | ||||||
| @ -56,3 +73,18 @@ class HonButtonEntity(HonEntity, ButtonEntity): | |||||||
|  |  | ||||||
|     async def async_press(self) -> None: |     async def async_press(self) -> None: | ||||||
|         await self._device.commands[self.entity_description.key].send() |         await self._device.commands[self.entity_description.key].send() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class HonFeatureRequestButton(HonEntity, ButtonEntity): | ||||||
|  |     def __init__(self, hass, coordinator, entry, device: HonAppliance) -> None: | ||||||
|  |         super().__init__(hass, entry, coordinator, device) | ||||||
|  |  | ||||||
|  |         self._device = device | ||||||
|  |         self._attr_unique_id = f"{super().unique_id}_log_device_info" | ||||||
|  |         self._attr_icon = "mdi:information" | ||||||
|  |         self._attr_name = "Log Device Info" | ||||||
|  |         self._attr_entity_category = EntityCategory.DIAGNOSTIC | ||||||
|  |         self._attr_entity_registry_enabled_default = False | ||||||
|  |  | ||||||
|  |     async def async_press(self) -> None: | ||||||
|  |         _LOGGER.error("Device Info:\n" + self._device.diagnose) | ||||||
|  | |||||||
| @ -20,8 +20,12 @@ class HonFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): | |||||||
|  |  | ||||||
|     async def async_step_user(self, user_input=None): |     async def async_step_user(self, user_input=None): | ||||||
|         if user_input is None: |         if user_input is None: | ||||||
|             return self.async_show_form(step_id="user", data_schema=vol.Schema( |             return self.async_show_form( | ||||||
|                 {vol.Required(CONF_EMAIL): str, vol.Required(CONF_PASSWORD): str})) |                 step_id="user", | ||||||
|  |                 data_schema=vol.Schema( | ||||||
|  |                     {vol.Required(CONF_EMAIL): str, vol.Required(CONF_PASSWORD): str} | ||||||
|  |                 ), | ||||||
|  |             ) | ||||||
|  |  | ||||||
|         self._email = user_input[CONF_EMAIL] |         self._email = user_input[CONF_EMAIL] | ||||||
|         self._password = user_input[CONF_PASSWORD] |         self._password = user_input[CONF_PASSWORD] | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| import logging | import logging | ||||||
| from datetime import timedelta | from datetime import timedelta | ||||||
|  |  | ||||||
| from pyhon.device import HonDevice | from pyhon.appliance import HonAppliance | ||||||
|  |  | ||||||
| from homeassistant.helpers.entity import DeviceInfo | from homeassistant.helpers.entity import DeviceInfo | ||||||
| from homeassistant.helpers.update_coordinator import CoordinatorEntity | from homeassistant.helpers.update_coordinator import CoordinatorEntity | ||||||
| @ -15,30 +15,37 @@ _LOGGER = logging.getLogger(__name__) | |||||||
| class HonEntity(CoordinatorEntity): | class HonEntity(CoordinatorEntity): | ||||||
|     _attr_has_entity_name = True |     _attr_has_entity_name = True | ||||||
|  |  | ||||||
|     def __init__(self, hass, entry, coordinator, device: HonDevice) -> None: |     def __init__(self, hass, entry, coordinator, device: HonAppliance) -> None: | ||||||
|         super().__init__(coordinator) |         super().__init__(coordinator) | ||||||
|  |  | ||||||
|         self._hon = hass.data[DOMAIN][entry.unique_id] |         self._hon = hass.data[DOMAIN][entry.unique_id] | ||||||
|         self._hass = hass |         self._hass = hass | ||||||
|         self._device = device |         self._device = device | ||||||
|  |  | ||||||
|         self._attr_unique_id = self._device.mac_address |         self._attr_unique_id = self._device.unique_id | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def device_info(self): |     def device_info(self): | ||||||
|         return DeviceInfo( |         return DeviceInfo( | ||||||
|             identifiers={(DOMAIN, self._device.mac_address)}, |             identifiers={(DOMAIN, self._device.unique_id)}, | ||||||
|             manufacturer=self._device.get("brand", ""), |             manufacturer=self._device.get("brand", ""), | ||||||
|             name=self._device.nick_name if self._device.nick_name else self._device.model_name, |             name=self._device.nick_name | ||||||
|  |             if self._device.nick_name | ||||||
|  |             else self._device.model_name, | ||||||
|             model=self._device.model_name, |             model=self._device.model_name, | ||||||
|             sw_version=self._device.get("fwVersion", ""), |             sw_version=self._device.get("fwVersion", ""), | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |  | ||||||
| class HonCoordinator(DataUpdateCoordinator): | class HonCoordinator(DataUpdateCoordinator): | ||||||
|     def __init__(self, hass, device: HonDevice): |     def __init__(self, hass, device: HonAppliance): | ||||||
|         """Initialize my coordinator.""" |         """Initialize my coordinator.""" | ||||||
|         super().__init__(hass, _LOGGER, name=device.mac_address, update_interval=timedelta(seconds=30)) |         super().__init__( | ||||||
|  |             hass, | ||||||
|  |             _LOGGER, | ||||||
|  |             name=device.unique_id, | ||||||
|  |             update_interval=timedelta(seconds=30), | ||||||
|  |         ) | ||||||
|         self._device = device |         self._device = device | ||||||
|  |  | ||||||
|     async def _async_update_data(self): |     async def _async_update_data(self): | ||||||
|  | |||||||
| @ -6,7 +6,6 @@ | |||||||
|   "documentation": "https://github.com/Andre0512/hon/", |   "documentation": "https://github.com/Andre0512/hon/", | ||||||
|   "iot_class": "cloud_polling", |   "iot_class": "cloud_polling", | ||||||
|   "issue_tracker": "https://github.com/Andre0512/hon/issues", |   "issue_tracker": "https://github.com/Andre0512/hon/issues", | ||||||
|   "requirements": ["pyhOn==0.4.1"], |   "requirements": ["pyhOn==0.8.0b7"], | ||||||
|   "version": "0.4.0" |   "version": "0.6.0-beta.7" | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from pyhon import HonConnection | from pyhon import Hon | ||||||
| from pyhon.parameter import HonParameterRange | from pyhon.parameter.range import HonParameterRange | ||||||
|  |  | ||||||
| from homeassistant.components.number import ( | from homeassistant.components.number import ( | ||||||
|     NumberEntity, |     NumberEntity, | ||||||
| @ -22,20 +22,20 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { | |||||||
|             name="Delay Time", |             name="Delay Time", | ||||||
|             icon="mdi:timer-plus", |             icon="mdi:timer-plus", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             native_unit_of_measurement=UnitOfTime.MINUTES |             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||||
|         ), |         ), | ||||||
|         NumberEntityDescription( |         NumberEntityDescription( | ||||||
|             key="startProgram.rinseIterations", |             key="startProgram.rinseIterations", | ||||||
|             name="Rinse Iterations", |             name="Rinse Iterations", | ||||||
|             icon="mdi:rotate-right", |             icon="mdi:rotate-right", | ||||||
|             entity_category=EntityCategory.CONFIG |             entity_category=EntityCategory.CONFIG, | ||||||
|         ), |         ), | ||||||
|         NumberEntityDescription( |         NumberEntityDescription( | ||||||
|             key="startProgram.mainWashTime", |             key="startProgram.mainWashTime", | ||||||
|             name="Main Wash Time", |             name="Main Wash Time", | ||||||
|             icon="mdi:clock-start", |             icon="mdi:clock-start", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             native_unit_of_measurement=UnitOfTime.MINUTES |             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
|     "TD": ( |     "TD": ( | ||||||
| @ -44,34 +44,34 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { | |||||||
|             name="Delay time", |             name="Delay time", | ||||||
|             icon="mdi:timer-plus", |             icon="mdi:timer-plus", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             native_unit_of_measurement=UnitOfTime.MINUTES |             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||||
|         ), |         ), | ||||||
|         NumberEntityDescription( |         NumberEntityDescription( | ||||||
|             key="startProgram.dryLevel", |             key="startProgram.dryLevel", | ||||||
|             name="Dry level", |             name="Dry level", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             icon="mdi:hair-dryer", |             icon="mdi:hair-dryer", | ||||||
|             translation_key="tumbledryerdrylevel" |             translation_key="tumbledryerdrylevel", | ||||||
|         ), |         ), | ||||||
|         NumberEntityDescription( |         NumberEntityDescription( | ||||||
|             key="startProgram.tempLevel", |             key="startProgram.tempLevel", | ||||||
|             name="Temperature level", |             name="Temperature level", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             icon="mdi:thermometer", |             icon="mdi:thermometer", | ||||||
|             translation_key="tumbledryertemplevel" |             translation_key="tumbledryertemplevel", | ||||||
|         ), |         ), | ||||||
|         NumberEntityDescription( |         NumberEntityDescription( | ||||||
|             key="startProgram.antiCreaseTime", |             key="startProgram.antiCreaseTime", | ||||||
|             name="Anti-Crease time", |             name="Anti-Crease time", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             icon="mdi:timer", |             icon="mdi:timer", | ||||||
|             native_unit_of_measurement=UnitOfTime.MINUTES |             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||||
|         ), |         ), | ||||||
|         NumberEntityDescription( |         NumberEntityDescription( | ||||||
|             key="startProgram.sterilizationStatus", |             key="startProgram.sterilizationStatus", | ||||||
|             name="Sterilization status", |             name="Sterilization status", | ||||||
|             icon="mdi:clock-start", |             icon="mdi:clock-start", | ||||||
|             entity_category=EntityCategory.CONFIG |             entity_category=EntityCategory.CONFIG, | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
|     "WD": ( |     "WD": ( | ||||||
| @ -80,7 +80,7 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { | |||||||
|             name="Delay Time", |             name="Delay Time", | ||||||
|             icon="mdi:timer-plus", |             icon="mdi:timer-plus", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             native_unit_of_measurement=UnitOfTime.MINUTES |             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
|     "OV": ( |     "OV": ( | ||||||
| @ -89,45 +89,73 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { | |||||||
|             name="Delay time", |             name="Delay time", | ||||||
|             icon="mdi:timer-plus", |             icon="mdi:timer-plus", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             native_unit_of_measurement=UnitOfTime.MINUTES |             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||||
|         ), |         ), | ||||||
|         NumberEntityDescription( |         NumberEntityDescription( | ||||||
|             key="startProgram.tempSel", |             key="startProgram.tempSel", | ||||||
|             name="Target Temperature", |             name="Target Temperature", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             icon="mdi:thermometer", |             icon="mdi:thermometer", | ||||||
|             native_unit_of_measurement=UnitOfTemperature.CELSIUS |             native_unit_of_measurement=UnitOfTemperature.CELSIUS, | ||||||
|         ), |         ), | ||||||
|  |  | ||||||
|         NumberEntityDescription( |         NumberEntityDescription( | ||||||
|             key="startProgram.prTime", |             key="startProgram.prTime", | ||||||
|             name="Program Duration", |             name="Program Duration", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             icon="mdi:timelapse", |             icon="mdi:timelapse", | ||||||
|             native_unit_of_measurement=UnitOfTime.MINUTES |             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||||
|  |         ), | ||||||
|  |     ), | ||||||
|  |     "IH": ( | ||||||
|  |         NumberEntityDescription( | ||||||
|  |             key="startProgram.temp", | ||||||
|  |             name="Temperature", | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |             icon="mdi:thermometer", | ||||||
|  |         ), | ||||||
|  |         NumberEntityDescription( | ||||||
|  |             key="startProgram.powerManagement", | ||||||
|  |             name="Power Management", | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |             icon="mdi:timelapse", | ||||||
|  |         ), | ||||||
|  |     ), | ||||||
|  |     "DW": ( | ||||||
|  |         NumberEntityDescription( | ||||||
|  |             key="startProgram.delayTime", | ||||||
|  |             name="Delay time", | ||||||
|  |             icon="mdi:timer-plus", | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||||
|  |         ), | ||||||
|  |         NumberEntityDescription( | ||||||
|  |             key="startProgram.waterHard", | ||||||
|  |             name="Water hard", | ||||||
|  |             icon="mdi:water", | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: | async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: | ||||||
|     hon: HonConnection = hass.data[DOMAIN][entry.unique_id] |     hon: Hon = hass.data[DOMAIN][entry.unique_id] | ||||||
|     coordinators = hass.data[DOMAIN]["coordinators"] |     coordinators = hass.data[DOMAIN]["coordinators"] | ||||||
|     appliances = [] |     appliances = [] | ||||||
|     for device in hon.devices: |     for device in hon.appliances: | ||||||
|         if device.mac_address in coordinators: |         if device.unique_id in coordinators: | ||||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] |             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||||
|         else: |         else: | ||||||
|             coordinator = HonCoordinator(hass, device) |             coordinator = HonCoordinator(hass, device) | ||||||
|             hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator |             hass.data[DOMAIN]["coordinators"][device.unique_id] = coordinator | ||||||
|         await coordinator.async_config_entry_first_refresh() |         await coordinator.async_config_entry_first_refresh() | ||||||
|  |  | ||||||
|         if descriptions := NUMBERS.get(device.appliance_type): |         if descriptions := NUMBERS.get(device.appliance_type): | ||||||
|             for description in descriptions: |             for description in descriptions: | ||||||
|                 if not device.settings.get(description.key): |                 if not device.settings.get(description.key): | ||||||
|                     continue |                     continue | ||||||
|                 appliances.extend([ |                 appliances.extend( | ||||||
|                     HonNumberEntity(hass, coordinator, entry, device, description)] |                     [HonNumberEntity(hass, coordinator, entry, device, description)] | ||||||
|                 ) |                 ) | ||||||
|  |  | ||||||
|     async_add_entities(appliances) |     async_add_entities(appliances) | ||||||
|  | |||||||
| @ -2,9 +2,9 @@ from __future__ import annotations | |||||||
|  |  | ||||||
| import logging | import logging | ||||||
|  |  | ||||||
| from pyhon import HonConnection | from pyhon import Hon | ||||||
| from pyhon.device import HonDevice | from pyhon.appliance import HonAppliance | ||||||
| from pyhon.parameter import HonParameterFixed | from pyhon.parameter.fixed import HonParameterFixed | ||||||
|  |  | ||||||
| from homeassistant.components.select import SelectEntity, SelectEntityDescription | from homeassistant.components.select import SelectEntity, SelectEntityDescription | ||||||
| from homeassistant.config_entries import ConfigEntry | from homeassistant.config_entries import ConfigEntry | ||||||
| @ -24,20 +24,20 @@ SELECTS = { | |||||||
|             name="Spin speed", |             name="Spin speed", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             icon="mdi:numeric", |             icon="mdi:numeric", | ||||||
|             unit_of_measurement=REVOLUTIONS_PER_MINUTE |             unit_of_measurement=REVOLUTIONS_PER_MINUTE, | ||||||
|         ), |         ), | ||||||
|         SelectEntityDescription( |         SelectEntityDescription( | ||||||
|             key="startProgram.temp", |             key="startProgram.temp", | ||||||
|             name="Temperature", |             name="Temperature", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             icon="mdi:thermometer", |             icon="mdi:thermometer", | ||||||
|             unit_of_measurement=UnitOfTemperature.CELSIUS |             unit_of_measurement=UnitOfTemperature.CELSIUS, | ||||||
|         ), |         ), | ||||||
|         SelectEntityDescription( |         SelectEntityDescription( | ||||||
|             key="startProgram.program", |             key="startProgram.program", | ||||||
|             name="Program", |             name="Program", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             translation_key="programs" |             translation_key="programs", | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
|     "TD": ( |     "TD": ( | ||||||
| @ -45,14 +45,14 @@ SELECTS = { | |||||||
|             key="startProgram.program", |             key="startProgram.program", | ||||||
|             name="Program", |             name="Program", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             translation_key="programs" |             translation_key="programs", | ||||||
|         ), |         ), | ||||||
|         SelectEntityDescription( |         SelectEntityDescription( | ||||||
|             key="startProgram.dryTimeMM", |             key="startProgram.dryTimeMM", | ||||||
|             name="Time", |             name="Time", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             icon="mdi:timer", |             icon="mdi:timer", | ||||||
|             unit_of_measurement=UnitOfTime.MINUTES |             unit_of_measurement=UnitOfTime.MINUTES, | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
|     "WD": ( |     "WD": ( | ||||||
| @ -60,7 +60,7 @@ SELECTS = { | |||||||
|             key="startProgram.program", |             key="startProgram.program", | ||||||
|             name="Program", |             name="Program", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             translation_key="programs" |             translation_key="programs", | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
|     "OV": ( |     "OV": ( | ||||||
| @ -68,40 +68,59 @@ SELECTS = { | |||||||
|             key="startProgram.program", |             key="startProgram.program", | ||||||
|             name="Program", |             name="Program", | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|  |             translation_key="programs", | ||||||
|         ), |         ), | ||||||
|         SelectEntityDescription( |         SelectEntityDescription( | ||||||
|             key="startProgram.preheatStatus", |             key="startProgram.preheatStatus", | ||||||
|             name="Preheat", |             name="Preheat", | ||||||
|             entity_category=EntityCategory.CONFIG |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |     ), | ||||||
|  |     "IH": ( | ||||||
|  |         SelectEntityDescription( | ||||||
|  |             key="startProgram.program", | ||||||
|  |             name="Program", | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |             translation_key="programs", | ||||||
|  |         ), | ||||||
|  |     ), | ||||||
|  |     "DW": ( | ||||||
|  |         SelectEntityDescription( | ||||||
|  |             key="startProgram.program", | ||||||
|  |             name="Program", | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |             translation_key="programs_dw", | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: | async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: | ||||||
|     hon: HonConnection = hass.data[DOMAIN][entry.unique_id] |     hon: Hon = hass.data[DOMAIN][entry.unique_id] | ||||||
|     coordinators = hass.data[DOMAIN]["coordinators"] |     coordinators = hass.data[DOMAIN]["coordinators"] | ||||||
|     appliances = [] |     appliances = [] | ||||||
|     for device in hon.devices: |     for device in hon.appliances: | ||||||
|         if device.mac_address in coordinators: |         if device.unique_id in coordinators: | ||||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] |             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||||
|         else: |         else: | ||||||
|             coordinator = HonCoordinator(hass, device) |             coordinator = HonCoordinator(hass, device) | ||||||
|             hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator |             hass.data[DOMAIN]["coordinators"][device.unique_id] = coordinator | ||||||
|         await coordinator.async_config_entry_first_refresh() |         await coordinator.async_config_entry_first_refresh() | ||||||
|  |  | ||||||
|         if descriptions := SELECTS.get(device.appliance_type): |         if descriptions := SELECTS.get(device.appliance_type): | ||||||
|             for description in descriptions: |             for description in descriptions: | ||||||
|                 if not device.settings.get(description.key): |                 if not device.settings.get(description.key): | ||||||
|                     continue |                     continue | ||||||
|                 appliances.extend([ |                 appliances.extend( | ||||||
|                     HonSelectEntity(hass, coordinator, entry, device, description)] |                     [HonSelectEntity(hass, coordinator, entry, device, description)] | ||||||
|                 ) |                 ) | ||||||
|     async_add_entities(appliances) |     async_add_entities(appliances) | ||||||
|  |  | ||||||
|  |  | ||||||
| class HonSelectEntity(HonEntity, SelectEntity): | class HonSelectEntity(HonEntity, SelectEntity): | ||||||
|     def __init__(self, hass, coordinator, entry, device: HonDevice, description) -> None: |     def __init__( | ||||||
|  |         self, hass, coordinator, entry, device: HonAppliance, description | ||||||
|  |     ) -> None: | ||||||
|         super().__init__(hass, entry, coordinator, device) |         super().__init__(hass, entry, coordinator, device) | ||||||
|  |  | ||||||
|         self._coordinator = coordinator |         self._coordinator = coordinator | ||||||
| @ -128,7 +147,9 @@ class HonSelectEntity(HonEntity, SelectEntity): | |||||||
|     @callback |     @callback | ||||||
|     def _handle_coordinator_update(self): |     def _handle_coordinator_update(self): | ||||||
|         setting = self._device.settings[self.entity_description.key] |         setting = self._device.settings[self.entity_description.key] | ||||||
|         if not isinstance(self._device.settings[self.entity_description.key], HonParameterFixed): |         if not isinstance( | ||||||
|  |             self._device.settings[self.entity_description.key], HonParameterFixed | ||||||
|  |         ): | ||||||
|             self._attr_options: list[str] = setting.values |             self._attr_options: list[str] = setting.values | ||||||
|         else: |         else: | ||||||
|             self._attr_options = [setting.value] |             self._attr_options = [setting.value] | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import logging | import logging | ||||||
|  |  | ||||||
| from pyhon import HonConnection | from pyhon import Hon | ||||||
|  |  | ||||||
| from homeassistant.components.sensor import ( | from homeassistant.components.sensor import ( | ||||||
|     SensorEntity, |     SensorEntity, | ||||||
| @ -16,11 +16,12 @@ from homeassistant.const import ( | |||||||
|     UnitOfMass, |     UnitOfMass, | ||||||
|     UnitOfPower, |     UnitOfPower, | ||||||
|     UnitOfTime, |     UnitOfTime, | ||||||
|     UnitOfTemperature |     UnitOfTemperature, | ||||||
| ) | ) | ||||||
| from homeassistant.core import callback | from homeassistant.core import callback | ||||||
| from homeassistant.helpers.entity import EntityCategory | from homeassistant.helpers.entity import EntityCategory | ||||||
| from homeassistant.helpers.typing import StateType | from homeassistant.helpers.typing import StateType | ||||||
|  | from homeassistant.const import PERCENTAGE | ||||||
|  |  | ||||||
| from .const import DOMAIN | from .const import DOMAIN | ||||||
| from .hon import HonCoordinator, HonEntity | from .hon import HonCoordinator, HonEntity | ||||||
| @ -34,20 +35,20 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | |||||||
|             name="Total Power", |             name="Total Power", | ||||||
|             device_class=SensorDeviceClass.ENERGY, |             device_class=SensorDeviceClass.ENERGY, | ||||||
|             state_class=SensorStateClass.TOTAL_INCREASING, |             state_class=SensorStateClass.TOTAL_INCREASING, | ||||||
|             native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR |             native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="totalWaterUsed", |             key="totalWaterUsed", | ||||||
|             name="Total Water", |             name="Total Water", | ||||||
|             device_class=SensorDeviceClass.WATER, |             device_class=SensorDeviceClass.WATER, | ||||||
|             state_class=SensorStateClass.TOTAL_INCREASING, |             state_class=SensorStateClass.TOTAL_INCREASING, | ||||||
|             native_unit_of_measurement=UnitOfVolume.LITERS |             native_unit_of_measurement=UnitOfVolume.LITERS, | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="totalWashCycle", |             key="totalWashCycle", | ||||||
|             name="Total Wash Cycle", |             name="Total Wash Cycle", | ||||||
|             state_class=SensorStateClass.TOTAL_INCREASING, |             state_class=SensorStateClass.TOTAL_INCREASING, | ||||||
|             icon="mdi:counter" |             icon="mdi:counter", | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="currentElectricityUsed", |             key="currentElectricityUsed", | ||||||
| @ -55,13 +56,13 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | |||||||
|             state_class=SensorStateClass.MEASUREMENT, |             state_class=SensorStateClass.MEASUREMENT, | ||||||
|             device_class=SensorDeviceClass.POWER, |             device_class=SensorDeviceClass.POWER, | ||||||
|             native_unit_of_measurement=UnitOfPower.KILO_WATT, |             native_unit_of_measurement=UnitOfPower.KILO_WATT, | ||||||
|             icon="mdi:lightning-bolt" |             icon="mdi:lightning-bolt", | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="currentWaterUsed", |             key="currentWaterUsed", | ||||||
|             name="Current Water Used", |             name="Current Water Used", | ||||||
|             state_class=SensorStateClass.MEASUREMENT, |             state_class=SensorStateClass.MEASUREMENT, | ||||||
|             icon="mdi:water" |             icon="mdi:water", | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="startProgram.weight", |             key="startProgram.weight", | ||||||
| @ -69,19 +70,16 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | |||||||
|             state_class=SensorStateClass.MEASUREMENT, |             state_class=SensorStateClass.MEASUREMENT, | ||||||
|             entity_category=EntityCategory.CONFIG, |             entity_category=EntityCategory.CONFIG, | ||||||
|             native_unit_of_measurement=UnitOfMass.KILOGRAMS, |             native_unit_of_measurement=UnitOfMass.KILOGRAMS, | ||||||
|             icon="mdi:weight-kilogram" |             icon="mdi:weight-kilogram", | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="machMode", |             key="machMode", | ||||||
|             name="Machine Status", |             name="Machine Status", | ||||||
|             icon="mdi:information", |             icon="mdi:information", | ||||||
|             translation_key="mode" |             translation_key="mode", | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="errors", |             key="errors", name="Error", icon="mdi:math-log", translation_key="errors" | ||||||
|             name="Error", |  | ||||||
|             icon="mdi:math-log", |  | ||||||
|             translation_key="errors" |  | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="remainingTimeMM", |             key="remainingTimeMM", | ||||||
| @ -103,13 +101,10 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | |||||||
|             key="machMode", |             key="machMode", | ||||||
|             name="Machine Status", |             name="Machine Status", | ||||||
|             icon="mdi:information", |             icon="mdi:information", | ||||||
|             translation_key="mode" |             translation_key="mode", | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="errors", |             key="errors", name="Error", icon="mdi:math-log", translation_key="errors" | ||||||
|             name="Error", |  | ||||||
|             icon="mdi:math-log", |  | ||||||
|             translation_key="errors" |  | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="remainingTimeMM", |             key="remainingTimeMM", | ||||||
| @ -129,25 +124,25 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | |||||||
|             key="prCode", |             key="prCode", | ||||||
|             name="Program", |             name="Program", | ||||||
|             icon="mdi:tumble-dryer", |             icon="mdi:tumble-dryer", | ||||||
|             translation_key="tumbledryerprogram" |             translation_key="tumbledryerprogram", | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="prPhase", |             key="prPhase", | ||||||
|             name="Program Phase", |             name="Program Phase", | ||||||
|             icon="mdi:tumble-dryer", |             icon="mdi:tumble-dryer", | ||||||
|             translation_key="tumbledryerprogramphase" |             translation_key="tumbledryerprogramphase", | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="dryLevel", |             key="dryLevel", | ||||||
|             name="Dry level", |             name="Dry level", | ||||||
|             icon="mdi:hair-dryer", |             icon="mdi:hair-dryer", | ||||||
|             translation_key="tumbledryerdrylevel" |             translation_key="tumbledryerdrylevel", | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="tempLevel", |             key="tempLevel", | ||||||
|             name="Temperature level", |             name="Temperature level", | ||||||
|             icon="mdi:thermometer", |             icon="mdi:thermometer", | ||||||
|             translation_key="tumbledryertemplevel" |             translation_key="tumbledryertemplevel", | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
|     "WD": ( |     "WD": ( | ||||||
| @ -155,7 +150,7 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | |||||||
|             key="machMode", |             key="machMode", | ||||||
|             name="Machine Status", |             name="Machine Status", | ||||||
|             icon="mdi:information", |             icon="mdi:information", | ||||||
|             translation_key="mode" |             translation_key="mode", | ||||||
|         ), |         ), | ||||||
|         SensorEntityDescription( |         SensorEntityDescription( | ||||||
|             key="spinSpeed", |             key="spinSpeed", | ||||||
| @ -227,28 +222,109 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | |||||||
|             icon="mdi:thermometer", |             icon="mdi:thermometer", | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
|  |     "IH": ( | ||||||
|  |         SensorEntityDescription( | ||||||
|  |             key="remainingTimeMM", | ||||||
|  |             name="Remaining Time", | ||||||
|  |             icon="mdi:timer", | ||||||
|  |             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||||
|  |         ), | ||||||
|  |         SensorEntityDescription( | ||||||
|  |             key="temp", | ||||||
|  |             name="Temperature", | ||||||
|  |             icon="mdi:thermometer", | ||||||
|  |             state_class=SensorStateClass.MEASUREMENT, | ||||||
|  |             native_unit_of_measurement=UnitOfTemperature.CELSIUS, | ||||||
|  |         ), | ||||||
|  |         SensorEntityDescription(key="errors", name="Error", icon="mdi:math-log"), | ||||||
|  |     ), | ||||||
|  |     "DW": ( | ||||||
|  |         SensorEntityDescription( | ||||||
|  |             key="startProgram.ecoIndex", | ||||||
|  |             name="Eco Index", | ||||||
|  |             icon="mdi:sprout", | ||||||
|  |             state_class=SensorStateClass.MEASUREMENT, | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |         SensorEntityDescription( | ||||||
|  |             key="startProgram.waterEfficiency", | ||||||
|  |             name="Water Efficiency", | ||||||
|  |             icon="mdi:water", | ||||||
|  |             state_class=SensorStateClass.MEASUREMENT, | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |         SensorEntityDescription( | ||||||
|  |             key="startProgram.waterSaving", | ||||||
|  |             name="Water Saving", | ||||||
|  |             icon="mdi:water-percent", | ||||||
|  |             state_class=SensorStateClass.MEASUREMENT, | ||||||
|  |             native_unit_of_measurement=PERCENTAGE, | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |         SensorEntityDescription( | ||||||
|  |             key="startProgram.temp", | ||||||
|  |             name="Temperature", | ||||||
|  |             icon="mdi:thermometer", | ||||||
|  |             state_class=SensorStateClass.MEASUREMENT, | ||||||
|  |             native_unit_of_measurement=UnitOfTemperature.CELSIUS, | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |         SensorEntityDescription( | ||||||
|  |             key="startProgram.energyLabel", | ||||||
|  |             name="Energy Label", | ||||||
|  |             icon="mdi:lightning-bolt-circle", | ||||||
|  |             state_class=SensorStateClass.MEASUREMENT, | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |         SensorEntityDescription( | ||||||
|  |             key="startProgram.remainingTime", | ||||||
|  |             name="Time", | ||||||
|  |             icon="mdi:timer", | ||||||
|  |             state_class=SensorStateClass.MEASUREMENT, | ||||||
|  |             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |         SensorEntityDescription( | ||||||
|  |             key="machMode", | ||||||
|  |             name="Machine Status", | ||||||
|  |             icon="mdi:information", | ||||||
|  |             translation_key="mode_dw", | ||||||
|  |         ), | ||||||
|  |         SensorEntityDescription( | ||||||
|  |             key="errors", name="Error", icon="mdi:math-log", translation_key="errors" | ||||||
|  |         ), | ||||||
|  |         SensorEntityDescription( | ||||||
|  |             key="remainingTimeMM", | ||||||
|  |             name="Remaining Time", | ||||||
|  |             icon="mdi:timer", | ||||||
|  |             state_class=SensorStateClass.MEASUREMENT, | ||||||
|  |             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||||
|  |         ), | ||||||
|  |     ), | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: | async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: | ||||||
|     hon: HonConnection = hass.data[DOMAIN][entry.unique_id] |     hon: Hon = hass.data[DOMAIN][entry.unique_id] | ||||||
|     coordinators = hass.data[DOMAIN]["coordinators"] |     coordinators = hass.data[DOMAIN]["coordinators"] | ||||||
|     appliances = [] |     appliances = [] | ||||||
|     for device in hon.devices: |     for device in hon.appliances: | ||||||
|         if device.mac_address in coordinators: |         if device.unique_id in coordinators: | ||||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] |             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||||
|         else: |         else: | ||||||
|             coordinator = HonCoordinator(hass, device) |             coordinator = HonCoordinator(hass, device) | ||||||
|             hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator |             hass.data[DOMAIN]["coordinators"][device.unique_id] = coordinator | ||||||
|         await coordinator.async_config_entry_first_refresh() |         await coordinator.async_config_entry_first_refresh() | ||||||
|  |  | ||||||
|         if descriptions := SENSORS.get(device.appliance_type): |         if descriptions := SENSORS.get(device.appliance_type): | ||||||
|             for description in descriptions: |             for description in descriptions: | ||||||
|                 if not device.get(description.key): |                 if not device.get(description.key): | ||||||
|                     _LOGGER.warning("[%s] Can't setup %s", device.appliance_type, description.key) |                     _LOGGER.warning( | ||||||
|  |                         "[%s] Can't setup %s", device.appliance_type, description.key | ||||||
|  |                     ) | ||||||
|                     continue |                     continue | ||||||
|                 appliances.extend([ |                 appliances.extend( | ||||||
|                     HonSensorEntity(hass, coordinator, entry, device, description)] |                     [HonSensorEntity(hass, coordinator, entry, device, description)] | ||||||
|                 ) |                 ) | ||||||
|  |  | ||||||
|     async_add_entities(appliances) |     async_add_entities(appliances) | ||||||
|  | |||||||
| @ -1,13 +1,13 @@ | |||||||
| import logging | import logging | ||||||
|  |  | ||||||
| from dataclasses import dataclass | from dataclasses import dataclass | ||||||
| from typing import Any | from typing import Any | ||||||
|  |  | ||||||
| from homeassistant.components.switch import SwitchEntityDescription, SwitchEntity | from homeassistant.components.switch import SwitchEntityDescription, SwitchEntity | ||||||
| from homeassistant.config_entries import ConfigEntry | from homeassistant.config_entries import ConfigEntry | ||||||
| from homeassistant.const import EntityCategory | from homeassistant.const import EntityCategory | ||||||
| from pyhon import HonConnection | from pyhon import Hon | ||||||
| from pyhon.device import HonDevice | from pyhon.appliance import HonAppliance | ||||||
|  | from pyhon.parameter.range import HonParameterRange | ||||||
|  |  | ||||||
| from .const import DOMAIN | from .const import DOMAIN | ||||||
| from .hon import HonCoordinator, HonEntity | from .hon import HonCoordinator, HonEntity | ||||||
| @ -22,8 +22,8 @@ class HonSwitchEntityDescriptionMixin: | |||||||
|  |  | ||||||
|  |  | ||||||
| @dataclass | @dataclass | ||||||
| class HonSwitchEntityDescription(HonSwitchEntityDescriptionMixin, | class HonSwitchEntityDescription( | ||||||
|                                  SwitchEntityDescription |     HonSwitchEntityDescriptionMixin, SwitchEntityDescription | ||||||
| ): | ): | ||||||
|     pass |     pass | ||||||
|  |  | ||||||
| @ -48,13 +48,13 @@ SWITCHES: dict[str, tuple[HonSwitchEntityDescription, ...]] = { | |||||||
|             key="startProgram.delayStatus", |             key="startProgram.delayStatus", | ||||||
|             name="Delay Status", |             name="Delay Status", | ||||||
|             icon="mdi:timer-check", |             icon="mdi:timer-check", | ||||||
|             entity_category=EntityCategory.CONFIG |             entity_category=EntityCategory.CONFIG, | ||||||
|         ), |         ), | ||||||
|         HonSwitchEntityDescription( |         HonSwitchEntityDescription( | ||||||
|             key="startProgram.haier_SoakPrewashSelection", |             key="startProgram.haier_SoakPrewashSelection", | ||||||
|             name="Soak Prewash Selection", |             name="Soak Prewash Selection", | ||||||
|             icon="mdi:tshirt-crew", |             icon="mdi:tshirt-crew", | ||||||
|             entity_category=EntityCategory.CONFIG |             entity_category=EntityCategory.CONFIG, | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
|     "TD": ( |     "TD": ( | ||||||
| @ -89,29 +89,79 @@ SWITCHES: dict[str, tuple[HonSwitchEntityDescription, ...]] = { | |||||||
|             turn_off_key="resumeProgram", |             turn_off_key="resumeProgram", | ||||||
|         ), |         ), | ||||||
|     ), |     ), | ||||||
|  |     "DW": ( | ||||||
|  |         HonSwitchEntityDescription( | ||||||
|  |             key="active", | ||||||
|  |             name="Dish Washer", | ||||||
|  |             icon="mdi:dishwasher", | ||||||
|  |             turn_on_key="startProgram", | ||||||
|  |             turn_off_key="stopProgram", | ||||||
|  |         ), | ||||||
|  |         HonSwitchEntityDescription( | ||||||
|  |             key="startProgram.extraDry", | ||||||
|  |             name="Extra Dry", | ||||||
|  |             icon="mdi:hair-dryer", | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |         HonSwitchEntityDescription( | ||||||
|  |             key="startProgram.halfLoad", | ||||||
|  |             name="Half Load", | ||||||
|  |             icon="mdi:fraction-one-half", | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |         HonSwitchEntityDescription( | ||||||
|  |             key="startProgram.openDoor", | ||||||
|  |             name="Open Door", | ||||||
|  |             icon="mdi:door-open", | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |         HonSwitchEntityDescription( | ||||||
|  |             key="startProgram.threeInOne", | ||||||
|  |             name="Three in One", | ||||||
|  |             icon="mdi:numeric-3-box-outline", | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |         HonSwitchEntityDescription( | ||||||
|  |             key="startProgram.ecoExpress", | ||||||
|  |             name="Eco Express", | ||||||
|  |             icon="mdi:sprout", | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |         HonSwitchEntityDescription( | ||||||
|  |             key="startProgram.addDish", | ||||||
|  |             name="Add Dish", | ||||||
|  |             icon="mdi:silverware-fork-knife", | ||||||
|  |             entity_category=EntityCategory.CONFIG, | ||||||
|  |         ), | ||||||
|  |     ), | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: | async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: | ||||||
|     hon: HonConnection = hass.data[DOMAIN][entry.unique_id] |     hon: Hon = hass.data[DOMAIN][entry.unique_id] | ||||||
|     coordinators = hass.data[DOMAIN]["coordinators"] |     coordinators = hass.data[DOMAIN]["coordinators"] | ||||||
|     appliances = [] |     appliances = [] | ||||||
|     for device in hon.devices: |     for device in hon.appliances: | ||||||
|         if device.mac_address in coordinators: |         if device.unique_id in coordinators: | ||||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] |             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||||
|         else: |         else: | ||||||
|             coordinator = HonCoordinator(hass, device) |             coordinator = HonCoordinator(hass, device) | ||||||
|             hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator |             hass.data[DOMAIN]["coordinators"][device.unique_id] = coordinator | ||||||
|         await coordinator.async_config_entry_first_refresh() |         await coordinator.async_config_entry_first_refresh() | ||||||
|  |  | ||||||
|         if descriptions := SWITCHES.get(device.appliance_type): |         if descriptions := SWITCHES.get(device.appliance_type): | ||||||
|             for description in descriptions: |             for description in descriptions: | ||||||
|                 if device.get(description.key) is not None or device.commands.get(description.key) is not None: |                 if ( | ||||||
|                     appliances.extend([ |                     device.get(description.key) is not None | ||||||
|                         HonSwitchEntity(hass, coordinator, entry, device, description)] |                     or device.commands.get(description.key) is not None | ||||||
|  |                 ): | ||||||
|  |                     appliances.extend( | ||||||
|  |                         [HonSwitchEntity(hass, coordinator, entry, device, description)] | ||||||
|                     ) |                     ) | ||||||
|                 else: |                 else: | ||||||
|                     _LOGGER.warning("[%s] Can't setup %s", device.appliance_type, description.key) |                     _LOGGER.warning( | ||||||
|  |                         "[%s] Can't setup %s", device.appliance_type, description.key | ||||||
|  |                     ) | ||||||
|  |  | ||||||
|     async_add_entities(appliances) |     async_add_entities(appliances) | ||||||
|  |  | ||||||
| @ -119,30 +169,38 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | |||||||
| class HonSwitchEntity(HonEntity, SwitchEntity): | class HonSwitchEntity(HonEntity, SwitchEntity): | ||||||
|     entity_description: HonSwitchEntityDescription |     entity_description: HonSwitchEntityDescription | ||||||
|  |  | ||||||
|     def __init__(self, hass, coordinator, entry, device: HonDevice, description: HonSwitchEntityDescription) -> None: |     def __init__( | ||||||
|  |         self, | ||||||
|  |         hass, | ||||||
|  |         coordinator, | ||||||
|  |         entry, | ||||||
|  |         device: HonAppliance, | ||||||
|  |         description: HonSwitchEntityDescription, | ||||||
|  |     ) -> None: | ||||||
|         super().__init__(hass, entry, coordinator, device) |         super().__init__(hass, entry, coordinator, device) | ||||||
|         self._coordinator = coordinator |         self._coordinator = coordinator | ||||||
|         self._device = device |         self._device = device | ||||||
|         self.entity_description = description |         self.entity_description = description | ||||||
|         self._attr_unique_id = f"{super().unique_id}{description.key}" |         self._attr_unique_id = f"{super().unique_id}{description.key}" | ||||||
|  |  | ||||||
|     def available(self) -> bool: |  | ||||||
|         if self.entity_category == EntityCategory.CONFIG: |  | ||||||
|             return self._device.settings[self.entity_description.key].typology != "fixed" |  | ||||||
|         return True |  | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def is_on(self) -> bool | None: |     def is_on(self) -> bool | None: | ||||||
|         """Return True if entity is on.""" |         """Return True if entity is on.""" | ||||||
|         if self.entity_category == EntityCategory.CONFIG: |         if self.entity_category == EntityCategory.CONFIG: | ||||||
|             setting = self._device.settings[self.entity_description.key] |             setting = self._device.settings[self.entity_description.key] | ||||||
|             return setting.value == "1" or hasattr(setting, "min") and setting.value != setting.min |             return ( | ||||||
|  |                 setting.value == "1" | ||||||
|  |                 or hasattr(setting, "min") | ||||||
|  |                 and setting.value != setting.min | ||||||
|  |             ) | ||||||
|         return self._device.get(self.entity_description.key, False) |         return self._device.get(self.entity_description.key, False) | ||||||
|  |  | ||||||
|     async def async_turn_on(self, **kwargs: Any) -> None: |     async def async_turn_on(self, **kwargs: Any) -> None: | ||||||
|         if self.entity_category == EntityCategory.CONFIG: |         if self.entity_category == EntityCategory.CONFIG: | ||||||
|             setting = self._device.settings[self.entity_description.key] |             setting = self._device.settings[self.entity_description.key] | ||||||
|             setting.value = setting.max |             setting.value = ( | ||||||
|  |                 setting.max if isinstance(setting, HonParameterRange) else "1" | ||||||
|  |             ) | ||||||
|             self.async_write_ha_state() |             self.async_write_ha_state() | ||||||
|         else: |         else: | ||||||
|             await self._device.commands[self.entity_description.turn_on_key].send() |             await self._device.commands[self.entity_description.turn_on_key].send() | ||||||
| @ -150,7 +208,9 @@ class HonSwitchEntity(HonEntity, SwitchEntity): | |||||||
|     async def async_turn_off(self, **kwargs: Any) -> None: |     async def async_turn_off(self, **kwargs: Any) -> None: | ||||||
|         if self.entity_category == EntityCategory.CONFIG: |         if self.entity_category == EntityCategory.CONFIG: | ||||||
|             setting = self._device.settings[self.entity_description.key] |             setting = self._device.settings[self.entity_description.key] | ||||||
|             setting.value = setting.min |             setting.value = ( | ||||||
|  |                 setting.min if isinstance(setting, HonParameterRange) else "0" | ||||||
|  |             ) | ||||||
|             self.async_write_ha_state() |             self.async_write_ha_state() | ||||||
|         else: |         else: | ||||||
|             await self._device.commands[self.entity_description.turn_off_key].send() |             await self._device.commands[self.entity_description.turn_off_key].send() | ||||||
|  | |||||||
| @ -72,6 +72,16 @@ | |||||||
|           "13": "Ready to Store H-2", |           "13": "Ready to Store H-2", | ||||||
|           "14": "Extra Dry H-3" |           "14": "Extra Dry H-3" | ||||||
|         } |         } | ||||||
|  |       }, | ||||||
|  |       "mode_dw": { | ||||||
|  |         "state": { | ||||||
|  |           "0": "Disconnected", | ||||||
|  |           "1": "Ready", | ||||||
|  |           "2": "Running", | ||||||
|  |           "3": "Delayed start", | ||||||
|  |           "5": "Delayed start cancelled", | ||||||
|  |           "7": "Finished" | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "select": { |     "select": { | ||||||
| @ -402,7 +412,170 @@ | |||||||
|           "wool": "Wool", |           "wool": "Wool", | ||||||
|           "wool_and_delicates_49": "Wool and Delicates 49'", |           "wool_and_delicates_49": "Wool and Delicates 49'", | ||||||
|           "wool_dry": "Wool Dry", |           "wool_dry": "Wool Dry", | ||||||
|           "wool_soft_care": "Wool and Soft Car" |           "wool_soft_care": "Wool and Soft Car", | ||||||
|  |           "bakery": "Pasta and Pastries", | ||||||
|  |           "bakery_steam": "Steam-baked bread", | ||||||
|  |           "bottom_heating": "Bottom Heating", | ||||||
|  |           "bottom_heating_fan": "Bottom Heating + Fan", | ||||||
|  |           "bread": "Bread", | ||||||
|  |           "bread_steam": "Steam-baked pastries", | ||||||
|  |           "combi": "Combi", | ||||||
|  |           "convection_fan": "Convection + Fan", | ||||||
|  |           "convection_fan_turnspit": "Convection + Fan + Turnspit", | ||||||
|  |           "conventional": "Conventional", | ||||||
|  |           "conventional_turnspit": "Convection + Turnspit", | ||||||
|  |           "defrost": "Defrost", | ||||||
|  |           "descaling": "Descaling", | ||||||
|  |           "fish": "Fish", | ||||||
|  |           "fish_steam": "Steam-cooked fish", | ||||||
|  |           "grill_cata": "Grill", | ||||||
|  |           "grill_fan_cata": "Grill fan", | ||||||
|  |           "grill_fan_pyro": "Grill + Fan", | ||||||
|  |           "grill_pyro": "Grill", | ||||||
|  |           "h20_clean": "H2O-Clean", | ||||||
|  |           "iot_bread": "Bread", | ||||||
|  |           "iot_h20_clean": "h2O clean", | ||||||
|  |           "leavening": "Leavening", | ||||||
|  |           "light_fan": "Light Fan", | ||||||
|  |           "low_temp_cooking": "Low Temperature Cooking", | ||||||
|  |           "low_temp_cooking_fish": "Low Temperature Cooking - Fish", | ||||||
|  |           "low_temp_cooking_fish_steam": "Low Temperature Steam Cooking - Fish", | ||||||
|  |           "low_temp_cooking_meat": "Low Temperature Cooking - Meat", | ||||||
|  |           "low_temp_cooking_meat_steam": "Low Temperature Steam Cooking - Meat", | ||||||
|  |           "low_temp_cooking_steam": "Low Temperature Steam Cooking", | ||||||
|  |           "meat": "Meat", | ||||||
|  |           "meat_steam": "Steam-cooked meat", | ||||||
|  |           "multi_level": "Multi-Level", | ||||||
|  |           "paella": "Paella", | ||||||
|  |           "pasta_and_bakery": "Pasta and Bakery", | ||||||
|  |           "pizza": "Pizza", | ||||||
|  |           "pyrolysis": "Pyrolysis", | ||||||
|  |           "pyrolysis_plus": "Pyrolysis +", | ||||||
|  |           "red_meat": "Red Meat", | ||||||
|  |           "red_meat_steam": "Steam-cooked red meat", | ||||||
|  |           "regenerate": "Regeneration", | ||||||
|  |           "soft_plus": "Soft+", | ||||||
|  |           "super_grill": "Super Grill", | ||||||
|  |           "tailor_bake": "Tailor bake", | ||||||
|  |           "tailor_bake_cata": "Tailor Bake", | ||||||
|  |           "tailor_bake_pyro": "Tailor Bake", | ||||||
|  |           "vegetables": "Vegetables", | ||||||
|  |           "vegetables_cata": "Vegetables", | ||||||
|  |           "vegetables_pyro": "Vegetables", | ||||||
|  |           "water_discharge": "Water Drain", | ||||||
|  |           "white_meat": "White Meat", | ||||||
|  |           "white_meat_steam": "Steam-cooked white meat", | ||||||
|  |           "iot_standard_boiling": "Boiling", | ||||||
|  |           "iot_standard_frying": "Frying", | ||||||
|  |           "iot_standard_keep_warm": "Keep Warm", | ||||||
|  |           "iot_standard_melting": "Melting", | ||||||
|  |           "iot_standard_simmering": "Simmering" | ||||||
|  |         } | ||||||
|  |       }, | ||||||
|  |       "programs_dw": { | ||||||
|  |         "state": { | ||||||
|  |           "59_min": "Rapid 59'", | ||||||
|  |           "auto_care": "Auto Care", | ||||||
|  |           "auto_care_soil": "Auto Care", | ||||||
|  |           "auto_hygiene": "Auto Hygiene", | ||||||
|  |           "auto_plus": "AutoPlus", | ||||||
|  |           "auto_rapid": "Auto Rapid", | ||||||
|  |           "auto_sensor": "Auto Sensor", | ||||||
|  |           "auto_sensor_soil": "Auto Sensor", | ||||||
|  |           "auto_universal": "Auto Universal 50 - 60°C", | ||||||
|  |           "auto_universal_plus": "Auto Universal+ 65 - 75°C", | ||||||
|  |           "auto_universal_plus_soil": "Auto Universal+ 65 - 75°C", | ||||||
|  |           "auto_universal_soil": "Auto Universal 50 - 60°C", | ||||||
|  |           "auto_wash": "Auto Wash", | ||||||
|  |           "auto_wash_soil": "Auto Wash", | ||||||
|  |           "classe_a_59": "A Wash 59' 65°C", | ||||||
|  |           "delicate": "Delicate 45°C", | ||||||
|  |           "dishwasher_care": "Limescale cleaning", | ||||||
|  |           "eco": "Eco", | ||||||
|  |           "eco_asynch": "Eco 45°C", | ||||||
|  |           "eco_bldc": "Eco 45°C", | ||||||
|  |           "eco_synch": "Eco 45°C", | ||||||
|  |           "gentle_wash": "Gentle wash", | ||||||
|  |           "glass": "Glass", | ||||||
|  |           "glassware": "Glassware 45°C", | ||||||
|  |           "glass_care": "Glass Care", | ||||||
|  |           "hygiene": "Hygiene", | ||||||
|  |           "hygiene_plus": "Hygiene+ 75°C", | ||||||
|  |           "intensive": "Intensive", | ||||||
|  |           "intensive_rapid": "Intensive Rapid", | ||||||
|  |           "iot_auto_sensor": "Auto Sensor", | ||||||
|  |           "iot_auto_universal_soil": "Auto Universal 50 - 60°C", | ||||||
|  |           "iot_auto_wash_soil": "Auto Wash", | ||||||
|  |           "iot_baby_care": "Baby Care", | ||||||
|  |           "iot_breakfast": "Breakfast", | ||||||
|  |           "iot_checkup": "Check-Up", | ||||||
|  |           "iot_china_crystals": "China Crystals", | ||||||
|  |           "iot_classe_a_59": "Rapid 59'", | ||||||
|  |           "iot_cocktail_glasses": "Coktail Glasses", | ||||||
|  |           "iot_cocktail_glasses_soil": "Coktail Glasses", | ||||||
|  |           "iot_daily_care": "Daily Care", | ||||||
|  |           "iot_daily_care_soil": "Daily Care", | ||||||
|  |           "iot_delicate": "Delicate 45°C", | ||||||
|  |           "iot_dinner_for_two": "Dinner for 2", | ||||||
|  |           "iot_dinner_for_two_soil": "Dinner for 2", | ||||||
|  |           "iot_dreft_quick_cycle": "Dreft Quick", | ||||||
|  |           "iot_eco_asynch": "Eco 45°C", | ||||||
|  |           "iot_eco_bldc": "Eco 45°C", | ||||||
|  |           "iot_eco_synch": "Eco 45°C", | ||||||
|  |           "iot_extra_hygiene": "Extra Hygiene", | ||||||
|  |           "iot_fairy_quick_cycle": "Fairy Short", | ||||||
|  |           "iot_happy_hour": "Happy Hour", | ||||||
|  |           "iot_jar_quick_cycle": "Jar Quick", | ||||||
|  |           "iot_party": "Party", | ||||||
|  |           "iot_party_soil": "Party", | ||||||
|  |           "iot_pizza_menu": "Pizza Menu", | ||||||
|  |           "iot_pizza_menu_soil": "Pizza Menu", | ||||||
|  |           "iot_plastic_tupperware": "Plastic & Tupperware", | ||||||
|  |           "iot_porcelain": "Porcelain", | ||||||
|  |           "iot_pot_and_pans": "Pot & Pans", | ||||||
|  |           "iot_pot_and_pans_soil": "Pot & Pans", | ||||||
|  |           "iot_power_mix_wash": "Power Mix Wash", | ||||||
|  |           "iot_power_mix_wash_soil": "Power Mix Wash", | ||||||
|  |           "iot_prewash": "Pre-wash", | ||||||
|  |           "iot_pyrex_and_glassware": "Pyrex & Glassware", | ||||||
|  |           "iot_rapid_29": "Rapid 29'", | ||||||
|  |           "iot_rapid_39": "Rapid 39' 60°C", | ||||||
|  |           "iot_single": "Single", | ||||||
|  |           "iot_steam": "Steam 75°C", | ||||||
|  |           "iot_super_flash": "Super Flash", | ||||||
|  |           "iot_super_wash": "Super Wash", | ||||||
|  |           "iot_turbopower": "TurboPower", | ||||||
|  |           "iot_universal": "Universal 60°C", | ||||||
|  |           "iot_wok_grids_maxi_pans": "Special Pans (Wok, Grids & Maxi Pans)", | ||||||
|  |           "iot_wok_grids_maxi_pans_soil": "Special Pans (Wok, Grids & Maxi Pans)", | ||||||
|  |           "iot_yes_quick_cycle": "Yes Quick", | ||||||
|  |           "night": "Night 55°C", | ||||||
|  |           "prewash": "Pre-wash", | ||||||
|  |           "rapid_20": "Rapid 20'", | ||||||
|  |           "rapid_24": "Rapid 24'", | ||||||
|  |           "rapid_29": "Rapid 29' 50°C", | ||||||
|  |           "rapid_35": "Wash&Dry 35'", | ||||||
|  |           "rapid_39": "Rapid 39' 60°C", | ||||||
|  |           "rapid_49": "Rapid 49'", | ||||||
|  |           "rapid_59": "Rapid 59'", | ||||||
|  |           "sanitising": "Sanitising", | ||||||
|  |           "silence": "Silence", | ||||||
|  |           "silent": "Silent", | ||||||
|  |           "silent_care": "Silent Care", | ||||||
|  |           "smart_ai": "Smart AI", | ||||||
|  |           "smart_ai_pro": "Smart AI Pro", | ||||||
|  |           "smart_ai_rapid": "Smart AI Rapid", | ||||||
|  |           "special": "Special", | ||||||
|  |           "special_pw_prz": "Special", | ||||||
|  |           "steam": "Steam 75°C", | ||||||
|  |           "steam_plus": "Steam Plus 75°C", | ||||||
|  |           "total_care": "Total Care 50°C", | ||||||
|  |           "ultra_silence": "Ultra Silence 55°C", | ||||||
|  |           "ultra_silent": "Ultra Silent 55°C", | ||||||
|  |           "universal": "Universal 60°C", | ||||||
|  |           "universal_plus": "Universal Plus 70°C", | ||||||
|  |           "zone_wash": "Flex Zone Wash", | ||||||
|  |           "zoom_39": "Zoom 39 min" | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| { | { | ||||||
|     "name": "Haier hOn", |     "name": "Haier hOn", | ||||||
|     "render_readme": true, |     "homeassistant": "2023.2.0", | ||||||
|     "homeassistant": "2023.2.0" |     "zip_release": true, | ||||||
|  |     "filename": "haier_hon.zip" | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										38
									
								
								info.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								info.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | # Haier hOn | ||||||
|  | [](https://github.com/Andre0512/hon/releases/latest) | ||||||
|  |  | ||||||
|  | [](https://analytics.home-assistant.io/)   | ||||||
|  | Support for home appliances of Haier's mobile app hOn.  | ||||||
|  |  | ||||||
|  | ## Supported Appliances | ||||||
|  | - [Washing Machine](https://github.com/Andre0512/hon#washing-machine) | ||||||
|  | - [Tumble Dryer](https://github.com/Andre0512/hon#tumble-dryer) | ||||||
|  | - [Washer Dryer](https://github.com/Andre0512/hon#washer-dryer) | ||||||
|  | - [Oven](https://github.com/Andre0512/hon#oven) | ||||||
|  | - [Hob](https://github.com/Andre0512/hon#hob) | ||||||
|  | - [Dish Washer](https://github.com/Andre0512/hon#dish-washer) | ||||||
|  |  | ||||||
|  | ## Tested Appliances | ||||||
|  | - Haier WD90-B14TEAM5 | ||||||
|  | - Haier HD80-A3959 | ||||||
|  | - Haier HWO60SM2F3XH | ||||||
|  | - Hoover H-WASH 500 | ||||||
|  |  | ||||||
|  | ## Configuration | ||||||
|  |  | ||||||
|  | **Method 1**: [](https://my.home-assistant.io/redirect/config_flow_start/?domain=hon) | ||||||
|  |  | ||||||
|  | **Method 2**: Settings > Devices & Services > Add Integration > **Haier hOn**   | ||||||
|  | _If the integration is not in the list, you need to clear the browser cache._ | ||||||
|  |  | ||||||
|  | ## Contribute | ||||||
|  | Want to help us to support more appliances? Or add more sensors? Or help with translating? Or beautify some icons or captions?  | ||||||
|  | Check out the [project on GitHub](https://github.com/Andre0512/hon), every contribution is welcome! | ||||||
|  |  | ||||||
|  | ## Useful Links | ||||||
|  | * [GitHub repository](https://github.com/Andre0512/hon) (please add a star if you like this integration!) | ||||||
|  | * [pyhOn library](https://github.com/Andre0512/pyhOn) | ||||||
|  | * [Release notes](https://github.com/Andre0512/hon/releases) | ||||||
|  | * [Discussion and help](https://github.com/Andre0512/hon/discussions) | ||||||
|  | * [Issues](https://github.com/Andre0512/hon/issues) | ||||||
|  |  | ||||||
							
								
								
									
										56
									
								
								scripts/sensor_docs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								scripts/sensor_docs.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | |||||||
|  | from custom_components.hon.binary_sensor import BINARY_SENSORS | ||||||
|  | from custom_components.hon.button import BUTTONS | ||||||
|  | from custom_components.hon.number import NUMBERS | ||||||
|  | from custom_components.hon.select import SELECTS | ||||||
|  | from custom_components.hon.sensor import SENSORS | ||||||
|  | from custom_components.hon.switch import SWITCHES | ||||||
|  |  | ||||||
|  | APPLIANCES = { | ||||||
|  |     "AC": "Air conditioner", | ||||||
|  |     "AP": "Air purifier", | ||||||
|  |     "AS": "Air scanner", | ||||||
|  |     "DW": "Dish washer", | ||||||
|  |     "HO": "Hood", | ||||||
|  |     "IH": "Hob", | ||||||
|  |     "MW": "Microwave", | ||||||
|  |     "OV": "Oven", | ||||||
|  |     "REF": "Fridge", | ||||||
|  |     "RVC": "Robot vacuum cleaner", | ||||||
|  |     "TD": "Tumble dryer", | ||||||
|  |     "WC": "Wine Cellar", | ||||||
|  |     "WD": "Washer dryer", | ||||||
|  |     "WH": "Water Heater", | ||||||
|  |     "WM": "Washing machine", | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ENTITY_CATEGORY_SORT = ["control", "config", "sensor"] | ||||||
|  |  | ||||||
|  | entities = { | ||||||
|  |     "binary_sensor": BINARY_SENSORS, | ||||||
|  |     "button": BUTTONS, | ||||||
|  |     "number": NUMBERS, | ||||||
|  |     "select": SELECTS, | ||||||
|  |     "sensor": SENSORS, | ||||||
|  |     "switch": SWITCHES, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | result = {} | ||||||
|  | for entity_type, appliances in entities.items(): | ||||||
|  |     for appliance, data in appliances.items(): | ||||||
|  |         for entity in data: | ||||||
|  |             attributes = (entity.key, entity.name, entity.icon, entity_type) | ||||||
|  |             category = "control" if entity_type in ["switch", "button"] else "sensor" | ||||||
|  |             result.setdefault(appliance, {}).setdefault( | ||||||
|  |                 entity.entity_category or category, [] | ||||||
|  |             ).append(attributes) | ||||||
|  |  | ||||||
|  | for appliance, categories in sorted(result.items()): | ||||||
|  |     print(f"### {APPLIANCES[appliance]}") | ||||||
|  |     categories = {k: categories[k] for k in ENTITY_CATEGORY_SORT if k in categories} | ||||||
|  |     for category, data in categories.items(): | ||||||
|  |         print(f"#### {str(category).capitalize()}s") | ||||||
|  |         print("| Name | Icon | Entity | Key |") | ||||||
|  |         print("| --- | --- | --- | --- |") | ||||||
|  |         for key, name, icon, entity_type in sorted(data, key=lambda d: d[1]): | ||||||
|  |             icon = f"`{icon}`" if icon else "" | ||||||
|  |             print(f"| {name} | {icon} | `{entity_type}` | `{key}` |") | ||||||
		Reference in New Issue
	
	Block a user
	