diff --git a/homeassistant/components/yolink/__init__.py b/homeassistant/components/yolink/__init__.py index c10cc8158ea..c3633800685 100644 --- a/homeassistant/components/yolink/__init__.py +++ b/homeassistant/components/yolink/__init__.py @@ -121,8 +121,20 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: raise ConfigEntryNotReady from err device_coordinators = {} + + # revese mapping + device_pairing_mapping = {} for device in yolink_home.get_devices(): - device_coordinator = YoLinkCoordinator(hass, device) + if (parent_id := device.get_paired_device_id()) is not None: + device_pairing_mapping[parent_id] = device.device_id + + for device in yolink_home.get_devices(): + paried_device: YoLinkDevice | None = None + if ( + paried_device_id := device_pairing_mapping.get(device.device_id) + ) is not None: + paried_device = yolink_home.get_device(paried_device_id) + device_coordinator = YoLinkCoordinator(hass, device, paried_device) try: await device_coordinator.async_config_entry_first_refresh() except ConfigEntryNotReady: diff --git a/homeassistant/components/yolink/coordinator.py b/homeassistant/components/yolink/coordinator.py index f22e416511b..e322961d179 100644 --- a/homeassistant/components/yolink/coordinator.py +++ b/homeassistant/components/yolink/coordinator.py @@ -20,7 +20,12 @@ _LOGGER = logging.getLogger(__name__) class YoLinkCoordinator(DataUpdateCoordinator[dict]): """YoLink DataUpdateCoordinator.""" - def __init__(self, hass: HomeAssistant, device: YoLinkDevice) -> None: + def __init__( + self, + hass: HomeAssistant, + device: YoLinkDevice, + paired_device: YoLinkDevice | None = None, + ) -> None: """Init YoLink DataUpdateCoordinator. fetch state every 30 minutes base on yolink device heartbeat interval @@ -31,16 +36,30 @@ class YoLinkCoordinator(DataUpdateCoordinator[dict]): hass, _LOGGER, name=DOMAIN, update_interval=timedelta(minutes=30) ) self.device = device + self.paired_device = paired_device async def _async_update_data(self) -> dict: """Fetch device state.""" try: async with async_timeout.timeout(10): device_state_resp = await self.device.fetch_state() + device_state = device_state_resp.data.get(ATTR_DEVICE_STATE) + if self.paired_device is not None and device_state is not None: + paried_device_state_resp = await self.paired_device.fetch_state() + paried_device_state = paried_device_state_resp.data.get( + ATTR_DEVICE_STATE + ) + if ( + paried_device_state is not None + and ATTR_DEVICE_STATE in paried_device_state + ): + device_state[ATTR_DEVICE_STATE] = paried_device_state[ + ATTR_DEVICE_STATE + ] except YoLinkAuthFailError as yl_auth_err: raise ConfigEntryAuthFailed from yl_auth_err except YoLinkClientError as yl_client_err: raise UpdateFailed from yl_client_err - if ATTR_DEVICE_STATE in device_state_resp.data: - return device_state_resp.data[ATTR_DEVICE_STATE] + if device_state is not None: + return device_state return {} diff --git a/homeassistant/components/yolink/cover.py b/homeassistant/components/yolink/cover.py index 0d1f1e590b4..6cc1ea3acd6 100644 --- a/homeassistant/components/yolink/cover.py +++ b/homeassistant/components/yolink/cover.py @@ -4,7 +4,7 @@ from __future__ import annotations from typing import Any from yolink.client_request import ClientRequest -from yolink.const import ATTR_GARAGE_DOOR_CONTROLLER +from yolink.const import ATTR_DEVICE_FINGER, ATTR_GARAGE_DOOR_CONTROLLER from homeassistant.components.cover import ( CoverDeviceClass, @@ -30,7 +30,8 @@ async def async_setup_entry( entities = [ YoLinkCoverEntity(config_entry, device_coordinator) for device_coordinator in device_coordinators.values() - if device_coordinator.device.device_type == ATTR_GARAGE_DOOR_CONTROLLER + if device_coordinator.device.device_type + in [ATTR_GARAGE_DOOR_CONTROLLER, ATTR_DEVICE_FINGER] ] async_add_entities(entities) @@ -58,8 +59,12 @@ class YoLinkCoverEntity(YoLinkEntity, CoverEntity): """Update HA Entity State.""" if (state_val := state.get("state")) is None: return - self._attr_is_closed = state_val == "closed" - self.async_write_ha_state() + if self.coordinator.paired_device is None: + self._attr_is_closed = None + self.async_write_ha_state() + elif state_val in ["open", "closed"]: + self._attr_is_closed = state_val == "closed" + self.async_write_ha_state() async def toggle_garage_state(self) -> None: """Toggle Garage door state.""" diff --git a/homeassistant/components/yolink/manifest.json b/homeassistant/components/yolink/manifest.json index 088ddd114f8..ced0d527c7d 100644 --- a/homeassistant/components/yolink/manifest.json +++ b/homeassistant/components/yolink/manifest.json @@ -6,5 +6,5 @@ "dependencies": ["auth", "application_credentials"], "documentation": "https://www.home-assistant.io/integrations/yolink", "iot_class": "cloud_push", - "requirements": ["yolink-api==0.2.9"] + "requirements": ["yolink-api==0.3.0"] } diff --git a/homeassistant/components/yolink/sensor.py b/homeassistant/components/yolink/sensor.py index 149bdc0adf8..e4d0aa38fbe 100644 --- a/homeassistant/components/yolink/sensor.py +++ b/homeassistant/components/yolink/sensor.py @@ -8,6 +8,7 @@ from yolink.const import ( ATTR_DEVICE_CO_SMOKE_SENSOR, ATTR_DEVICE_DIMMER, ATTR_DEVICE_DOOR_SENSOR, + ATTR_DEVICE_FINGER, ATTR_DEVICE_LEAK_SENSOR, ATTR_DEVICE_LOCK, ATTR_DEVICE_MANIPULATOR, @@ -67,6 +68,7 @@ class YoLinkSensorEntityDescription( SENSOR_DEVICE_TYPE = [ ATTR_DEVICE_DIMMER, ATTR_DEVICE_DOOR_SENSOR, + ATTR_DEVICE_FINGER, ATTR_DEVICE_LEAK_SENSOR, ATTR_DEVICE_MOTION_SENSOR, ATTR_DEVICE_MULTI_OUTLET, @@ -86,6 +88,7 @@ SENSOR_DEVICE_TYPE = [ BATTERY_POWER_SENSOR = [ ATTR_DEVICE_DOOR_SENSOR, + ATTR_DEVICE_FINGER, ATTR_DEVICE_LEAK_SENSOR, ATTR_DEVICE_MOTION_SENSOR, ATTR_DEVICE_POWER_FAILURE_ALARM, @@ -129,6 +132,7 @@ SENSOR_TYPES: tuple[YoLinkSensorEntityDescription, ...] = ( state_class=SensorStateClass.MEASUREMENT, value=cvt_battery, exists_fn=lambda device: device.device_type in BATTERY_POWER_SENSOR, + should_update_entity=lambda value: value is not None, ), YoLinkSensorEntityDescription( key="humidity", diff --git a/homeassistant/components/yolink/switch.py b/homeassistant/components/yolink/switch.py index 415c1e9584d..018fcb84988 100644 --- a/homeassistant/components/yolink/switch.py +++ b/homeassistant/components/yolink/switch.py @@ -5,6 +5,7 @@ from collections.abc import Callable from dataclasses import dataclass from typing import Any +from yolink.client_request import ClientRequest from yolink.const import ( ATTR_DEVICE_MANIPULATOR, ATTR_DEVICE_MULTI_OUTLET, @@ -160,11 +161,17 @@ class YoLinkSwitchEntity(YoLinkEntity, SwitchEntity): async def call_state_change(self, state: str) -> None: """Call setState api to change switch state.""" - await self.call_device( - OutletRequestBuilder.set_state_request( + client_request: ClientRequest = None + if self.coordinator.device.device_type in [ + ATTR_DEVICE_OUTLET, + ATTR_DEVICE_MULTI_OUTLET, + ]: + client_request = OutletRequestBuilder.set_state_request( state, self.entity_description.plug_index ) - ) + else: + client_request = ClientRequest("setState", {"state": state}) + await self.call_device(client_request) self._attr_is_on = self._get_state(state, self.entity_description.plug_index) self.async_write_ha_state() diff --git a/requirements_all.txt b/requirements_all.txt index e36f8fe52fa..0e2b4467c8b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2723,7 +2723,7 @@ yeelight==0.7.12 yeelightsunflower==0.0.10 # homeassistant.components.yolink -yolink-api==0.2.9 +yolink-api==0.3.0 # homeassistant.components.youless youless-api==1.0.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f2e3d281f88..34faeba0e2a 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1999,7 +1999,7 @@ yalexs==1.5.1 yeelight==0.7.12 # homeassistant.components.yolink -yolink-api==0.2.9 +yolink-api==0.3.0 # homeassistant.components.youless youless-api==1.0.1