1 """Voice over IP (VoIP) implementation."""
3 from __future__
import annotations
6 from functools
import partial
8 from pathlib
import Path
10 from typing
import TYPE_CHECKING
12 from voip_utils
import (
24 select
as pipeline_select,
29 from .const
import CHANNELS, DOMAIN, RATE, RTP_AUDIO_SETTINGS, WIDTH
32 from .devices
import VoIPDevices
34 _LOGGER = logging.getLogger(__name__)
41 rtcp_state: RtcpState |
None =
None,
42 ) -> VoipDatagramProtocol:
43 """Plays a pre-recorded message if pipeline is misconfigured."""
44 voip_device = devices.async_get_or_create(call_info)
46 pipeline_id = pipeline_select.get_chosen_pipeline(hass, DOMAIN, voip_device.voip_id)
49 except PipelineNotFound:
54 or (pipeline.stt_engine
is None)
55 or (pipeline.tts_engine
is None)
61 opus_payload_type=call_info.opus_payload_type,
62 rtcp_state=rtcp_state,
65 if (protocol := voip_device.protocol)
is None:
66 raise ValueError(
"VoIP satellite not found")
68 protocol._rtp_input.opus_payload_type = call_info.opus_payload_type
69 protocol._rtp_output.opus_payload_type = call_info.opus_payload_type
71 protocol.rtcp_state = rtcp_state
72 if protocol.rtcp_state
is not None:
74 protocol.rtcp_state.bye_callback = protocol.disconnect
80 """HA UDP server for Voice over IP (VoIP)."""
82 def __init__(self, hass: HomeAssistant, devices: VoIPDevices) ->
None:
83 """Set up VoIP call handler."""
86 username=
"homeassistant",
87 id=time.monotonic_ns(),
88 session_name=
"voip_hass",
91 valid_protocol_factory=
lambda call_info, rtcp_state:
make_protocol(
92 hass, devices, call_info, rtcp_state
94 invalid_protocol_factory=(
98 opus_payload_type=call_info.opus_payload_type,
99 rtcp_state=rtcp_state,
109 device = self.
devicesdevices.async_get_or_create(call_info)
110 return device.async_allow_call(self.
hasshass)
113 """Signal wait_closed when transport is completely closed."""
117 """Wait for connection_lost to be called."""
122 """Plays a pre-recorded message on a loop."""
128 opus_payload_type: int,
129 message_delay: float = 1.0,
130 loop_delay: float = 2.0,
131 rtcp_state: RtcpState |
None =
None,
133 """Set up RTP server."""
138 opus_payload_type=opus_payload_type,
139 rtcp_state=rtcp_state,
145 self.
_audio_task_audio_task: asyncio.Task |
None =
None
149 """Handle raw audio chunk."""
150 if self.transport
is None:
155 file_path = Path(__file__).parent / self.
file_namefile_name
161 "voip_not_connected",
165 await self.
hasshass.async_add_executor_job(
170 **RTP_AUDIO_SETTINGS,
174 await asyncio.sleep(self.
loop_delayloop_delay)
def connection_lost(self, exc)
None __init__(self, HomeAssistant hass, VoIPDevices devices)
bool is_valid_call(self, CallInfo call_info)
None on_chunk(self, bytes audio_bytes)
None __init__(self, HomeAssistant hass, str file_name, int opus_payload_type, float message_delay=1.0, float loop_delay=2.0, RtcpState|None rtcp_state=None)
Pipeline async_get_pipeline(HomeAssistant hass, str|None pipeline_id=None)
VoipDatagramProtocol make_protocol(HomeAssistant hass, VoIPDevices devices, CallInfo call_info, RtcpState|None rtcp_state=None)