Compare commits
	
		
			10 Commits
		
	
	
		
			v0.8.0-bet
			...
			v0.8
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4b95accf02 | |||
| 998b691a8e | |||
| 6084e4a034 | |||
| 5bc3120000 | |||
| 0f9f0dee4c | |||
| 80b3741f2f | |||
| c433714a94 | |||
| 228cf3cf73 | |||
| 1a50e8112d | |||
| 57ecd7c3a5 | 
							
								
								
									
										38
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								README.md
									
									
									
									
									
								
							| @ -12,9 +12,10 @@ Home Assistant integration for [Haier's mobile app hOn](https://hon-smarthome.co | ||||
| - [Washer Dryer](https://github.com/Andre0512/hon#washer-dryer) | ||||
| - [Oven](https://github.com/Andre0512/hon#oven) | ||||
| - [Dish Washer](https://github.com/Andre0512/hon#dish-washer) | ||||
| - [Air conditioner](https://github.com/Andre0512/hon#air-conditioner) | ||||
| - [Fridge](https://github.com/Andre0512/hon#fridge) | ||||
| - [Hob](https://github.com/Andre0512/hon#hob) [BETA] | ||||
| - [Air conditioner](https://github.com/Andre0512/hon#air-conditioner) [BETA] | ||||
| - [Fridge](https://github.com/Andre0512/hon#fridge) [BETA] | ||||
| - [Hood](https://github.com/Andre0512/hon#hood) [BETA] | ||||
|  | ||||
| ## Installation | ||||
| **Method 1:** [](https://my.home-assistant.io/redirect/hacs_repository/?owner=Andre0512&repository=hon&category=integration) | ||||
| @ -61,10 +62,14 @@ Translation of internal names like programs are available for all languages whic | ||||
| ## Supported Models | ||||
| Support has been confirmed for these models, but many more will work. Please add already supported devices [with this form to complete the list](https://forms.gle/bTSD8qFotdZFytbf8). | ||||
| - Haier AD105S2SM3FA | ||||
| - Haier AS20HPL1HRA | ||||
| - Haier AS25PBAHRA | ||||
| - Haier AS25S2SF1FA-WH | ||||
| - Haier AS25TADHRA-2 | ||||
| - Haier AS35TADHRA-2 | ||||
| - Haier EG9012B19SU1JD | ||||
| - Haier HA2MTSJ68MC | ||||
| - Haier HADG6DS46BWIFI | ||||
| - Haier HD80-A3959 | ||||
| - Haier HW90-B14TEAM5 | ||||
| - Haier HW100-B14959U1 | ||||
| @ -76,6 +81,7 @@ Support has been confirmed for these models, but many more will work. Please add | ||||
| - Candy CCE4T620EWU | ||||
| - Candy CIS633SCTTWIFI | ||||
| - Candy CSOE C10DE-80 | ||||
| - Candy RO44 1286DWMC4-07 | ||||
| - Candy ROE H9A3TCEX-S | ||||
| - Candy RPW41066BWMR/1-S | ||||
| - Hoover H-WASH 500 | ||||
| @ -225,6 +231,34 @@ For every device exists a hidden button which can be used to log all infos of yo | ||||
| | Rinse Aid | `spray-bottle` | `binary_sensor` | `rinseAidStatus` | | ||||
| | Salt | `shaker-outline` | `binary_sensor` | `saltStatus` | | ||||
|  | ||||
| ### Hood | ||||
| #### Controls | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Start Program | `hvac` | `button` | `startProgram` | | ||||
| | Stop Program | `hvac-off` | `button` | `stopProgram` | | ||||
| #### Configs | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Light status | `lightbulb` | `number` | `startProgram.lightStatus` | | ||||
| | Wind speed | `fan` | `number` | `startProgram.windSpeed` | | ||||
| #### Sensors | ||||
| | Name | Icon | Entity | Key | | ||||
| | --- | --- | --- | --- | | ||||
| | Delay time | `clock-start` | `sensor` | `delayTime` | | ||||
| | Delay time status | `clock-start` | `sensor` | `delayTimeStatus` | | ||||
| | Errors | `alert-circle` | `sensor` | `errors` | | ||||
| | Filter Cleaning Alarm Status |  | `sensor` | `filterCleaningAlarmStatus` | | ||||
| | Filter Cleaning Status |  | `sensor` | `filterCleaningStatus` | | ||||
| | Last Work Time | `clock-start` | `sensor` | `lastWorkTime` | | ||||
| | Light Status | `lightbulb` | `sensor` | `lightStatus` | | ||||
| | Mach Mode |  | `sensor` | `machMode` | | ||||
| | On / Off Status | `lightbulb` | `sensor` | `onOffStatus` | | ||||
| | Quick Delay Time Status |  | `sensor` | `quickDelayTimeStatus` | | ||||
| | RGB Light Color | `lightbulb` | `sensor` | `rgbLightColors` | | ||||
| | RGB Light Status | `lightbulb` | `sensor` | `rgbLightStatus` | | ||||
| | Wind Speed | `fan` | `sensor` | `windSpeed` | | ||||
|  | ||||
| ### Hob | ||||
| #### Controls | ||||
| | Name | Icon | Entity | Key | | ||||
|  | ||||
| @ -271,9 +271,10 @@ class HonBinarySensorEntity(HonEntity, BinarySensorEntity): | ||||
|         ) | ||||
|  | ||||
|     @callback | ||||
|     def _handle_coordinator_update(self): | ||||
|     def _handle_coordinator_update(self, update=True) -> None: | ||||
|         self._attr_native_value = ( | ||||
|             self._device.get(self.entity_description.key, "") | ||||
|             == self.entity_description.on_value | ||||
|         ) | ||||
|         self.async_write_ha_state() | ||||
|         if update: | ||||
|             self.async_write_ha_state() | ||||
|  | ||||
| @ -35,6 +35,20 @@ BUTTONS: dict[str, tuple[ButtonEntityDescription, ...]] = { | ||||
|             translation_key="stop_program", | ||||
|         ), | ||||
|     ), | ||||
|     "HO": ( | ||||
|         ButtonEntityDescription( | ||||
|             key="startProgram", | ||||
|             name="Start Program", | ||||
|             icon="mdi:hvac", | ||||
|             translation_key="start_program", | ||||
|         ), | ||||
|         ButtonEntityDescription( | ||||
|             key="stopProgram", | ||||
|             name="Stop Program", | ||||
|             icon="mdi:hvac-off", | ||||
|             translation_key="stop_program", | ||||
|         ), | ||||
|     ), | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -1,8 +1,6 @@ | ||||
| import logging | ||||
| from dataclasses import dataclass | ||||
|  | ||||
| from pyhon.appliance import HonAppliance | ||||
|  | ||||
| from homeassistant.components.climate import ( | ||||
|     ClimateEntity, | ||||
|     ClimateEntityDescription, | ||||
| @ -22,6 +20,8 @@ from homeassistant.const import ( | ||||
|     TEMP_CELSIUS, | ||||
| ) | ||||
| from homeassistant.core import callback | ||||
| from pyhon.appliance import HonAppliance | ||||
|  | ||||
| from .const import HON_HVAC_MODE, HON_FAN, HON_HVAC_PROGRAM, DOMAIN | ||||
| from .hon import HonEntity | ||||
|  | ||||
| @ -103,12 +103,12 @@ class HonACClimateEntity(HonEntity, ClimateEntity): | ||||
|         self._attr_max_temp = device.settings["settings.tempSel"].max | ||||
|         self._attr_min_temp = device.settings["settings.tempSel"].min | ||||
|  | ||||
|         self._attr_hvac_modes = [HVACMode.OFF] + [ | ||||
|             HON_HVAC_MODE[mode] for mode in device.settings["settings.machMode"].values | ||||
|         ] | ||||
|         self._attr_fan_modes = [FAN_OFF] + [ | ||||
|             HON_FAN[mode] for mode in device.settings["settings.windSpeed"].values | ||||
|         ] | ||||
|         self._attr_hvac_modes = [HVACMode.OFF] | ||||
|         for mode in device.settings["settings.machMode"].values: | ||||
|             self._attr_hvac_modes.append(HON_HVAC_MODE[mode]) | ||||
|         self._attr_fan_modes = [FAN_OFF] | ||||
|         for mode in device.settings["settings.windSpeed"].values: | ||||
|             self._attr_fan_modes.append(HON_FAN[mode]) | ||||
|         self._attr_swing_modes = [ | ||||
|             SWING_OFF, | ||||
|             SWING_VERTICAL, | ||||
| @ -123,6 +123,23 @@ class HonACClimateEntity(HonEntity, ClimateEntity): | ||||
|  | ||||
|         self._handle_coordinator_update(update=False) | ||||
|  | ||||
|     @property | ||||
|     def target_temperature(self) -> int | None: | ||||
|         """Return the temperature we try to reach.""" | ||||
|         return int(float(self._device.get("tempSel"))) | ||||
|  | ||||
|     @property | ||||
|     def current_temperature(self) -> float | None: | ||||
|         """Return the current temperature.""" | ||||
|         return float(self._device.get("tempIndoor")) | ||||
|  | ||||
|     async def async_set_temperature(self, **kwargs): | ||||
|         if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None: | ||||
|             return False | ||||
|         self._device.settings["settings.tempSel"].value = str(int(temperature)) | ||||
|         await self._device.commands["settings"].send() | ||||
|         self.async_write_ha_state() | ||||
|  | ||||
|     @property | ||||
|     def hvac_mode(self) -> HVACMode | str | None: | ||||
|         if self._device.get("onOffStatus") == "0": | ||||
| @ -131,24 +148,44 @@ class HonACClimateEntity(HonEntity, ClimateEntity): | ||||
|             return HON_HVAC_MODE[self._device.get("machMode")] | ||||
|  | ||||
|     async def async_set_hvac_mode(self, hvac_mode): | ||||
|         if hvac_mode == HVACMode.OFF: | ||||
|             await self._device.commands["stopProgram"].send() | ||||
|         else: | ||||
|             self._device.settings["startProgram.program"].value = HON_HVAC_PROGRAM[ | ||||
|                 hvac_mode | ||||
|             ] | ||||
|             await self._device.commands["startProgram"].send() | ||||
|         self._attr_hvac_mode = hvac_mode | ||||
|         if hvac_mode == HVACMode.OFF: | ||||
|             command = "stopProgram" | ||||
|         else: | ||||
|             mode = HON_HVAC_PROGRAM[hvac_mode] | ||||
|             self._device.settings["startProgram.program"].value = mode | ||||
|             command = "startProgram" | ||||
|         await self._device.commands[command].send() | ||||
|         self._device.sync_command(command, "settings") | ||||
|         self.async_write_ha_state() | ||||
|  | ||||
|     @property | ||||
|     def fan_mode(self) -> str | None: | ||||
|         """Return the fan setting.""" | ||||
|         return HON_FAN[self._device.get("windSpeed")] | ||||
|  | ||||
|     async def async_set_fan_mode(self, fan_mode): | ||||
|         mode_number = list(HON_FAN.values()).index(fan_mode) | ||||
|         self._device.settings["settings.windSpeed"].value = list(HON_FAN.keys())[ | ||||
|             mode_number | ||||
|         ] | ||||
|         mode = list(HON_FAN.keys())[mode_number] | ||||
|         self._device.settings["settings.windSpeed"].value = mode | ||||
|         self._attr_fan_mode = fan_mode | ||||
|         await self._device.commands["settings"].send() | ||||
|         self.async_write_ha_state() | ||||
|  | ||||
|     @property | ||||
|     def swing_mode(self) -> str | None: | ||||
|         """Return the swing setting.""" | ||||
|         horizontal = self._device.get("windDirectionHorizontal") | ||||
|         vertical = self._device.get("windDirectionVertical") | ||||
|         if horizontal == "7" and vertical == "8": | ||||
|             return SWING_BOTH | ||||
|         elif horizontal == "7": | ||||
|             return SWING_HORIZONTAL | ||||
|         elif vertical == "8": | ||||
|             return SWING_VERTICAL | ||||
|         else: | ||||
|             return SWING_OFF | ||||
|  | ||||
|     async def async_set_swing_mode(self, swing_mode): | ||||
|         horizontal = self._device.settings["settings.windDirectionHorizontal"] | ||||
|         vertical = self._device.settings["settings.windDirectionVertical"] | ||||
| @ -164,35 +201,13 @@ class HonACClimateEntity(HonEntity, ClimateEntity): | ||||
|         await self._device.commands["settings"].send() | ||||
|         self.async_write_ha_state() | ||||
|  | ||||
|     async def async_set_temperature(self, **kwargs): | ||||
|         if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None: | ||||
|             return False | ||||
|         self._device.settings["settings.tempSel"].value = str(int(temperature)) | ||||
|         await self._device.commands["settings"].send() | ||||
|         self.async_write_ha_state() | ||||
|  | ||||
|     @callback | ||||
|     def _handle_coordinator_update(self, update=True) -> None: | ||||
|         self._attr_target_temperature = int(float(self._device.get("tempSel"))) | ||||
|         self._attr_current_temperature = float(self._device.get("tempIndoor")) | ||||
|  | ||||
|         if self._device.get("onOffStatus") == "0": | ||||
|             self._attr_hvac_mode = HVACMode.OFF | ||||
|         else: | ||||
|             self._attr_hvac_mode = HON_HVAC_MODE[self._device.get("machMode")] | ||||
|  | ||||
|         self._attr_fan_mode = HON_FAN[self._device.get("windSpeed")] | ||||
|  | ||||
|         horizontal = self._device.get("windDirectionHorizontal") | ||||
|         vertical = self._device.get("windDirectionVertical") | ||||
|         if horizontal == "7" and vertical == "8": | ||||
|             self._attr_swing_mode = SWING_BOTH | ||||
|         elif horizontal == "7": | ||||
|             self._attr_swing_mode = SWING_HORIZONTAL | ||||
|         elif vertical == "8": | ||||
|             self._attr_swing_mode = SWING_VERTICAL | ||||
|         else: | ||||
|             self._attr_swing_mode = SWING_OFF | ||||
|         self._attr_target_temperature = self.target_temperature | ||||
|         self._attr_current_temperature = self.current_temperature | ||||
|         self._attr_hvac_mode = self.hvac_mode | ||||
|         self._attr_fan_mode = self.fan_mode | ||||
|         self._attr_swing_mode = self.swing_mode | ||||
|         if update: | ||||
|             self.async_write_ha_state() | ||||
|  | ||||
|  | ||||
| @ -7,6 +7,7 @@ from homeassistant.components.climate import ( | ||||
| ) | ||||
|  | ||||
| DOMAIN = "hon" | ||||
| UPDATE_INTERVAL = 10 | ||||
|  | ||||
| PLATFORMS = [ | ||||
|     "sensor", | ||||
| @ -110,8 +111,9 @@ TUMBLE_DRYER_PR_PHASE = { | ||||
|     "1": "TD_CMD&CTRL.STATUS_PHASE.PHASE_HEAT_STROKE", | ||||
|     "2": "WASHING_CMD&CTRL.PHASE_DRYING.TITLE", | ||||
|     "3": "TD_CMD&CTRL.STATUS_PHASE.PHASE_COOLDOWN", | ||||
|     "8": "unknown", | ||||
|     "11": "WASHING_CMD&CTRL.PHASE_READY.TITLE", | ||||
|     "12": "unkown", | ||||
|     "12": "unknown", | ||||
|     "13": "TD_CMD&CTRL.STATUS_PHASE.PHASE_COOLDOWN", | ||||
|     "14": "TD_CMD&CTRL.STATUS_PHASE.PHASE_HEAT_STROKE", | ||||
|     "15": "TD_CMD&CTRL.STATUS_PHASE.PHASE_HEAT_STROKE", | ||||
|  | ||||
| @ -1,12 +1,13 @@ | ||||
| import logging | ||||
| from datetime import timedelta | ||||
|  | ||||
| from homeassistant.core import callback | ||||
| from homeassistant.helpers.entity import DeviceInfo | ||||
| from homeassistant.helpers.update_coordinator import CoordinatorEntity | ||||
| from homeassistant.helpers.update_coordinator import DataUpdateCoordinator | ||||
| from pyhon.appliance import HonAppliance | ||||
|  | ||||
| from .const import DOMAIN | ||||
| from .const import DOMAIN, UPDATE_INTERVAL | ||||
|  | ||||
| _LOGGER = logging.getLogger(__name__) | ||||
|  | ||||
| @ -21,13 +22,14 @@ class HonEntity(CoordinatorEntity): | ||||
|         self._hon = hass.data[DOMAIN][entry.unique_id] | ||||
|         self._hass = hass | ||||
|         self._coordinator = coordinator | ||||
|         self._device = device | ||||
|         self._device: HonAppliance = device | ||||
|  | ||||
|         if description is not None: | ||||
|             self.entity_description = description | ||||
|             self._attr_unique_id = f"{self._device.unique_id}{description.key}" | ||||
|         else: | ||||
|             self._attr_unique_id = self._device.unique_id | ||||
|         self._handle_coordinator_update(update=False) | ||||
|  | ||||
|     @property | ||||
|     def device_info(self): | ||||
| @ -41,6 +43,11 @@ class HonEntity(CoordinatorEntity): | ||||
|             sw_version=self._device.get("fwVersion", ""), | ||||
|         ) | ||||
|  | ||||
|     @callback | ||||
|     def _handle_coordinator_update(self, update: bool = True) -> None: | ||||
|         if update: | ||||
|             self.async_write_ha_state() | ||||
|  | ||||
|  | ||||
| class HonCoordinator(DataUpdateCoordinator): | ||||
|     def __init__(self, hass, device: HonAppliance): | ||||
| @ -49,7 +56,7 @@ class HonCoordinator(DataUpdateCoordinator): | ||||
|             hass, | ||||
|             _LOGGER, | ||||
|             name=device.unique_id, | ||||
|             update_interval=timedelta(seconds=30), | ||||
|             update_interval=timedelta(seconds=UPDATE_INTERVAL), | ||||
|         ) | ||||
|         self._device = device | ||||
|  | ||||
|  | ||||
| @ -9,7 +9,7 @@ | ||||
|   "iot_class": "cloud_polling", | ||||
|   "issue_tracker": "https://github.com/Andre0512/hon/issues", | ||||
|   "requirements": [ | ||||
|     "pyhOn==0.12.0" | ||||
|     "pyhOn==0.12.4" | ||||
|   ], | ||||
|   "version": "0.8.0-beta.8" | ||||
|   "version": "0.8.1" | ||||
| } | ||||
|  | ||||
| @ -9,7 +9,7 @@ from homeassistant.components.number import ( | ||||
| from homeassistant.config_entries import ConfigEntry | ||||
| from homeassistant.const import UnitOfTime, UnitOfTemperature | ||||
| from homeassistant.core import callback | ||||
| from homeassistant.helpers.entity import EntityCategory, Entity | ||||
| from homeassistant.helpers.entity import EntityCategory | ||||
| from pyhon.parameter.range import HonParameterRange | ||||
|  | ||||
| from .const import DOMAIN | ||||
| @ -162,6 +162,20 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { | ||||
|             translation_key="freezer_temp_sel", | ||||
|         ), | ||||
|     ), | ||||
|     "HO": ( | ||||
|         HonNumberEntityDescription( | ||||
|             key="startProgram.windSpeed", | ||||
|             name="Wind speed", | ||||
|             icon="mdi:fan", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|         HonNumberEntityDescription( | ||||
|             key="startProgram.lightStatus", | ||||
|             name="Light status", | ||||
|             icon="mdi:lightbulb", | ||||
|             entity_category=EntityCategory.CONFIG, | ||||
|         ), | ||||
|     ), | ||||
| } | ||||
|  | ||||
| NUMBERS["WD"] = unique_entities(NUMBERS["WM"], NUMBERS["TD"]) | ||||
| @ -209,14 +223,15 @@ class HonNumberEntity(HonEntity, NumberEntity): | ||||
|         await self.coordinator.async_refresh() | ||||
|  | ||||
|     @callback | ||||
|     def _handle_coordinator_update(self): | ||||
|     def _handle_coordinator_update(self, update=True) -> None: | ||||
|         setting = self._device.settings[self.entity_description.key] | ||||
|         if isinstance(setting, HonParameterRange): | ||||
|             self._attr_native_max_value = setting.max | ||||
|             self._attr_native_min_value = setting.min | ||||
|             self._attr_native_step = setting.step | ||||
|         self._attr_native_value = setting.value | ||||
|         self.async_write_ha_state() | ||||
|         if update: | ||||
|             self.async_write_ha_state() | ||||
|  | ||||
|     @property | ||||
|     def available(self) -> bool: | ||||
|  | ||||
| @ -7,7 +7,7 @@ from homeassistant.components.select import SelectEntity, SelectEntityDescriptio | ||||
| from homeassistant.config_entries import ConfigEntry | ||||
| from homeassistant.const import UnitOfTemperature, UnitOfTime, REVOLUTIONS_PER_MINUTE | ||||
| from homeassistant.core import callback | ||||
| from homeassistant.helpers.entity import EntityCategory, Entity | ||||
| from homeassistant.helpers.entity import EntityCategory | ||||
| from pyhon.appliance import HonAppliance | ||||
| from pyhon.parameter.fixed import HonParameterFixed | ||||
|  | ||||
| @ -179,7 +179,7 @@ class HonSelectEntity(HonEntity, SelectEntity): | ||||
|         await self.coordinator.async_refresh() | ||||
|  | ||||
|     @callback | ||||
|     def _handle_coordinator_update(self): | ||||
|     def _handle_coordinator_update(self, update=True) -> None: | ||||
|         setting = self._device.settings.get(self.entity_description.key) | ||||
|         if setting is None: | ||||
|             self._attr_available = False | ||||
| @ -189,7 +189,8 @@ class HonSelectEntity(HonEntity, SelectEntity): | ||||
|             self._attr_available = True | ||||
|             self._attr_options: list[str] = setting.values | ||||
|             self._attr_native_value = setting.value | ||||
|         self.async_write_ha_state() | ||||
|         if update: | ||||
|             self.async_write_ha_state() | ||||
|  | ||||
|     @property | ||||
|     def available(self) -> bool: | ||||
|  | ||||
| @ -1,8 +1,6 @@ | ||||
| import logging | ||||
| from dataclasses import dataclass | ||||
|  | ||||
| from pyhon.appliance import HonAppliance | ||||
|  | ||||
| from homeassistant.components.sensor import ( | ||||
|     SensorEntity, | ||||
|     SensorDeviceClass, | ||||
| @ -22,7 +20,8 @@ from homeassistant.const import ( | ||||
| ) | ||||
| from homeassistant.core import callback | ||||
| from homeassistant.helpers.entity import EntityCategory | ||||
| from homeassistant.helpers.typing import StateType | ||||
| from pyhon.appliance import HonAppliance | ||||
|  | ||||
| from . import const | ||||
| from .const import DOMAIN | ||||
| from .hon import HonEntity, unique_entities | ||||
| @ -535,6 +534,71 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { | ||||
|             key="errors", name="Error", icon="mdi:math-log", translation_key="errors" | ||||
|         ), | ||||
|     ), | ||||
|     "HO": ( | ||||
|         HonSensorEntityDescription( | ||||
|             key="delayTime", | ||||
|             name="Delay time", | ||||
|             icon="mdi:clock-start", | ||||
|             state_class=SensorStateClass.MEASUREMENT, | ||||
|             native_unit_of_measurement=UnitOfTime.MINUTES, | ||||
|         ), | ||||
|         HonSensorEntityDescription( | ||||
|             key="delayTimeStatus", | ||||
|             name="Delay time status", | ||||
|             icon="mdi:clock-start", | ||||
|         ), | ||||
|         HonSensorEntityDescription( | ||||
|             key="errors", | ||||
|             name="Errors", | ||||
|             icon="mdi:alert-circle", | ||||
|         ), | ||||
|         HonSensorEntityDescription( | ||||
|             key="filterCleaningAlarmStatus", | ||||
|             name="Filter Cleaning Alarm Status", | ||||
|         ), | ||||
|         HonSensorEntityDescription( | ||||
|             key="filterCleaningStatus", | ||||
|             name="Filter Cleaning Status", | ||||
|         ), | ||||
|         HonSensorEntityDescription( | ||||
|             key="lastWorkTime", | ||||
|             name="Last Work Time", | ||||
|             icon="mdi:clock-start", | ||||
|         ), | ||||
|         HonSensorEntityDescription( | ||||
|             key="lightStatus", | ||||
|             name="Light Status", | ||||
|             icon="mdi:lightbulb", | ||||
|         ), | ||||
|         HonSensorEntityDescription( | ||||
|             key="machMode", | ||||
|             name="Mach Mode", | ||||
|         ), | ||||
|         HonSensorEntityDescription( | ||||
|             key="onOffStatus", | ||||
|             name="On / Off Status", | ||||
|             icon="mdi:lightbulb", | ||||
|         ), | ||||
|         HonSensorEntityDescription( | ||||
|             key="quickDelayTimeStatus", | ||||
|             name="Quick Delay Time Status", | ||||
|         ), | ||||
|         HonSensorEntityDescription( | ||||
|             key="rgbLightColors", | ||||
|             name="RGB Light Color", | ||||
|             icon="mdi:lightbulb", | ||||
|         ), | ||||
|         HonSensorEntityDescription( | ||||
|             key="rgbLightStatus", | ||||
|             name="RGB Light Status", | ||||
|             icon="mdi:lightbulb", | ||||
|         ), | ||||
|         HonSensorEntityDescription( | ||||
|             key="windSpeed", | ||||
|             name="Wind Speed", | ||||
|             icon="mdi:fan", | ||||
|         ), | ||||
|     ), | ||||
| } | ||||
| SENSORS["WD"] = unique_entities(SENSORS["WM"], SENSORS["TD"]) | ||||
|  | ||||
| @ -570,19 +634,20 @@ class HonSensorEntity(HonEntity, SensorEntity): | ||||
|             ).values + ["No Program"] | ||||
|  | ||||
|     @callback | ||||
|     def _handle_coordinator_update(self): | ||||
|     def _handle_coordinator_update(self, update=True) -> None: | ||||
|         value = self._device.get(self.entity_description.key, "") | ||||
|         if not value and self.entity_description.state_class is not None: | ||||
|             self._attr_native_value = 0 | ||||
|         self._attr_native_value = value | ||||
|         self.async_write_ha_state() | ||||
|         if update: | ||||
|             self.async_write_ha_state() | ||||
|  | ||||
|  | ||||
| class HonConfigSensorEntity(HonEntity, SensorEntity): | ||||
|     entity_description: HonConfigSensorEntityDescription | ||||
|  | ||||
|     @callback | ||||
|     def _handle_coordinator_update(self): | ||||
|     def _handle_coordinator_update(self, update=True) -> None: | ||||
|         value = self._device.settings.get(self.entity_description.key, None) | ||||
|         if self.entity_description.state_class is not None: | ||||
|             if value and value.value: | ||||
| @ -593,4 +658,5 @@ class HonConfigSensorEntity(HonEntity, SensorEntity): | ||||
|                 self._attr_native_value = 0 | ||||
|         else: | ||||
|             self._attr_native_value = value.value | ||||
|         self.async_write_ha_state() | ||||
|         if update: | ||||
|             self.async_write_ha_state() | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| import logging | ||||
| from dataclasses import dataclass | ||||
| from datetime import datetime, timedelta | ||||
| from typing import Any | ||||
|  | ||||
| from homeassistant.components.switch import SwitchEntityDescription, SwitchEntity | ||||
| @ -394,10 +395,11 @@ class HonSwitchEntity(HonEntity, SwitchEntity): | ||||
|         ) | ||||
|  | ||||
|     @callback | ||||
|     def _handle_coordinator_update(self): | ||||
|     def _handle_coordinator_update(self, update=True) -> None: | ||||
|         value = self._device.get(self.entity_description.key, "0") | ||||
|         self._attr_state = value == "1" | ||||
|         self.async_write_ha_state() | ||||
|         if update: | ||||
|             self.async_write_ha_state() | ||||
|  | ||||
|  | ||||
| class HonControlSwitchEntity(HonEntity, SwitchEntity): | ||||
| @ -410,9 +412,13 @@ class HonControlSwitchEntity(HonEntity, SwitchEntity): | ||||
|  | ||||
|     async def async_turn_on(self, **kwargs: Any) -> None: | ||||
|         await self._device.commands[self.entity_description.turn_on_key].send() | ||||
|         self._device.attributes[self.entity_description.key] = True | ||||
|         self.async_write_ha_state() | ||||
|  | ||||
|     async def async_turn_off(self, **kwargs: Any) -> None: | ||||
|         await self._device.commands[self.entity_description.turn_off_key].send() | ||||
|         self._device.attributes[self.entity_description.key] = False | ||||
|         self.async_write_ha_state() | ||||
|  | ||||
|     @property | ||||
|     def available(self) -> bool: | ||||
| @ -423,6 +429,18 @@ class HonControlSwitchEntity(HonEntity, SwitchEntity): | ||||
|             and self._device.get("attributes.lastConnEvent.category") != "DISCONNECTED" | ||||
|         ) | ||||
|  | ||||
|     @property | ||||
|     def extra_state_attributes(self) -> dict[str, Any]: | ||||
|         """Return the optional state attributes.""" | ||||
|         result = {} | ||||
|         if remaining_time := int(self._device.get("remainingTimeMM", 0)): | ||||
|             delay_time = int(self._device.get("delayTime", 0)) | ||||
|             result["start_time"] = datetime.now() + timedelta(minutes=delay_time) | ||||
|             result["end_time"] = datetime.now() + timedelta( | ||||
|                 minutes=delay_time + remaining_time | ||||
|             ) | ||||
|         return result | ||||
|  | ||||
|  | ||||
| class HonConfigSwitchEntity(HonEntity, SwitchEntity): | ||||
|     entity_description: HonConfigSwitchEntityDescription | ||||
| @ -454,7 +472,8 @@ class HonConfigSwitchEntity(HonEntity, SwitchEntity): | ||||
|         await self.coordinator.async_refresh() | ||||
|  | ||||
|     @callback | ||||
|     def _handle_coordinator_update(self): | ||||
|     def _handle_coordinator_update(self, update=True) -> None: | ||||
|         value = self._device.settings.get(self.entity_description.key, "0") | ||||
|         self._attr_state = value == "1" | ||||
|         self.async_write_ha_state() | ||||
|         if update: | ||||
|             self.async_write_ha_state() | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Sušení", | ||||
|                     "11": "Připraveno", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Fáze" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Trocknen", | ||||
|                     "11": "Bereit", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Phase" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Στέγνωμα", | ||||
|                     "11": "Ετοιμος", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Φάση" | ||||
|             }, | ||||
|  | ||||
| @ -127,7 +127,7 @@ | ||||
|                     "20": "Drying", | ||||
|                     "11": "Ready", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Phase" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Secado", | ||||
|                     "11": "Listo", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Fase" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Séchage", | ||||
|                     "11": "Prêt", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Phase" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "יִבּוּשׁ", | ||||
|                     "11": "מוּכָן", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "שלב" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Sušenje", | ||||
|                     "11": "Spremno", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Faza" | ||||
|             }, | ||||
|  | ||||
| @ -117,7 +117,7 @@ | ||||
|                     "20": "Asciugatura", | ||||
|                     "11": "Pronta", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Fase" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Drogen", | ||||
|                     "11": "Klaar", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Fase" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Suszenie", | ||||
|                     "11": "Gotowe", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Faza" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Secagem", | ||||
|                     "11": "Pronto", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Fase" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Uscare", | ||||
|                     "11": "Pregătit", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Fază" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Сушка", | ||||
|                     "11": "Готово", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Фаза" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Sušenie", | ||||
|                     "11": "Pripravené", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Fáza" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Sušenje", | ||||
|                     "11": "Pripravljen", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Faza" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Sušenje", | ||||
|                     "11": "Spremno", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Faza" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "Kurutma", | ||||
|                     "11": "Hazır", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "Aşama" | ||||
|             }, | ||||
|  | ||||
| @ -59,7 +59,7 @@ | ||||
|                     "20": "烘干", | ||||
|                     "11": "就绪", | ||||
|                     "17": "unknown", | ||||
|                     "12": "unkown" | ||||
|                     "12": "unknown" | ||||
|                 }, | ||||
|                 "name": "阶段" | ||||
|             }, | ||||
|  | ||||
							
								
								
									
										12
									
								
								info.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								info.md
									
									
									
									
									
								
							| @ -9,10 +9,11 @@ Support for home appliances of Haier's mobile app hOn. | ||||
| - [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) | ||||
| - [Air conditioner](https://github.com/Andre0512/hon#air-conditioner) [BETA] | ||||
| - [Fridge](https://github.com/Andre0512/hon#fridge) [BETA] | ||||
| - [Air conditioner](https://github.com/Andre0512/hon#air-conditioner) | ||||
| - [Fridge](https://github.com/Andre0512/hon#fridge) | ||||
| - [Hob](https://github.com/Andre0512/hon#hob) [BETA] | ||||
| - [Hood](https://github.com/Andre0512/hon#hood) [BETA] | ||||
|  | ||||
| ## Configuration | ||||
|  | ||||
| @ -50,10 +51,14 @@ Translation of internal names like programs are available for all languages whic | ||||
| ## Supported Models | ||||
| Support has been confirmed for these models, but many more will work. Please add already supported devices [with this form to complete the list](https://forms.gle/bTSD8qFotdZFytbf8). | ||||
| - Haier AD105S2SM3FA | ||||
| - Haier AS20HPL1HRA | ||||
| - Haier AS25PBAHRA | ||||
| - Haier AS25S2SF1FA-WH | ||||
| - Haier AS25TADHRA-2 | ||||
| - Haier AS35TADHRA-2 | ||||
| - Haier EG9012B19SU1JD | ||||
| - Haier HA2MTSJ68MC | ||||
| - Haier HADG6DS46BWIFI | ||||
| - Haier HD80-A3959 | ||||
| - Haier HW90-B14TEAM5 | ||||
| - Haier HW100-B14959U1 | ||||
| @ -65,6 +70,7 @@ Support has been confirmed for these models, but many more will work. Please add | ||||
| - Candy CCE4T620EWU | ||||
| - Candy CIS633SCTTWIFI | ||||
| - Candy CSOE C10DE-80 | ||||
| - Candy RO44 1286DWMC4-07 | ||||
| - Candy ROE H9A3TCEX-S | ||||
| - Candy RPW41066BWMR/1-S | ||||
| - Hoover H-WASH 500 | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	