Fix YoLink SpeakerHub support (#107925)
* improve * Fix when hub offline/online message pushing * fix as suggestion * check config entry load state * Add exception translation
This commit is contained in:
parent
521e9eb869
commit
34220200c1
5 changed files with 47 additions and 8 deletions
|
@ -19,8 +19,10 @@ from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers import (
|
||||||
aiohttp_client,
|
aiohttp_client,
|
||||||
config_entry_oauth2_flow,
|
config_entry_oauth2_flow,
|
||||||
|
config_validation as cv,
|
||||||
device_registry as dr,
|
device_registry as dr,
|
||||||
)
|
)
|
||||||
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from . import api
|
from . import api
|
||||||
from .const import DOMAIN, YOLINK_EVENT
|
from .const import DOMAIN, YOLINK_EVENT
|
||||||
|
@ -30,6 +32,8 @@ from .services import async_register_services
|
||||||
|
|
||||||
SCAN_INTERVAL = timedelta(minutes=5)
|
SCAN_INTERVAL = timedelta(minutes=5)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
PLATFORMS = [
|
PLATFORMS = [
|
||||||
Platform.BINARY_SENSOR,
|
Platform.BINARY_SENSOR,
|
||||||
|
@ -96,6 +100,14 @@ class YoLinkHomeStore:
|
||||||
device_coordinators: dict[str, YoLinkCoordinator]
|
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:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up yolink from a config entry."""
|
"""Set up yolink from a config entry."""
|
||||||
hass.data.setdefault(DOMAIN, {})
|
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)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
async_register_services(hass, entry)
|
|
||||||
|
|
||||||
async def async_yolink_unload(event) -> None:
|
async def async_yolink_unload(event) -> None:
|
||||||
"""Unload yolink."""
|
"""Unload yolink."""
|
||||||
await yolink_home.async_unload()
|
await yolink_home.async_unload()
|
||||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from yolink.client_request import ClientRequest
|
from yolink.client_request import ClientRequest
|
||||||
from yolink.const import ATTR_DEVICE_SPEAKER_HUB
|
from yolink.const import ATTR_DEVICE_SPEAKER_HUB
|
||||||
|
@ -30,6 +31,7 @@ class YoLinkNumberTypeConfigEntityDescription(NumberEntityDescription):
|
||||||
"""YoLink NumberEntity description."""
|
"""YoLink NumberEntity description."""
|
||||||
|
|
||||||
exists_fn: Callable[[YoLinkDevice], bool]
|
exists_fn: Callable[[YoLinkDevice], bool]
|
||||||
|
should_update_entity: Callable
|
||||||
value: Callable
|
value: Callable
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,6 +39,14 @@ NUMBER_TYPE_CONF_SUPPORT_DEVICES = [ATTR_DEVICE_SPEAKER_HUB]
|
||||||
|
|
||||||
SUPPORT_SET_VOLUME_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, ...] = (
|
DEVICE_CONFIG_DESCRIPTIONS: tuple[YoLinkNumberTypeConfigEntityDescription, ...] = (
|
||||||
YoLinkNumberTypeConfigEntityDescription(
|
YoLinkNumberTypeConfigEntityDescription(
|
||||||
key=OPTIONS_VALUME,
|
key=OPTIONS_VALUME,
|
||||||
|
@ -48,7 +58,8 @@ DEVICE_CONFIG_DESCRIPTIONS: tuple[YoLinkNumberTypeConfigEntityDescription, ...]
|
||||||
native_unit_of_measurement=None,
|
native_unit_of_measurement=None,
|
||||||
icon="mdi:volume-high",
|
icon="mdi:volume-high",
|
||||||
exists_fn=lambda device: device.device_type in SUPPORT_SET_VOLUME_DEVICES,
|
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
|
@callback
|
||||||
def update_entity_state(self, state: dict) -> None:
|
def update_entity_state(self, state: dict) -> None:
|
||||||
"""Update HA Entity State."""
|
"""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._attr_native_value = attr_val
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
from yolink.client_request import ClientRequest
|
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.core import HomeAssistant, ServiceCall
|
||||||
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
from homeassistant.helpers import config_validation as cv, device_registry as dr
|
from homeassistant.helpers import config_validation as cv, device_registry as dr
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
|
@ -19,7 +20,7 @@ from .const import (
|
||||||
SERVICE_PLAY_ON_SPEAKER_HUB = "play_on_speaker_hub"
|
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."""
|
"""Register services for YoLink integration."""
|
||||||
|
|
||||||
async def handle_speaker_hub_play_call(service_call: ServiceCall) -> None:
|
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_registry = dr.async_get(hass)
|
||||||
device_entry = device_registry.async_get(service_data[ATTR_TARGET_DEVICE])
|
device_entry = device_registry.async_get(service_data[ATTR_TARGET_DEVICE])
|
||||||
if device_entry is not None:
|
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]
|
home_store = hass.data[DOMAIN][entry.entry_id]
|
||||||
for identifier in device_entry.identifiers:
|
for identifier in device_entry.identifiers:
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -7,9 +7,7 @@ play_on_speaker_hub:
|
||||||
device:
|
device:
|
||||||
filter:
|
filter:
|
||||||
- integration: yolink
|
- integration: yolink
|
||||||
manufacturer: YoLink
|
|
||||||
model: SpeakerHub
|
model: SpeakerHub
|
||||||
|
|
||||||
message:
|
message:
|
||||||
required: true
|
required: true
|
||||||
example: hello, yolink
|
example: hello, yolink
|
||||||
|
|
|
@ -37,6 +37,11 @@
|
||||||
"button_4_long_press": "Button_4 (long press)"
|
"button_4_long_press": "Button_4 (long press)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"exceptions": {
|
||||||
|
"invalid_config_entry": {
|
||||||
|
"message": "Config entry not found or not loaded!"
|
||||||
|
}
|
||||||
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
"switch": {
|
"switch": {
|
||||||
"usb_ports": { "name": "USB ports" },
|
"usb_ports": { "name": "USB ports" },
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue