diff --git a/custom_components/ha_blueair/blueair_update_coordinator.py b/custom_components/ha_blueair/blueair_update_coordinator.py index aff389e..61c81a6 100644 --- a/custom_components/ha_blueair/blueair_update_coordinator.py +++ b/custom_components/ha_blueair/blueair_update_coordinator.py @@ -150,6 +150,11 @@ def filter_expired(self) -> bool | None: """Return the current filter status.""" pass + @property + @abstractmethod + def auto_regulated_humidity(self) -> bool | None | NotImplemented: + pass + async def set_fan_speed(self, new_speed) -> None: await self.blueair_api_device.set_fan_speed(new_speed) await self.async_request_refresh() @@ -177,3 +182,7 @@ async def set_fan_auto_mode(self, value) -> None: @abstractmethod async def set_wick_dry_mode(self, value) -> None: pass + + @abstractmethod + async def set_auto_regulated_humidity(self, value) -> None: + pass diff --git a/custom_components/ha_blueair/blueair_update_coordinator_device.py b/custom_components/ha_blueair/blueair_update_coordinator_device.py index 339f6a7..593dea2 100644 --- a/custom_components/ha_blueair/blueair_update_coordinator_device.py +++ b/custom_components/ha_blueair/blueair_update_coordinator_device.py @@ -101,6 +101,10 @@ def wick_dry_mode(self) -> bool | None | NotImplemented: def water_shortage(self) -> bool | None | NotImplemented: return NotImplemented + @property + def auto_regulated_humidity(self) -> bool | None | NotImplemented: + return NotImplemented + async def set_brightness(self, brightness) -> None: # Convert Home Assistant brightness (0-255) to brightness (0-4) await self.blueair_api_device.set_brightness(round(brightness * 4 / 255.0)) @@ -117,3 +121,6 @@ async def set_fan_auto_mode(self, value) -> None: async def set_wick_dry_mode(self, value) -> None: raise NotImplementedError + + async def set_auto_regulated_humidity(self, value) -> None: + raise NotImplementedError diff --git a/custom_components/ha_blueair/blueair_update_coordinator_device_aws.py b/custom_components/ha_blueair/blueair_update_coordinator_device_aws.py index b53f4de..06606d5 100644 --- a/custom_components/ha_blueair/blueair_update_coordinator_device_aws.py +++ b/custom_components/ha_blueair/blueair_update_coordinator_device_aws.py @@ -71,6 +71,10 @@ def temperature(self) -> int | None | NotImplemented: def humidity(self) -> int | None | NotImplemented: return self.blueair_api_device.humidity + @property + def auto_regulated_humidity(self) -> int | None | NotImplemented: + return self.blueair_api_device.auto_regulated_humidity + @property def voc(self) -> int | None | NotImplemented: return self.blueair_api_device.tVOC @@ -135,3 +139,7 @@ async def set_fan_auto_mode(self, value) -> None: async def set_wick_dry_mode(self, value) -> None: await self.blueair_api_device.set_wick_dry_mode(value) await self.async_request_refresh() + + async def set_auto_regulated_humidity(self, value) -> None: + await self.blueair_api_device.set_auto_regulated_humidity(value) + await self.async_request_refresh() diff --git a/custom_components/ha_blueair/const.py b/custom_components/ha_blueair/const.py index 5e81a5e..6e10317 100644 --- a/custom_components/ha_blueair/const.py +++ b/custom_components/ha_blueair/const.py @@ -3,7 +3,7 @@ # Integration Setting Constants CONFIG_FLOW_VERSION: int = 2 -PLATFORMS = ["binary_sensor", "fan", "sensor", "light", "switch"] +PLATFORMS = ["binary_sensor", "fan", "humidifier", "light", "sensor", "switch"] # Home Assistant Data Storage Constants DATA_DEVICES: str = "api_devices" @@ -15,3 +15,6 @@ DEFAULT_FAN_SPEED_PERCENTAGE = 50 FILTER_EXPIRED_THRESHOLD = 95 + +# Custom Mode Constants +MODE_FAN_SPEED = "fan_speed" diff --git a/custom_components/ha_blueair/humidifier.py b/custom_components/ha_blueair/humidifier.py new file mode 100644 index 0000000..beaf653 --- /dev/null +++ b/custom_components/ha_blueair/humidifier.py @@ -0,0 +1,122 @@ +"""Support for Blueair humidifiers.""" + +from __future__ import annotations + +import logging + +from homeassistant.components.humidifier import ( + MODE_AUTO, + MODE_SLEEP, + HumidifierDeviceClass, + HumidifierEntity, + HumidifierEntityFeature, +) + +from .blueair_update_coordinator_device_aws import BlueairUpdateCoordinator +from .const import MODE_FAN_SPEED +from .entity import BlueairEntity, async_setup_entry_helper + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up the Blueair humidifier from config entry""" + + async_setup_entry_helper( + hass, + config_entry, + async_add_entities, + entity_classes=[ + BlueairAwsHumidifier, + ], + ) + + +class BlueairAwsHumidifier(BlueairEntity, HumidifierEntity): + """Controls Humidifier.""" + + @classmethod + def is_implemented(kls, coordinator): + return coordinator.auto_regulated_humidity is not NotImplemented + + def __init__(self, coordinator: BlueairUpdateCoordinator): + """Initialize the humidifer.""" + self._attr_device_class = HumidifierDeviceClass.HUMIDIFIER + self._attr_supported_features = HumidifierEntityFeature.MODES + self._attr_translation_key = "ha_blueair" + self._attr_available_modes = [ + MODE_AUTO, + MODE_SLEEP, + MODE_FAN_SPEED, + ] + super().__init__("Humidifier", coordinator) + + @property + def available(self) -> bool: + """Return if entity is available.""" + return self.coordinator.last_update_success and self.coordinator.online + + @property + def mode(self): + if self.coordinator.night_mode: + return MODE_SLEEP + elif self.coordinator.fan_auto_mode: + return MODE_AUTO + elif self.is_on: + return MODE_FAN_SPEED + else: + return + + @property + def is_on(self) -> int: + return self.coordinator.is_on + + @property + def target_humidity(self): + return self.coordinator.auto_regulated_humidity + + @property + def current_humidity(self): + return self.coordinator.humidity + + async def async_turn_off(self, **kwargs: any) -> None: + await self.coordinator.set_running(False) + self.async_write_ha_state() + + async def async_turn_on( + self, + percentage: int | None = None, + preset_mode: str | None = None, + **kwargs: any, + ) -> None: + await self.coordinator.set_running(True) + self.async_write_ha_state() + + async def async_set_mode(self, mode): + if mode == MODE_AUTO: + # This mode doesn't apply when off + await self.coordinator.set_fan_auto_mode(True) + await self.coordinator.set_running(True) + self.async_write_ha_state() + elif mode == MODE_SLEEP: + # This mode doesn't apply when off + await self.coordinator.set_night_mode(True) + await self.coordinator.set_fan_auto_mode(False) + await self.coordinator.set_running(False) + self.async_write_ha_state() + elif mode == MODE_FAN_SPEED: + # This mode doesn't apply when off + await self.coordinator.set_fan_auto_mode(False) + await self.coordinator.set_night_mode(False) + await self.coordinator.set_running(True) + + self.async_write_ha_state() + else: + raise ValueError(f"Invalid mode: {mode}") + + async def async_set_humidity(self, humidity): + """Set the humidity level. Sets Humidifier to 'On' to comply with hass requirements, and sets mode to Auto since this is the only mode in which the target humidity is used.""" + + await self.coordinator.set_auto_regulated_humidity(humidity) + await self.coordinator.set_fan_auto_mode(True) + await self.async_turn_on() diff --git a/custom_components/ha_blueair/strings.json b/custom_components/ha_blueair/strings.json index b535ce1..0e531c6 100644 --- a/custom_components/ha_blueair/strings.json +++ b/custom_components/ha_blueair/strings.json @@ -18,5 +18,18 @@ "error": { "auth": "Login failed. Please use official app to logout and log back in and try again." } + }, + "entity": { + "humidifier": { + "ha_blueair": { + "state_attributes": { + "mode": { + "state": { + "fan_speed": "Fan Speed" + } + } + } + } + } } } diff --git a/custom_components/ha_blueair/translations/en.json b/custom_components/ha_blueair/translations/en.json index b535ce1..0e531c6 100644 --- a/custom_components/ha_blueair/translations/en.json +++ b/custom_components/ha_blueair/translations/en.json @@ -18,5 +18,18 @@ "error": { "auth": "Login failed. Please use official app to logout and log back in and try again." } + }, + "entity": { + "humidifier": { + "ha_blueair": { + "state_attributes": { + "mode": { + "state": { + "fan_speed": "Fan Speed" + } + } + } + } + } } }