Fix YoLink SpeakerHub support (#107925)

* improve

* Fix when hub offline/online message pushing

* fix as suggestion

* check config entry load state

* Add exception translation
pull/109883/head
Matrix 2024-02-07 20:42:33 +08:00 committed by Franck Nijhof
parent e720b398d2
commit f61c70b686
No known key found for this signature in database
GPG Key ID: D62583BA8AB11CA3
5 changed files with 47 additions and 8 deletions

View File

@ -19,8 +19,10 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
from homeassistant.helpers import (
aiohttp_client,
config_entry_oauth2_flow,
config_validation as cv,
device_registry as dr,
)
from homeassistant.helpers.typing import ConfigType
from . import api
from .const import DOMAIN, YOLINK_EVENT
@ -30,6 +32,8 @@ from .services import async_register_services
SCAN_INTERVAL = timedelta(minutes=5)
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
PLATFORMS = [
Platform.BINARY_SENSOR,
@ -96,6 +100,14 @@ class YoLinkHomeStore:
device_coordinators: dict[str, YoLinkCoordinator]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up YoLink."""
async_register_services(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up yolink from a config entry."""
hass.data.setdefault(DOMAIN, {})
@ -147,8 +159,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
async_register_services(hass, entry)
async def async_yolink_unload(event) -> None:
"""Unload yolink."""
await yolink_home.async_unload()

View File

@ -4,6 +4,7 @@ from __future__ import annotations
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_SPEAKER_HUB
@ -30,6 +31,7 @@ class YoLinkNumberTypeConfigEntityDescription(NumberEntityDescription):
"""YoLink NumberEntity description."""
exists_fn: Callable[[YoLinkDevice], bool]
should_update_entity: Callable
value: Callable
@ -37,6 +39,14 @@ NUMBER_TYPE_CONF_SUPPORT_DEVICES = [ATTR_DEVICE_SPEAKER_HUB]
SUPPORT_SET_VOLUME_DEVICES = [ATTR_DEVICE_SPEAKER_HUB]
def get_volume_value(state: dict[str, Any]) -> int | None:
"""Get volume option."""
if (options := state.get("options")) is not None:
return options.get("volume")
return None
DEVICE_CONFIG_DESCRIPTIONS: tuple[YoLinkNumberTypeConfigEntityDescription, ...] = (
YoLinkNumberTypeConfigEntityDescription(
key=OPTIONS_VALUME,
@ -48,7 +58,8 @@ DEVICE_CONFIG_DESCRIPTIONS: tuple[YoLinkNumberTypeConfigEntityDescription, ...]
native_unit_of_measurement=None,
icon="mdi:volume-high",
exists_fn=lambda device: device.device_type in SUPPORT_SET_VOLUME_DEVICES,
value=lambda state: state["options"]["volume"],
should_update_entity=lambda value: value is not None,
value=get_volume_value,
),
)
@ -98,7 +109,10 @@ class YoLinkNumberTypeConfigEntity(YoLinkEntity, NumberEntity):
@callback
def update_entity_state(self, state: dict) -> None:
"""Update HA Entity State."""
attr_val = self.entity_description.value(state)
if (
attr_val := self.entity_description.value(state)
) is None and self.entity_description.should_update_entity(attr_val) is False:
return
self._attr_native_value = attr_val
self.async_write_ha_state()

View File

@ -3,8 +3,9 @@
import voluptuous as vol
from yolink.client_request import ClientRequest
from homeassistant.config_entries import ConfigEntry
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers import config_validation as cv, device_registry as dr
from .const import (
@ -19,7 +20,7 @@ from .const import (
SERVICE_PLAY_ON_SPEAKER_HUB = "play_on_speaker_hub"
def async_register_services(hass: HomeAssistant, entry: ConfigEntry) -> None:
def async_register_services(hass: HomeAssistant) -> None:
"""Register services for YoLink integration."""
async def handle_speaker_hub_play_call(service_call: ServiceCall) -> None:
@ -28,6 +29,17 @@ def async_register_services(hass: HomeAssistant, entry: ConfigEntry) -> None:
device_registry = dr.async_get(hass)
device_entry = device_registry.async_get(service_data[ATTR_TARGET_DEVICE])
if device_entry is not None:
for entry_id in device_entry.config_entries:
if (entry := hass.config_entries.async_get_entry(entry_id)) is None:
continue
if entry.domain == DOMAIN:
break
if entry is None or entry.state == ConfigEntryState.NOT_LOADED:
raise ServiceValidationError(
"Config entry not found or not loaded!",
translation_domain=DOMAIN,
translation_key="invalid_config_entry",
)
home_store = hass.data[DOMAIN][entry.entry_id]
for identifier in device_entry.identifiers:
if (

View File

@ -7,9 +7,7 @@ play_on_speaker_hub:
device:
filter:
- integration: yolink
manufacturer: YoLink
model: SpeakerHub
message:
required: true
example: hello, yolink

View File

@ -37,6 +37,11 @@
"button_4_long_press": "Button_4 (long press)"
}
},
"exceptions": {
"invalid_config_entry": {
"message": "Config entry not found or not loaded!"
}
},
"entity": {
"switch": {
"usb_ports": { "name": "USB ports" },