Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forward timer events to Wyoming satellites #118128

Merged
merged 3 commits into from
May 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion homeassistant/components/wyoming/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def _make_satellite(
device_id=device.id,
)

return WyomingSatellite(hass, service, satellite_device)
return WyomingSatellite(hass, config_entry, service, satellite_device)


async def update_listener(hass: HomeAssistant, entry: ConfigEntry):
Expand Down
4 changes: 2 additions & 2 deletions homeassistant/components/wyoming/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
"name": "Wyoming Protocol",
"codeowners": ["@balloob", "@synesthesiam"],
"config_flow": true,
"dependencies": ["assist_pipeline"],
"dependencies": ["assist_pipeline", "intent"],
"documentation": "https://www.home-assistant.io/integrations/wyoming",
"integration_type": "service",
"iot_class": "local_push",
"requirements": ["wyoming==1.5.3"],
"requirements": ["wyoming==1.5.4"],
"zeroconf": ["_wyoming._tcp.local."]
}
85 changes: 70 additions & 15 deletions homeassistant/components/wyoming/satellite.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,20 @@
from wyoming.audio import AudioChunk, AudioChunkConverter, AudioStart, AudioStop
from wyoming.client import AsyncTcpClient
from wyoming.error import Error
from wyoming.event import Event
from wyoming.info import Describe, Info
from wyoming.ping import Ping, Pong
from wyoming.pipeline import PipelineStage, RunPipeline
from wyoming.satellite import PauseSatellite, RunSatellite
from wyoming.timer import TimerCancelled, TimerFinished, TimerStarted, TimerUpdated
from wyoming.tts import Synthesize, SynthesizeVoice
from wyoming.vad import VoiceStarted, VoiceStopped
from wyoming.wake import Detect, Detection

from homeassistant.components import assist_pipeline, stt, tts
from homeassistant.components import assist_pipeline, intent, stt, tts
from homeassistant.components.assist_pipeline import select as pipeline_select
from homeassistant.core import Context, HomeAssistant
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import Context, HomeAssistant, callback

from .const import DOMAIN
from .data import WyomingService
Expand Down Expand Up @@ -49,10 +52,15 @@ class WyomingSatellite:
"""Remove voice satellite running the Wyoming protocol."""

def __init__(
self, hass: HomeAssistant, service: WyomingService, device: SatelliteDevice
self,
hass: HomeAssistant,
config_entry: ConfigEntry,
service: WyomingService,
device: SatelliteDevice,
) -> None:
"""Initialize satellite."""
self.hass = hass
self.config_entry = config_entry
self.service = service
self.device = device
self.is_running = True
Expand All @@ -73,6 +81,10 @@ async def run(self) -> None:
"""Run and maintain a connection to satellite."""
_LOGGER.debug("Running satellite task")

unregister_timer_handler = intent.async_register_timer_handler(
self.hass, self.device.device_id, self._handle_timer
)

try:
while self.is_running:
try:
Expand All @@ -97,6 +109,8 @@ async def run(self) -> None:
# Wait to restart
await self.on_restart()
finally:
unregister_timer_handler()

# Ensure sensor is off (before stop)
self.device.set_is_active(False)

Expand Down Expand Up @@ -142,7 +156,8 @@ async def on_stopped(self) -> None:
def _send_pause(self) -> None:
"""Send a pause message to satellite."""
if self._client is not None:
self.hass.async_create_background_task(
self.config_entry.async_create_background_task(
self.hass,
self._client.write_event(PauseSatellite().event()),
"pause satellite",
)
Expand Down Expand Up @@ -207,11 +222,11 @@ async def _run_pipeline_loop(self) -> None:
send_ping = True

# Read events and check for pipeline end in parallel
pipeline_ended_task = self.hass.async_create_background_task(
self._pipeline_ended_event.wait(), "satellite pipeline ended"
pipeline_ended_task = self.config_entry.async_create_background_task(
self.hass, self._pipeline_ended_event.wait(), "satellite pipeline ended"
)
client_event_task = self.hass.async_create_background_task(
self._client.read_event(), "satellite event read"
client_event_task = self.config_entry.async_create_background_task(
self.hass, self._client.read_event(), "satellite event read"
)
pending = {pipeline_ended_task, client_event_task}

Expand All @@ -222,8 +237,8 @@ async def _run_pipeline_loop(self) -> None:
if send_ping:
# Ensure satellite is still connected
send_ping = False
self.hass.async_create_background_task(
self._send_delayed_ping(), "ping satellite"
self.config_entry.async_create_background_task(
self.hass, self._send_delayed_ping(), "ping satellite"
)

async with asyncio.timeout(_PING_TIMEOUT):
Expand All @@ -234,8 +249,12 @@ async def _run_pipeline_loop(self) -> None:
# Pipeline run end event was received
_LOGGER.debug("Pipeline finished")
self._pipeline_ended_event.clear()
pipeline_ended_task = self.hass.async_create_background_task(
self._pipeline_ended_event.wait(), "satellite pipeline ended"
pipeline_ended_task = (
self.config_entry.async_create_background_task(
self.hass,
self._pipeline_ended_event.wait(),
"satellite pipeline ended",
)
)
pending.add(pipeline_ended_task)

Expand Down Expand Up @@ -307,8 +326,8 @@ async def _run_pipeline_loop(self) -> None:
_LOGGER.debug("Unexpected event from satellite: %s", client_event)

# Next event
client_event_task = self.hass.async_create_background_task(
self._client.read_event(), "satellite event read"
client_event_task = self.config_entry.async_create_background_task(
self.hass, self._client.read_event(), "satellite event read"
)
pending.add(client_event_task)

Expand Down Expand Up @@ -348,7 +367,8 @@ def _run_pipeline_once(
)
self._is_pipeline_running = True
self._pipeline_ended_event.clear()
self.hass.async_create_background_task(
self.config_entry.async_create_background_task(
self.hass,
assist_pipeline.async_pipeline_from_audio_stream(
self.hass,
context=Context(),
Expand Down Expand Up @@ -544,3 +564,38 @@ async def _stt_stream(self) -> AsyncGenerator[bytes, None]:
yield chunk
except asyncio.CancelledError:
pass # ignore

@callback
def _handle_timer(
self, event_type: intent.TimerEventType, timer: intent.TimerInfo
) -> None:
"""Forward timer events to satellite."""
assert self._client is not None

_LOGGER.debug("Timer event: type=%s, info=%s", event_type, timer)
event: Event | None = None
if event_type == intent.TimerEventType.STARTED:
event = TimerStarted(
id=timer.id,
total_seconds=timer.seconds,
name=timer.name,
start_hours=timer.start_hours,
start_minutes=timer.start_minutes,
start_seconds=timer.start_seconds,
).event()
elif event_type == intent.TimerEventType.UPDATED:
event = TimerUpdated(
id=timer.id,
is_active=timer.is_active,
total_seconds=timer.seconds,
).event()
elif event_type == intent.TimerEventType.CANCELLED:
event = TimerCancelled(id=timer.id).event()
elif event_type == intent.TimerEventType.FINISHED:
event = TimerFinished(id=timer.id).event()

if event is not None:
# Send timer event to satellite
self.config_entry.async_create_background_task(
self.hass, self._client.write_event(event), "wyoming timer event"
)
2 changes: 1 addition & 1 deletion requirements_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2894,7 +2894,7 @@ wled==0.18.0
wolf-comm==0.0.8

# homeassistant.components.wyoming
wyoming==1.5.3
wyoming==1.5.4

# homeassistant.components.xbox
xbox-webapi==2.0.11
Expand Down
2 changes: 1 addition & 1 deletion requirements_test_all.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2250,7 +2250,7 @@ wled==0.18.0
wolf-comm==0.0.8

# homeassistant.components.wyoming
wyoming==1.5.3
wyoming==1.5.4

# homeassistant.components.xbox
xbox-webapi==2.0.11
Expand Down
Loading