diff --git a/custom_components/hOn/__init__.py b/custom_components/hOn/__init__.py index 7d0800e..8c3917b 100644 --- a/custom_components/hOn/__init__.py +++ b/custom_components/hOn/__init__.py @@ -38,3 +38,12 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, platform) ) + return True + + +async def async_unload_entry(hass, entry: ConfigEntry) -> bool: + unload = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) + if unload: + if not hass.data[DOMAIN]: + hass.data.pop(DOMAIN, None) + return unload diff --git a/custom_components/hOn/const.py b/custom_components/hOn/const.py index f6fdb4d..ad87ea4 100755 --- a/custom_components/hOn/const.py +++ b/custom_components/hOn/const.py @@ -4,5 +4,6 @@ PLATFORMS = [ "sensor", "select", "number", + "switch", "button" ] diff --git a/custom_components/hOn/number.py b/custom_components/hOn/number.py index c206a26..0c41648 100644 --- a/custom_components/hOn/number.py +++ b/custom_components/hOn/number.py @@ -17,11 +17,6 @@ from .hon import HonEntity, HonCoordinator NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { "WM": ( - NumberEntityDescription( - key="startProgram.delayStatus", - name="Delay Status", - entity_category=EntityCategory.CONFIG - ), NumberEntityDescription( key="startProgram.delayTime", name="Delay Time", @@ -29,11 +24,6 @@ NUMBERS: dict[str, tuple[NumberEntityDescription, ...]] = { entity_category=EntityCategory.CONFIG, native_unit_of_measurement=UnitOfTime.MINUTES ), - NumberEntityDescription( - key="startProgram.haier_SoakPrewashSelection", - name="Soak Prewash Selection", - entity_category=EntityCategory.CONFIG - ), NumberEntityDescription( key="startProgram.rinseIterations", name="Rinse Iterations", diff --git a/custom_components/hOn/sensor.py b/custom_components/hOn/sensor.py index 6e21f5e..774019f 100644 --- a/custom_components/hOn/sensor.py +++ b/custom_components/hOn/sensor.py @@ -9,7 +9,7 @@ from homeassistant.components.sensor import ( SensorEntityDescription, ) from homeassistant.config_entries import ConfigEntry -from homeassistant.const import UnitOfEnergy, UnitOfVolume, UnitOfMass +from homeassistant.const import UnitOfEnergy, UnitOfVolume, UnitOfMass, UnitOfPower from homeassistant.core import callback from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.typing import StateType @@ -45,6 +45,8 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { key="currentElectricityUsed", name="Current Electricity Used", state_class=SensorStateClass.MEASUREMENT, + device_class=SensorDeviceClass.POWER, + native_unit_of_measurement=UnitOfPower.KILO_WATT, icon="mdi:lightning-bolt" ), SensorEntityDescription( @@ -61,6 +63,16 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { native_unit_of_measurement=UnitOfMass.KILOGRAMS, icon="mdi:weight-kilogram" ), + SensorEntityDescription( + key="machMode", + name="Mode", + translation_key="mode" + ), + SensorEntityDescription( + key="errors", + name="Last Error", + translation_key="errors" + ), ) } diff --git a/custom_components/hOn/switch.py b/custom_components/hOn/switch.py new file mode 100644 index 0000000..05155f1 --- /dev/null +++ b/custom_components/hOn/switch.py @@ -0,0 +1,113 @@ +from collections.abc import Callable, Coroutine +from dataclasses import dataclass +from typing import Any + +from pyhon import HonConnection +from pyhon.device import HonDevice + +from homeassistant.components.switch import SwitchEntityDescription, SwitchEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import EntityCategory + +from .const import DOMAIN +from .hon import HonCoordinator, HonEntity + + +@dataclass +class HonSwitchEntityDescriptionMixin: + turn_on_key: str = "" + turn_off_key: str = "" + + +@dataclass +class HonSwitchEntityDescription(HonSwitchEntityDescriptionMixin, + SwitchEntityDescription +): + pass + + +SWITCHES: dict[str, tuple[HonSwitchEntityDescription, ...]] = { + "WM": ( + HonSwitchEntityDescription( + key="startProgram", + name="Start Program", + icon="mdi:play", + turn_on_key="startProgram", + turn_off_key="stopProgram", + ), + HonSwitchEntityDescription( + key="startProgram.delayStatus", + name="Delay Status", + entity_category=EntityCategory.CONFIG + ), + HonSwitchEntityDescription( + key="startProgram.haier_SoakPrewashSelection", + name="Soak Prewash Selection", + entity_category=EntityCategory.CONFIG + ), + ), +} + + +async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None: + hon: HonConnection = hass.data[DOMAIN][entry.unique_id] + coordinators = hass.data[DOMAIN]["coordinators"] + appliances = [] + for device in hon.devices: + if device.mac_address in coordinators: + coordinator = hass.data[DOMAIN]["coordinators"][device.mac_address] + else: + coordinator = HonCoordinator(hass, device) + hass.data[DOMAIN]["coordinators"][device.mac_address] = coordinator + await coordinator.async_config_entry_first_refresh() + + if descriptions := SWITCHES.get(device.appliance_type_name): + for description in descriptions: + appliances.extend([ + HonSwitchEntity(hass, coordinator, entry, device, description)] + ) + + async_add_entities(appliances) + + +class HonSwitchEntity(HonEntity, SwitchEntity): + entity_description: HonSwitchEntityDescription + + def __init__(self, hass, coordinator, entry, device: HonDevice, description: HonSwitchEntityDescription) -> None: + super().__init__(hass, entry, coordinator, device) + self._coordinator = coordinator + self._device = device + self.entity_description = description + self._attr_unique_id = f"{super().unique_id}{description.key}" + + def available(self) -> bool: + if self.entity_category == EntityCategory.CONFIG: + return self._device.settings[self.entity_description.key].typology == "fixed" + return True + + + @property + def is_on(self) -> bool | None: + """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 self._device.data.get(self.entity_description.key, "") + + async def async_turn_on(self, **kwargs: Any) -> None: + if self.entity_category == EntityCategory.CONFIG: + setting = self._device.settings[self.entity_description.key] + setting.value = setting.max + self.async_write_ha_state() + else: + await self._device.commands[self.entity_description.turn_on_key].send() + + async def async_turn_off(self, **kwargs: Any) -> None: + if self.entity_category == EntityCategory.CONFIG: + setting = self._device.settings[self.entity_description.key] + setting.value = setting.min + self.async_write_ha_state() + else: + await self._device.commands[self.entity_description.turn_off_key].send() + + diff --git a/custom_components/hOn/translations/en.json b/custom_components/hOn/translations/en.json index 07c0c04..c65dadc 100644 --- a/custom_components/hOn/translations/en.json +++ b/custom_components/hOn/translations/en.json @@ -1,14 +1,32 @@ { - "config": { - "step": { - "user": { - "title": "hOn", - "description": "Please enters your hOn credentials", - "data": { - "email": "Email Address", - "password": "Password" - } - } + "config": { + "step": { + "user": { + "title": "hOn", + "description": "Please enters your hOn credentials", + "data": { + "email": "Email Address", + "password": "Password" } + } } -} \ No newline at end of file + }, + "entity": { + "sensor": { + "mode": { + "state": { + "1": "Ready", + "2": "Running", + "5": "Scheduled", + "6": "Error", + "7": "Finished" + } + }, + "errors": { + "00": "No error", + "100000000000": "E2: Check if the door is closed", + "8000000000000": "E4: Check the water supply" + } + } + } +}