diff --git a/.coveragerc b/.coveragerc index 0a2e8e57fa8..0fb2210b3cd 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1498,6 +1498,7 @@ omit = homeassistant/components/yolink/climate.py homeassistant/components/yolink/const.py homeassistant/components/yolink/coordinator.py + homeassistant/components/yolink/cover.py homeassistant/components/yolink/entity.py homeassistant/components/yolink/lock.py homeassistant/components/yolink/sensor.py diff --git a/homeassistant/components/yolink/__init__.py b/homeassistant/components/yolink/__init__.py index 714b0b25070..3257d64d265 100644 --- a/homeassistant/components/yolink/__init__.py +++ b/homeassistant/components/yolink/__init__.py @@ -27,6 +27,7 @@ SCAN_INTERVAL = timedelta(minutes=5) PLATFORMS = [ Platform.BINARY_SENSOR, Platform.CLIMATE, + Platform.COVER, Platform.LOCK, Platform.SENSOR, Platform.SIREN, diff --git a/homeassistant/components/yolink/binary_sensor.py b/homeassistant/components/yolink/binary_sensor.py index b296e01fa56..6e29bccf437 100644 --- a/homeassistant/components/yolink/binary_sensor.py +++ b/homeassistant/components/yolink/binary_sensor.py @@ -47,6 +47,13 @@ SENSOR_DEVICE_TYPE = [ ] +def is_door_sensor(device: YoLinkDevice) -> bool: + """Check Door Sensor type.""" + return device.device_type == ATTR_DEVICE_DOOR_SENSOR and ( + device.parent_id is None or device.parent_id == "null" + ) + + SENSOR_TYPES: tuple[YoLinkBinarySensorEntityDescription, ...] = ( YoLinkBinarySensorEntityDescription( key="door_state", @@ -54,7 +61,7 @@ SENSOR_TYPES: tuple[YoLinkBinarySensorEntityDescription, ...] = ( device_class=BinarySensorDeviceClass.DOOR, name="State", value=lambda value: value == "open" if value is not None else None, - exists_fn=lambda device: device.device_type == ATTR_DEVICE_DOOR_SENSOR, + exists_fn=is_door_sensor, ), YoLinkBinarySensorEntityDescription( key="motion_state", diff --git a/homeassistant/components/yolink/cover.py b/homeassistant/components/yolink/cover.py new file mode 100644 index 00000000000..e7e32764ca8 --- /dev/null +++ b/homeassistant/components/yolink/cover.py @@ -0,0 +1,86 @@ +"""YoLink Garage Door.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.components.cover import ( + CoverDeviceClass, + CoverEntity, + CoverEntityFeature, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import ATTR_COORDINATORS, ATTR_DEVICE_DOOR_SENSOR, DOMAIN +from .coordinator import YoLinkCoordinator +from .entity import YoLinkEntity + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up YoLink garage door from a config entry.""" + device_coordinators = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATORS] + entities = [ + YoLinkCoverEntity(config_entry, device_coordinator) + for device_coordinator in device_coordinators.values() + if device_coordinator.device.device_type == ATTR_DEVICE_DOOR_SENSOR + and device_coordinator.device.parent_id is not None + and device_coordinator.device.parent_id != "null" + ] + async_add_entities(entities) + + +class YoLinkCoverEntity(YoLinkEntity, CoverEntity): + """YoLink Cover Entity.""" + + def __init__( + self, + config_entry: ConfigEntry, + coordinator: YoLinkCoordinator, + ) -> None: + """Init YoLink garage door entity.""" + super().__init__(config_entry, coordinator) + self._attr_unique_id = f"{coordinator.device.device_id}_door_state" + self._attr_name = f"{coordinator.device.device_name} (State)" + self._attr_device_class = CoverDeviceClass.GARAGE + self._attr_supported_features = ( + CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE + ) + + @callback + def update_entity_state(self, state: dict[str, Any]) -> None: + """Update HA Entity State.""" + self._attr_is_closed = state.get("state") == "closed" + self.async_write_ha_state() + + async def toggle_garage_state(self, state: str) -> None: + """Toggle Garage door state.""" + # make sure current state is correct + await self.coordinator.async_refresh() + if state == "open" and self.is_closed is False: + return + if state == "close" and self.is_closed is True: + return + # get paired controller + door_controller_coordinator = self.hass.data[DOMAIN][ + self.config_entry.entry_id + ][ATTR_COORDINATORS].get(self.coordinator.device.parent_id) + if door_controller_coordinator is None: + raise ValueError( + "This device has not been paired with a garage door controller" + ) + # call controller api open/close garage door + await door_controller_coordinator.device.call_device_http_api("toggle", None) + await self.coordinator.async_refresh() + + async def async_open_cover(self, **kwargs: Any) -> None: + """Open garage door.""" + await self.toggle_garage_state("open") + + async def async_close_cover(self, **kwargs: Any) -> None: + """Close garage door.""" + await self.toggle_garage_state("close") diff --git a/homeassistant/components/yolink/manifest.json b/homeassistant/components/yolink/manifest.json index d2a02a44c42..0db736938f7 100644 --- a/homeassistant/components/yolink/manifest.json +++ b/homeassistant/components/yolink/manifest.json @@ -3,7 +3,7 @@ "name": "YoLink", "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/yolink", - "requirements": ["yolink-api==0.0.8"], + "requirements": ["yolink-api==0.0.9"], "dependencies": ["auth", "application_credentials"], "codeowners": ["@matrixd2"], "iot_class": "cloud_push" diff --git a/requirements_all.txt b/requirements_all.txt index 1f549530318..ad2c15cf77b 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2486,7 +2486,7 @@ yeelight==0.7.10 yeelightsunflower==0.0.10 # homeassistant.components.yolink -yolink-api==0.0.8 +yolink-api==0.0.9 # homeassistant.components.youless youless-api==0.16 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index ba7307a7cc3..784b740d459 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1662,7 +1662,7 @@ yalexs==1.1.25 yeelight==0.7.10 # homeassistant.components.yolink -yolink-api==0.0.8 +yolink-api==0.0.9 # homeassistant.components.youless youless-api==0.16