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

Adds voice assistant announce #939

Merged
17 changes: 17 additions & 0 deletions aioesphomeapi/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -1571,6 +1571,23 @@ message VoiceAssistantTimerEventResponse {
bool is_active = 6;
}

message VoiceAssistantAnnounceRequest {
option (id) = 119;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_VOICE_ASSISTANT";

string media_id = 1;
string text = 2;
}

message VoiceAssistantAnnounceFinished {
option (id) = 120;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_VOICE_ASSISTANT";

bool success = 1;
}

// ==================== ALARM CONTROL PANEL ====================
enum AlarmControlPanelState {
ALARM_STATE_DISARMED = 0;
Expand Down
242 changes: 133 additions & 109 deletions aioesphomeapi/api_pb2.py

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions aioesphomeapi/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
UnsubscribeBluetoothLEAdvertisementsRequest,
UpdateCommandRequest,
ValveCommandRequest,
VoiceAssistantAnnounceFinished,
VoiceAssistantAnnounceRequest,
VoiceAssistantAudio,
VoiceAssistantEventData,
VoiceAssistantEventResponse,
Expand Down Expand Up @@ -127,6 +129,7 @@
UpdateCommand,
UserService,
UserServiceArgType,
VoiceAssistantAnnounceFinished as VoiceAssistantAnnounceFinishedModel,
VoiceAssistantAudioData,
VoiceAssistantAudioSettings as VoiceAssistantAudioSettingsModel,
VoiceAssistantCommand,
Expand Down Expand Up @@ -1417,6 +1420,19 @@ def send_voice_assistant_timer_event(
)
self._get_connection().send_message(req)

async def send_voice_assistant_announcement_await_response(
self,
media_id: str,
timeout: float,
text: str = "",
) -> VoiceAssistantAnnounceFinishedModel:
resp = await self._get_connection().send_message_await_response(
VoiceAssistantAnnounceRequest(media_id=media_id, text=text),
VoiceAssistantAnnounceFinished,
timeout,
)
return VoiceAssistantAnnounceFinishedModel.from_pb(resp)

def alarm_control_panel_command(
self,
key: int,
Expand Down
4 changes: 4 additions & 0 deletions aioesphomeapi/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@
UpdateStateResponse,
ValveCommandRequest,
ValveStateResponse,
VoiceAssistantAnnounceFinished,
VoiceAssistantAnnounceRequest,
VoiceAssistantAudio,
VoiceAssistantEventResponse,
VoiceAssistantRequest,
Expand Down Expand Up @@ -392,6 +394,8 @@ def __init__(self, error: BluetoothGATTError) -> None:
116: ListEntitiesUpdateResponse,
117: UpdateStateResponse,
118: UpdateCommandRequest,
119: VoiceAssistantAnnounceRequest,
120: VoiceAssistantAnnounceFinished,
}

MESSAGE_NUMBER_TO_PROTO = tuple(MESSAGE_TYPE_TO_PROTO.values())
6 changes: 6 additions & 0 deletions aioesphomeapi/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ class VoiceAssistantFeature(enum.IntFlag):
SPEAKER = 1 << 1
API_AUDIO = 1 << 2
TIMERS = 1 << 3
ANNOUNCE = 1 << 4


class VoiceAssistantSubscriptionFlag(enum.IntFlag):
Expand Down Expand Up @@ -1291,6 +1292,11 @@ class VoiceAssistantAudioData(APIModelBase):
end: bool = False


@_frozen_dataclass_decorator
class VoiceAssistantAnnounceFinished(APIModelBase):
success: bool = False


class LogLevel(APIIntEnum):
LOG_LEVEL_NONE = 0
LOG_LEVEL_ERROR = 1
Expand Down
31 changes: 31 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@
TimeCommandRequest,
UpdateCommandRequest,
ValveCommandRequest,
VoiceAssistantAnnounceFinished,
VoiceAssistantAnnounceRequest,
VoiceAssistantAudio,
VoiceAssistantAudioSettings,
VoiceAssistantEventData,
Expand Down Expand Up @@ -109,6 +111,7 @@
UserService,
UserServiceArg,
UserServiceArgType,
VoiceAssistantAnnounceFinished as VoiceAssistantAnnounceFinishedModel,
VoiceAssistantAudioSettings as VoiceAssistantAudioSettingsModel,
VoiceAssistantEventType as VoiceAssistantEventModelType,
VoiceAssistantTimerEventType as VoiceAssistantTimerEventModelType,
Expand Down Expand Up @@ -2557,6 +2560,34 @@ async def test_send_voice_assistant_timer_event(auth_client: APIClient) -> None:
)


@pytest.mark.asyncio
async def test_send_voice_assistant_announcement_await_response(
api_client: tuple[
APIClient, APIConnection, asyncio.Transport, APIPlaintextFrameHelper
],
) -> None:
client, connection, _transport, protocol = api_client
original_send_message = connection.send_message

def send_message(msg):
assert msg == VoiceAssistantAnnounceRequest(
media_id="test-media-id", text="test-text"
)
original_send_message(msg)

with patch.object(connection, "send_message", new=send_message):
announcement_task = asyncio.create_task(
client.send_voice_assistant_announcement_await_response(
media_id="test-media-id", timeout=60.0, text="test-text"
)
)
await asyncio.sleep(0)
response: message.Message = VoiceAssistantAnnounceFinished(success=True)
mock_data_received(protocol, generate_plaintext_packet(response))
finished = await announcement_task
assert isinstance(finished, VoiceAssistantAnnounceFinishedModel)


@pytest.mark.asyncio
async def test_api_version_after_connection_closed(
api_client: tuple[
Expand Down
4 changes: 1 addition & 3 deletions tests/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,9 +549,7 @@ async def _do_finish_connect(self, *args, **kwargs):
connect_task = asyncio.create_task(connect(conn, login=False))
await connected.wait()

with (
pytest.raises(raised_exception),
):
with (pytest.raises(raised_exception),):
await asyncio.sleep(0)
await connect_task

Expand Down
Loading