From 3ed335d356c33e4656c30c798fdee7b4251b06b2 Mon Sep 17 00:00:00 2001 From: Andre Basche Date: Sat, 1 Jul 2023 14:24:04 +0200 Subject: [PATCH] Add lock --- README.md | 2 +- custom_components/hon/const.py | 1 + custom_components/hon/lock.py | 85 +++++++++++++++++++++++++++++++++ custom_components/hon/sensor.py | 3 +- custom_components/hon/switch.py | 5 -- scripts/check.py | 7 ++- scripts/sensor_docs.py | 10 ++-- 7 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 custom_components/hon/lock.py diff --git a/README.md b/README.md index cb0fbc6..0b9c4c6 100644 --- a/README.md +++ b/README.md @@ -181,7 +181,6 @@ For every device exists a button under diagnostics which can be used to log all | Aroma Time On | `thermometer` | `number` | `settings.aromaTimeOn` | | Diffuser Level | | `select` | `settings.aromaStatus` | | Light status | | `light` | `settings.lightStatus` | -| Lock Status | | `switch` | `lockStatus` | | Mode | `run` | `select` | `settings.machMode` | | Pollen Level | | `number` | `settings.pollenLevel` | | Touch Tone | | `switch` | `touchToneStatus` | @@ -192,6 +191,7 @@ For every device exists a button under diagnostics which can be used to log all | CO Level | | `sensor` | `coLevel` | | Error | `math-log` | `sensor` | `errors` | | Humidity | | `sensor` | `humidityIndoor` | +| Lock Status | | `lock` | `lockStatus` | | Main Filter Status | | `sensor` | `mainFilterStatus` | | On | `power-cycle` | `binary_sensor` | `attributes.parameters.onOffStatus` | | PM 10 | | `sensor` | `pm10ValueIndoor` | diff --git a/custom_components/hon/const.py b/custom_components/hon/const.py index d2155ac..fdcc1f2 100644 --- a/custom_components/hon/const.py +++ b/custom_components/hon/const.py @@ -19,6 +19,7 @@ PLATFORMS = [ "climate", "fan", "light", + "lock", ] APPLIANCES = { diff --git a/custom_components/hon/lock.py b/custom_components/hon/lock.py new file mode 100644 index 0000000..b130136 --- /dev/null +++ b/custom_components/hon/lock.py @@ -0,0 +1,85 @@ +import logging +from typing import Any + +from homeassistant.components.lock import LockEntity, LockEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import callback +from pyhon.parameter.base import HonParameter +from pyhon.parameter.range import HonParameterRange + +from .const import DOMAIN +from .hon import HonEntity + +_LOGGER = logging.getLogger(__name__) + +LOCKS: dict[str, tuple[LockEntityDescription, ...]] = { + "AP": ( + LockEntityDescription( + key="lockStatus", + name="Lock Status", + translation_key="mode", + ), + ), +} + + +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 LOCKS.get(device.appliance_type, []): + if ( + f"settings.{description.key}" not in device.available_settings + or device.get(description.key) is None + ): + continue + entity = HonLockEntity(hass, entry, device, description) + await entity.coordinator.async_config_entry_first_refresh() + entities.append(entity) + + async_add_entities(entities) + + +class HonLockEntity(HonEntity, LockEntity): + entity_description: LockEntityDescription + + @property + def is_locked(self) -> bool | None: + """Return a boolean for the state of the lock.""" + """Return True if entity is on.""" + return self._device.get(self.entity_description.key, 0) == 1 + + async def async_lock(self, **kwargs: Any) -> None: + """Lock method.""" + setting = self._device.settings[f"settings.{self.entity_description.key}"] + if type(setting) == HonParameter: + return + setting.value = setting.max if isinstance(setting, HonParameterRange) else 1 + self.async_write_ha_state() + await self._device.commands["settings"].send() + await self.coordinator.async_refresh() + + async def async_unlock(self, **kwargs: Any) -> None: + """Unlock method.""" + setting = self._device.settings[f"settings.{self.entity_description.key}"] + if type(setting) == HonParameter: + return + setting.value = setting.min if isinstance(setting, HonParameterRange) else 0 + self.async_write_ha_state() + await self._device.commands["settings"].send() + await self.coordinator.async_refresh() + + @property + def available(self) -> bool: + """Return True if entity is available.""" + return ( + super().available + and int(self._device.get("remoteCtrValid", 1)) == 1 + and self._device.get("attributes.lastConnEvent.category") != "DISCONNECTED" + ) + + @callback + def _handle_coordinator_update(self, update=True) -> None: + value = self._device.get(self.entity_description.key, 0) + self._attr_is_locked = self.is_locked + if update: + self.async_write_ha_state() diff --git a/custom_components/hon/sensor.py b/custom_components/hon/sensor.py index cb54814..668fb0b 100644 --- a/custom_components/hon/sensor.py +++ b/custom_components/hon/sensor.py @@ -12,7 +12,6 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( PERCENTAGE, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, - CONCENTRATION_PARTS_PER_BILLION, CONCENTRATION_PARTS_PER_MILLION, ) from homeassistant.const import ( @@ -736,7 +735,7 @@ SENSORS: dict[str, tuple[SensorEntityDescription, ...]] = { name="VOC", state_class=SensorStateClass.MEASUREMENT, device_class=SensorDeviceClass.VOLATILE_ORGANIC_COMPOUNDS, - native_unit_of_measurement=CONCENTRATION_PARTS_PER_BILLION, + native_unit_of_measurement=CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, translation_key="voc", ), HonSensorEntityDescription( diff --git a/custom_components/hon/switch.py b/custom_components/hon/switch.py index 3823643..8e919fe 100644 --- a/custom_components/hon/switch.py +++ b/custom_components/hon/switch.py @@ -336,11 +336,6 @@ SWITCHES: dict[str, tuple[HonSwitchEntityDescription, ...]] = { name="Touch Tone", translation_key="touch_tone", ), - HonSwitchEntityDescription( - key="lockStatus", - name="Lock Status", - translation_key="mode", - ), ), } diff --git a/scripts/check.py b/scripts/check.py index 150259f..2045d71 100755 --- a/scripts/check.py +++ b/scripts/check.py @@ -2,6 +2,7 @@ import sys from pathlib import Path + if __name__ == "__main__": sys.path.insert(0, str(Path(__file__).parent.parent)) @@ -10,6 +11,7 @@ from custom_components.hon.button import BUTTONS from custom_components.hon.climate import CLIMATES from custom_components.hon.fan import FANS from custom_components.hon.light import LIGHTS +from custom_components.hon.lock import LOCKS from custom_components.hon.number import NUMBERS from custom_components.hon.select import SELECTS from custom_components.hon.sensor import SENSORS @@ -18,12 +20,13 @@ from custom_components.hon.switch import SWITCHES entities = { "binary_sensor": BINARY_SENSORS, "button": BUTTONS, - "light": LIGHTS, "climate": CLIMATES, + "fan": FANS, + "light": LIGHTS, + "lock": LOCKS, "number": NUMBERS, "select": SELECTS, "sensor": SENSORS, - "fan": FANS, "switch": SWITCHES, } diff --git a/scripts/sensor_docs.py b/scripts/sensor_docs.py index cc144d4..855efe0 100755 --- a/scripts/sensor_docs.py +++ b/scripts/sensor_docs.py @@ -11,12 +11,13 @@ if __name__ == "__main__": from custom_components.hon.const import APPLIANCES from custom_components.hon.binary_sensor import BINARY_SENSORS from custom_components.hon.button import BUTTONS -from custom_components.hon.light import LIGHTS from custom_components.hon.climate import CLIMATES +from custom_components.hon.fan import FANS +from custom_components.hon.light import LIGHTS +from custom_components.hon.lock import LOCKS 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, @@ -28,12 +29,13 @@ ENTITY_CATEGORY_SORT = ["control", "config", "sensor"] entities = { "binary_sensor": BINARY_SENSORS, "button": BUTTONS, - "light": LIGHTS, "climate": CLIMATES, + "fan": FANS, + "light": LIGHTS, + "lock": LOCKS, "number": NUMBERS, "select": SELECTS, "sensor": SENSORS, - "fan": FANS, "switch": SWITCHES, }