Home Assistant Unofficial Reference 2024.12.1
data.py
Go to the documentation of this file.
1 """Base class for Wyoming providers."""
2 
3 from __future__ import annotations
4 
5 import asyncio
6 
7 from wyoming.client import AsyncTcpClient
8 from wyoming.info import Describe, Info
9 
10 from homeassistant.const import Platform
11 
12 from .error import WyomingError
13 
14 _INFO_TIMEOUT = 1
15 _INFO_RETRY_WAIT = 2
16 _INFO_RETRIES = 3
17 
18 
20  """Hold info for Wyoming service."""
21 
22  def __init__(self, host: str, port: int, info: Info) -> None:
23  """Initialize Wyoming service."""
24  self.hosthost = host
25  self.portport = port
26  self.infoinfo = info
27  self.platformsplatforms = []
28 
29  if (self.infoinfo.satellite is not None) and self.infoinfo.satellite.installed:
30  # Don't load platforms for satellite services, such as local wake
31  # word detection.
32  return
33 
34  if any(asr.installed for asr in info.asr):
35  self.platformsplatforms.append(Platform.STT)
36  if any(tts.installed for tts in info.tts):
37  self.platformsplatforms.append(Platform.TTS)
38  if any(wake.installed for wake in info.wake):
39  self.platformsplatforms.append(Platform.WAKE_WORD)
40  if any(intent.installed for intent in info.intent) or any(
41  handle.installed for handle in info.handle
42  ):
43  self.platformsplatforms.append(Platform.CONVERSATION)
44 
45  def has_services(self) -> bool:
46  """Return True if services are installed that Home Assistant can use."""
47  return (
48  any(asr for asr in self.infoinfo.asr if asr.installed)
49  or any(tts for tts in self.infoinfo.tts if tts.installed)
50  or any(wake for wake in self.infoinfo.wake if wake.installed)
51  or any(intent for intent in self.infoinfo.intent if intent.installed)
52  or any(handle for handle in self.infoinfo.handle if handle.installed)
53  or ((self.infoinfo.satellite is not None) and self.infoinfo.satellite.installed)
54  )
55 
56  def get_name(self) -> str | None:
57  """Return name of first installed usable service."""
58 
59  # Wyoming satellite
60  # Must be checked first because satellites may contain wake services, etc.
61  if (self.infoinfo.satellite is not None) and self.infoinfo.satellite.installed:
62  return self.infoinfo.satellite.name
63 
64  # ASR = automated speech recognition (speech-to-text)
65  asr_installed = [asr for asr in self.infoinfo.asr if asr.installed]
66  if asr_installed:
67  return asr_installed[0].name
68 
69  # TTS = text-to-speech
70  tts_installed = [tts for tts in self.infoinfo.tts if tts.installed]
71  if tts_installed:
72  return tts_installed[0].name
73 
74  # wake-word-detection
75  wake_installed = [wake for wake in self.infoinfo.wake if wake.installed]
76  if wake_installed:
77  return wake_installed[0].name
78 
79  # intent recognition (text -> intent)
80  intent_installed = [intent for intent in self.infoinfo.intent if intent.installed]
81  if intent_installed:
82  return intent_installed[0].name
83 
84  # intent handling (text -> text)
85  handle_installed = [handle for handle in self.infoinfo.handle if handle.installed]
86  if handle_installed:
87  return handle_installed[0].name
88 
89  return None
90 
91  @classmethod
92  async def create(cls, host: str, port: int) -> WyomingService | None:
93  """Create a Wyoming service."""
94  info = await load_wyoming_info(host, port)
95  if info is None:
96  return None
97 
98  return cls(host, port, info)
99 
100 
102  host: str,
103  port: int,
104  retries: int = _INFO_RETRIES,
105  retry_wait: float = _INFO_RETRY_WAIT,
106  timeout: float = _INFO_TIMEOUT,
107 ) -> Info | None:
108  """Load info from Wyoming server."""
109  wyoming_info: Info | None = None
110 
111  for _ in range(retries + 1):
112  try:
113  async with AsyncTcpClient(host, port) as client, asyncio.timeout(timeout):
114  # Describe -> Info
115  await client.write_event(Describe().event())
116  while True:
117  event = await client.read_event()
118  if event is None:
119  raise WyomingError( # noqa: TRY301
120  "Connection closed unexpectedly",
121  )
122 
123  if Info.is_type(event.type):
124  wyoming_info = Info.from_event(event)
125  break # while
126 
127  if wyoming_info is not None:
128  break # for
129  except (TimeoutError, OSError, WyomingError):
130  # Sleep and try again
131  await asyncio.sleep(retry_wait)
132 
133  return wyoming_info
WyomingService|None create(cls, str host, int port)
Definition: data.py:92
None __init__(self, str host, int port, Info info)
Definition: data.py:22
Info|None load_wyoming_info(str host, int port, int retries=_INFO_RETRIES, float retry_wait=_INFO_RETRY_WAIT, float timeout=_INFO_TIMEOUT)
Definition: data.py:107