Add Yolink lock support (#73069)

* Add yolink lock support

* Update .coveragerct

* extract the commons
This commit is contained in:
Matrix 2022-06-06 11:58:29 +08:00 committed by GitHub
parent 1744e7224b
commit db53ab5fd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 105 additions and 34 deletions

View file

@ -1495,6 +1495,7 @@ omit =
homeassistant/components/yolink/const.py
homeassistant/components/yolink/coordinator.py
homeassistant/components/yolink/entity.py
homeassistant/components/yolink/lock.py
homeassistant/components/yolink/sensor.py
homeassistant/components/yolink/siren.py
homeassistant/components/yolink/switch.py

View file

@ -24,7 +24,13 @@ from .coordinator import YoLinkCoordinator
SCAN_INTERVAL = timedelta(minutes=5)
PLATFORMS = [Platform.BINARY_SENSOR, Platform.SENSOR, Platform.SIREN, Platform.SWITCH]
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.LOCK,
Platform.SENSOR,
Platform.SIREN,
Platform.SWITCH,
]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:

View file

@ -96,7 +96,7 @@ async def async_setup_entry(
if description.exists_fn(binary_sensor_device_coordinator.device):
entities.append(
YoLinkBinarySensorEntity(
binary_sensor_device_coordinator, description
config_entry, binary_sensor_device_coordinator, description
)
)
async_add_entities(entities)
@ -109,11 +109,12 @@ class YoLinkBinarySensorEntity(YoLinkEntity, BinarySensorEntity):
def __init__(
self,
config_entry: ConfigEntry,
coordinator: YoLinkCoordinator,
description: YoLinkBinarySensorEntityDescription,
) -> None:
"""Init YoLink Sensor."""
super().__init__(coordinator)
super().__init__(config_entry, coordinator)
self.entity_description = description
self._attr_unique_id = (
f"{coordinator.device.device_id} {self.entity_description.key}"

View file

@ -20,3 +20,4 @@ ATTR_DEVICE_LEAK_SENSOR = "LeakSensor"
ATTR_DEVICE_VIBRATION_SENSOR = "VibrationSensor"
ATTR_DEVICE_OUTLET = "Outlet"
ATTR_DEVICE_SIREN = "Siren"
ATTR_DEVICE_LOCK = "Lock"

View file

@ -3,7 +3,11 @@ from __future__ import annotations
from abc import abstractmethod
from yolink.exception import YoLinkAuthFailError, YoLinkClientError
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@ -16,10 +20,12 @@ class YoLinkEntity(CoordinatorEntity[YoLinkCoordinator]):
def __init__(
self,
config_entry: ConfigEntry,
coordinator: YoLinkCoordinator,
) -> None:
"""Init YoLink Entity."""
super().__init__(coordinator)
self.config_entry = config_entry
@property
def device_id(self) -> str:
@ -52,3 +58,15 @@ class YoLinkEntity(CoordinatorEntity[YoLinkCoordinator]):
@abstractmethod
def update_entity_state(self, state: dict) -> None:
"""Parse and update entity state, should be overridden."""
async def call_device_api(self, command: str, params: dict) -> None:
"""Call device Api."""
try:
# call_device_http_api will check result, fail by raise YoLinkClientError
await self.coordinator.device.call_device_http_api(command, params)
except YoLinkAuthFailError as yl_auth_err:
self.config_entry.async_start_reauth(self.hass)
raise HomeAssistantError(yl_auth_err) from yl_auth_err
except YoLinkClientError as yl_client_err:
self.coordinator.last_update_success = False
raise HomeAssistantError(yl_client_err) from yl_client_err

View file

@ -0,0 +1,65 @@
"""YoLink Lock."""
from __future__ import annotations
from typing import Any
from homeassistant.components.lock import LockEntity
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_LOCK, 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 lock from a config entry."""
device_coordinators = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATORS]
entities = [
YoLinkLockEntity(config_entry, device_coordinator)
for device_coordinator in device_coordinators.values()
if device_coordinator.device.device_type == ATTR_DEVICE_LOCK
]
async_add_entities(entities)
class YoLinkLockEntity(YoLinkEntity, LockEntity):
"""YoLink Lock Entity."""
def __init__(
self,
config_entry: ConfigEntry,
coordinator: YoLinkCoordinator,
) -> None:
"""Init YoLink Lock."""
super().__init__(config_entry, coordinator)
self._attr_unique_id = f"{coordinator.device.device_id}_lock_state"
self._attr_name = f"{coordinator.device.device_name}(LockState)"
@callback
def update_entity_state(self, state: dict[str, Any]) -> None:
"""Update HA Entity State."""
state_value = state.get("state")
self._attr_is_locked = (
state_value == "locked" if state_value is not None else None
)
self.async_write_ha_state()
async def call_lock_state_change(self, state: str) -> None:
"""Call setState api to change lock state."""
await self.call_device_api("setState", {"state": state})
self._attr_is_locked = state == "lock"
self.async_write_ha_state()
async def async_lock(self, **kwargs: Any) -> None:
"""Lock device."""
await self.call_lock_state_change("lock")
async def async_unlock(self, **kwargs: Any) -> None:
"""Unlock device."""
await self.call_lock_state_change("unlock")

View file

@ -21,6 +21,7 @@ from homeassistant.util import percentage
from .const import (
ATTR_COORDINATORS,
ATTR_DEVICE_DOOR_SENSOR,
ATTR_DEVICE_LOCK,
ATTR_DEVICE_MOTION_SENSOR,
ATTR_DEVICE_TH_SENSOR,
ATTR_DEVICE_VIBRATION_SENSOR,
@ -51,6 +52,7 @@ SENSOR_DEVICE_TYPE = [
ATTR_DEVICE_MOTION_SENSOR,
ATTR_DEVICE_TH_SENSOR,
ATTR_DEVICE_VIBRATION_SENSOR,
ATTR_DEVICE_LOCK,
]
BATTERY_POWER_SENSOR = [
@ -58,6 +60,7 @@ BATTERY_POWER_SENSOR = [
ATTR_DEVICE_TH_SENSOR,
ATTR_DEVICE_MOTION_SENSOR,
ATTR_DEVICE_VIBRATION_SENSOR,
ATTR_DEVICE_LOCK,
]
@ -112,6 +115,7 @@ async def async_setup_entry(
if description.exists_fn(sensor_device_coordinator.device):
entities.append(
YoLinkSensorEntity(
config_entry,
sensor_device_coordinator,
description,
)
@ -126,11 +130,12 @@ class YoLinkSensorEntity(YoLinkEntity, SensorEntity):
def __init__(
self,
config_entry: ConfigEntry,
coordinator: YoLinkCoordinator,
description: YoLinkSensorEntityDescription,
) -> None:
"""Init YoLink Sensor."""
super().__init__(coordinator)
super().__init__(config_entry, coordinator)
self.entity_description = description
self._attr_unique_id = (
f"{coordinator.device.device_id} {self.entity_description.key}"

View file

@ -6,7 +6,6 @@ from dataclasses import dataclass
from typing import Any
from yolink.device import YoLinkDevice
from yolink.exception import YoLinkAuthFailError, YoLinkClientError
from homeassistant.components.siren import (
SirenEntity,
@ -15,7 +14,6 @@ from homeassistant.components.siren import (
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ATTR_COORDINATORS, ATTR_DEVICE_SIREN, DOMAIN
@ -79,8 +77,7 @@ class YoLinkSirenEntity(YoLinkEntity, SirenEntity):
description: YoLinkSirenEntityDescription,
) -> None:
"""Init YoLink Siren."""
super().__init__(coordinator)
self.config_entry = config_entry
super().__init__(config_entry, coordinator)
self.entity_description = description
self._attr_unique_id = (
f"{coordinator.device.device_id} {self.entity_description.key}"
@ -102,17 +99,7 @@ class YoLinkSirenEntity(YoLinkEntity, SirenEntity):
async def call_state_change(self, state: bool) -> None:
"""Call setState api to change siren state."""
try:
# call_device_http_api will check result, fail by raise YoLinkClientError
await self.coordinator.device.call_device_http_api(
"setState", {"state": {"alarm": state}}
)
except YoLinkAuthFailError as yl_auth_err:
self.config_entry.async_start_reauth(self.hass)
raise HomeAssistantError(yl_auth_err) from yl_auth_err
except YoLinkClientError as yl_client_err:
self.coordinator.last_update_success = False
raise HomeAssistantError(yl_client_err) from yl_client_err
await self.call_device_api("setState", {"state": {"alarm": state}})
self._attr_is_on = self.entity_description.value("alert" if state else "normal")
self.async_write_ha_state()

View file

@ -6,7 +6,6 @@ from dataclasses import dataclass
from typing import Any
from yolink.device import YoLinkDevice
from yolink.exception import YoLinkAuthFailError, YoLinkClientError
from homeassistant.components.switch import (
SwitchDeviceClass,
@ -15,7 +14,6 @@ from homeassistant.components.switch import (
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ATTR_COORDINATORS, ATTR_DEVICE_OUTLET, DOMAIN
@ -80,8 +78,7 @@ class YoLinkSwitchEntity(YoLinkEntity, SwitchEntity):
description: YoLinkSwitchEntityDescription,
) -> None:
"""Init YoLink Outlet."""
super().__init__(coordinator)
self.config_entry = config_entry
super().__init__(config_entry, coordinator)
self.entity_description = description
self._attr_unique_id = (
f"{coordinator.device.device_id} {self.entity_description.key}"
@ -100,17 +97,7 @@ class YoLinkSwitchEntity(YoLinkEntity, SwitchEntity):
async def call_state_change(self, state: str) -> None:
"""Call setState api to change outlet state."""
try:
# call_device_http_api will check result, fail by raise YoLinkClientError
await self.coordinator.device.call_device_http_api(
"setState", {"state": state}
)
except YoLinkAuthFailError as yl_auth_err:
self.config_entry.async_start_reauth(self.hass)
raise HomeAssistantError(yl_auth_err) from yl_auth_err
except YoLinkClientError as yl_client_err:
self.coordinator.last_update_success = False
raise HomeAssistantError(yl_client_err) from yl_client_err
await self.call_device_api("setState", {"state": state})
self._attr_is_on = self.entity_description.value(state)
self.async_write_ha_state()