From 7fc3b23d43cfbea956fd8fc0d6aae23f487c371a Mon Sep 17 00:00:00 2001 From: Yu Feng Date: Sat, 16 Nov 2024 22:45:02 -0800 Subject: [PATCH 01/11] add model name. --- .../ha_blueair/blueair_aws_data_update_coordinator.py | 2 +- custom_components/ha_blueair/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py index 5e22fe3..5f54210 100644 --- a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py +++ b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py @@ -57,7 +57,7 @@ def manufacturer(self) -> str: @property def model(self) -> str: - return "protect?" + return self.blueair_api_device.type_name @property def fan_speed(self) -> int: diff --git a/custom_components/ha_blueair/manifest.json b/custom_components/ha_blueair/manifest.json index dd1b434..0c0b2a4 100644 --- a/custom_components/ha_blueair/manifest.json +++ b/custom_components/ha_blueair/manifest.json @@ -6,6 +6,6 @@ "documentation": "https://github.com/dahlb/ha_blueair", "iot_class": "cloud_polling", "issue_tracker": "https://github.com/dahlb/ha_blueair/issues", - "requirements": ["blueair-api==1.9.5"], + "requirements": ["git+https://github.com/rainwoodman/blueair_api.git@main#blueair_api"], "version": "1.9.6" } From 7e747d3645a4254e016878742f62277757625864 Mon Sep 17 00:00:00 2001 From: Yu Feng Date: Sun, 17 Nov 2024 01:28:15 -0800 Subject: [PATCH 02/11] Better i35 support. * speed count = 64 * some of the sensors do not apply * set a default perentage to 50% when turning on. (because device does not remember last speed) * added a few sensors. * added model name look up for AWS devices i35 and 7470i * fixed a few mis-typed type annotations. --- custom_components/ha_blueair/binary_sensor.py | 23 ++++++++-- .../blueair_aws_data_update_coordinator.py | 45 ++++++++++++++++--- custom_components/ha_blueair/fan.py | 11 ++--- custom_components/ha_blueair/manifest.json | 1 - custom_components/ha_blueair/sensor.py | 16 ++++--- custom_components/ha_blueair/switch.py | 24 ++++++++++ 6 files changed, 100 insertions(+), 20 deletions(-) diff --git a/custom_components/ha_blueair/binary_sensor.py b/custom_components/ha_blueair/binary_sensor.py index 6e18380..8e10e8f 100644 --- a/custom_components/ha_blueair/binary_sensor.py +++ b/custom_components/ha_blueair/binary_sensor.py @@ -8,6 +8,7 @@ from .const import DOMAIN, DATA_DEVICES, DATA_AWS_DEVICES from .blueair_data_update_coordinator import BlueairDataUpdateCoordinator +from .blueair_aws_data_update_coordinator import BlueairAwsDataUpdateCoordinator from .entity import BlueairEntity @@ -25,7 +26,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): ) async_add_entities(entities) - aws_devices: list[BlueairDataUpdateCoordinator] = hass.data[DOMAIN][ + aws_devices: list[BlueairAwsDataUpdateCoordinator] = hass.data[DOMAIN][ DATA_AWS_DEVICES ] entities = [] @@ -34,6 +35,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): [ BlueairFilterExpiredSensor(device), BlueairOnlineSensor(device), + BlueairWaterShortageSensor(device), ] ) async_add_entities(entities) @@ -55,11 +57,11 @@ class BlueairFilterExpiredSensor(BlueairEntity, BinarySensorEntity): _attr_icon = "mdi:air-filter" def __init__(self, device): + """Initialize the temperature sensor.""" self.entity_description = EntityDescription( key=f"#{device.blueair_api_device.uuid}-filter-expired", device_class=BinarySensorDeviceClass.PROBLEM, ) - """Initialize the temperature sensor.""" super().__init__("Filter Expiration", device) @property @@ -72,11 +74,11 @@ class BlueairOnlineSensor(BlueairEntity, BinarySensorEntity): _attr_icon = "mdi:wifi-check" def __init__(self, device): + """Initialize the temperature sensor.""" self.entity_description = EntityDescription( key=f"#{device.blueair_api_device.uuid}-online", device_class=BinarySensorDeviceClass.CONNECTIVITY, ) - """Initialize the temperature sensor.""" super().__init__("Online", device) @property @@ -90,3 +92,18 @@ def icon(self) -> str | None: return self._attr_icon else: return "mdi:wifi-strength-outline" + +class BlueairWaterShortageSensor(BlueairEntity, BinarySensorEntity): + _attr_icon = "mdi:water-alert-outline" + + def __init__(self, device): + self.entity_description = EntityDescription( + key=f"#{device.blueair_api_device.uuid}-water-shortage", + device_class=BinarySensorDeviceClass.PROBLEM, + ) + super().__init__("Water Shortage", device) + + @property + def is_on(self) -> bool | None: + """Return true if the binary sensor is on.""" + return self._device.water_shortage diff --git a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py index 5f54210..a8020db 100644 --- a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py +++ b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py @@ -1,7 +1,7 @@ """Blueair device object.""" import logging from datetime import timedelta - +import enum from blueair_api import DeviceAws as BlueAirApiDeviceAws from asyncio import sleep @@ -13,6 +13,11 @@ _LOGGER = logging.getLogger(__name__) +class ModelEnum(enum.StrEnum): + UNKNOWN = "Unknown" + HUMIDIFIER_I35 = "Blueair Humidifier i35" + PROTECT_7470I = "Blueair Protect 7470i" + class BlueairAwsDataUpdateCoordinator(DataUpdateCoordinator): """Blueair device object.""" @@ -23,7 +28,6 @@ def __init__( self.hass: HomeAssistant = hass self.blueair_api_device: BlueAirApiDeviceAws = blueair_api_device self._manufacturer: str = "BlueAir" - super().__init__( hass, _LOGGER, @@ -56,14 +60,27 @@ def manufacturer(self) -> str: return self._manufacturer @property - def model(self) -> str: - return self.blueair_api_device.type_name + def model(self) -> ModelEnum: + if (self.blueair_api_device.type_name == "Humidifier" and + self.blueair_api_device.sku == "111633"): + return ModelEnum.HUMIDIFIER_I35 + if self.blueair_api_device.sku == "105826": + return ModelEnum.PROTECT_7470I + return ModelEnum.UNKNOWN @property def fan_speed(self) -> int: """Return the current fan speed.""" return self.blueair_api_device.fan_speed + @property + def speed_count(self) -> int: + """Return the max fan speed.""" + if self.model == ModelEnum.HUMIDIFIER_I35: + return 64 + else: + return 100 + @property def is_on(self) -> False: """Return the current fan state.""" @@ -118,11 +135,21 @@ def online(self) -> bool: def fan_auto_mode(self) -> bool: return self.blueair_api_device.fan_auto_mode + @property + def wick_dry_mode(self) -> bool: + return self.blueair_api_device.wick_dry_mode + + @property + def water_shortage(self) -> bool: + return self.blueair_api_device.water_shortage + @property def filter_expired(self) -> bool: """Return the current filter status.""" - return (self.blueair_api_device.filter_usage is not None - and self.blueair_api_device.filter_usage >= 95) + if self.blueair_api_device.filter_usage is not None: + return self.blueair_api_device.filter_usage >= 95 + if self.blueair_api_device.wick_usage is not None: + return self.blueair_api_device.wick_usage >= 95 async def set_fan_speed(self, new_speed) -> None: self.blueair_api_device.fan_speed = new_speed @@ -159,3 +186,9 @@ async def set_fan_auto_mode(self, value) -> None: await self.blueair_api_device.set_fan_auto_mode(value) await sleep(5) await self.async_refresh() + + async def set_wick_dry_mode(self, value) -> None: + self.blueair_api_device.wick_dry_mode = value + await self.blueair_api_device.set_wick_dry_mode(value) + await sleep(5) + await self.async_refresh() diff --git a/custom_components/ha_blueair/fan.py b/custom_components/ha_blueair/fan.py index f97773a..11056a2 100644 --- a/custom_components/ha_blueair/fan.py +++ b/custom_components/ha_blueair/fan.py @@ -107,10 +107,10 @@ def is_on(self) -> int: @property def percentage(self) -> int: """Return the current speed percentage.""" - return self._device.fan_speed + return int(self._device.fan_speed / self._device.speed_count * 100) async def async_set_percentage(self, percentage: int) -> None: - await self._device.set_fan_speed(percentage) + await self._device.set_fan_speed(int(percentage / 100 * self._device.speed_count)) self.async_write_ha_state() async def async_turn_off(self, **kwargs: any) -> None: @@ -125,10 +125,11 @@ async def async_turn_on( ) -> None: await self._device.set_running(True) self.async_write_ha_state() - if percentage is not None: - await self.async_set_percentage(percentage=percentage) + if percentage is None: + percentage = 50 + await self.async_set_percentage(percentage=percentage) @property def speed_count(self) -> int: """Return the number of speeds the fan supports.""" - return 100 + return self._device.speed_count diff --git a/custom_components/ha_blueair/manifest.json b/custom_components/ha_blueair/manifest.json index 0c0b2a4..2e40629 100644 --- a/custom_components/ha_blueair/manifest.json +++ b/custom_components/ha_blueair/manifest.json @@ -6,6 +6,5 @@ "documentation": "https://github.com/dahlb/ha_blueair", "iot_class": "cloud_polling", "issue_tracker": "https://github.com/dahlb/ha_blueair/issues", - "requirements": ["git+https://github.com/rainwoodman/blueair_api.git@main#blueair_api"], "version": "1.9.6" } diff --git a/custom_components/ha_blueair/sensor.py b/custom_components/ha_blueair/sensor.py index ba2de04..7d5c6c5 100644 --- a/custom_components/ha_blueair/sensor.py +++ b/custom_components/ha_blueair/sensor.py @@ -10,7 +10,7 @@ from .const import DOMAIN, DATA_AWS_DEVICES -from .blueair_data_update_coordinator import BlueairDataUpdateCoordinator +from .blueair_aws_data_update_coordinator import BlueairAwsDataUpdateCoordinator, ModelEnum from .entity import BlueairEntity @@ -20,17 +20,23 @@ async def async_setup_entry(hass, config_entry, async_add_entities): DATA_AWS_DEVICES ] entities = [] + for device in aws_devices: - entities.extend( - [ + if device.model in (ModelEnum.UNKNOWN, ModelEnum.PROTECT_7470I): + entities.extend([ BlueairTemperatureSensor(device), BlueairHumiditySensor(device), BlueairVOCSensor(device), BlueairPM1Sensor(device), BlueairPM10Sensor(device), BlueairPM25Sensor(device), - ] - ) + ]) + elif device.model == ModelEnum.HUMIDIFIER_I35: + entities.extend([ + BlueairTemperatureSensor(device), + BlueairHumiditySensor(device), + ]) + async_add_entities(entities) diff --git a/custom_components/ha_blueair/switch.py b/custom_components/ha_blueair/switch.py index 075058f..57c8b69 100644 --- a/custom_components/ha_blueair/switch.py +++ b/custom_components/ha_blueair/switch.py @@ -22,6 +22,7 @@ async def async_setup_entry(hass, _config_entry, async_add_entities): BlueairChildLockSwitchEntity(device), BlueairAutoFanModeSwitchEntity(device), BlueairNightModeSwitchEntity(device), + BlueairWickDryModeSwitchEntity(device), ] ) async_add_entities(entities) @@ -87,3 +88,26 @@ async def async_turn_on(self, **kwargs): async def async_turn_off(self, **kwargs): await self._device.set_night_mode(False) self.async_write_ha_state() + +class BlueairWickDryModeSwitchEntity(BlueairEntity, SwitchEntity): + _attr_device_class = SwitchDeviceClass.SWITCH + + def __init__(self, device): + super().__init__("Wick Dry Mode", device) + + @property + def is_on(self) -> int | None: + return self._device.wick_dry_mode + + @property + def available(self) -> bool: + """Return True if entity is available.""" + return self._device.wick_dry_mode is not None + + async def async_turn_on(self, **kwargs): + await self._device.set_wick_dry_mode(True) + self.async_write_ha_state() + + async def async_turn_off(self, **kwargs): + await self._device.set_wick_dry_mode(False) + self.async_write_ha_state() From a5b9f1ef740d7b572b2e96689b9f20c97c3289a7 Mon Sep 17 00:00:00 2001 From: Yu Feng Date: Tue, 26 Nov 2024 16:35:02 -0800 Subject: [PATCH 03/11] Remove unused property mutation. the setters will and shall internally update these 'local cached state' properties anyway. --- .../blueair_aws_data_update_coordinator.py | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py index a8020db..7980ee4 100644 --- a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py +++ b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py @@ -12,12 +12,16 @@ _LOGGER = logging.getLogger(__name__) +_ASYNC_REFRESH_WAIT_SECONDS = 5 +_FILTER_EXPIRED_THRESHOLD = 95 + class ModelEnum(enum.StrEnum): UNKNOWN = "Unknown" HUMIDIFIER_I35 = "Blueair Humidifier i35" PROTECT_7470I = "Blueair Protect 7470i" + class BlueairAwsDataUpdateCoordinator(DataUpdateCoordinator): """Blueair device object.""" @@ -61,8 +65,7 @@ def manufacturer(self) -> str: @property def model(self) -> ModelEnum: - if (self.blueair_api_device.type_name == "Humidifier" and - self.blueair_api_device.sku == "111633"): + if self.blueair_api_device.sku == "111633": return ModelEnum.HUMIDIFIER_I35 if self.blueair_api_device.sku == "105826": return ModelEnum.PROTECT_7470I @@ -147,48 +150,43 @@ def water_shortage(self) -> bool: def filter_expired(self) -> bool: """Return the current filter status.""" if self.blueair_api_device.filter_usage is not None: - return self.blueair_api_device.filter_usage >= 95 + return (self.blueair_api_device.filter_usage >= + _FILTER_EXPIRED_THRESHOLD) if self.blueair_api_device.wick_usage is not None: - return self.blueair_api_device.wick_usage >= 95 + return (self.blueair_api_device.wick_usage >= + _FILTER_EXPIRED_THRESHOLD) async def set_fan_speed(self, new_speed) -> None: - self.blueair_api_device.fan_speed = new_speed await self.blueair_api_device.set_fan_speed(new_speed) - await sleep(5) + await sleep(_ASYNC_REFRESH_WAIT_SECONDS) await self.async_refresh() async def set_running(self, running) -> None: - self.blueair_api_device.running = running await self.blueair_api_device.set_running(running) - await sleep(5) + await sleep(_ASYNC_REFRESH_WAIT_SECONDS) await self.async_refresh() async def set_brightness(self, brightness) -> None: - self.blueair_api_device.brightness = brightness await self.blueair_api_device.set_brightness(brightness) - await sleep(5) + await sleep(_ASYNC_REFRESH_WAIT_SECONDS) await self.async_refresh() async def set_child_lock(self, locked) -> None: - self.blueair_api_device.child_lock = locked await self.blueair_api_device.set_child_lock(locked) - await sleep(5) + await sleep(_ASYNC_REFRESH_WAIT_SECONDS) await self.async_refresh() async def set_night_mode(self, mode) -> None: - self.blueair_api_device.night_mode = mode await self.blueair_api_device.set_night_mode(mode) - await sleep(5) + await sleep(_ASYNC_REFRESH_WAIT_SECONDS) await self.async_refresh() async def set_fan_auto_mode(self, value) -> None: - self.blueair_api_device.fan_auto_mode = value await self.blueair_api_device.set_fan_auto_mode(value) - await sleep(5) + await sleep(_ASYNC_REFRESH_WAIT_SECONDS) await self.async_refresh() async def set_wick_dry_mode(self, value) -> None: - self.blueair_api_device.wick_dry_mode = value await self.blueair_api_device.set_wick_dry_mode(value) - await sleep(5) + await sleep(_ASYNC_REFRESH_WAIT_SECONDS) await self.async_refresh() From 843d8106d1d3b9d9782c301eae9fdef734c36db8 Mon Sep 17 00:00:00 2001 From: Yu Feng Date: Tue, 26 Nov 2024 16:53:23 -0800 Subject: [PATCH 04/11] comment about default fan speed. --- custom_components/ha_blueair/fan.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/custom_components/ha_blueair/fan.py b/custom_components/ha_blueair/fan.py index 11056a2..8926477 100644 --- a/custom_components/ha_blueair/fan.py +++ b/custom_components/ha_blueair/fan.py @@ -12,6 +12,8 @@ from .entity import BlueairEntity +_DEFAULT_FAN_SPEED_PERCENTAGE = 50 + async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the Blueair fans from config entry.""" devices: list[BlueairDataUpdateCoordinator] = hass.data[DOMAIN][DATA_DEVICES] @@ -126,8 +128,14 @@ async def async_turn_on( await self._device.set_running(True) self.async_write_ha_state() if percentage is None: - percentage = 50 - await self.async_set_percentage(percentage=percentage) + # FIXME: i35 (and probably others) do not remember the + # last fan speed and always set the speed to 0. I don't know + # where to store the last fan speed such that it persists across + # HA reboots. Thus we set the default turn_on fan speed to 50% + # to make sure the fan actually spins at all. + percentage = _DEFAULT_FAN_SPEED_PERCENTAGE + if percentage is not None: + await self.async_set_percentage(percentage=percentage) @property def speed_count(self) -> int: From 11303cf551a047630ac8a394ab61d905d09ef181 Mon Sep 17 00:00:00 2001 From: Yu Feng Date: Tue, 26 Nov 2024 20:49:57 -0800 Subject: [PATCH 05/11] bump version of blueair api. --- custom_components/ha_blueair/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/custom_components/ha_blueair/manifest.json b/custom_components/ha_blueair/manifest.json index 2e40629..7a6eb83 100644 --- a/custom_components/ha_blueair/manifest.json +++ b/custom_components/ha_blueair/manifest.json @@ -6,5 +6,6 @@ "documentation": "https://github.com/dahlb/ha_blueair", "iot_class": "cloud_polling", "issue_tracker": "https://github.com/dahlb/ha_blueair/issues", + "requirements": ["blueair-api==1.10.1"], "version": "1.9.6" } From 73bf5bf36c317bef82e4d6c58179560ed623ac05 Mon Sep 17 00:00:00 2001 From: Yu Feng Date: Tue, 26 Nov 2024 20:53:31 -0800 Subject: [PATCH 06/11] fix type annotation error. --- custom_components/ha_blueair/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/ha_blueair/sensor.py b/custom_components/ha_blueair/sensor.py index 7d5c6c5..ec8cbb3 100644 --- a/custom_components/ha_blueair/sensor.py +++ b/custom_components/ha_blueair/sensor.py @@ -16,7 +16,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the Blueair sensors from config entry.""" - aws_devices: list[BlueairDataUpdateCoordinator] = hass.data[DOMAIN][ + aws_devices: list[BlueairAwsDataUpdateCoordinator] = hass.data[DOMAIN][ DATA_AWS_DEVICES ] entities = [] From fe4c88ef165df7f7d21c7a07b0aadee170966ea5 Mon Sep 17 00:00:00 2001 From: Yu Feng Date: Tue, 26 Nov 2024 22:13:47 -0800 Subject: [PATCH 07/11] Use the debouncer, recommended by ha documentation --- .../blueair_aws_data_update_coordinator.py | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py index 7980ee4..263232e 100644 --- a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py +++ b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py @@ -12,7 +12,6 @@ _LOGGER = logging.getLogger(__name__) -_ASYNC_REFRESH_WAIT_SECONDS = 5 _FILTER_EXPIRED_THRESHOLD = 95 @@ -158,35 +157,28 @@ def filter_expired(self) -> bool: async def set_fan_speed(self, new_speed) -> None: await self.blueair_api_device.set_fan_speed(new_speed) - await sleep(_ASYNC_REFRESH_WAIT_SECONDS) - await self.async_refresh() + await self.async_request_refresh() async def set_running(self, running) -> None: await self.blueair_api_device.set_running(running) - await sleep(_ASYNC_REFRESH_WAIT_SECONDS) - await self.async_refresh() + await self.async_request_refresh() async def set_brightness(self, brightness) -> None: await self.blueair_api_device.set_brightness(brightness) - await sleep(_ASYNC_REFRESH_WAIT_SECONDS) - await self.async_refresh() + await self.async_request_refresh() async def set_child_lock(self, locked) -> None: await self.blueair_api_device.set_child_lock(locked) - await sleep(_ASYNC_REFRESH_WAIT_SECONDS) - await self.async_refresh() + await self.async_request_refresh() async def set_night_mode(self, mode) -> None: await self.blueair_api_device.set_night_mode(mode) - await sleep(_ASYNC_REFRESH_WAIT_SECONDS) - await self.async_refresh() + await self.async_request_refresh() async def set_fan_auto_mode(self, value) -> None: await self.blueair_api_device.set_fan_auto_mode(value) - await sleep(_ASYNC_REFRESH_WAIT_SECONDS) - await self.async_refresh() + await self.async_request_refresh() async def set_wick_dry_mode(self, value) -> None: await self.blueair_api_device.set_wick_dry_mode(value) - await sleep(_ASYNC_REFRESH_WAIT_SECONDS) - await self.async_refresh() + await self.async_request_refresh() From 6dff0184b1f401f44ae2e75a424b9e94a796dda0 Mon Sep 17 00:00:00 2001 From: Yu Feng Date: Wed, 27 Nov 2024 23:05:41 -0800 Subject: [PATCH 08/11] Set Deboncer to use the 5 second delay. Also avoid the first, immediate action because the cloud side state is not updated till after 5 seconds. --- .../ha_blueair/blueair_aws_data_update_coordinator.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py index 263232e..a6f790d 100644 --- a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py +++ b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py @@ -7,6 +7,7 @@ from asyncio import sleep from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed +from homeassistant.helpers.debounce import Debouncer from .const import DOMAIN @@ -36,6 +37,9 @@ def __init__( _LOGGER, name=f"{DOMAIN}-{self.blueair_api_device.uuid}", update_interval=timedelta(minutes=10), + request_refresh_debouncer=Debouncer( + hass, _LOGGER, cooldown=5.0, immediate=False, + ), ) async def _async_update_data(self): From ceae02565784a6de9a59fe9ec4a3d20e401d2b27 Mon Sep 17 00:00:00 2001 From: Yu Feng Date: Wed, 27 Nov 2024 23:18:52 -0800 Subject: [PATCH 09/11] Move constant to const.py. --- custom_components/ha_blueair/const.py | 3 +++ custom_components/ha_blueair/entity.py | 6 +++++- custom_components/ha_blueair/fan.py | 6 ++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/custom_components/ha_blueair/const.py b/custom_components/ha_blueair/const.py index 248c4f5..d0b835c 100644 --- a/custom_components/ha_blueair/const.py +++ b/custom_components/ha_blueair/const.py @@ -13,3 +13,6 @@ REGION_EU = "eu" REGION_USA = "us" REGIONS = [REGION_USA, REGION_EU] + +DEFAULT_FAN_SPEED_PERCENTAGE = 50 + diff --git a/custom_components/ha_blueair/entity.py b/custom_components/ha_blueair/entity.py index 581e365..49c12e8 100644 --- a/custom_components/ha_blueair/entity.py +++ b/custom_components/ha_blueair/entity.py @@ -6,6 +6,7 @@ ) from .const import DOMAIN +from .blueair_data_update_coordinator import BlueairDataUpdateCoordinator from .blueair_aws_data_update_coordinator import BlueairAwsDataUpdateCoordinator @@ -18,7 +19,7 @@ class BlueairEntity(CoordinatorEntity): def __init__( self, entity_type: str, - device: BlueairAwsDataUpdateCoordinator, + device: BlueairAwsDataUpdateCoordinator | BlueairDataUpdateCoordinator, **kwargs, ) -> None: super().__init__(device) @@ -40,6 +41,9 @@ def device_info(self) -> DeviceInfo: async def async_update(self): """Update Blueair entity.""" + if not self.enabled: + return + await self._device.async_request_refresh() self._attr_available = self._device.blueair_api_device.wifi_working diff --git a/custom_components/ha_blueair/fan.py b/custom_components/ha_blueair/fan.py index 8926477..7528b9c 100644 --- a/custom_components/ha_blueair/fan.py +++ b/custom_components/ha_blueair/fan.py @@ -6,14 +6,12 @@ FanEntityFeature, ) -from .const import DOMAIN, DATA_DEVICES, DATA_AWS_DEVICES +from .const import DOMAIN, DATA_DEVICES, DATA_AWS_DEVICES, DEFAULT_FAN_SPEED_PERCENTAGE from .blueair_data_update_coordinator import BlueairDataUpdateCoordinator from .blueair_aws_data_update_coordinator import BlueairAwsDataUpdateCoordinator from .entity import BlueairEntity -_DEFAULT_FAN_SPEED_PERCENTAGE = 50 - async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the Blueair fans from config entry.""" devices: list[BlueairDataUpdateCoordinator] = hass.data[DOMAIN][DATA_DEVICES] @@ -133,7 +131,7 @@ async def async_turn_on( # where to store the last fan speed such that it persists across # HA reboots. Thus we set the default turn_on fan speed to 50% # to make sure the fan actually spins at all. - percentage = _DEFAULT_FAN_SPEED_PERCENTAGE + percentage = DEFAULT_FAN_SPEED_PERCENTAGE if percentage is not None: await self.async_set_percentage(percentage=percentage) From 19414240cb9ade33f5a607606b1a65f33e96417c Mon Sep 17 00:00:00 2001 From: Yu Feng Date: Wed, 27 Nov 2024 23:29:54 -0800 Subject: [PATCH 10/11] remove unused sleep import. --- .../ha_blueair/blueair_aws_data_update_coordinator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py index a6f790d..e68deb9 100644 --- a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py +++ b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py @@ -4,7 +4,6 @@ import enum from blueair_api import DeviceAws as BlueAirApiDeviceAws -from asyncio import sleep from homeassistant.core import HomeAssistant from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.debounce import Debouncer From 5f270d0ddf08cf6ebe57bb9cc09d8931ceca3d63 Mon Sep 17 00:00:00 2001 From: Yu Feng Date: Thu, 28 Nov 2024 00:57:26 -0800 Subject: [PATCH 11/11] Move const and bump blueair_api version. --- .../ha_blueair/blueair_aws_data_update_coordinator.py | 8 +++----- custom_components/ha_blueair/const.py | 2 ++ custom_components/ha_blueair/manifest.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py index e68deb9..e2fba9b 100644 --- a/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py +++ b/custom_components/ha_blueair/blueair_aws_data_update_coordinator.py @@ -8,12 +8,10 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.helpers.debounce import Debouncer -from .const import DOMAIN +from .const import DOMAIN, FILTER_EXPIRED_THRESHOLD _LOGGER = logging.getLogger(__name__) -_FILTER_EXPIRED_THRESHOLD = 95 - class ModelEnum(enum.StrEnum): UNKNOWN = "Unknown" @@ -153,10 +151,10 @@ def filter_expired(self) -> bool: """Return the current filter status.""" if self.blueair_api_device.filter_usage is not None: return (self.blueair_api_device.filter_usage >= - _FILTER_EXPIRED_THRESHOLD) + FILTER_EXPIRED_THRESHOLD) if self.blueair_api_device.wick_usage is not None: return (self.blueair_api_device.wick_usage >= - _FILTER_EXPIRED_THRESHOLD) + FILTER_EXPIRED_THRESHOLD) async def set_fan_speed(self, new_speed) -> None: await self.blueair_api_device.set_fan_speed(new_speed) diff --git a/custom_components/ha_blueair/const.py b/custom_components/ha_blueair/const.py index d0b835c..421a34e 100644 --- a/custom_components/ha_blueair/const.py +++ b/custom_components/ha_blueair/const.py @@ -15,4 +15,6 @@ REGIONS = [REGION_USA, REGION_EU] DEFAULT_FAN_SPEED_PERCENTAGE = 50 +FILTER_EXPIRED_THRESHOLD = 95 + diff --git a/custom_components/ha_blueair/manifest.json b/custom_components/ha_blueair/manifest.json index 7a6eb83..fa74e6c 100644 --- a/custom_components/ha_blueair/manifest.json +++ b/custom_components/ha_blueair/manifest.json @@ -6,6 +6,6 @@ "documentation": "https://github.com/dahlb/ha_blueair", "iot_class": "cloud_polling", "issue_tracker": "https://github.com/dahlb/ha_blueair/issues", - "requirements": ["blueair-api==1.10.1"], + "requirements": ["blueair-api==1.11.0"], "version": "1.9.6" }