From c38cd2c545ae474a05a13981e3558876c1620b8c Mon Sep 17 00:00:00 2001 From: sususweet Date: Fri, 20 Dec 2024 07:09:58 +0800 Subject: [PATCH] Try to fix initialize error in some situations. (#51) * fix: Change the name of "Fresh" mode to "Purification" synchronized with the official app * fix: command sent to wrong device when multiple device available. * feat: add continuous switch according to official app only available when manual mode is on. * fix: try to fix initialize error in some situations. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * feat: bump version to 1.6.3 * fix: optimize refresh interval. * fix: change direct access to hass.helpers.event, which should be updated to import functions used from event directly to adapt HA 2025.5. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../deye_dehumidifier/__init__.py | 20 +++++-- .../deye_dehumidifier/binary_sensor.py | 26 ++++++--- .../deye_dehumidifier/data_coordinator.py | 8 +-- custom_components/deye_dehumidifier/fan.py | 22 ++++++-- .../deye_dehumidifier/humidifier.py | 23 +++++--- .../deye_dehumidifier/manifest.json | 2 +- custom_components/deye_dehumidifier/sensor.py | 26 ++++++--- custom_components/deye_dehumidifier/switch.py | 54 +++++++++++++++---- 8 files changed, 139 insertions(+), 42 deletions(-) diff --git a/custom_components/deye_dehumidifier/__init__.py b/custom_components/deye_dehumidifier/__init__.py index c59c2bc..8fda444 100644 --- a/custom_components/deye_dehumidifier/__init__.py +++ b/custom_components/deye_dehumidifier/__init__.py @@ -2,6 +2,8 @@ from __future__ import annotations +import logging + from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant, callback @@ -39,6 +41,8 @@ Platform.FAN, ] +_LOGGER = logging.getLogger(__name__) + async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Deye Dehumidifier from a config entry.""" @@ -71,12 +75,12 @@ def on_auth_token_refreshed(auth_token: str) -> None: await cloud_api.get_device_list(), ) ) + coordinator_map = {} for device in device_list: coordinator = DeyeDataUpdateCoordinator( hass, device, mqtt_client, cloud_api ) - device[DATA_COORDINATOR] = coordinator - await device[DATA_COORDINATOR].async_config_entry_first_refresh() + coordinator_map[device["device_id"]] = coordinator except DeyeCloudApiInvalidAuthError as err: raise ConfigEntryAuthFailed from err @@ -88,6 +92,7 @@ def on_auth_token_refreshed(auth_token: str) -> None: DATA_CLOUD_API: cloud_api, DATA_MQTT_CLIENT: mqtt_client, DATA_DEVICE_LIST: device_list, + DATA_COORDINATOR: coordinator_map, } await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) @@ -110,13 +115,14 @@ class DeyeEntity(CoordinatorEntity, Entity): def __init__( self, + coordinator: DeyeDataUpdateCoordinator, device: DeyeApiResponseDeviceInfo, mqtt_client: DeyeMqttClient, cloud_api: DeyeCloudApi, ) -> None: + super().__init__(coordinator) """Initialize the instance.""" - self.coordinator = device[DATA_COORDINATOR] - super().__init__(self.coordinator) + self.coordinator = coordinator self._device = device self._mqtt_client = mqtt_client self._cloud_api = cloud_api @@ -138,10 +144,14 @@ def __init__( self.device_state = DeyeDeviceState( "1411000000370000000000000000003C3C0000000000" # 20°C/60%RH as the default state ) + + async def async_added_to_hass(self) -> None: + """When entity is added to Home Assistant.""" remove_handle = self.coordinator.async_add_listener( self._handle_coordinator_update ) self.async_on_remove(remove_handle) + await super().async_added_to_hass() async def publish_command_async(self, attribute, value): """Push command to a queue and deal with them together.""" @@ -150,7 +160,7 @@ async def publish_command_async(self, attribute, value): "call_humidifier_method", {"device_id": self._device["device_id"], "prop": attribute, "value": value}, ) - await self.coordinator.async_request_refresh() + self.coordinator.mute_subscription_for_a_while() @property def available(self): diff --git a/custom_components/deye_dehumidifier/binary_sensor.py b/custom_components/deye_dehumidifier/binary_sensor.py index 254758a..48a1c4c 100644 --- a/custom_components/deye_dehumidifier/binary_sensor.py +++ b/custom_components/deye_dehumidifier/binary_sensor.py @@ -14,8 +14,14 @@ from libdeye.mqtt_client import DeyeMqttClient from libdeye.types import DeyeApiResponseDeviceInfo -from . import DeyeEntity -from .const import DATA_CLOUD_API, DATA_DEVICE_LIST, DATA_MQTT_CLIENT, DOMAIN +from . import DeyeDataUpdateCoordinator, DeyeEntity +from .const import ( + DATA_CLOUD_API, + DATA_COORDINATOR, + DATA_DEVICE_LIST, + DATA_MQTT_CLIENT, + DOMAIN, +) async def async_setup_entry( @@ -30,10 +36,16 @@ async def async_setup_entry( async_add_entities( [ DeyeWaterTankBinarySensor( - device, data[DATA_MQTT_CLIENT], data[DATA_CLOUD_API] + data[DATA_COORDINATOR][device["device_id"]], + device, + data[DATA_MQTT_CLIENT], + data[DATA_CLOUD_API], ), DeyeDefrostingBinarySensor( - device, data[DATA_MQTT_CLIENT], data[DATA_CLOUD_API] + data[DATA_COORDINATOR][device["device_id"]], + device, + data[DATA_MQTT_CLIENT], + data[DATA_CLOUD_API], ), ] ) @@ -48,12 +60,13 @@ class DeyeWaterTankBinarySensor(DeyeEntity, BinarySensorEntity): def __init__( self, + coordinator: DeyeDataUpdateCoordinator, device: DeyeApiResponseDeviceInfo, mqtt_client: DeyeMqttClient, cloud_api: DeyeCloudApi, ) -> None: """Initialize the sensor.""" - super().__init__(device, mqtt_client, cloud_api) + super().__init__(coordinator, device, mqtt_client, cloud_api) assert self._attr_unique_id is not None self._attr_unique_id += "-water-tank" self.entity_id = f"binary_sensor.{self.entity_id_base}_water_tank" @@ -73,12 +86,13 @@ class DeyeDefrostingBinarySensor(DeyeEntity, BinarySensorEntity): def __init__( self, + coordinator: DeyeDataUpdateCoordinator, device: DeyeApiResponseDeviceInfo, mqtt_client: DeyeMqttClient, cloud_api: DeyeCloudApi, ) -> None: """Initialize the sensor.""" - super().__init__(device, mqtt_client, cloud_api) + super().__init__(coordinator, device, mqtt_client, cloud_api) assert self._attr_unique_id is not None self._attr_unique_id += "-defrosting" self.entity_id = f"binary_sensor.{self.entity_id_base}_defrosting" diff --git a/custom_components/deye_dehumidifier/data_coordinator.py b/custom_components/deye_dehumidifier/data_coordinator.py index 5bd0bb0..ae394be 100644 --- a/custom_components/deye_dehumidifier/data_coordinator.py +++ b/custom_components/deye_dehumidifier/data_coordinator.py @@ -18,7 +18,7 @@ def __init__(self, hass, device, mqtt_client, cloud_api): _LOGGER, name="deye_data_update_coordinator", update_method=self.poll_device_state, - update_interval=timedelta(seconds=10), + update_interval=timedelta(seconds=5), ) self._mqtt_client = mqtt_client self._cloud_api = cloud_api @@ -61,9 +61,9 @@ def update_device_state(self, state: DeyeDeviceState) -> None: self.receive_queue.put_nowait(state) # self.async_set_updated_data(state) - async def async_request_refresh(self) -> None: - self.mute_subscription_for_a_while() - await super().async_request_refresh() + # async def async_request_refresh(self) -> None: + # self.mute_subscription_for_a_while() + # await super().async_request_refresh() async def poll_device_state(self) -> DeyeDeviceState: """ diff --git a/custom_components/deye_dehumidifier/fan.py b/custom_components/deye_dehumidifier/fan.py index d8cc579..c0d30e0 100644 --- a/custom_components/deye_dehumidifier/fan.py +++ b/custom_components/deye_dehumidifier/fan.py @@ -17,8 +17,14 @@ from libdeye.types import DeyeApiResponseDeviceInfo, DeyeFanSpeed from libdeye.utils import get_product_feature_config -from . import DeyeEntity -from .const import DATA_CLOUD_API, DATA_DEVICE_LIST, DATA_MQTT_CLIENT, DOMAIN +from . import DeyeDataUpdateCoordinator, DeyeEntity +from .const import ( + DATA_CLOUD_API, + DATA_COORDINATOR, + DATA_DEVICE_LIST, + DATA_MQTT_CLIENT, + DOMAIN, +) async def async_setup_entry( @@ -33,7 +39,14 @@ async def async_setup_entry( feature_config = get_product_feature_config(device["product_id"]) if len(feature_config["fan_speed"]) > 0: async_add_entities( - [DeyeFan(device, data[DATA_MQTT_CLIENT], data[DATA_CLOUD_API])] + [ + DeyeFan( + data[DATA_COORDINATOR][device["device_id"]], + device, + data[DATA_MQTT_CLIENT], + data[DATA_CLOUD_API], + ) + ] ) @@ -44,12 +57,13 @@ class DeyeFan(DeyeEntity, FanEntity): def __init__( self, + coordinator: DeyeDataUpdateCoordinator, device: DeyeApiResponseDeviceInfo, mqtt_client: DeyeMqttClient, cloud_api: DeyeCloudApi, ) -> None: """Initialize the fan entity.""" - super().__init__(device, mqtt_client, cloud_api) + super().__init__(coordinator, device, mqtt_client, cloud_api) assert self._attr_unique_id is not None self._attr_unique_id += "-fan" self.entity_id = f"fan.{self.entity_id_base}_fan" diff --git a/custom_components/deye_dehumidifier/humidifier.py b/custom_components/deye_dehumidifier/humidifier.py index 201ab98..02fa2b0 100644 --- a/custom_components/deye_dehumidifier/humidifier.py +++ b/custom_components/deye_dehumidifier/humidifier.py @@ -14,14 +14,21 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.event import async_track_time_interval from libdeye.cloud_api import DeyeCloudApi from libdeye.device_state_command import DeyeDeviceState from libdeye.mqtt_client import DeyeMqttClient from libdeye.types import DeyeApiResponseDeviceInfo, DeyeDeviceMode from libdeye.utils import get_product_feature_config -from . import DeyeEntity -from .const import DATA_CLOUD_API, DATA_DEVICE_LIST, DATA_MQTT_CLIENT, DOMAIN +from . import DeyeDataUpdateCoordinator, DeyeEntity +from .const import ( + DATA_CLOUD_API, + DATA_COORDINATOR, + DATA_DEVICE_LIST, + DATA_MQTT_CLIENT, + DOMAIN, +) MODE_MANUAL = "manual" MODE_AIR_PURIFIER = "air_purifier" @@ -38,7 +45,10 @@ async def async_setup_entry( for device in data[DATA_DEVICE_LIST]: deye_dehumidifier = DeyeDehumidifier( - device, data[DATA_MQTT_CLIENT], data[DATA_CLOUD_API] + data[DATA_COORDINATOR][device["device_id"]], + device, + data[DATA_MQTT_CLIENT], + data[DATA_CLOUD_API], ) async_add_entities([deye_dehumidifier]) @@ -52,12 +62,13 @@ class DeyeDehumidifier(DeyeEntity, HumidifierEntity): def __init__( self, + coordinator: DeyeDataUpdateCoordinator, device: DeyeApiResponseDeviceInfo, mqtt_client: DeyeMqttClient, cloud_api: DeyeCloudApi, ) -> None: """Initialize the humidifier entity.""" - super().__init__(device, mqtt_client, cloud_api) + super().__init__(coordinator, device, mqtt_client, cloud_api) assert self._attr_unique_id is not None self.subscription_muted: CALLBACK_TYPE | None = None self._attr_unique_id += "-dehumidifier" @@ -81,8 +92,8 @@ async def call_method(self, event): async def async_added_to_hass(self) -> None: await super().async_added_to_hass() - self.hass.helpers.event.async_track_time_interval( - self.put_device_state, timedelta(seconds=5) + async_track_time_interval( + self.hass, self.put_device_state, timedelta(seconds=2) ) self.hass.bus.async_listen("call_humidifier_method", self.call_method) diff --git a/custom_components/deye_dehumidifier/manifest.json b/custom_components/deye_dehumidifier/manifest.json index 7d9b87f..9bffc67 100644 --- a/custom_components/deye_dehumidifier/manifest.json +++ b/custom_components/deye_dehumidifier/manifest.json @@ -7,5 +7,5 @@ "iot_class": "cloud_push", "issue_tracker": "https://github.com/stackia/ha-deye-dehumidifier/issues", "requirements": ["libdeye==1.3.0"], - "version": "1.6.2" + "version": "1.6.3" } diff --git a/custom_components/deye_dehumidifier/sensor.py b/custom_components/deye_dehumidifier/sensor.py index 14ca940..fc3a73b 100644 --- a/custom_components/deye_dehumidifier/sensor.py +++ b/custom_components/deye_dehumidifier/sensor.py @@ -15,8 +15,14 @@ from libdeye.mqtt_client import DeyeMqttClient from libdeye.types import DeyeApiResponseDeviceInfo -from . import DeyeEntity -from .const import DATA_CLOUD_API, DATA_DEVICE_LIST, DATA_MQTT_CLIENT, DOMAIN +from . import DeyeDataUpdateCoordinator, DeyeEntity +from .const import ( + DATA_CLOUD_API, + DATA_COORDINATOR, + DATA_DEVICE_LIST, + DATA_MQTT_CLIENT, + DOMAIN, +) async def async_setup_entry( @@ -31,10 +37,16 @@ async def async_setup_entry( async_add_entities( [ DeyeHumiditySensor( - device, data[DATA_MQTT_CLIENT], data[DATA_CLOUD_API] + data[DATA_COORDINATOR][device["device_id"]], + device, + data[DATA_MQTT_CLIENT], + data[DATA_CLOUD_API], ), DeyeTemperatureSensor( - device, data[DATA_MQTT_CLIENT], data[DATA_CLOUD_API] + data[DATA_COORDINATOR][device["device_id"]], + device, + data[DATA_MQTT_CLIENT], + data[DATA_CLOUD_API], ), ] ) @@ -50,12 +62,13 @@ class DeyeHumiditySensor(DeyeEntity, SensorEntity): def __init__( self, + coordinator: DeyeDataUpdateCoordinator, device: DeyeApiResponseDeviceInfo, mqtt_client: DeyeMqttClient, cloud_api: DeyeCloudApi, ) -> None: """Initialize the sensor.""" - super().__init__(device, mqtt_client, cloud_api) + super().__init__(coordinator, device, mqtt_client, cloud_api) assert self._attr_unique_id is not None self._attr_unique_id += "-humidity" self.entity_id = f"sensor.{self.entity_id_base}_humidity" @@ -76,12 +89,13 @@ class DeyeTemperatureSensor(DeyeEntity, SensorEntity): def __init__( self, + coordinator: DeyeDataUpdateCoordinator, device: DeyeApiResponseDeviceInfo, mqtt_client: DeyeMqttClient, cloud_api: DeyeCloudApi, ) -> None: """Initialize the sensor.""" - super().__init__(device, mqtt_client, cloud_api) + super().__init__(coordinator, device, mqtt_client, cloud_api) assert self._attr_unique_id is not None self._attr_unique_id += "-temperature" self.entity_id = f"sensor.{self.entity_id_base}_temperature" diff --git a/custom_components/deye_dehumidifier/switch.py b/custom_components/deye_dehumidifier/switch.py index 267a037..cfe4a30 100644 --- a/custom_components/deye_dehumidifier/switch.py +++ b/custom_components/deye_dehumidifier/switch.py @@ -14,8 +14,14 @@ from libdeye.types import DeyeApiResponseDeviceInfo, DeyeDeviceMode from libdeye.utils import get_product_feature_config -from . import DeyeEntity -from .const import DATA_CLOUD_API, DATA_DEVICE_LIST, DATA_MQTT_CLIENT, DOMAIN +from . import DeyeDataUpdateCoordinator, DeyeEntity +from .const import ( + DATA_CLOUD_API, + DATA_COORDINATOR, + DATA_DEVICE_LIST, + DATA_MQTT_CLIENT, + DOMAIN, +) async def async_setup_entry( @@ -28,21 +34,45 @@ async def async_setup_entry( for device in data[DATA_DEVICE_LIST]: async_add_entities( - [DeyeChildLockSwitch(device, data[DATA_MQTT_CLIENT], data[DATA_CLOUD_API])] + [ + DeyeChildLockSwitch( + data[DATA_COORDINATOR][device["device_id"]], + device, + data[DATA_MQTT_CLIENT], + data[DATA_CLOUD_API], + ) + ] ) async_add_entities( - [DeyeContinuousSwitch(device, data[DATA_MQTT_CLIENT], data[DATA_CLOUD_API])] + [ + DeyeContinuousSwitch( + data[DATA_COORDINATOR][device["device_id"]], + device, + data[DATA_MQTT_CLIENT], + data[DATA_CLOUD_API], + ) + ] ) feature_config = get_product_feature_config(device["product_id"]) if feature_config["anion"]: async_add_entities( - [DeyeAnionSwitch(device, data[DATA_MQTT_CLIENT], data[DATA_CLOUD_API])] + [ + DeyeAnionSwitch( + data[DATA_COORDINATOR][device["device_id"]], + device, + data[DATA_MQTT_CLIENT], + data[DATA_CLOUD_API], + ) + ] ) if feature_config["water_pump"]: async_add_entities( [ DeyeWaterPumpSwitch( - device, data[DATA_MQTT_CLIENT], data[DATA_CLOUD_API] + data[DATA_COORDINATOR][device["device_id"]], + device, + data[DATA_MQTT_CLIENT], + data[DATA_CLOUD_API], ) ] ) @@ -57,12 +87,13 @@ class DeyeChildLockSwitch(DeyeEntity, SwitchEntity): def __init__( self, + coordinator: DeyeDataUpdateCoordinator, device: DeyeApiResponseDeviceInfo, mqtt_client: DeyeMqttClient, cloud_api: DeyeCloudApi, ) -> None: """Initialize the switch.""" - super().__init__(device, mqtt_client, cloud_api) + super().__init__(coordinator, device, mqtt_client, cloud_api) assert self._attr_unique_id is not None self._attr_unique_id += "-child-lock" self.entity_id = f"switch.{self.entity_id_base}_child_lock" @@ -92,12 +123,13 @@ class DeyeAnionSwitch(DeyeEntity, SwitchEntity): def __init__( self, + coordinator: DeyeDataUpdateCoordinator, device: DeyeApiResponseDeviceInfo, mqtt_client: DeyeMqttClient, cloud_api: DeyeCloudApi, ) -> None: """Initialize the switch.""" - super().__init__(device, mqtt_client, cloud_api) + super().__init__(coordinator, device, mqtt_client, cloud_api) assert self._attr_unique_id is not None self._attr_unique_id += "-anion" self.entity_id = f"switch.{self.entity_id_base}_anion" @@ -127,12 +159,13 @@ class DeyeWaterPumpSwitch(DeyeEntity, SwitchEntity): def __init__( self, + coordinator: DeyeDataUpdateCoordinator, device: DeyeApiResponseDeviceInfo, mqtt_client: DeyeMqttClient, cloud_api: DeyeCloudApi, ) -> None: """Initialize the switch.""" - super().__init__(device, mqtt_client, cloud_api) + super().__init__(coordinator, device, mqtt_client, cloud_api) assert self._attr_unique_id is not None self._attr_unique_id += "-water-pump" self.entity_id = f"switch.{self.entity_id_base}_water_pump" @@ -162,12 +195,13 @@ class DeyeContinuousSwitch(DeyeEntity, SwitchEntity): def __init__( self, + coordinator: DeyeDataUpdateCoordinator, device: DeyeApiResponseDeviceInfo, mqtt_client: DeyeMqttClient, cloud_api: DeyeCloudApi, ) -> None: """Initialize the switch.""" - super().__init__(device, mqtt_client, cloud_api) + super().__init__(coordinator, device, mqtt_client, cloud_api) assert self._attr_unique_id is not None self._attr_unique_id += "-continuous" self.entity_id = f"switch.{self.entity_id_base}_continuous"