mirror of
https://github.com/esphome/aioesphomeapi.git
synced 2025-01-02 18:38:05 +01:00
Add get/set/config messages for voice assistant (#956)
This commit is contained in:
parent
bf6aff7ab0
commit
709cb3fd15
@ -1589,6 +1589,36 @@ message VoiceAssistantAnnounceFinished {
|
|||||||
bool success = 1;
|
bool success = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message VoiceAssistantWakeWord {
|
||||||
|
uint32 id = 1;
|
||||||
|
string wake_word = 2;
|
||||||
|
repeated string trained_languages = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VoiceAssistantConfigurationRequest {
|
||||||
|
option (id) = 121;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||||
|
}
|
||||||
|
|
||||||
|
message VoiceAssistantConfigurationResponse {
|
||||||
|
option (id) = 122;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||||
|
|
||||||
|
repeated VoiceAssistantWakeWord available_wake_words = 1;
|
||||||
|
repeated uint32 active_wake_words = 2;
|
||||||
|
uint32 max_active_wake_words = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message VoiceAssistantSetConfiguration {
|
||||||
|
option (id) = 123;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_VOICE_ASSISTANT";
|
||||||
|
|
||||||
|
repeated uint32 active_wake_words = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== ALARM CONTROL PANEL ====================
|
// ==================== ALARM CONTROL PANEL ====================
|
||||||
enum AlarmControlPanelState {
|
enum AlarmControlPanelState {
|
||||||
ALARM_STATE_DISARMED = 0;
|
ALARM_STATE_DISARMED = 0;
|
||||||
|
File diff suppressed because one or more lines are too long
@ -73,10 +73,13 @@ from .api_pb2 import ( # type: ignore
|
|||||||
VoiceAssistantAnnounceFinished,
|
VoiceAssistantAnnounceFinished,
|
||||||
VoiceAssistantAnnounceRequest,
|
VoiceAssistantAnnounceRequest,
|
||||||
VoiceAssistantAudio,
|
VoiceAssistantAudio,
|
||||||
|
VoiceAssistantConfigurationRequest,
|
||||||
|
VoiceAssistantConfigurationResponse,
|
||||||
VoiceAssistantEventData,
|
VoiceAssistantEventData,
|
||||||
VoiceAssistantEventResponse,
|
VoiceAssistantEventResponse,
|
||||||
VoiceAssistantRequest,
|
VoiceAssistantRequest,
|
||||||
VoiceAssistantResponse,
|
VoiceAssistantResponse,
|
||||||
|
VoiceAssistantSetConfiguration,
|
||||||
VoiceAssistantTimerEventResponse,
|
VoiceAssistantTimerEventResponse,
|
||||||
)
|
)
|
||||||
from .client_callbacks import (
|
from .client_callbacks import (
|
||||||
@ -133,6 +136,7 @@ from .model import (
|
|||||||
VoiceAssistantAudioData,
|
VoiceAssistantAudioData,
|
||||||
VoiceAssistantAudioSettings as VoiceAssistantAudioSettingsModel,
|
VoiceAssistantAudioSettings as VoiceAssistantAudioSettingsModel,
|
||||||
VoiceAssistantCommand,
|
VoiceAssistantCommand,
|
||||||
|
VoiceAssistantConfigurationResponse as VoiceAssistantConfigurationResponseModel,
|
||||||
VoiceAssistantEventType,
|
VoiceAssistantEventType,
|
||||||
VoiceAssistantSubscriptionFlag,
|
VoiceAssistantSubscriptionFlag,
|
||||||
VoiceAssistantTimerEventType,
|
VoiceAssistantTimerEventType,
|
||||||
@ -1459,6 +1463,22 @@ class APIClient:
|
|||||||
)
|
)
|
||||||
return VoiceAssistantAnnounceFinishedModel.from_pb(resp)
|
return VoiceAssistantAnnounceFinishedModel.from_pb(resp)
|
||||||
|
|
||||||
|
async def get_voice_assistant_configuration(
|
||||||
|
self, timeout: float
|
||||||
|
) -> VoiceAssistantConfigurationResponseModel:
|
||||||
|
resp = await self._get_connection().send_message_await_response(
|
||||||
|
VoiceAssistantConfigurationRequest(),
|
||||||
|
VoiceAssistantConfigurationResponse,
|
||||||
|
timeout,
|
||||||
|
)
|
||||||
|
return VoiceAssistantConfigurationResponseModel.from_pb(resp)
|
||||||
|
|
||||||
|
async def set_voice_assistant_configuration(
|
||||||
|
self, active_wake_words: list[int]
|
||||||
|
) -> None:
|
||||||
|
req = VoiceAssistantSetConfiguration(active_wake_words=active_wake_words)
|
||||||
|
self._get_connection().send_message(req)
|
||||||
|
|
||||||
def alarm_control_panel_command(
|
def alarm_control_panel_command(
|
||||||
self,
|
self,
|
||||||
key: int,
|
key: int,
|
||||||
|
@ -121,9 +121,12 @@ from .api_pb2 import ( # type: ignore
|
|||||||
VoiceAssistantAnnounceFinished,
|
VoiceAssistantAnnounceFinished,
|
||||||
VoiceAssistantAnnounceRequest,
|
VoiceAssistantAnnounceRequest,
|
||||||
VoiceAssistantAudio,
|
VoiceAssistantAudio,
|
||||||
|
VoiceAssistantConfigurationRequest,
|
||||||
|
VoiceAssistantConfigurationResponse,
|
||||||
VoiceAssistantEventResponse,
|
VoiceAssistantEventResponse,
|
||||||
VoiceAssistantRequest,
|
VoiceAssistantRequest,
|
||||||
VoiceAssistantResponse,
|
VoiceAssistantResponse,
|
||||||
|
VoiceAssistantSetConfiguration,
|
||||||
VoiceAssistantTimerEventResponse,
|
VoiceAssistantTimerEventResponse,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -396,6 +399,9 @@ MESSAGE_TYPE_TO_PROTO = {
|
|||||||
118: UpdateCommandRequest,
|
118: UpdateCommandRequest,
|
||||||
119: VoiceAssistantAnnounceRequest,
|
119: VoiceAssistantAnnounceRequest,
|
||||||
120: VoiceAssistantAnnounceFinished,
|
120: VoiceAssistantAnnounceFinished,
|
||||||
|
121: VoiceAssistantConfigurationRequest,
|
||||||
|
122: VoiceAssistantConfigurationResponse,
|
||||||
|
123: VoiceAssistantSetConfiguration,
|
||||||
}
|
}
|
||||||
|
|
||||||
MESSAGE_NUMBER_TO_PROTO = tuple(MESSAGE_TYPE_TO_PROTO.values())
|
MESSAGE_NUMBER_TO_PROTO = tuple(MESSAGE_TYPE_TO_PROTO.values())
|
||||||
|
@ -1298,6 +1298,42 @@ class VoiceAssistantAnnounceFinished(APIModelBase):
|
|||||||
success: bool = False
|
success: bool = False
|
||||||
|
|
||||||
|
|
||||||
|
@_frozen_dataclass_decorator
|
||||||
|
class VoiceAssistantWakeWord(APIModelBase):
|
||||||
|
id: int
|
||||||
|
wake_word: str
|
||||||
|
trained_languages: list[str]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def convert_list(cls, value: list[Any]) -> list[VoiceAssistantWakeWord]:
|
||||||
|
ret = []
|
||||||
|
for x in value:
|
||||||
|
if isinstance(x, dict):
|
||||||
|
ret.append(VoiceAssistantWakeWord.from_dict(x))
|
||||||
|
else:
|
||||||
|
ret.append(VoiceAssistantWakeWord.from_pb(x))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
@_frozen_dataclass_decorator
|
||||||
|
class VoiceAssistantConfigurationResponse(APIModelBase):
|
||||||
|
available_wake_words: list[VoiceAssistantWakeWord] = converter_field(
|
||||||
|
default_factory=list, converter=VoiceAssistantWakeWord.convert_list
|
||||||
|
)
|
||||||
|
active_wake_words: list[int] = converter_field(default_factory=list, converter=list)
|
||||||
|
max_active_wake_words: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
@_frozen_dataclass_decorator
|
||||||
|
class VoiceAssistantConfigurationRequest(APIModelBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@_frozen_dataclass_decorator
|
||||||
|
class VoiceAssistantSetConfiguration(APIModelBase):
|
||||||
|
active_wake_words: list[int] = converter_field(default_factory=list, converter=list)
|
||||||
|
|
||||||
|
|
||||||
class LogLevel(APIIntEnum):
|
class LogLevel(APIIntEnum):
|
||||||
LOG_LEVEL_NONE = 0
|
LOG_LEVEL_NONE = 0
|
||||||
LOG_LEVEL_ERROR = 1
|
LOG_LEVEL_ERROR = 1
|
||||||
|
@ -71,11 +71,15 @@ from aioesphomeapi.api_pb2 import (
|
|||||||
VoiceAssistantAnnounceRequest,
|
VoiceAssistantAnnounceRequest,
|
||||||
VoiceAssistantAudio,
|
VoiceAssistantAudio,
|
||||||
VoiceAssistantAudioSettings,
|
VoiceAssistantAudioSettings,
|
||||||
|
VoiceAssistantConfigurationRequest,
|
||||||
|
VoiceAssistantConfigurationResponse,
|
||||||
VoiceAssistantEventData,
|
VoiceAssistantEventData,
|
||||||
VoiceAssistantEventResponse,
|
VoiceAssistantEventResponse,
|
||||||
VoiceAssistantRequest,
|
VoiceAssistantRequest,
|
||||||
VoiceAssistantResponse,
|
VoiceAssistantResponse,
|
||||||
|
VoiceAssistantSetConfiguration,
|
||||||
VoiceAssistantTimerEventResponse,
|
VoiceAssistantTimerEventResponse,
|
||||||
|
VoiceAssistantWakeWord,
|
||||||
)
|
)
|
||||||
from aioesphomeapi.client import APIClient, BluetoothConnectionDroppedError
|
from aioesphomeapi.client import APIClient, BluetoothConnectionDroppedError
|
||||||
from aioesphomeapi.connection import APIConnection
|
from aioesphomeapi.connection import APIConnection
|
||||||
@ -113,6 +117,7 @@ from aioesphomeapi.model import (
|
|||||||
UserServiceArgType,
|
UserServiceArgType,
|
||||||
VoiceAssistantAnnounceFinished as VoiceAssistantAnnounceFinishedModel,
|
VoiceAssistantAnnounceFinished as VoiceAssistantAnnounceFinishedModel,
|
||||||
VoiceAssistantAudioSettings as VoiceAssistantAudioSettingsModel,
|
VoiceAssistantAudioSettings as VoiceAssistantAudioSettingsModel,
|
||||||
|
VoiceAssistantConfigurationResponse as VoiceAssistantConfigurationResponseModel,
|
||||||
VoiceAssistantEventType as VoiceAssistantEventModelType,
|
VoiceAssistantEventType as VoiceAssistantEventModelType,
|
||||||
VoiceAssistantTimerEventType as VoiceAssistantTimerEventModelType,
|
VoiceAssistantTimerEventType as VoiceAssistantTimerEventModelType,
|
||||||
)
|
)
|
||||||
@ -2660,6 +2665,57 @@ async def test_subscribe_voice_assistant_announcement_finished(
|
|||||||
assert len(send.mock_calls) == 0
|
assert len(send.mock_calls) == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_get_voice_assistant_configuration(
|
||||||
|
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 == VoiceAssistantConfigurationRequest()
|
||||||
|
original_send_message(msg)
|
||||||
|
|
||||||
|
with patch.object(connection, "send_message", new=send_message):
|
||||||
|
config_task = asyncio.create_task(
|
||||||
|
client.get_voice_assistant_configuration(timeout=1.0)
|
||||||
|
)
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
response: message.Message = VoiceAssistantConfigurationResponse(
|
||||||
|
available_wake_words=[
|
||||||
|
VoiceAssistantWakeWord(
|
||||||
|
id=1,
|
||||||
|
wake_word="okay nabu",
|
||||||
|
trained_languages=["en"],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
active_wake_words=[1],
|
||||||
|
max_active_wake_words=1,
|
||||||
|
)
|
||||||
|
mock_data_received(protocol, generate_plaintext_packet(response))
|
||||||
|
config = await config_task
|
||||||
|
assert isinstance(config, VoiceAssistantConfigurationResponseModel)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_set_voice_assistant_configuration(
|
||||||
|
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 == VoiceAssistantSetConfiguration(active_wake_words=[1])
|
||||||
|
original_send_message(msg)
|
||||||
|
|
||||||
|
with patch.object(connection, "send_message", new=send_message):
|
||||||
|
await client.set_voice_assistant_configuration([1])
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_api_version_after_connection_closed(
|
async def test_api_version_after_connection_closed(
|
||||||
api_client: tuple[
|
api_client: tuple[
|
||||||
|
@ -118,7 +118,9 @@ from aioesphomeapi.model import (
|
|||||||
UserServiceArgType,
|
UserServiceArgType,
|
||||||
ValveInfo,
|
ValveInfo,
|
||||||
ValveState,
|
ValveState,
|
||||||
|
VoiceAssistantConfigurationResponse,
|
||||||
VoiceAssistantFeature,
|
VoiceAssistantFeature,
|
||||||
|
VoiceAssistantWakeWord,
|
||||||
build_unique_id,
|
build_unique_id,
|
||||||
converter_field,
|
converter_field,
|
||||||
)
|
)
|
||||||
@ -681,3 +683,30 @@ def test_media_player_supported_format_convert_list() -> None:
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_voice_assistant_wake_word_convert_list() -> None:
|
||||||
|
"""Test list conversion for VoiceAssistantWakeWord."""
|
||||||
|
assert VoiceAssistantConfigurationResponse.from_dict(
|
||||||
|
{
|
||||||
|
"available_wake_words": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"wake_word": "okay nabu",
|
||||||
|
"trained_languages": ["en"],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"active_wake_words": [1],
|
||||||
|
"max_active_wake_words": 1,
|
||||||
|
}
|
||||||
|
) == VoiceAssistantConfigurationResponse(
|
||||||
|
available_wake_words=[
|
||||||
|
VoiceAssistantWakeWord(
|
||||||
|
id=1,
|
||||||
|
wake_word="okay nabu",
|
||||||
|
trained_languages=["en"],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
active_wake_words=[1],
|
||||||
|
max_active_wake_words=1,
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user