From caf34b194c708ed6bb0f0dcb026d9cec99d65f8b Mon Sep 17 00:00:00 2001 From: Vincent Le Bourlot Date: Wed, 17 Aug 2022 11:36:40 +0200 Subject: [PATCH] Fix setting speed level for air purifiers. (#46) * Fix a wrong default value in has_feature. * Add SET_SPEED as supported feature and fix PUR131S speed range * fix night_light discovery * fix air quality and filter life sensors * fix style. --- custom_components/vesync/common.py | 10 ++++++-- custom_components/vesync/fan.py | 18 +++++++++----- custom_components/vesync/light.py | 2 +- custom_components/vesync/sensor.py | 40 +++++++++++++++++------------- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/custom_components/vesync/common.py b/custom_components/vesync/common.py index bac014d..f80283f 100644 --- a/custom_components/vesync/common.py +++ b/custom_components/vesync/common.py @@ -21,7 +21,7 @@ _LOGGER = logging.getLogger(__name__) def has_feature(device, dictionary, attribute): """Return the detail of the attribute.""" - return getattr(device, dictionary, None).get(attribute, None) is not None + return getattr(device, dictionary, {}).get(attribute, None) is not None def is_humidifier(device_type: str) -> bool: @@ -48,7 +48,13 @@ async def async_process_devices(hass, manager): await hass.async_add_executor_job(manager.update) - _LOGGER.debug("Found the following devices: %s", async_redact_data({k: [d.__dict__ for d in v] for k,v in manager._dev_list.items()},["cid","uuid","mac_id"])) + _LOGGER.debug( + "Found the following devices: %s", + async_redact_data( + {k: [d.__dict__ for d in v] for k, v in manager._dev_list.items()}, + ["cid", "uuid", "mac_id"], + ), + ) if manager.fans: for fan in manager.fans: diff --git a/custom_components/vesync/fan.py b/custom_components/vesync/fan.py index 7579041..48eebdd 100644 --- a/custom_components/vesync/fan.py +++ b/custom_components/vesync/fan.py @@ -13,7 +13,7 @@ from homeassistant.util.percentage import ( ranged_value_to_percentage, ) -from .common import VeSyncDevice +from .common import VeSyncDevice, has_feature from .const import ( DEV_TYPE_TO_HA, DOMAIN, @@ -76,8 +76,11 @@ class VeSyncFanHA(VeSyncDevice, FanEntity): """Initialize the VeSync fan device.""" super().__init__(fan) self.smartfan = fan - if hasattr(self.smartfan, "config_dict"): + self._speed_range = (1, 1) + self._attr_preset_modes = [VS_MODE_MANUAL, VS_MODE_AUTO, VS_MODE_SLEEP] + if has_feature(self.smartfan, "config_dict", VS_LEVELS): self._speed_range = (1, max(self.smartfan.config_dict[VS_LEVELS])) + if has_feature(self.smartfan, "config_dict", VS_MODES): self._attr_preset_modes = [ VS_MODE_MANUAL, *[ @@ -86,14 +89,17 @@ class VeSyncFanHA(VeSyncDevice, FanEntity): if mode in self.smartfan.config_dict[VS_MODES] ], ] - else: - self._speed_range = (1, 1) - self._attr_preset_modes = [VS_MODE_MANUAL, VS_MODE_AUTO, VS_MODE_SLEEP] + if self.smartfan.device_type == "LV-PUR131S": + self._speed_range = (1, 3) @property def supported_features(self): """Flag supported features.""" - return FanEntityFeature.PRESET_MODE if self.speed_count > 1 else 0 + return ( + FanEntityFeature.SET_SPEED | FanEntityFeature.PRESET_MODE + if self.speed_count > 1 + else FanEntityFeature.SET_SPEED + ) @property def percentage(self): diff --git a/custom_components/vesync/light.py b/custom_components/vesync/light.py index cc4c865..1d83cbc 100644 --- a/custom_components/vesync/light.py +++ b/custom_components/vesync/light.py @@ -50,7 +50,7 @@ def _setup_entities(devices, async_add_entities): entities.append(VeSyncDimmableLightHA(dev)) if DEV_TYPE_TO_HA.get(dev.device_type) in ("bulb-tunable-white",): entities.append(VeSyncTunableWhiteLightHA(dev)) - if dev.night_light: + if hasattr(dev, "night_light"): entities.append(VeSyncNightLightHA(dev)) async_add_entities(entities, update_before_add=True) diff --git a/custom_components/vesync/sensor.py b/custom_components/vesync/sensor.py index 00e27af..71502db 100644 --- a/custom_components/vesync/sensor.py +++ b/custom_components/vesync/sensor.py @@ -171,6 +171,9 @@ class VeSyncHumidifierSensorEntity(VeSyncBaseEntity, SensorEntity): class VeSyncAirQualitySensor(VeSyncHumidifierSensorEntity): """Representation of an air quality sensor.""" + _attr_state_class = SensorStateClass.MEASUREMENT + _attr_device_class = SensorDeviceClass.AQI + @property def unique_id(self): """Return unique ID for air quality sensor on device.""" @@ -181,25 +184,15 @@ class VeSyncAirQualitySensor(VeSyncHumidifierSensorEntity): """Return sensor name.""" return f"{super().name} air quality" - @property - def device_class(self): - """Return the air quality device class.""" - return SensorDeviceClass.AQI - @property def native_value(self): """Return the air quality index.""" - return self.smarthumidifier.details["air_quality_value"] - - @property - def native_unit_of_measurement(self): - """Return the % unit of measurement.""" - return " " - - @property - def state_class(self): - """Return the measurement state class.""" - return SensorStateClass.MEASUREMENT + quality = None + if has_feature(self.smarthumidifier, "details", "air_quality"): + quality = self.smarthumidifier.details["air_quality"] + elif has_feature(self.smarthumidifier, "details", "air_quality_value"): + quality = self.smarthumidifier.details["air_quality_value"] + return quality.capitalize() if isinstance(quality, str) else quality class VeSyncFilterLifeSensor(VeSyncHumidifierSensorEntity): @@ -223,7 +216,11 @@ class VeSyncFilterLifeSensor(VeSyncHumidifierSensorEntity): @property def native_value(self): """Return the filter life index.""" - return self.smarthumidifier.details["filter_life"] + return ( + self.smarthumidifier.filter_life + if hasattr(self.smarthumidifier, "filter_life") + else self.smarthumidifier.details["filter_life"] + ) @property def native_unit_of_measurement(self): @@ -235,6 +232,15 @@ class VeSyncFilterLifeSensor(VeSyncHumidifierSensorEntity): """Return the measurement state class.""" return SensorStateClass.MEASUREMENT + @property + def state_attributes(self): + """Return the state attributes.""" + return ( + self.smarthumidifier.details["filter_life"] + if isinstance(self.smarthumidifier.details["filter_life"], dict) + else {} + ) + class VeSyncHumiditySensor(VeSyncHumidifierSensorEntity): """Representation of current humidity for a VeSync humidifier."""