core/homeassistant/components/wyoming/data.py

78 lines
2.3 KiB
Python
Raw Normal View History

"""Base class for Wyoming providers."""
from __future__ import annotations
import asyncio
import async_timeout
from wyoming.client import AsyncTcpClient
from wyoming.info import Describe, Info
from homeassistant.const import Platform
from .error import WyomingError
_INFO_TIMEOUT = 1
_INFO_RETRY_WAIT = 2
_INFO_RETRIES = 3
class WyomingService:
"""Hold info for Wyoming service."""
def __init__(self, host: str, port: int, info: Info) -> None:
"""Initialize Wyoming service."""
self.host = host
self.port = port
self.info = info
platforms = []
if any(asr.installed for asr in info.asr):
platforms.append(Platform.STT)
if any(tts.installed for tts in info.tts):
platforms.append(Platform.TTS)
self.platforms = platforms
@classmethod
async def create(cls, host: str, port: int) -> WyomingService | None:
"""Create a Wyoming service."""
info = await load_wyoming_info(host, port)
if info is None:
return None
return cls(host, port, info)
async def load_wyoming_info(
host: str,
port: int,
retries: int = _INFO_RETRIES,
retry_wait: float = _INFO_RETRY_WAIT,
timeout: float = _INFO_TIMEOUT,
) -> Info | None:
"""Load info from Wyoming server."""
wyoming_info: Info | None = None
for _ in range(retries + 1):
try:
async with AsyncTcpClient(host, port) as client:
with async_timeout.timeout(timeout):
# Describe -> Info
await client.write_event(Describe().event())
while True:
event = await client.read_event()
if event is None:
raise WyomingError(
"Connection closed unexpectedly",
)
if Info.is_type(event.type):
wyoming_info = Info.from_event(event)
break # while
if wyoming_info is not None:
break # for
except (asyncio.TimeoutError, OSError, WyomingError):
# Sleep and try again
await asyncio.sleep(retry_wait)
return wyoming_info