Compare commits

...

7 Commits

Author SHA1 Message Date
5bc3120000 Bump version 2023-06-08 22:07:56 +02:00
0f9f0dee4c Fix issues when changing climate mode #52 2023-06-08 21:46:36 +02:00
80b3741f2f Reduce lagging update 2023-06-08 20:01:55 +02:00
c433714a94 Refactor and update for lagging climate 2023-06-08 19:59:43 +02:00
228cf3cf73 Bump version 2023-06-07 02:33:53 +02:00
1a50e8112d Update readme, fix typo 2023-05-30 05:33:11 +02:00
57ecd7c3a5 Adding HO integration (#56)
* Update button.py

* Update number.py

* Update sensor.py
2023-05-30 05:22:02 +02:00
31 changed files with 273 additions and 94 deletions

View File

@ -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) - [Washer Dryer](https://github.com/Andre0512/hon#washer-dryer)
- [Oven](https://github.com/Andre0512/hon#oven) - [Oven](https://github.com/Andre0512/hon#oven)
- [Dish Washer](https://github.com/Andre0512/hon#dish-washer) - [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] - [Hob](https://github.com/Andre0512/hon#hob) [BETA]
- [Air conditioner](https://github.com/Andre0512/hon#air-conditioner) [BETA] - [Hood](https://github.com/Andre0512/hon#hood) [BETA]
- [Fridge](https://github.com/Andre0512/hon#fridge) [BETA]
## Installation ## Installation
**Method 1:** [![Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=Andre0512&repository=hon&category=integration) **Method 1:** [![Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](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 ## 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). 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 AD105S2SM3FA
- Haier AS20HPL1HRA
- Haier AS25PBAHRA - Haier AS25PBAHRA
- Haier AS25S2SF1FA-WH
- Haier AS25TADHRA-2 - Haier AS25TADHRA-2
- Haier AS35TADHRA-2 - Haier AS35TADHRA-2
- Haier EG9012B19SU1JD - Haier EG9012B19SU1JD
- Haier HA2MTSJ68MC
- Haier HADG6DS46BWIFI
- Haier HD80-A3959 - Haier HD80-A3959
- Haier HW90-B14TEAM5 - Haier HW90-B14TEAM5
- Haier HW100-B14959U1 - Haier HW100-B14959U1
@ -76,6 +81,7 @@ Support has been confirmed for these models, but many more will work. Please add
- Candy CCE4T620EWU - Candy CCE4T620EWU
- Candy CIS633SCTTWIFI - Candy CIS633SCTTWIFI
- Candy CSOE C10DE-80 - Candy CSOE C10DE-80
- Candy RO44 1286DWMC4-07
- Candy ROE H9A3TCEX-S - Candy ROE H9A3TCEX-S
- Candy RPW41066BWMR/1-S - Candy RPW41066BWMR/1-S
- Hoover H-WASH 500 - 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` | | Rinse Aid | `spray-bottle` | `binary_sensor` | `rinseAidStatus` |
| Salt | `shaker-outline` | `binary_sensor` | `saltStatus` | | 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 ### Hob
#### Controls #### Controls
| Name | Icon | Entity | Key | | Name | Icon | Entity | Key |

View File

@ -271,9 +271,10 @@ class HonBinarySensorEntity(HonEntity, BinarySensorEntity):
) )
@callback @callback
def _handle_coordinator_update(self): def _handle_coordinator_update(self, update=True) -> None:
self._attr_native_value = ( self._attr_native_value = (
self._device.get(self.entity_description.key, "") self._device.get(self.entity_description.key, "")
== self.entity_description.on_value == self.entity_description.on_value
) )
self.async_write_ha_state() if update:
self.async_write_ha_state()

View File

@ -35,6 +35,20 @@ BUTTONS: dict[str, tuple[ButtonEntityDescription, ...]] = {
translation_key="stop_program", 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",
),
),
} }

View File

@ -1,8 +1,6 @@
import logging import logging
from dataclasses import dataclass from dataclasses import dataclass
from pyhon.appliance import HonAppliance
from homeassistant.components.climate import ( from homeassistant.components.climate import (
ClimateEntity, ClimateEntity,
ClimateEntityDescription, ClimateEntityDescription,
@ -22,6 +20,8 @@ from homeassistant.const import (
TEMP_CELSIUS, TEMP_CELSIUS,
) )
from homeassistant.core import callback from homeassistant.core import callback
from pyhon.appliance import HonAppliance
from .const import HON_HVAC_MODE, HON_FAN, HON_HVAC_PROGRAM, DOMAIN from .const import HON_HVAC_MODE, HON_FAN, HON_HVAC_PROGRAM, DOMAIN
from .hon import HonEntity from .hon import HonEntity
@ -103,12 +103,12 @@ class HonACClimateEntity(HonEntity, ClimateEntity):
self._attr_max_temp = device.settings["settings.tempSel"].max self._attr_max_temp = device.settings["settings.tempSel"].max
self._attr_min_temp = device.settings["settings.tempSel"].min self._attr_min_temp = device.settings["settings.tempSel"].min
self._attr_hvac_modes = [HVACMode.OFF] + [ self._attr_hvac_modes = [HVACMode.OFF]
HON_HVAC_MODE[mode] for mode in device.settings["settings.machMode"].values for mode in device.settings["settings.machMode"].values:
] self._attr_hvac_modes.append(HON_HVAC_MODE[mode])
self._attr_fan_modes = [FAN_OFF] + [ self._attr_fan_modes = [FAN_OFF]
HON_FAN[mode] for mode in device.settings["settings.windSpeed"].values for mode in device.settings["settings.windSpeed"].values:
] self._attr_fan_modes.append(HON_FAN[mode])
self._attr_swing_modes = [ self._attr_swing_modes = [
SWING_OFF, SWING_OFF,
SWING_VERTICAL, SWING_VERTICAL,
@ -123,6 +123,23 @@ class HonACClimateEntity(HonEntity, ClimateEntity):
self._handle_coordinator_update(update=False) 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 @property
def hvac_mode(self) -> HVACMode | str | None: def hvac_mode(self) -> HVACMode | str | None:
if self._device.get("onOffStatus") == "0": if self._device.get("onOffStatus") == "0":
@ -131,24 +148,44 @@ class HonACClimateEntity(HonEntity, ClimateEntity):
return HON_HVAC_MODE[self._device.get("machMode")] return HON_HVAC_MODE[self._device.get("machMode")]
async def async_set_hvac_mode(self, hvac_mode): 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 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() 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): async def async_set_fan_mode(self, fan_mode):
mode_number = list(HON_FAN.values()).index(fan_mode) mode_number = list(HON_FAN.values()).index(fan_mode)
self._device.settings["settings.windSpeed"].value = list(HON_FAN.keys())[ mode = list(HON_FAN.keys())[mode_number]
mode_number self._device.settings["settings.windSpeed"].value = mode
] self._attr_fan_mode = fan_mode
await self._device.commands["settings"].send() await self._device.commands["settings"].send()
self.async_write_ha_state() 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): async def async_set_swing_mode(self, swing_mode):
horizontal = self._device.settings["settings.windDirectionHorizontal"] horizontal = self._device.settings["settings.windDirectionHorizontal"]
vertical = self._device.settings["settings.windDirectionVertical"] vertical = self._device.settings["settings.windDirectionVertical"]
@ -164,35 +201,13 @@ class HonACClimateEntity(HonEntity, ClimateEntity):
await self._device.commands["settings"].send() await self._device.commands["settings"].send()
self.async_write_ha_state() 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 @callback
def _handle_coordinator_update(self, update=True) -> None: def _handle_coordinator_update(self, update=True) -> None:
self._attr_target_temperature = int(float(self._device.get("tempSel"))) self._attr_target_temperature = self.target_temperature
self._attr_current_temperature = float(self._device.get("tempIndoor")) self._attr_current_temperature = self.current_temperature
self._attr_hvac_mode = self.hvac_mode
if self._device.get("onOffStatus") == "0": self._attr_fan_mode = self.fan_mode
self._attr_hvac_mode = HVACMode.OFF self._attr_swing_mode = self.swing_mode
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
if update: if update:
self.async_write_ha_state() self.async_write_ha_state()

View File

@ -7,6 +7,7 @@ from homeassistant.components.climate import (
) )
DOMAIN = "hon" DOMAIN = "hon"
UPDATE_INTERVAL = 10
PLATFORMS = [ PLATFORMS = [
"sensor", "sensor",
@ -111,7 +112,7 @@ TUMBLE_DRYER_PR_PHASE = {
"2": "WASHING_CMD&CTRL.PHASE_DRYING.TITLE", "2": "WASHING_CMD&CTRL.PHASE_DRYING.TITLE",
"3": "TD_CMD&CTRL.STATUS_PHASE.PHASE_COOLDOWN", "3": "TD_CMD&CTRL.STATUS_PHASE.PHASE_COOLDOWN",
"11": "WASHING_CMD&CTRL.PHASE_READY.TITLE", "11": "WASHING_CMD&CTRL.PHASE_READY.TITLE",
"12": "unkown", "12": "unknown",
"13": "TD_CMD&CTRL.STATUS_PHASE.PHASE_COOLDOWN", "13": "TD_CMD&CTRL.STATUS_PHASE.PHASE_COOLDOWN",
"14": "TD_CMD&CTRL.STATUS_PHASE.PHASE_HEAT_STROKE", "14": "TD_CMD&CTRL.STATUS_PHASE.PHASE_HEAT_STROKE",
"15": "TD_CMD&CTRL.STATUS_PHASE.PHASE_HEAT_STROKE", "15": "TD_CMD&CTRL.STATUS_PHASE.PHASE_HEAT_STROKE",

View File

@ -1,12 +1,13 @@
import logging import logging
from datetime import timedelta from datetime import timedelta
from homeassistant.core import callback
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
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from pyhon.appliance import HonAppliance from pyhon.appliance import HonAppliance
from .const import DOMAIN from .const import DOMAIN, UPDATE_INTERVAL
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -21,13 +22,14 @@ class HonEntity(CoordinatorEntity):
self._hon = hass.data[DOMAIN][entry.unique_id] self._hon = hass.data[DOMAIN][entry.unique_id]
self._hass = hass self._hass = hass
self._coordinator = coordinator self._coordinator = coordinator
self._device = device self._device: HonAppliance = device
if description is not None: if description is not None:
self.entity_description = description self.entity_description = description
self._attr_unique_id = f"{self._device.unique_id}{description.key}" self._attr_unique_id = f"{self._device.unique_id}{description.key}"
else: else:
self._attr_unique_id = self._device.unique_id self._attr_unique_id = self._device.unique_id
self._handle_coordinator_update(update=False)
@property @property
def device_info(self): def device_info(self):
@ -41,6 +43,11 @@ class HonEntity(CoordinatorEntity):
sw_version=self._device.get("fwVersion", ""), 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): class HonCoordinator(DataUpdateCoordinator):
def __init__(self, hass, device: HonAppliance): def __init__(self, hass, device: HonAppliance):
@ -49,7 +56,7 @@ class HonCoordinator(DataUpdateCoordinator):
hass, hass,
_LOGGER, _LOGGER,
name=device.unique_id, name=device.unique_id,
update_interval=timedelta(seconds=30), update_interval=timedelta(seconds=UPDATE_INTERVAL),
) )
self._device = device self._device = device

View File

@ -9,7 +9,7 @@
"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": [ "requirements": [
"pyhOn==0.12.0" "pyhOn==0.12.2"
], ],
"version": "0.8.0-beta.8" "version": "0.8.0"
} }

View File

@ -9,7 +9,7 @@ from homeassistant.components.number import (
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfTime, UnitOfTemperature from homeassistant.const import UnitOfTime, UnitOfTemperature
from homeassistant.core import callback 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 pyhon.parameter.range import HonParameterRange
from .const import DOMAIN from .const import DOMAIN
@ -162,6 +162,20 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = {
translation_key="freezer_temp_sel", 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"]) NUMBERS["WD"] = unique_entities(NUMBERS["WM"], NUMBERS["TD"])
@ -209,14 +223,15 @@ class HonNumberEntity(HonEntity, NumberEntity):
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
@callback @callback
def _handle_coordinator_update(self): def _handle_coordinator_update(self, update=True) -> None:
setting = self._device.settings[self.entity_description.key] setting = self._device.settings[self.entity_description.key]
if isinstance(setting, HonParameterRange): if isinstance(setting, HonParameterRange):
self._attr_native_max_value = setting.max self._attr_native_max_value = setting.max
self._attr_native_min_value = setting.min self._attr_native_min_value = setting.min
self._attr_native_step = setting.step self._attr_native_step = setting.step
self._attr_native_value = setting.value self._attr_native_value = setting.value
self.async_write_ha_state() if update:
self.async_write_ha_state()
@property @property
def available(self) -> bool: def available(self) -> bool:

View File

@ -7,7 +7,7 @@ from homeassistant.components.select import SelectEntity, SelectEntityDescriptio
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import UnitOfTemperature, UnitOfTime, REVOLUTIONS_PER_MINUTE from homeassistant.const import UnitOfTemperature, UnitOfTime, REVOLUTIONS_PER_MINUTE
from homeassistant.core import callback 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.appliance import HonAppliance
from pyhon.parameter.fixed import HonParameterFixed from pyhon.parameter.fixed import HonParameterFixed
@ -179,7 +179,7 @@ class HonSelectEntity(HonEntity, SelectEntity):
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
@callback @callback
def _handle_coordinator_update(self): def _handle_coordinator_update(self, update=True) -> None:
setting = self._device.settings.get(self.entity_description.key) setting = self._device.settings.get(self.entity_description.key)
if setting is None: if setting is None:
self._attr_available = False self._attr_available = False
@ -189,7 +189,8 @@ class HonSelectEntity(HonEntity, SelectEntity):
self._attr_available = True self._attr_available = True
self._attr_options: list[str] = setting.values self._attr_options: list[str] = setting.values
self._attr_native_value = setting.value self._attr_native_value = setting.value
self.async_write_ha_state() if update:
self.async_write_ha_state()
@property @property
def available(self) -> bool: def available(self) -> bool:

View File

@ -1,8 +1,6 @@
import logging import logging
from dataclasses import dataclass from dataclasses import dataclass
from pyhon.appliance import HonAppliance
from homeassistant.components.sensor import ( from homeassistant.components.sensor import (
SensorEntity, SensorEntity,
SensorDeviceClass, SensorDeviceClass,
@ -22,7 +20,8 @@ from homeassistant.const import (
) )
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 pyhon.appliance import HonAppliance
from . import const from . import const
from .const import DOMAIN from .const import DOMAIN
from .hon import HonEntity, unique_entities 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" 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"]) SENSORS["WD"] = unique_entities(SENSORS["WM"], SENSORS["TD"])
@ -570,19 +634,20 @@ class HonSensorEntity(HonEntity, SensorEntity):
).values + ["No Program"] ).values + ["No Program"]
@callback @callback
def _handle_coordinator_update(self): def _handle_coordinator_update(self, update=True) -> None:
value = self._device.get(self.entity_description.key, "") value = self._device.get(self.entity_description.key, "")
if not value and self.entity_description.state_class is not None: if not value and self.entity_description.state_class is not None:
self._attr_native_value = 0 self._attr_native_value = 0
self._attr_native_value = value self._attr_native_value = value
self.async_write_ha_state() if update:
self.async_write_ha_state()
class HonConfigSensorEntity(HonEntity, SensorEntity): class HonConfigSensorEntity(HonEntity, SensorEntity):
entity_description: HonConfigSensorEntityDescription entity_description: HonConfigSensorEntityDescription
@callback @callback
def _handle_coordinator_update(self): def _handle_coordinator_update(self, update=True) -> None:
value = self._device.settings.get(self.entity_description.key, None) value = self._device.settings.get(self.entity_description.key, None)
if self.entity_description.state_class is not None: if self.entity_description.state_class is not None:
if value and value.value: if value and value.value:
@ -593,4 +658,5 @@ class HonConfigSensorEntity(HonEntity, SensorEntity):
self._attr_native_value = 0 self._attr_native_value = 0
else: else:
self._attr_native_value = value.value self._attr_native_value = value.value
self.async_write_ha_state() if update:
self.async_write_ha_state()

View File

@ -1,5 +1,6 @@
import logging import logging
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime, timedelta
from typing import Any from typing import Any
from homeassistant.components.switch import SwitchEntityDescription, SwitchEntity from homeassistant.components.switch import SwitchEntityDescription, SwitchEntity
@ -394,10 +395,11 @@ class HonSwitchEntity(HonEntity, SwitchEntity):
) )
@callback @callback
def _handle_coordinator_update(self): def _handle_coordinator_update(self, update=True) -> None:
value = self._device.get(self.entity_description.key, "0") value = self._device.get(self.entity_description.key, "0")
self._attr_state = value == "1" self._attr_state = value == "1"
self.async_write_ha_state() if update:
self.async_write_ha_state()
class HonControlSwitchEntity(HonEntity, SwitchEntity): class HonControlSwitchEntity(HonEntity, SwitchEntity):
@ -410,9 +412,13 @@ class HonControlSwitchEntity(HonEntity, SwitchEntity):
async def async_turn_on(self, **kwargs: Any) -> None: async def async_turn_on(self, **kwargs: Any) -> None:
await self._device.commands[self.entity_description.turn_on_key].send() 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: async def async_turn_off(self, **kwargs: Any) -> None:
await self._device.commands[self.entity_description.turn_off_key].send() 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 @property
def available(self) -> bool: def available(self) -> bool:
@ -423,6 +429,18 @@ class HonControlSwitchEntity(HonEntity, SwitchEntity):
and self._device.get("attributes.lastConnEvent.category") != "DISCONNECTED" 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): class HonConfigSwitchEntity(HonEntity, SwitchEntity):
entity_description: HonConfigSwitchEntityDescription entity_description: HonConfigSwitchEntityDescription
@ -454,7 +472,8 @@ class HonConfigSwitchEntity(HonEntity, SwitchEntity):
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
@callback @callback
def _handle_coordinator_update(self): def _handle_coordinator_update(self, update=True) -> None:
value = self._device.settings.get(self.entity_description.key, "0") value = self._device.settings.get(self.entity_description.key, "0")
self._attr_state = value == "1" self._attr_state = value == "1"
self.async_write_ha_state() if update:
self.async_write_ha_state()

View File

@ -59,7 +59,7 @@
"20": "Sušení", "20": "Sušení",
"11": "Připraveno", "11": "Připraveno",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Fáze" "name": "Fáze"
}, },

View File

@ -59,7 +59,7 @@
"20": "Trocknen", "20": "Trocknen",
"11": "Bereit", "11": "Bereit",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Phase" "name": "Phase"
}, },

View File

@ -59,7 +59,7 @@
"20": "Στέγνωμα", "20": "Στέγνωμα",
"11": "Ετοιμος", "11": "Ετοιμος",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Φάση" "name": "Φάση"
}, },

View File

@ -127,7 +127,7 @@
"20": "Drying", "20": "Drying",
"11": "Ready", "11": "Ready",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Phase" "name": "Phase"
}, },

View File

@ -59,7 +59,7 @@
"20": "Secado", "20": "Secado",
"11": "Listo", "11": "Listo",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Fase" "name": "Fase"
}, },

View File

@ -59,7 +59,7 @@
"20": "Séchage", "20": "Séchage",
"11": "Prêt", "11": "Prêt",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Phase" "name": "Phase"
}, },

View File

@ -59,7 +59,7 @@
"20": "יִבּוּשׁ", "20": "יִבּוּשׁ",
"11": "מוּכָן", "11": "מוּכָן",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "שלב" "name": "שלב"
}, },

View File

@ -59,7 +59,7 @@
"20": "Sušenje", "20": "Sušenje",
"11": "Spremno", "11": "Spremno",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Faza" "name": "Faza"
}, },

View File

@ -117,7 +117,7 @@
"20": "Asciugatura", "20": "Asciugatura",
"11": "Pronta", "11": "Pronta",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Fase" "name": "Fase"
}, },

View File

@ -59,7 +59,7 @@
"20": "Drogen", "20": "Drogen",
"11": "Klaar", "11": "Klaar",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Fase" "name": "Fase"
}, },

View File

@ -59,7 +59,7 @@
"20": "Suszenie", "20": "Suszenie",
"11": "Gotowe", "11": "Gotowe",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Faza" "name": "Faza"
}, },

View File

@ -59,7 +59,7 @@
"20": "Secagem", "20": "Secagem",
"11": "Pronto", "11": "Pronto",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Fase" "name": "Fase"
}, },

View File

@ -59,7 +59,7 @@
"20": "Uscare", "20": "Uscare",
"11": "Pregătit", "11": "Pregătit",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Fază" "name": "Fază"
}, },

View File

@ -59,7 +59,7 @@
"20": "Сушка", "20": "Сушка",
"11": "Готово", "11": "Готово",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Фаза" "name": "Фаза"
}, },

View File

@ -59,7 +59,7 @@
"20": "Sušenie", "20": "Sušenie",
"11": "Pripravené", "11": "Pripravené",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Fáza" "name": "Fáza"
}, },

View File

@ -59,7 +59,7 @@
"20": "Sušenje", "20": "Sušenje",
"11": "Pripravljen", "11": "Pripravljen",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Faza" "name": "Faza"
}, },

View File

@ -59,7 +59,7 @@
"20": "Sušenje", "20": "Sušenje",
"11": "Spremno", "11": "Spremno",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Faza" "name": "Faza"
}, },

View File

@ -59,7 +59,7 @@
"20": "Kurutma", "20": "Kurutma",
"11": "Hazır", "11": "Hazır",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "Aşama" "name": "Aşama"
}, },

View File

@ -59,7 +59,7 @@
"20": "烘干", "20": "烘干",
"11": "就绪", "11": "就绪",
"17": "unknown", "17": "unknown",
"12": "unkown" "12": "unknown"
}, },
"name": "阶段" "name": "阶段"
}, },

12
info.md
View File

@ -9,10 +9,11 @@ Support for home appliances of Haier's mobile app hOn.
- [Tumble Dryer](https://github.com/Andre0512/hon#tumble-dryer) - [Tumble Dryer](https://github.com/Andre0512/hon#tumble-dryer)
- [Washer Dryer](https://github.com/Andre0512/hon#washer-dryer) - [Washer Dryer](https://github.com/Andre0512/hon#washer-dryer)
- [Oven](https://github.com/Andre0512/hon#oven) - [Oven](https://github.com/Andre0512/hon#oven)
- [Hob](https://github.com/Andre0512/hon#hob)
- [Dish Washer](https://github.com/Andre0512/hon#dish-washer) - [Dish Washer](https://github.com/Andre0512/hon#dish-washer)
- [Air conditioner](https://github.com/Andre0512/hon#air-conditioner) [BETA] - [Air conditioner](https://github.com/Andre0512/hon#air-conditioner)
- [Fridge](https://github.com/Andre0512/hon#fridge) [BETA] - [Fridge](https://github.com/Andre0512/hon#fridge)
- [Hob](https://github.com/Andre0512/hon#hob) [BETA]
- [Hood](https://github.com/Andre0512/hon#hood) [BETA]
## Configuration ## Configuration
@ -50,10 +51,14 @@ Translation of internal names like programs are available for all languages whic
## Supported Models ## 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). 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 AD105S2SM3FA
- Haier AS20HPL1HRA
- Haier AS25PBAHRA - Haier AS25PBAHRA
- Haier AS25S2SF1FA-WH
- Haier AS25TADHRA-2 - Haier AS25TADHRA-2
- Haier AS35TADHRA-2 - Haier AS35TADHRA-2
- Haier EG9012B19SU1JD - Haier EG9012B19SU1JD
- Haier HA2MTSJ68MC
- Haier HADG6DS46BWIFI
- Haier HD80-A3959 - Haier HD80-A3959
- Haier HW90-B14TEAM5 - Haier HW90-B14TEAM5
- Haier HW100-B14959U1 - Haier HW100-B14959U1
@ -65,6 +70,7 @@ Support has been confirmed for these models, but many more will work. Please add
- Candy CCE4T620EWU - Candy CCE4T620EWU
- Candy CIS633SCTTWIFI - Candy CIS633SCTTWIFI
- Candy CSOE C10DE-80 - Candy CSOE C10DE-80
- Candy RO44 1286DWMC4-07
- Candy ROE H9A3TCEX-S - Candy ROE H9A3TCEX-S
- Candy RPW41066BWMR/1-S - Candy RPW41066BWMR/1-S
- Hoover H-WASH 500 - Hoover H-WASH 500