Compare commits
	
		
			12 Commits
		
	
	
		
			v0.5.0-bet
			...
			v0.6.0-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| d91b3edb40 | |||
| 83c5e3479e | |||
| b0cd020941 | |||
| 593842144a | |||
| 9a6e1155f9 | |||
| 365a3af171 | |||
| 6de6ff375c | |||
| 89d2fd4af1 | |||
| 92add01a59 | |||
| 4a685e67e0 | |||
| 6303843116 | |||
| 907bc44533 | 
							
								
								
									
										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 | ||||
							
								
								
									
										27
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								README.md
									
									
									
									
									
								
							| @ -9,6 +9,7 @@ Home Assistant integration for Haier hOn: support for Haier/Candy/Hoover home ap | ||||
| - Washer Dryer | ||||
| - Washing Machine | ||||
| - Oven | ||||
| - Hob | ||||
|  | ||||
| ## Installation | ||||
| **Method 1:** [](https://my.home-assistant.io/redirect/hacs_repository/?owner=Andre0512&repository=hon&category=integration) | ||||
| @ -26,14 +27,34 @@ _Restart Home Assistant_ | ||||
| **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 | ||||
| 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) | ||||
|    ```commandline | ||||
|     $ pip install pyhOn | ||||
|     ``` | ||||
| 2. Use the commandline tool to read out all appliance data from your account | ||||
| 2. Use the command line tool to read out all appliance data from your account | ||||
|     ```commandline | ||||
|     $ pyhOn | ||||
|     User for hOn account: user.name@example.com | ||||
| @ -70,7 +91,7 @@ Any kind of contribution is welcome! | ||||
| #### Tips and Tricks | ||||
| - 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. | ||||
| - Use [pyhOn](https://github.com/Andre0512/pyhOn)s translate command to read out the official translations  | ||||
| - Use [pyhOn's translate command](https://github.com/Andre0512/pyhOn#translation) to read out the official translations  | ||||
|  | ||||
| ## Tested Devices | ||||
| - Haier WD90-B14TEAM5 | ||||
|  | ||||
| @ -28,7 +28,9 @@ CONFIG_SCHEMA = vol.Schema( | ||||
|  | ||||
| async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): | ||||
|     session = aiohttp_client.async_get_clientsession(hass) | ||||
|     hon = await Hon(entry.data["email"], entry.data["password"], session=session).create() | ||||
|     hon = await Hon( | ||||
|         entry.data["email"], entry.data["password"], session=session | ||||
|     ).create() | ||||
|     hass.data.setdefault(DOMAIN, {}) | ||||
|     hass.data[DOMAIN][entry.unique_id] = hon | ||||
|     hass.data[DOMAIN]["coordinators"] = {} | ||||
|  | ||||
| @ -3,8 +3,11 @@ from dataclasses import dataclass | ||||
|  | ||||
| from pyhon import Hon | ||||
|  | ||||
| from homeassistant.components.binary_sensor import BinarySensorEntityDescription, BinarySensorDeviceClass, \ | ||||
|     BinarySensorEntity | ||||
| from homeassistant.components.binary_sensor import ( | ||||
|     BinarySensorEntityDescription, | ||||
|     BinarySensorDeviceClass, | ||||
|     BinarySensorEntity, | ||||
| ) | ||||
| from homeassistant.config_entries import ConfigEntry | ||||
| from homeassistant.core import callback | ||||
| from .const import DOMAIN | ||||
| @ -19,7 +22,9 @@ class HonBinarySensorEntityDescriptionMixin: | ||||
|  | ||||
|  | ||||
| @dataclass | ||||
| class HonBinarySensorEntityDescription(HonBinarySensorEntityDescriptionMixin, BinarySensorEntityDescription): | ||||
| class HonBinarySensorEntityDescription( | ||||
|     HonBinarySensorEntityDescriptionMixin, BinarySensorEntityDescription | ||||
| ): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| @ -30,7 +35,7 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = { | ||||
|             name="Remote Control", | ||||
|             device_class=BinarySensorDeviceClass.CONNECTIVITY, | ||||
|             on_value="CONNECTED", | ||||
|             icon="mdi:remote" | ||||
|             icon="mdi:remote", | ||||
|         ), | ||||
|         HonBinarySensorEntityDescription( | ||||
|             key="doorLockStatus", | ||||
| @ -65,7 +70,7 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = { | ||||
|             name="Remote Control", | ||||
|             device_class=BinarySensorDeviceClass.CONNECTIVITY, | ||||
|             on_value="CONNECTED", | ||||
|             icon="mdi:remote" | ||||
|             icon="mdi:remote", | ||||
|         ), | ||||
|         HonBinarySensorEntityDescription( | ||||
|             key="startProgram.prewash", | ||||
| @ -99,24 +104,59 @@ BINARY_SENSORS: dict[str, tuple[HonBinarySensorEntityDescription, ...]] = { | ||||
|     "OV": ( | ||||
|         HonBinarySensorEntityDescription( | ||||
|             key="attributes.lastConnEvent.category", | ||||
|             name="Online", | ||||
|             name="Connection", | ||||
|             device_class=BinarySensorDeviceClass.CONNECTIVITY, | ||||
|             on_value="CONNECTED", | ||||
|             icon="mdi:wifi" | ||||
|             icon="mdi:wifi", | ||||
|         ), | ||||
|         HonBinarySensorEntityDescription( | ||||
|             key="attributes.parameters.remoteCtrValid", | ||||
|             name="On", | ||||
|             name="Remote Control", | ||||
|             device_class=BinarySensorDeviceClass.CONNECTIVITY, | ||||
|             on_value="1", | ||||
|             icon="mdi:remote" | ||||
|             icon="mdi:remote", | ||||
|         ), | ||||
|         HonBinarySensorEntityDescription( | ||||
|             key="attributes.parameters.onOffStatus", | ||||
|             name="On", | ||||
|             device_class=BinarySensorDeviceClass.RUNNING, | ||||
|             on_value="1", | ||||
|             icon="mdi:power-cycle" | ||||
|             icon="mdi:power-cycle", | ||||
|         ), | ||||
|     ), | ||||
|     "IV": ( | ||||
|         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", | ||||
|         ), | ||||
|     ), | ||||
| } | ||||
| @ -127,20 +167,26 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
|     coordinators = hass.data[DOMAIN]["coordinators"] | ||||
|     appliances = [] | ||||
|     for device in hon.appliances: | ||||
|         if device.mac_address in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] | ||||
|         if device.unique_id in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||
|         else: | ||||
|             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() | ||||
|  | ||||
|         if descriptions := BINARY_SENSORS.get(device.appliance_type): | ||||
|             for description in descriptions: | ||||
|                 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 | ||||
|                 appliances.extend([ | ||||
|                     HonBinarySensorEntity(hass, coordinator, entry, device, description)] | ||||
|                 appliances.extend( | ||||
|                     [ | ||||
|                         HonBinarySensorEntity( | ||||
|                             hass, coordinator, entry, device, description | ||||
|                         ) | ||||
|                     ] | ||||
|                 ) | ||||
|  | ||||
|     async_add_entities(appliances) | ||||
| @ -159,9 +205,15 @@ class HonBinarySensorEntity(HonEntity, BinarySensorEntity): | ||||
|  | ||||
|     @property | ||||
|     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 | ||||
|     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() | ||||
|  | ||||
| @ -1,11 +1,18 @@ | ||||
| import logging | ||||
| import urllib | ||||
| from urllib.parse import quote | ||||
|  | ||||
| from homeassistant.components.button import ButtonEntityDescription, ButtonEntity | ||||
| from homeassistant.config_entries import ConfigEntry | ||||
| from pyhon import Hon | ||||
| from pyhon.appliance import HonAppliance | ||||
|  | ||||
| from homeassistant.const import EntityCategory | ||||
| from .const import DOMAIN | ||||
| from .hon import HonCoordinator, HonEntity | ||||
|  | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
|  | ||||
| BUTTONS: dict[str, tuple[ButtonEntityDescription, ...]] = { | ||||
|     "OV": ( | ||||
|         ButtonEntityDescription( | ||||
| @ -18,7 +25,14 @@ BUTTONS: dict[str, tuple[ButtonEntityDescription, ...]] = { | ||||
|             name="Stop Program", | ||||
|             icon="mdi:power-off", | ||||
|         ), | ||||
|     ) | ||||
|     ), | ||||
|     "IV": ( | ||||
|         ButtonEntityDescription( | ||||
|             key="startProgram", | ||||
|             name="Start Program", | ||||
|             icon="mdi:pot-steam", | ||||
|         ), | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -27,26 +41,29 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
|     coordinators = hass.data[DOMAIN]["coordinators"] | ||||
|     appliances = [] | ||||
|     for device in hon.appliances: | ||||
|         if device.mac_address in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] | ||||
|         if device.unique_id in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||
|         else: | ||||
|             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() | ||||
|  | ||||
|         if descriptions := BUTTONS.get(device.appliance_type): | ||||
|             for description in descriptions: | ||||
|                 if not device.commands.get(description.key): | ||||
|                     continue | ||||
|                 appliances.extend([ | ||||
|                     HonButtonEntity(hass, coordinator, entry, device, description)] | ||||
|                 appliances.extend( | ||||
|                     [HonButtonEntity(hass, coordinator, entry, device, description)] | ||||
|                 ) | ||||
|         appliances.extend([HonFeatureRequestButton(hass, coordinator, entry, device)]) | ||||
|  | ||||
|     async_add_entities(appliances) | ||||
|  | ||||
|  | ||||
| class HonButtonEntity(HonEntity, ButtonEntity): | ||||
|     def __init__(self, hass, coordinator, entry, device: HonAppliance, description) -> None: | ||||
|     def __init__( | ||||
|         self, hass, coordinator, entry, device: HonAppliance, description | ||||
|     ) -> None: | ||||
|         super().__init__(hass, entry, coordinator, device) | ||||
|  | ||||
|         self._coordinator = coordinator | ||||
| @ -56,3 +73,18 @@ class HonButtonEntity(HonEntity, ButtonEntity): | ||||
|  | ||||
|     async def async_press(self) -> None: | ||||
|         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): | ||||
|         if user_input is None: | ||||
|             return self.async_show_form(step_id="user", data_schema=vol.Schema( | ||||
|                 {vol.Required(CONF_EMAIL): str, vol.Required(CONF_PASSWORD): str})) | ||||
|             return self.async_show_form( | ||||
|                 step_id="user", | ||||
|                 data_schema=vol.Schema( | ||||
|                     {vol.Required(CONF_EMAIL): str, vol.Required(CONF_PASSWORD): str} | ||||
|                 ), | ||||
|             ) | ||||
|  | ||||
|         self._email = user_input[CONF_EMAIL] | ||||
|         self._password = user_input[CONF_PASSWORD] | ||||
|  | ||||
| @ -22,14 +22,16 @@ class HonEntity(CoordinatorEntity): | ||||
|         self._hass = hass | ||||
|         self._device = device | ||||
|  | ||||
|         self._attr_unique_id = self._device.mac_address | ||||
|         self._attr_unique_id = self._device.unique_id | ||||
|  | ||||
|     @property | ||||
|     def device_info(self): | ||||
|         return DeviceInfo( | ||||
|             identifiers={(DOMAIN, self._device.mac_address)}, | ||||
|             identifiers={(DOMAIN, self._device.unique_id)}, | ||||
|             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, | ||||
|             sw_version=self._device.get("fwVersion", ""), | ||||
|         ) | ||||
| @ -38,7 +40,12 @@ class HonEntity(CoordinatorEntity): | ||||
| class HonCoordinator(DataUpdateCoordinator): | ||||
|     def __init__(self, hass, device: HonAppliance): | ||||
|         """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 | ||||
|  | ||||
|     async def _async_update_data(self): | ||||
|  | ||||
| @ -6,7 +6,6 @@ | ||||
|   "documentation": "https://github.com/Andre0512/hon/", | ||||
|   "iot_class": "cloud_polling", | ||||
|   "issue_tracker": "https://github.com/Andre0512/hon/issues", | ||||
|   "requirements": ["pyhOn==0.6.2"], | ||||
|   "version": "0.5.0-beta.3" | ||||
|   "requirements": ["pyhOn==0.8.0b3"], | ||||
|   "version": "0.6.0-beta.2" | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -22,20 +22,20 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { | ||||
|             name="Delay Time", | ||||
|             icon="mdi:timer-plus", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||
|         ), | ||||
|         NumberEntityDescription( | ||||
|             key="startProgram.rinseIterations", | ||||
|             name="Rinse Iterations", | ||||
|             icon="mdi:rotate-right", | ||||
|             entity_category=EntityCategory.CONFIG | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         NumberEntityDescription( | ||||
|             key="startProgram.mainWashTime", | ||||
|             name="Main Wash Time", | ||||
|             icon="mdi:clock-start", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||
|         ), | ||||
|     ), | ||||
|     "TD": ( | ||||
| @ -44,34 +44,34 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { | ||||
|             name="Delay time", | ||||
|             icon="mdi:timer-plus", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||
|         ), | ||||
|         NumberEntityDescription( | ||||
|             key="startProgram.dryLevel", | ||||
|             name="Dry level", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             icon="mdi:hair-dryer", | ||||
|             translation_key="tumbledryerdrylevel" | ||||
|             translation_key="tumbledryerdrylevel", | ||||
|         ), | ||||
|         NumberEntityDescription( | ||||
|             key="startProgram.tempLevel", | ||||
|             name="Temperature level", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             icon="mdi:thermometer", | ||||
|             translation_key="tumbledryertemplevel" | ||||
|             translation_key="tumbledryertemplevel", | ||||
|         ), | ||||
|         NumberEntityDescription( | ||||
|             key="startProgram.antiCreaseTime", | ||||
|             name="Anti-Crease time", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             icon="mdi:timer", | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||
|         ), | ||||
|         NumberEntityDescription( | ||||
|             key="startProgram.sterilizationStatus", | ||||
|             name="Sterilization status", | ||||
|             icon="mdi:clock-start", | ||||
|             entity_category=EntityCategory.CONFIG | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|     ), | ||||
|     "WD": ( | ||||
| @ -80,7 +80,7 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { | ||||
|             name="Delay Time", | ||||
|             icon="mdi:timer-plus", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||
|         ), | ||||
|     ), | ||||
|     "OV": ( | ||||
| @ -89,22 +89,35 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { | ||||
|             name="Delay time", | ||||
|             icon="mdi:timer-plus", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||
|         ), | ||||
|         NumberEntityDescription( | ||||
|             key="startProgram.tempSel", | ||||
|             name="Target Temperature", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             icon="mdi:thermometer", | ||||
|             native_unit_of_measurement=UnitOfTemperature.CELSIUS | ||||
|             native_unit_of_measurement=UnitOfTemperature.CELSIUS, | ||||
|         ), | ||||
|  | ||||
|         NumberEntityDescription( | ||||
|             key="startProgram.prTime", | ||||
|             name="Program Duration", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             icon="mdi:timelapse", | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||
|         ), | ||||
|     ), | ||||
|     "IV": ( | ||||
|         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", | ||||
|         ), | ||||
|     ), | ||||
| } | ||||
| @ -115,19 +128,19 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
|     coordinators = hass.data[DOMAIN]["coordinators"] | ||||
|     appliances = [] | ||||
|     for device in hon.appliances: | ||||
|         if device.mac_address in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] | ||||
|         if device.unique_id in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||
|         else: | ||||
|             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() | ||||
|  | ||||
|         if descriptions := NUMBERS.get(device.appliance_type): | ||||
|             for description in descriptions: | ||||
|                 if not device.settings.get(description.key): | ||||
|                     continue | ||||
|                 appliances.extend([ | ||||
|                     HonNumberEntity(hass, coordinator, entry, device, description)] | ||||
|                 appliances.extend( | ||||
|                     [HonNumberEntity(hass, coordinator, entry, device, description)] | ||||
|                 ) | ||||
|  | ||||
|     async_add_entities(appliances) | ||||
|  | ||||
| @ -24,20 +24,20 @@ SELECTS = { | ||||
|             name="Spin speed", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             icon="mdi:numeric", | ||||
|             unit_of_measurement=REVOLUTIONS_PER_MINUTE | ||||
|             unit_of_measurement=REVOLUTIONS_PER_MINUTE, | ||||
|         ), | ||||
|         SelectEntityDescription( | ||||
|             key="startProgram.temp", | ||||
|             name="Temperature", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             icon="mdi:thermometer", | ||||
|             unit_of_measurement=UnitOfTemperature.CELSIUS | ||||
|             unit_of_measurement=UnitOfTemperature.CELSIUS, | ||||
|         ), | ||||
|         SelectEntityDescription( | ||||
|             key="startProgram.program", | ||||
|             name="Program", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             translation_key="programs" | ||||
|             translation_key="programs", | ||||
|         ), | ||||
|     ), | ||||
|     "TD": ( | ||||
| @ -45,14 +45,14 @@ SELECTS = { | ||||
|             key="startProgram.program", | ||||
|             name="Program", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             translation_key="programs" | ||||
|             translation_key="programs", | ||||
|         ), | ||||
|         SelectEntityDescription( | ||||
|             key="startProgram.dryTimeMM", | ||||
|             name="Time", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             icon="mdi:timer", | ||||
|             unit_of_measurement=UnitOfTime.MINUTES | ||||
|             unit_of_measurement=UnitOfTime.MINUTES, | ||||
|         ), | ||||
|     ), | ||||
|     "WD": ( | ||||
| @ -60,7 +60,7 @@ SELECTS = { | ||||
|             key="startProgram.program", | ||||
|             name="Program", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             translation_key="programs" | ||||
|             translation_key="programs", | ||||
|         ), | ||||
|     ), | ||||
|     "OV": ( | ||||
| @ -68,11 +68,20 @@ SELECTS = { | ||||
|             key="startProgram.program", | ||||
|             name="Program", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             translation_key="programs", | ||||
|         ), | ||||
|         SelectEntityDescription( | ||||
|             key="startProgram.preheatStatus", | ||||
|             name="Preheat", | ||||
|             entity_category=EntityCategory.CONFIG | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|     ), | ||||
|     "IV": ( | ||||
|         SelectEntityDescription( | ||||
|             key="startProgram.program", | ||||
|             name="Program", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             translation_key="programs", | ||||
|         ), | ||||
|     ), | ||||
| } | ||||
| @ -83,25 +92,27 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
|     coordinators = hass.data[DOMAIN]["coordinators"] | ||||
|     appliances = [] | ||||
|     for device in hon.appliances: | ||||
|         if device.mac_address in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] | ||||
|         if device.unique_id in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||
|         else: | ||||
|             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() | ||||
|  | ||||
|         if descriptions := SELECTS.get(device.appliance_type): | ||||
|             for description in descriptions: | ||||
|                 if not device.settings.get(description.key): | ||||
|                     continue | ||||
|                 appliances.extend([ | ||||
|                     HonSelectEntity(hass, coordinator, entry, device, description)] | ||||
|                 appliances.extend( | ||||
|                     [HonSelectEntity(hass, coordinator, entry, device, description)] | ||||
|                 ) | ||||
|     async_add_entities(appliances) | ||||
|  | ||||
|  | ||||
| class HonSelectEntity(HonEntity, SelectEntity): | ||||
|     def __init__(self, hass, coordinator, entry, device: HonAppliance, description) -> None: | ||||
|     def __init__( | ||||
|         self, hass, coordinator, entry, device: HonAppliance, description | ||||
|     ) -> None: | ||||
|         super().__init__(hass, entry, coordinator, device) | ||||
|  | ||||
|         self._coordinator = coordinator | ||||
| @ -128,7 +139,9 @@ class HonSelectEntity(HonEntity, SelectEntity): | ||||
|     @callback | ||||
|     def _handle_coordinator_update(self): | ||||
|         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 | ||||
|         else: | ||||
|             self._attr_options = [setting.value] | ||||
|  | ||||
| @ -16,7 +16,7 @@ from homeassistant.const import ( | ||||
|     UnitOfMass, | ||||
|     UnitOfPower, | ||||
|     UnitOfTime, | ||||
|     UnitOfTemperature | ||||
|     UnitOfTemperature, | ||||
| ) | ||||
| from homeassistant.core import callback | ||||
| from homeassistant.helpers.entity import EntityCategory | ||||
| @ -34,20 +34,20 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | ||||
|             name="Total Power", | ||||
|             device_class=SensorDeviceClass.ENERGY, | ||||
|             state_class=SensorStateClass.TOTAL_INCREASING, | ||||
|             native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR | ||||
|             native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR, | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="totalWaterUsed", | ||||
|             name="Total Water", | ||||
|             device_class=SensorDeviceClass.WATER, | ||||
|             state_class=SensorStateClass.TOTAL_INCREASING, | ||||
|             native_unit_of_measurement=UnitOfVolume.LITERS | ||||
|             native_unit_of_measurement=UnitOfVolume.LITERS, | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="totalWashCycle", | ||||
|             name="Total Wash Cycle", | ||||
|             state_class=SensorStateClass.TOTAL_INCREASING, | ||||
|             icon="mdi:counter" | ||||
|             icon="mdi:counter", | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="currentElectricityUsed", | ||||
| @ -55,13 +55,13 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | ||||
|             state_class=SensorStateClass.MEASUREMENT, | ||||
|             device_class=SensorDeviceClass.POWER, | ||||
|             native_unit_of_measurement=UnitOfPower.KILO_WATT, | ||||
|             icon="mdi:lightning-bolt" | ||||
|             icon="mdi:lightning-bolt", | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="currentWaterUsed", | ||||
|             name="Current Water Used", | ||||
|             state_class=SensorStateClass.MEASUREMENT, | ||||
|             icon="mdi:water" | ||||
|             icon="mdi:water", | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="startProgram.weight", | ||||
| @ -69,19 +69,16 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | ||||
|             state_class=SensorStateClass.MEASUREMENT, | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|             native_unit_of_measurement=UnitOfMass.KILOGRAMS, | ||||
|             icon="mdi:weight-kilogram" | ||||
|             icon="mdi:weight-kilogram", | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="machMode", | ||||
|             name="Machine Status", | ||||
|             icon="mdi:information", | ||||
|             translation_key="mode" | ||||
|             translation_key="mode", | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="errors", | ||||
|             name="Error", | ||||
|             icon="mdi:math-log", | ||||
|             translation_key="errors" | ||||
|             key="errors", name="Error", icon="mdi:math-log", translation_key="errors" | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="remainingTimeMM", | ||||
| @ -103,13 +100,10 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | ||||
|             key="machMode", | ||||
|             name="Machine Status", | ||||
|             icon="mdi:information", | ||||
|             translation_key="mode" | ||||
|             translation_key="mode", | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="errors", | ||||
|             name="Error", | ||||
|             icon="mdi:math-log", | ||||
|             translation_key="errors" | ||||
|             key="errors", name="Error", icon="mdi:math-log", translation_key="errors" | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="remainingTimeMM", | ||||
| @ -129,25 +123,25 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | ||||
|             key="prCode", | ||||
|             name="Program", | ||||
|             icon="mdi:tumble-dryer", | ||||
|             translation_key="tumbledryerprogram" | ||||
|             translation_key="tumbledryerprogram", | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="prPhase", | ||||
|             name="Program Phase", | ||||
|             icon="mdi:tumble-dryer", | ||||
|             translation_key="tumbledryerprogramphase" | ||||
|             translation_key="tumbledryerprogramphase", | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="dryLevel", | ||||
|             name="Dry level", | ||||
|             icon="mdi:hair-dryer", | ||||
|             translation_key="tumbledryerdrylevel" | ||||
|             translation_key="tumbledryerdrylevel", | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="tempLevel", | ||||
|             name="Temperature level", | ||||
|             icon="mdi:thermometer", | ||||
|             translation_key="tumbledryertemplevel" | ||||
|             translation_key="tumbledryertemplevel", | ||||
|         ), | ||||
|     ), | ||||
|     "WD": ( | ||||
| @ -155,7 +149,7 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | ||||
|             key="machMode", | ||||
|             name="Machine Status", | ||||
|             icon="mdi:information", | ||||
|             translation_key="mode" | ||||
|             translation_key="mode", | ||||
|         ), | ||||
|         SensorEntityDescription( | ||||
|             key="spinSpeed", | ||||
| @ -227,6 +221,22 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | ||||
|             icon="mdi:thermometer", | ||||
|         ), | ||||
|     ), | ||||
|     "IV": ( | ||||
|         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"), | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -235,20 +245,22 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
|     coordinators = hass.data[DOMAIN]["coordinators"] | ||||
|     appliances = [] | ||||
|     for device in hon.appliances: | ||||
|         if device.mac_address in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] | ||||
|         if device.unique_id in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||
|         else: | ||||
|             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() | ||||
|  | ||||
|         if descriptions := SENSORS.get(device.appliance_type): | ||||
|             for description in descriptions: | ||||
|                 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 | ||||
|                 appliances.extend([ | ||||
|                     HonSensorEntity(hass, coordinator, entry, device, description)] | ||||
|                 appliances.extend( | ||||
|                     [HonSensorEntity(hass, coordinator, entry, device, description)] | ||||
|                 ) | ||||
|  | ||||
|     async_add_entities(appliances) | ||||
|  | ||||
| @ -22,9 +22,9 @@ class HonSwitchEntityDescriptionMixin: | ||||
|  | ||||
|  | ||||
| @dataclass | ||||
| class HonSwitchEntityDescription(HonSwitchEntityDescriptionMixin, | ||||
|                                  SwitchEntityDescription | ||||
|                                  ): | ||||
| class HonSwitchEntityDescription( | ||||
|     HonSwitchEntityDescriptionMixin, SwitchEntityDescription | ||||
| ): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| @ -48,13 +48,13 @@ SWITCHES: dict[str, tuple[HonSwitchEntityDescription, ...]] = { | ||||
|             key="startProgram.delayStatus", | ||||
|             name="Delay Status", | ||||
|             icon="mdi:timer-check", | ||||
|             entity_category=EntityCategory.CONFIG | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         HonSwitchEntityDescription( | ||||
|             key="startProgram.haier_SoakPrewashSelection", | ||||
|             name="Soak Prewash Selection", | ||||
|             icon="mdi:tshirt-crew", | ||||
|             entity_category=EntityCategory.CONFIG | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|     ), | ||||
|     "TD": ( | ||||
| @ -97,21 +97,26 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
|     coordinators = hass.data[DOMAIN]["coordinators"] | ||||
|     appliances = [] | ||||
|     for device in hon.appliances: | ||||
|         if device.mac_address in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] | ||||
|         if device.unique_id in coordinators: | ||||
|             coordinator = hass.data[DOMAIN]["coordinators"][device.unique_id] | ||||
|         else: | ||||
|             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() | ||||
|  | ||||
|         if descriptions := SWITCHES.get(device.appliance_type): | ||||
|             for description in descriptions: | ||||
|                 if device.get(description.key) is not None or device.commands.get(description.key) is not None: | ||||
|                     appliances.extend([ | ||||
|                         HonSwitchEntity(hass, coordinator, entry, device, description)] | ||||
|                 if ( | ||||
|                     device.get(description.key) is not None | ||||
|                     or device.commands.get(description.key) is not None | ||||
|                 ): | ||||
|                     appliances.extend( | ||||
|                         [HonSwitchEntity(hass, coordinator, entry, device, description)] | ||||
|                     ) | ||||
|                 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) | ||||
|  | ||||
| @ -119,7 +124,14 @@ async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> Non | ||||
| class HonSwitchEntity(HonEntity, SwitchEntity): | ||||
|     entity_description: HonSwitchEntityDescription | ||||
|  | ||||
|     def __init__(self, hass, coordinator, entry, device: HonAppliance, description: HonSwitchEntityDescription) -> None: | ||||
|     def __init__( | ||||
|         self, | ||||
|         hass, | ||||
|         coordinator, | ||||
|         entry, | ||||
|         device: HonAppliance, | ||||
|         description: HonSwitchEntityDescription, | ||||
|     ) -> None: | ||||
|         super().__init__(hass, entry, coordinator, device) | ||||
|         self._coordinator = coordinator | ||||
|         self._device = device | ||||
| @ -128,7 +140,9 @@ class HonSwitchEntity(HonEntity, SwitchEntity): | ||||
|  | ||||
|     def available(self) -> bool: | ||||
|         if self.entity_category == EntityCategory.CONFIG: | ||||
|             return self._device.settings[self.entity_description.key].typology != "fixed" | ||||
|             return ( | ||||
|                 self._device.settings[self.entity_description.key].typology != "fixed" | ||||
|             ) | ||||
|         return True | ||||
|  | ||||
|     @property | ||||
| @ -136,7 +150,11 @@ class HonSwitchEntity(HonEntity, SwitchEntity): | ||||
|         """Return True if entity is on.""" | ||||
|         if self.entity_category == EntityCategory.CONFIG: | ||||
|             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) | ||||
|  | ||||
|     async def async_turn_on(self, **kwargs: Any) -> None: | ||||
|  | ||||
| @ -402,7 +402,64 @@ | ||||
|           "wool": "Wool", | ||||
|           "wool_and_delicates_49": "Wool and Delicates 49'", | ||||
|           "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" | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
| @ -1,4 +1,6 @@ | ||||
| { | ||||
|     "name": "Haier hOn", | ||||
|     "homeassistant": "2023.2.0" | ||||
|     "homeassistant": "2023.2.0", | ||||
|     "zip_release": true, | ||||
|     "filename": "haier_hon.zip" | ||||
| } | ||||
|  | ||||
							
								
								
									
										12
									
								
								info.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								info.md
									
									
									
									
									
								
							| @ -9,6 +9,7 @@ Support for home appliances of Haier's mobile app hOn. | ||||
| - Washer Dryer | ||||
| - Washing Machine | ||||
| - Oven | ||||
| - Hob | ||||
|  | ||||
| ## Tested Appliances | ||||
| - Haier WD90-B14TEAM5 | ||||
| @ -26,11 +27,14 @@ _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?  | ||||
| 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) | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	