Adds voice assistant announce (#939)

Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
Michael Hansen 2024-09-08 20:04:53 -05:00 committed by GitHub
parent be2a3a3934
commit 3ca83b2c66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 208 additions and 112 deletions

View File

@ -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;

File diff suppressed because one or more lines are too long

View File

@ -70,6 +70,8 @@ from .api_pb2 import ( # type: ignore
UnsubscribeBluetoothLEAdvertisementsRequest,
UpdateCommandRequest,
ValveCommandRequest,
VoiceAssistantAnnounceFinished,
VoiceAssistantAnnounceRequest,
VoiceAssistantAudio,
VoiceAssistantEventData,
VoiceAssistantEventResponse,
@ -127,6 +129,7 @@ from .model import (
UpdateCommand,
UserService,
UserServiceArgType,
VoiceAssistantAnnounceFinished as VoiceAssistantAnnounceFinishedModel,
VoiceAssistantAudioData,
VoiceAssistantAudioSettings as VoiceAssistantAudioSettingsModel,
VoiceAssistantCommand,
@ -1417,6 +1420,19 @@ class APIClient:
)
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,

View File

@ -118,6 +118,8 @@ from .api_pb2 import ( # type: ignore
UpdateStateResponse,
ValveCommandRequest,
ValveStateResponse,
VoiceAssistantAnnounceFinished,
VoiceAssistantAnnounceRequest,
VoiceAssistantAudio,
VoiceAssistantEventResponse,
VoiceAssistantRequest,
@ -392,6 +394,8 @@ MESSAGE_TYPE_TO_PROTO = {
116: ListEntitiesUpdateResponse,
117: UpdateStateResponse,
118: UpdateCommandRequest,
119: VoiceAssistantAnnounceRequest,
120: VoiceAssistantAnnounceFinished,
}
MESSAGE_NUMBER_TO_PROTO = tuple(MESSAGE_TYPE_TO_PROTO.values())

View File

@ -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):
@ -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

View File

@ -67,6 +67,8 @@ from aioesphomeapi.api_pb2 import (
TimeCommandRequest,
UpdateCommandRequest,
ValveCommandRequest,
VoiceAssistantAnnounceFinished,
VoiceAssistantAnnounceRequest,
VoiceAssistantAudio,
VoiceAssistantAudioSettings,
VoiceAssistantEventData,
@ -109,6 +111,7 @@ from aioesphomeapi.model import (
UserService,
UserServiceArg,
UserServiceArgType,
VoiceAssistantAnnounceFinished as VoiceAssistantAnnounceFinishedModel,
VoiceAssistantAudioSettings as VoiceAssistantAudioSettingsModel,
VoiceAssistantEventType as VoiceAssistantEventModelType,
VoiceAssistantTimerEventType as VoiceAssistantTimerEventModelType,
@ -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[

View File

@ -549,9 +549,7 @@ async def test_plaintext_connection_fails_handshake(
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