diff --git a/README.md b/README.md index 48ba375..85eb1a1 100644 --- a/README.md +++ b/README.md @@ -61,42 +61,18 @@ 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 -- Haier HWD100-B14979 -- Haier HWO60SM2F3XH -- Haier XIB 3B2SFS-80 -- Haier XIB 6B2D3FB -- Candy BCTDH7A1TE -- Candy CCE4T620EWU -- Candy CIS633SCTTWIFI -- Candy CSOE C10DE-80 -- Candy RO44 1286DWMC4-07 -- Candy ROE H9A3TCEX-S -- Candy RPW41066BWMR/1-S -- Hoover H-WASH 500 -- Hoover H-DRY 500 -- Hoover H7W4 48MBC-S -- Hoover H9A3TCBEXS-S -- Hoover HFB 6B2S3FX -- Hoover HLE C10DCE-80 -- Hoover HSOT3161WG -- Hoover HW 68AMC/1-80 -- Hoover HWPD 69AMBC/1-S -- Hoover HWPS4954DAMR-11 -- Hoover NDE H10A2TCE-80 -- Hoover NDE H9A2TSBEXS-S -- Hoover NDPHY10A2TCBEXSS + +| | **Haier** | **Hoover** | **Candy** | +|--------------------|------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------| +| **Washing Machine** | HW90-B14TEAM5
HW100-B14959U1 | H-WASH 500
H7W4 48MBC-S | RO44 1286DWMC4-07
HW 68AMC/1-80
HWPD 69AMBC/1-S | +| **Tumble Dryer** | HD80-A3959 | H-DRY 500
H9A3TCBEXS-S
HLE C10DCE-80
NDE H10A2TCE-80
NDE H9A2TSBEXS-S
NDPHY10A2TCBEXSS | BCTDH7A1TE
CSOE C10DE-80
ROE H9A3TCEX-S | +| **Washer Dryer** | HWD100-B14979 | HWPS4954DAMR-11 | RPW41066BWMR/1-S | +| **Oven** | HWO60SM2F3XH | HSOT3161WG | | +| **Dish Washer** | XIB 3B2SFS-80
XIB 6B2D3FB | HFB 6B2S3FX | | +| **Air conditioner** | AD105S2SM3FA
AS20HPL1HRA
AS25PBAHRA
AS25S2SF1FA-WH
AS25TADHRA-2
AS35TADHRA-2
| | | +| **Fridge** | HFW7720ENMB | | CCE4T620EWU | +| **Hob** | HA2MTSJ68MC | | CIS633SCTTWIFI | +| **Hood** | HADG6DS46BWIFI | | | ## Contribute Any kind of contribution is welcome! @@ -237,11 +213,11 @@ For every device exists a hidden button which can be used to log all infos of yo | --- | --- | --- | --- | | Start Program | `hvac` | `button` | `startProgram` | | Stop Program | `hvac-off` | `button` | `stopProgram` | +| Wind Speed | | `fan` | `settings.windSpeed` | #### Configs | Name | Icon | Entity | Key | | --- | --- | --- | --- | | Light status | `lightbulb` | `number` | `startProgram.lightStatus` | -| Wind speed | `fan` | `number` | `startProgram.windSpeed` | #### Sensors | Name | Icon | Entity | Key | | --- | --- | --- | --- | @@ -257,7 +233,6 @@ For every device exists a hidden button which can be used to log all infos of yo | 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 diff --git a/custom_components/hon/climate.py b/custom_components/hon/climate.py index 5c5fa2a..c8fd587 100644 --- a/custom_components/hon/climate.py +++ b/custom_components/hon/climate.py @@ -213,7 +213,7 @@ class HonACClimateEntity(HonEntity, ClimateEntity): class HonClimateEntity(HonEntity, ClimateEntity): - entity_description = HonClimateEntityDescription + entity_description: HonClimateEntityDescription def __init__(self, hass, entry, device: HonAppliance, description) -> None: super().__init__(hass, entry, device, description) diff --git a/custom_components/hon/const.py b/custom_components/hon/const.py index 1a2d022..5c89ef4 100644 --- a/custom_components/hon/const.py +++ b/custom_components/hon/const.py @@ -17,6 +17,7 @@ PLATFORMS = [ "button", "binary_sensor", "climate", + "fan", ] HON_HVAC_MODE = { diff --git a/custom_components/hon/fan.py b/custom_components/hon/fan.py new file mode 100644 index 0000000..80e3251 --- /dev/null +++ b/custom_components/hon/fan.py @@ -0,0 +1,122 @@ +import logging +import math +from dataclasses import dataclass +from typing import Any + +from homeassistant.components.fan import ( + FanEntityDescription, + FanEntity, + FanEntityFeature, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import callback +from homeassistant.util.percentage import ( + percentage_to_ranged_value, + ranged_value_to_percentage, +) +from pyhon.appliance import HonAppliance +from pyhon.parameter.range import HonParameterRange + +from .const import DOMAIN +from .hon import HonEntity + +_LOGGER = logging.getLogger(__name__) + + +@dataclass +class HonFanEntityDescription(FanEntityDescription): + pass + + +FANS = { + "HO": ( + HonFanEntityDescription( + key="settings.windSpeed", + name="Wind Speed", + translation_key="air_extraction", + ), + ), +} + + +async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: + entities = [] + for device in hass.data[DOMAIN][entry.unique_id].appliances: + for description in FANS.get(device.appliance_type, []): + if isinstance(description, HonFanEntityDescription): + if description.key not in device.available_settings or not device.get( + description.key.split(".")[-1] + ): + continue + entity = HonFanEntity(hass, entry, device, description) + else: + continue + await entity.coordinator.async_config_entry_first_refresh() + entities.append(entity) + async_add_entities(entities) + + +class HonFanEntity(HonEntity, FanEntity): + entity_description: HonFanEntityDescription + + def __init__(self, hass, entry, device: HonAppliance, description) -> None: + self._attr_supported_features = FanEntityFeature.SET_SPEED + self._wind_speed: HonParameterRange = device.settings.get(description.key) + self._speed_range = ( + int(self._wind_speed.values[1]), + int(self._wind_speed.values[-1]), + ) + self._command, self._parameter = description.key.split(".") + + super().__init__(hass, entry, device, description) + self._handle_coordinator_update(update=False) + + @property + def percentage(self) -> int | None: + """Return the current speed.""" + value = int(self._device.get(self._parameter, "0")) + return ranged_value_to_percentage(self._speed_range, value) + + @property + def speed_count(self) -> int: + """Return the number of speeds the fan supports.""" + return len(self._wind_speed.values[1:]) + + async def async_set_percentage(self, percentage: int) -> None: + """Set the speed percentage of the fan.""" + mode = math.ceil(percentage_to_ranged_value(self._speed_range, percentage)) + self._device.settings[self.entity_description.key].value = mode + await self._device.commands[self._command].send() + self.async_write_ha_state() + + @property + def is_on(self) -> bool | None: + """Return true if device is on.""" + mode = math.ceil(percentage_to_ranged_value(self._speed_range, self.percentage)) + return mode > self._wind_speed.min + + async def async_turn_on( + self, + percentage: int | None = None, + preset_mode: str | None = None, + **kwargs: Any, + ) -> None: + """Turn the entity on.""" + if percentage is None: + percentage = ranged_value_to_percentage( + self._speed_range, int(self._wind_speed.values[1]) + ) + await self.async_set_percentage(percentage) + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the entity off.""" + self._device.settings[self.entity_description.key].value = 0 + await self._device.commands[self._command].send() + self.async_write_ha_state() + + @callback + def _handle_coordinator_update(self, update=True) -> None: + self._wind_speed = self._device.settings.get(self.entity_description.key) + self._attr_percentage = self.percentage + if update: + self.async_write_ha_state() diff --git a/custom_components/hon/number.py b/custom_components/hon/number.py index cd9f182..43f3761 100644 --- a/custom_components/hon/number.py +++ b/custom_components/hon/number.py @@ -163,12 +163,6 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { ), ), "HO": ( - HonNumberEntityDescription( - key="startProgram.windSpeed", - name="Wind speed", - icon="mdi:fan", - entity_category=EntityCategory.CONFIG, - ), HonNumberEntityDescription( key="startProgram.lightStatus", name="Light status", diff --git a/custom_components/hon/sensor.py b/custom_components/hon/sensor.py index 58eddd5..7de75f9 100644 --- a/custom_components/hon/sensor.py +++ b/custom_components/hon/sensor.py @@ -593,11 +593,6 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { name="RGB Light Status", icon="mdi:lightbulb", ), - HonSensorEntityDescription( - key="windSpeed", - name="Wind Speed", - icon="mdi:fan", - ), ), } SENSORS["WD"] = unique_entities(SENSORS["WM"], SENSORS["TD"]) diff --git a/custom_components/hon/translations/cs.json b/custom_components/hon/translations/cs.json index b4b0174..32d551d 100644 --- a/custom_components/hon/translations/cs.json +++ b/custom_components/hon/translations/cs.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Odsávání vzduchu" + } } }, "config": { diff --git a/custom_components/hon/translations/de.json b/custom_components/hon/translations/de.json index 1e5de11..3948c4c 100644 --- a/custom_components/hon/translations/de.json +++ b/custom_components/hon/translations/de.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Dunstabzug" + } } }, "config": { diff --git a/custom_components/hon/translations/el.json b/custom_components/hon/translations/el.json index 8b44d1d..67ea26c 100644 --- a/custom_components/hon/translations/el.json +++ b/custom_components/hon/translations/el.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Εξαγωγή αέρα" + } } }, "config": { diff --git a/custom_components/hon/translations/en.json b/custom_components/hon/translations/en.json index 40ee14b..865947d 100644 --- a/custom_components/hon/translations/en.json +++ b/custom_components/hon/translations/en.json @@ -2056,6 +2056,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Air extraction" + } } } } \ No newline at end of file diff --git a/custom_components/hon/translations/es.json b/custom_components/hon/translations/es.json index d328fb1..716bef3 100644 --- a/custom_components/hon/translations/es.json +++ b/custom_components/hon/translations/es.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Extracción de aire" + } } }, "config": { diff --git a/custom_components/hon/translations/fr.json b/custom_components/hon/translations/fr.json index c114d0e..c8bef75 100644 --- a/custom_components/hon/translations/fr.json +++ b/custom_components/hon/translations/fr.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Extraction de l'air" + } } }, "config": { diff --git a/custom_components/hon/translations/he.json b/custom_components/hon/translations/he.json index 95543ba..9a735c9 100644 --- a/custom_components/hon/translations/he.json +++ b/custom_components/hon/translations/he.json @@ -1065,6 +1065,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Air extraction" + } } }, "config": { diff --git a/custom_components/hon/translations/hr.json b/custom_components/hon/translations/hr.json index cf4a912..56f91e6 100644 --- a/custom_components/hon/translations/hr.json +++ b/custom_components/hon/translations/hr.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Odvođenje zraka" + } } }, "config": { diff --git a/custom_components/hon/translations/it.json b/custom_components/hon/translations/it.json index 3528120..c1bfb25 100644 --- a/custom_components/hon/translations/it.json +++ b/custom_components/hon/translations/it.json @@ -2031,6 +2031,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Aspirazione aria" + } } } } \ No newline at end of file diff --git a/custom_components/hon/translations/nl.json b/custom_components/hon/translations/nl.json index 931ccd0..a8cd576 100644 --- a/custom_components/hon/translations/nl.json +++ b/custom_components/hon/translations/nl.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Luchtafvoer" + } } }, "config": { diff --git a/custom_components/hon/translations/pl.json b/custom_components/hon/translations/pl.json index 4ca0b36..a757390 100644 --- a/custom_components/hon/translations/pl.json +++ b/custom_components/hon/translations/pl.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Wyciąg powietrza" + } } }, "config": { diff --git a/custom_components/hon/translations/pt.json b/custom_components/hon/translations/pt.json index 0828ace..1e68672 100644 --- a/custom_components/hon/translations/pt.json +++ b/custom_components/hon/translations/pt.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Extração de ar" + } } }, "config": { diff --git a/custom_components/hon/translations/ro.json b/custom_components/hon/translations/ro.json index 496959f..8c55275 100644 --- a/custom_components/hon/translations/ro.json +++ b/custom_components/hon/translations/ro.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Extracția aerului" + } } }, "config": { diff --git a/custom_components/hon/translations/ru.json b/custom_components/hon/translations/ru.json index fda96d3..334f94d 100644 --- a/custom_components/hon/translations/ru.json +++ b/custom_components/hon/translations/ru.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Отвод воздуха" + } } }, "config": { diff --git a/custom_components/hon/translations/sk.json b/custom_components/hon/translations/sk.json index c362022..7828e6b 100644 --- a/custom_components/hon/translations/sk.json +++ b/custom_components/hon/translations/sk.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Odsávanie vzduchu" + } } }, "config": { diff --git a/custom_components/hon/translations/sl.json b/custom_components/hon/translations/sl.json index 9ecfb4a..e0b5fff 100644 --- a/custom_components/hon/translations/sl.json +++ b/custom_components/hon/translations/sl.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Odvajanje zraka" + } } }, "config": { diff --git a/custom_components/hon/translations/sr.json b/custom_components/hon/translations/sr.json index 0f6d151..7faaece 100644 --- a/custom_components/hon/translations/sr.json +++ b/custom_components/hon/translations/sr.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Usisavanje vazduha" + } } }, "config": { diff --git a/custom_components/hon/translations/tr.json b/custom_components/hon/translations/tr.json index 3ce58af..1c80588 100644 --- a/custom_components/hon/translations/tr.json +++ b/custom_components/hon/translations/tr.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "Hava tahliyesi" + } } }, "config": { diff --git a/custom_components/hon/translations/zh.json b/custom_components/hon/translations/zh.json index 2dd5af7..199f7ff 100644 --- a/custom_components/hon/translations/zh.json +++ b/custom_components/hon/translations/zh.json @@ -1979,6 +1979,11 @@ } } } + }, + "fan": { + "air_extraction": { + "name": "抽气" + } } }, "config": { diff --git a/info.md b/info.md index dd6d537..88d50e8 100644 --- a/info.md +++ b/info.md @@ -50,42 +50,18 @@ 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 -- Haier HWD100-B14979 -- Haier HWO60SM2F3XH -- Haier XIB 3B2SFS-80 -- Haier XIB 6B2D3FB -- Candy BCTDH7A1TE -- Candy CCE4T620EWU -- Candy CIS633SCTTWIFI -- Candy CSOE C10DE-80 -- Candy RO44 1286DWMC4-07 -- Candy ROE H9A3TCEX-S -- Candy RPW41066BWMR/1-S -- Hoover H-WASH 500 -- Hoover H-DRY 500 -- Hoover H7W4 48MBC-S -- Hoover H9A3TCBEXS-S -- Hoover HFB 6B2S3FX -- Hoover HLE C10DCE-80 -- Hoover HSOT3161WG -- Hoover HW 68AMC/1-80 -- Hoover HWPD 69AMBC/1-S -- Hoover HWPS4954DAMR-11 -- Hoover NDE H10A2TCE-80 -- Hoover NDE H9A2TSBEXS-S -- Hoover NDPHY10A2TCBEXSS + +| | **Haier** | **Hoover** | **Candy** | +|--------------------|------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------| +| **Washing Machine** | HW90-B14TEAM5
HW100-B14959U1 | H-WASH 500
H7W4 48MBC-S | RO44 1286DWMC4-07
HW 68AMC/1-80
HWPD 69AMBC/1-S | +| **Tumble Dryer** | HD80-A3959 | H-DRY 500
H9A3TCBEXS-S
HLE C10DCE-80
NDE H10A2TCE-80
NDE H9A2TSBEXS-S
NDPHY10A2TCBEXSS | BCTDH7A1TE
CSOE C10DE-80
ROE H9A3TCEX-S | +| **Washer Dryer** | HWD100-B14979 | HWPS4954DAMR-11 | RPW41066BWMR/1-S | +| **Oven** | HWO60SM2F3XH | HSOT3161WG | | +| **Dish Washer** | XIB 3B2SFS-80
XIB 6B2D3FB | HFB 6B2S3FX | | +| **Air conditioner** | AD105S2SM3FA
AS20HPL1HRA
AS25PBAHRA
AS25S2SF1FA-WH
AS25TADHRA-2
AS35TADHRA-2
| | | +| **Fridge** | HFW7720ENMB | | CCE4T620EWU | +| **Hob** | HA2MTSJ68MC | | CIS633SCTTWIFI | +| **Hood** | HADG6DS46BWIFI | | | ## Contribute Want to help us to support more appliances? Or add more sensors? Or help with translating? Or beautify some icons or captions? diff --git a/scripts/generate_translation.py b/scripts/generate_translation.py index 01164af..53d66bc 100755 --- a/scripts/generate_translation.py +++ b/scripts/generate_translation.py @@ -248,6 +248,7 @@ NAMES = { "freezer": "REF.ZONES.FREEZER", "oven": "GLOBALS.APPLIANCES_NAME.OV", }, + "fan": {"air_extraction": "HO.DASHBOARD.AIR_EXTRACTION_TITLE"}, } diff --git a/scripts/sensor_docs.py b/scripts/sensor_docs.py index 655dbf6..e009186 100755 --- a/scripts/sensor_docs.py +++ b/scripts/sensor_docs.py @@ -14,6 +14,7 @@ from custom_components.hon.climate import CLIMATES from custom_components.hon.number import NUMBERS from custom_components.hon.select import SELECTS from custom_components.hon.sensor import SENSORS +from custom_components.hon.fan import FANS from custom_components.hon.switch import ( SWITCHES, HonControlSwitchEntityDescription, @@ -48,6 +49,7 @@ entities = { "sensor": SENSORS, "switch": SWITCHES, "climate": CLIMATES, + "fan": FANS, } result = {}