mirror of
https://github.com/home-assistant/core.git
synced 2025-07-17 10:17:09 +00:00
Add YoLink new device types support 5009 & 5029 (#144323)
* Leak Stop * Fix as suggested.
This commit is contained in:
parent
3ff095cc51
commit
3f59b1c376
@ -26,7 +26,7 @@ from homeassistant.helpers import (
|
|||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from . import api
|
from . import api
|
||||||
from .const import DOMAIN, YOLINK_EVENT
|
from .const import ATTR_LORA_INFO, DOMAIN, YOLINK_EVENT
|
||||||
from .coordinator import YoLinkCoordinator
|
from .coordinator import YoLinkCoordinator
|
||||||
from .device_trigger import CONF_LONG_PRESS, CONF_SHORT_PRESS
|
from .device_trigger import CONF_LONG_PRESS, CONF_SHORT_PRESS
|
||||||
from .services import async_register_services
|
from .services import async_register_services
|
||||||
@ -72,6 +72,8 @@ class YoLinkHomeMessageListener(MessageListener):
|
|||||||
if device_coordinator is None:
|
if device_coordinator is None:
|
||||||
return
|
return
|
||||||
device_coordinator.dev_online = True
|
device_coordinator.dev_online = True
|
||||||
|
if (loraInfo := msg_data.get(ATTR_LORA_INFO)) is not None:
|
||||||
|
device_coordinator.dev_net_type = loraInfo.get("devNetType")
|
||||||
device_coordinator.async_set_updated_data(msg_data)
|
device_coordinator.async_set_updated_data(msg_data)
|
||||||
# handling events
|
# handling events
|
||||||
if (
|
if (
|
||||||
|
@ -11,6 +11,7 @@ from yolink.const import (
|
|||||||
ATTR_DEVICE_DOOR_SENSOR,
|
ATTR_DEVICE_DOOR_SENSOR,
|
||||||
ATTR_DEVICE_LEAK_SENSOR,
|
ATTR_DEVICE_LEAK_SENSOR,
|
||||||
ATTR_DEVICE_MOTION_SENSOR,
|
ATTR_DEVICE_MOTION_SENSOR,
|
||||||
|
ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER,
|
||||||
ATTR_DEVICE_VIBRATION_SENSOR,
|
ATTR_DEVICE_VIBRATION_SENSOR,
|
||||||
ATTR_DEVICE_WATER_METER_CONTROLLER,
|
ATTR_DEVICE_WATER_METER_CONTROLLER,
|
||||||
)
|
)
|
||||||
@ -51,6 +52,7 @@ SENSOR_DEVICE_TYPE = [
|
|||||||
ATTR_DEVICE_VIBRATION_SENSOR,
|
ATTR_DEVICE_VIBRATION_SENSOR,
|
||||||
ATTR_DEVICE_CO_SMOKE_SENSOR,
|
ATTR_DEVICE_CO_SMOKE_SENSOR,
|
||||||
ATTR_DEVICE_WATER_METER_CONTROLLER,
|
ATTR_DEVICE_WATER_METER_CONTROLLER,
|
||||||
|
ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -96,8 +98,14 @@ SENSOR_TYPES: tuple[YoLinkBinarySensorEntityDescription, ...] = (
|
|||||||
state_key="alarm",
|
state_key="alarm",
|
||||||
device_class=BinarySensorDeviceClass.MOISTURE,
|
device_class=BinarySensorDeviceClass.MOISTURE,
|
||||||
value=lambda state: state.get("leak") if state is not None else None,
|
value=lambda state: state.get("leak") if state is not None else None,
|
||||||
|
# This property will be lost during valve operation.
|
||||||
|
should_update_entity=lambda value: value is not None,
|
||||||
exists_fn=lambda device: (
|
exists_fn=lambda device: (
|
||||||
device.device_type == ATTR_DEVICE_WATER_METER_CONTROLLER
|
device.device_type
|
||||||
|
in [
|
||||||
|
ATTR_DEVICE_WATER_METER_CONTROLLER,
|
||||||
|
ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER,
|
||||||
|
]
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
YoLinkBinarySensorEntityDescription(
|
YoLinkBinarySensorEntityDescription(
|
||||||
|
@ -12,6 +12,7 @@ ATTR_VOLUME = "volume"
|
|||||||
ATTR_TEXT_MESSAGE = "message"
|
ATTR_TEXT_MESSAGE = "message"
|
||||||
ATTR_REPEAT = "repeat"
|
ATTR_REPEAT = "repeat"
|
||||||
ATTR_TONE = "tone"
|
ATTR_TONE = "tone"
|
||||||
|
ATTR_LORA_INFO = "loraInfo"
|
||||||
YOLINK_EVENT = f"{DOMAIN}_event"
|
YOLINK_EVENT = f"{DOMAIN}_event"
|
||||||
YOLINK_OFFLINE_TIME = 32400
|
YOLINK_OFFLINE_TIME = 32400
|
||||||
|
|
||||||
@ -37,5 +38,7 @@ DEV_MODEL_SWITCH_YS5708_UC = "YS5708-UC"
|
|||||||
DEV_MODEL_SWITCH_YS5708_EC = "YS5708-EC"
|
DEV_MODEL_SWITCH_YS5708_EC = "YS5708-EC"
|
||||||
DEV_MODEL_SWITCH_YS5709_UC = "YS5709-UC"
|
DEV_MODEL_SWITCH_YS5709_UC = "YS5709-UC"
|
||||||
DEV_MODEL_SWITCH_YS5709_EC = "YS5709-EC"
|
DEV_MODEL_SWITCH_YS5709_EC = "YS5709-EC"
|
||||||
|
DEV_MODEL_LEAK_STOP_YS5009 = "YS5009"
|
||||||
|
DEV_MODEL_LEAK_STOP_YS5029 = "YS5029"
|
||||||
DEV_MODEL_WATER_METER_YS5018_EC = "YS5018-EC"
|
DEV_MODEL_WATER_METER_YS5018_EC = "YS5018-EC"
|
||||||
DEV_MODEL_WATER_METER_YS5018_UC = "YS5018-UC"
|
DEV_MODEL_WATER_METER_YS5018_UC = "YS5018-UC"
|
||||||
|
@ -14,7 +14,7 @@ from homeassistant.core import HomeAssistant
|
|||||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||||
|
|
||||||
from .const import ATTR_DEVICE_STATE, DOMAIN, YOLINK_OFFLINE_TIME
|
from .const import ATTR_DEVICE_STATE, ATTR_LORA_INFO, DOMAIN, YOLINK_OFFLINE_TIME
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -47,6 +47,7 @@ class YoLinkCoordinator(DataUpdateCoordinator[dict]):
|
|||||||
self.device = device
|
self.device = device
|
||||||
self.paired_device = paired_device
|
self.paired_device = paired_device
|
||||||
self.dev_online = True
|
self.dev_online = True
|
||||||
|
self.dev_net_type = None
|
||||||
|
|
||||||
async def _async_update_data(self) -> dict:
|
async def _async_update_data(self) -> dict:
|
||||||
"""Fetch device state."""
|
"""Fetch device state."""
|
||||||
@ -83,5 +84,8 @@ class YoLinkCoordinator(DataUpdateCoordinator[dict]):
|
|||||||
)
|
)
|
||||||
raise UpdateFailed from yl_client_err
|
raise UpdateFailed from yl_client_err
|
||||||
if device_state is not None:
|
if device_state is not None:
|
||||||
|
dev_lora_info = device_state.get(ATTR_LORA_INFO)
|
||||||
|
if dev_lora_info is not None:
|
||||||
|
self.dev_net_type = dev_lora_info.get("devNetType")
|
||||||
return device_state
|
return device_state
|
||||||
return {}
|
return {}
|
||||||
|
@ -15,6 +15,7 @@ from yolink.const import (
|
|||||||
ATTR_DEVICE_MANIPULATOR,
|
ATTR_DEVICE_MANIPULATOR,
|
||||||
ATTR_DEVICE_MOTION_SENSOR,
|
ATTR_DEVICE_MOTION_SENSOR,
|
||||||
ATTR_DEVICE_MULTI_OUTLET,
|
ATTR_DEVICE_MULTI_OUTLET,
|
||||||
|
ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER,
|
||||||
ATTR_DEVICE_OUTLET,
|
ATTR_DEVICE_OUTLET,
|
||||||
ATTR_DEVICE_POWER_FAILURE_ALARM,
|
ATTR_DEVICE_POWER_FAILURE_ALARM,
|
||||||
ATTR_DEVICE_SIREN,
|
ATTR_DEVICE_SIREN,
|
||||||
@ -95,6 +96,7 @@ SENSOR_DEVICE_TYPE = [
|
|||||||
ATTR_DEVICE_VIBRATION_SENSOR,
|
ATTR_DEVICE_VIBRATION_SENSOR,
|
||||||
ATTR_DEVICE_WATER_DEPTH_SENSOR,
|
ATTR_DEVICE_WATER_DEPTH_SENSOR,
|
||||||
ATTR_DEVICE_WATER_METER_CONTROLLER,
|
ATTR_DEVICE_WATER_METER_CONTROLLER,
|
||||||
|
ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER,
|
||||||
ATTR_DEVICE_LOCK,
|
ATTR_DEVICE_LOCK,
|
||||||
ATTR_DEVICE_MANIPULATOR,
|
ATTR_DEVICE_MANIPULATOR,
|
||||||
ATTR_DEVICE_CO_SMOKE_SENSOR,
|
ATTR_DEVICE_CO_SMOKE_SENSOR,
|
||||||
@ -116,6 +118,7 @@ BATTERY_POWER_SENSOR = [
|
|||||||
ATTR_DEVICE_CO_SMOKE_SENSOR,
|
ATTR_DEVICE_CO_SMOKE_SENSOR,
|
||||||
ATTR_DEVICE_WATER_DEPTH_SENSOR,
|
ATTR_DEVICE_WATER_DEPTH_SENSOR,
|
||||||
ATTR_DEVICE_WATER_METER_CONTROLLER,
|
ATTR_DEVICE_WATER_METER_CONTROLLER,
|
||||||
|
ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER,
|
||||||
]
|
]
|
||||||
|
|
||||||
MCU_DEV_TEMPERATURE_SENSOR = [
|
MCU_DEV_TEMPERATURE_SENSOR = [
|
||||||
@ -211,14 +214,14 @@ SENSOR_TYPES: tuple[YoLinkSensorEntityDescription, ...] = (
|
|||||||
translation_key="power_failure_alarm",
|
translation_key="power_failure_alarm",
|
||||||
device_class=SensorDeviceClass.ENUM,
|
device_class=SensorDeviceClass.ENUM,
|
||||||
options=["normal", "alert", "off"],
|
options=["normal", "alert", "off"],
|
||||||
exists_fn=lambda device: device.device_type in ATTR_DEVICE_POWER_FAILURE_ALARM,
|
exists_fn=lambda device: device.device_type == ATTR_DEVICE_POWER_FAILURE_ALARM,
|
||||||
),
|
),
|
||||||
YoLinkSensorEntityDescription(
|
YoLinkSensorEntityDescription(
|
||||||
key="mute",
|
key="mute",
|
||||||
translation_key="power_failure_alarm_mute",
|
translation_key="power_failure_alarm_mute",
|
||||||
device_class=SensorDeviceClass.ENUM,
|
device_class=SensorDeviceClass.ENUM,
|
||||||
options=["muted", "unmuted"],
|
options=["muted", "unmuted"],
|
||||||
exists_fn=lambda device: device.device_type in ATTR_DEVICE_POWER_FAILURE_ALARM,
|
exists_fn=lambda device: device.device_type == ATTR_DEVICE_POWER_FAILURE_ALARM,
|
||||||
value=lambda value: "muted" if value is True else "unmuted",
|
value=lambda value: "muted" if value is True else "unmuted",
|
||||||
),
|
),
|
||||||
YoLinkSensorEntityDescription(
|
YoLinkSensorEntityDescription(
|
||||||
@ -226,7 +229,7 @@ SENSOR_TYPES: tuple[YoLinkSensorEntityDescription, ...] = (
|
|||||||
translation_key="power_failure_alarm_volume",
|
translation_key="power_failure_alarm_volume",
|
||||||
device_class=SensorDeviceClass.ENUM,
|
device_class=SensorDeviceClass.ENUM,
|
||||||
options=["low", "medium", "high"],
|
options=["low", "medium", "high"],
|
||||||
exists_fn=lambda device: device.device_type in ATTR_DEVICE_POWER_FAILURE_ALARM,
|
exists_fn=lambda device: device.device_type == ATTR_DEVICE_POWER_FAILURE_ALARM,
|
||||||
value=cvt_volume,
|
value=cvt_volume,
|
||||||
),
|
),
|
||||||
YoLinkSensorEntityDescription(
|
YoLinkSensorEntityDescription(
|
||||||
@ -234,14 +237,14 @@ SENSOR_TYPES: tuple[YoLinkSensorEntityDescription, ...] = (
|
|||||||
translation_key="power_failure_alarm_beep",
|
translation_key="power_failure_alarm_beep",
|
||||||
device_class=SensorDeviceClass.ENUM,
|
device_class=SensorDeviceClass.ENUM,
|
||||||
options=["enabled", "disabled"],
|
options=["enabled", "disabled"],
|
||||||
exists_fn=lambda device: device.device_type in ATTR_DEVICE_POWER_FAILURE_ALARM,
|
exists_fn=lambda device: device.device_type == ATTR_DEVICE_POWER_FAILURE_ALARM,
|
||||||
value=lambda value: "enabled" if value is True else "disabled",
|
value=lambda value: "enabled" if value is True else "disabled",
|
||||||
),
|
),
|
||||||
YoLinkSensorEntityDescription(
|
YoLinkSensorEntityDescription(
|
||||||
key="waterDepth",
|
key="waterDepth",
|
||||||
device_class=SensorDeviceClass.DISTANCE,
|
device_class=SensorDeviceClass.DISTANCE,
|
||||||
native_unit_of_measurement=UnitOfLength.METERS,
|
native_unit_of_measurement=UnitOfLength.METERS,
|
||||||
exists_fn=lambda device: device.device_type in ATTR_DEVICE_WATER_DEPTH_SENSOR,
|
exists_fn=lambda device: device.device_type == ATTR_DEVICE_WATER_DEPTH_SENSOR,
|
||||||
),
|
),
|
||||||
YoLinkSensorEntityDescription(
|
YoLinkSensorEntityDescription(
|
||||||
key="meter_reading",
|
key="meter_reading",
|
||||||
@ -251,7 +254,29 @@ SENSOR_TYPES: tuple[YoLinkSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.TOTAL_INCREASING,
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
should_update_entity=lambda value: value is not None,
|
should_update_entity=lambda value: value is not None,
|
||||||
exists_fn=lambda device: (
|
exists_fn=lambda device: (
|
||||||
device.device_type in ATTR_DEVICE_WATER_METER_CONTROLLER
|
device.device_type == ATTR_DEVICE_WATER_METER_CONTROLLER
|
||||||
|
),
|
||||||
|
),
|
||||||
|
YoLinkSensorEntityDescription(
|
||||||
|
key="meter_1_reading",
|
||||||
|
translation_key="water_meter_1_reading",
|
||||||
|
device_class=SensorDeviceClass.WATER,
|
||||||
|
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
|
||||||
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
|
should_update_entity=lambda value: value is not None,
|
||||||
|
exists_fn=lambda device: (
|
||||||
|
device.device_type == ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER
|
||||||
|
),
|
||||||
|
),
|
||||||
|
YoLinkSensorEntityDescription(
|
||||||
|
key="meter_2_reading",
|
||||||
|
translation_key="water_meter_2_reading",
|
||||||
|
device_class=SensorDeviceClass.WATER,
|
||||||
|
native_unit_of_measurement=UnitOfVolume.CUBIC_METERS,
|
||||||
|
state_class=SensorStateClass.TOTAL_INCREASING,
|
||||||
|
should_update_entity=lambda value: value is not None,
|
||||||
|
exists_fn=lambda device: (
|
||||||
|
device.device_type == ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
YoLinkSensorEntityDescription(
|
YoLinkSensorEntityDescription(
|
||||||
|
@ -90,6 +90,12 @@
|
|||||||
},
|
},
|
||||||
"water_meter_reading": {
|
"water_meter_reading": {
|
||||||
"name": "Water meter reading"
|
"name": "Water meter reading"
|
||||||
|
},
|
||||||
|
"water_meter_1_reading": {
|
||||||
|
"name": "Water meter 1 reading"
|
||||||
|
},
|
||||||
|
"water_meter_2_reading": {
|
||||||
|
"name": "Water meter 2 reading"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"number": {
|
"number": {
|
||||||
@ -100,6 +106,12 @@
|
|||||||
"valve": {
|
"valve": {
|
||||||
"meter_valve_state": {
|
"meter_valve_state": {
|
||||||
"name": "Valve state"
|
"name": "Valve state"
|
||||||
|
},
|
||||||
|
"meter_valve_1_state": {
|
||||||
|
"name": "Valve 1"
|
||||||
|
},
|
||||||
|
"meter_valve_2_state": {
|
||||||
|
"name": "Valve 2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -6,7 +6,11 @@ from collections.abc import Callable
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from yolink.client_request import ClientRequest
|
from yolink.client_request import ClientRequest
|
||||||
from yolink.const import ATTR_DEVICE_WATER_METER_CONTROLLER
|
from yolink.const import (
|
||||||
|
ATTR_DEVICE_MODEL_A,
|
||||||
|
ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER,
|
||||||
|
ATTR_DEVICE_WATER_METER_CONTROLLER,
|
||||||
|
)
|
||||||
from yolink.device import YoLinkDevice
|
from yolink.device import YoLinkDevice
|
||||||
|
|
||||||
from homeassistant.components.valve import (
|
from homeassistant.components.valve import (
|
||||||
@ -30,6 +34,7 @@ class YoLinkValveEntityDescription(ValveEntityDescription):
|
|||||||
|
|
||||||
exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True
|
exists_fn: Callable[[YoLinkDevice], bool] = lambda _: True
|
||||||
value: Callable = lambda state: state
|
value: Callable = lambda state: state
|
||||||
|
channel_index: int | None = None
|
||||||
|
|
||||||
|
|
||||||
DEVICE_TYPES: tuple[YoLinkValveEntityDescription, ...] = (
|
DEVICE_TYPES: tuple[YoLinkValveEntityDescription, ...] = (
|
||||||
@ -42,9 +47,32 @@ DEVICE_TYPES: tuple[YoLinkValveEntityDescription, ...] = (
|
|||||||
== ATTR_DEVICE_WATER_METER_CONTROLLER
|
== ATTR_DEVICE_WATER_METER_CONTROLLER
|
||||||
and not device.device_model_name.startswith(DEV_MODEL_WATER_METER_YS5007),
|
and not device.device_model_name.startswith(DEV_MODEL_WATER_METER_YS5007),
|
||||||
),
|
),
|
||||||
|
YoLinkValveEntityDescription(
|
||||||
|
key="valve_1_state",
|
||||||
|
translation_key="meter_valve_1_state",
|
||||||
|
device_class=ValveDeviceClass.WATER,
|
||||||
|
value=lambda value: value != "open" if value is not None else None,
|
||||||
|
exists_fn=lambda device: (
|
||||||
|
device.device_type == ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER
|
||||||
|
),
|
||||||
|
channel_index=0,
|
||||||
|
),
|
||||||
|
YoLinkValveEntityDescription(
|
||||||
|
key="valve_2_state",
|
||||||
|
translation_key="meter_valve_2_state",
|
||||||
|
device_class=ValveDeviceClass.WATER,
|
||||||
|
value=lambda value: value != "open" if value is not None else None,
|
||||||
|
exists_fn=lambda device: (
|
||||||
|
device.device_type == ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER
|
||||||
|
),
|
||||||
|
channel_index=1,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
DEVICE_TYPE = [ATTR_DEVICE_WATER_METER_CONTROLLER]
|
DEVICE_TYPE = [
|
||||||
|
ATTR_DEVICE_WATER_METER_CONTROLLER,
|
||||||
|
ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
@ -102,7 +130,17 @@ class YoLinkValveEntity(YoLinkEntity, ValveEntity):
|
|||||||
|
|
||||||
async def _async_invoke_device(self, state: str) -> None:
|
async def _async_invoke_device(self, state: str) -> None:
|
||||||
"""Call setState api to change valve state."""
|
"""Call setState api to change valve state."""
|
||||||
await self.call_device(ClientRequest("setState", {"valve": state}))
|
if (
|
||||||
|
self.coordinator.device.device_type
|
||||||
|
== ATTR_DEVICE_MULTI_WATER_METER_CONTROLLER
|
||||||
|
):
|
||||||
|
channel_index = self.entity_description.channel_index
|
||||||
|
if channel_index is not None:
|
||||||
|
await self.call_device(
|
||||||
|
ClientRequest("setState", {"valves": {str(channel_index): state}})
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await self.call_device(ClientRequest("setState", {"valve": state}))
|
||||||
self._attr_is_closed = state == "close"
|
self._attr_is_closed = state == "close"
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@ -113,3 +151,11 @@ class YoLinkValveEntity(YoLinkEntity, ValveEntity):
|
|||||||
async def async_close_valve(self) -> None:
|
async def async_close_valve(self) -> None:
|
||||||
"""Close valve."""
|
"""Close valve."""
|
||||||
await self._async_invoke_device("close")
|
await self._async_invoke_device("close")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def available(self) -> bool:
|
||||||
|
"""Return true is device is available."""
|
||||||
|
if self.coordinator.dev_net_type is not None:
|
||||||
|
# When the device operates in Class A mode, it cannot be controlled.
|
||||||
|
return self.coordinator.dev_net_type != ATTR_DEVICE_MODEL_A
|
||||||
|
return super().available
|
||||||
|
Loading…
x
Reference in New Issue
Block a user