Add Yolink lock support (#73069)
* Add yolink lock support * Update .coveragerct * extract the commons
This commit is contained in:
parent
1744e7224b
commit
db53ab5fd0
9 changed files with 105 additions and 34 deletions
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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}"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
65
homeassistant/components/yolink/lock.py
Normal file
65
homeassistant/components/yolink/lock.py
Normal 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")
|
|
@ -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}"
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue