mirror of
https://github.com/esphome/aioesphomeapi.git
synced 2024-12-18 16:09:00 +01:00
Updated fork of PR for Text input components (#532)
Co-authored-by: Maurits <maurits@vloop.nl> Co-authored-by: Daniel Dunn <dannydunn@eternityforest.com> Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
ae03a831b9
commit
5a8c0d8e23
@ -40,6 +40,7 @@ service APIConnection {
|
|||||||
rpc climate_command (ClimateCommandRequest) returns (void) {}
|
rpc climate_command (ClimateCommandRequest) returns (void) {}
|
||||||
rpc number_command (NumberCommandRequest) returns (void) {}
|
rpc number_command (NumberCommandRequest) returns (void) {}
|
||||||
rpc select_command (SelectCommandRequest) returns (void) {}
|
rpc select_command (SelectCommandRequest) returns (void) {}
|
||||||
|
rpc text_command (TextCommandRequest) returns (void) {}
|
||||||
rpc siren_command (SirenCommandRequest) returns (void) {}
|
rpc siren_command (SirenCommandRequest) returns (void) {}
|
||||||
rpc button_command (ButtonCommandRequest) returns (void) {}
|
rpc button_command (ButtonCommandRequest) returns (void) {}
|
||||||
rpc lock_command (LockCommandRequest) returns (void) {}
|
rpc lock_command (LockCommandRequest) returns (void) {}
|
||||||
@ -1555,3 +1556,48 @@ message AlarmControlPanelCommandRequest {
|
|||||||
AlarmControlPanelStateCommand command = 2;
|
AlarmControlPanelStateCommand command = 2;
|
||||||
string code = 3;
|
string code = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================== TEXT =====================
|
||||||
|
enum TextMode {
|
||||||
|
TEXT_MODE_TEXT = 0;
|
||||||
|
TEXT_MODE_PASSWORD = 1;
|
||||||
|
}
|
||||||
|
message ListEntitiesTextResponse {
|
||||||
|
option (id) = 97;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_TEXT";
|
||||||
|
|
||||||
|
string object_id = 1;
|
||||||
|
fixed32 key = 2;
|
||||||
|
string name = 3;
|
||||||
|
string unique_id = 4;
|
||||||
|
string icon = 5;
|
||||||
|
bool disabled_by_default = 6;
|
||||||
|
EntityCategory entity_category = 7;
|
||||||
|
|
||||||
|
uint32 min_length = 8;
|
||||||
|
uint32 max_length = 9;
|
||||||
|
string pattern = 10;
|
||||||
|
TextMode mode = 11;
|
||||||
|
}
|
||||||
|
message TextStateResponse {
|
||||||
|
option (id) = 98;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_TEXT";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
string state = 2;
|
||||||
|
// If the Text does not have a valid state yet.
|
||||||
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
|
bool missing_state = 3;
|
||||||
|
}
|
||||||
|
message TextCommandRequest {
|
||||||
|
option (id) = 99;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_TEXT";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
string state = 2;
|
||||||
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -2,9 +2,8 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from collections.abc import Awaitable, Coroutine
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Union, cast
|
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Coroutine, Union, cast
|
||||||
|
|
||||||
from google.protobuf import message
|
from google.protobuf import message
|
||||||
|
|
||||||
@ -69,6 +68,7 @@ from .api_pb2 import ( # type: ignore
|
|||||||
ListEntitiesServicesResponse,
|
ListEntitiesServicesResponse,
|
||||||
ListEntitiesSirenResponse,
|
ListEntitiesSirenResponse,
|
||||||
ListEntitiesSwitchResponse,
|
ListEntitiesSwitchResponse,
|
||||||
|
ListEntitiesTextResponse,
|
||||||
ListEntitiesTextSensorResponse,
|
ListEntitiesTextSensorResponse,
|
||||||
LockCommandRequest,
|
LockCommandRequest,
|
||||||
LockStateResponse,
|
LockStateResponse,
|
||||||
@ -92,7 +92,9 @@ from .api_pb2 import ( # type: ignore
|
|||||||
SubscribeVoiceAssistantRequest,
|
SubscribeVoiceAssistantRequest,
|
||||||
SwitchCommandRequest,
|
SwitchCommandRequest,
|
||||||
SwitchStateResponse,
|
SwitchStateResponse,
|
||||||
|
TextCommandRequest,
|
||||||
TextSensorStateResponse,
|
TextSensorStateResponse,
|
||||||
|
TextStateResponse,
|
||||||
UnsubscribeBluetoothLEAdvertisementsRequest,
|
UnsubscribeBluetoothLEAdvertisementsRequest,
|
||||||
VoiceAssistantAudioSettings,
|
VoiceAssistantAudioSettings,
|
||||||
VoiceAssistantEventData,
|
VoiceAssistantEventData,
|
||||||
@ -165,8 +167,10 @@ from .model import (
|
|||||||
SirenState,
|
SirenState,
|
||||||
SwitchInfo,
|
SwitchInfo,
|
||||||
SwitchState,
|
SwitchState,
|
||||||
|
TextInfo,
|
||||||
TextSensorInfo,
|
TextSensorInfo,
|
||||||
TextSensorState,
|
TextSensorState,
|
||||||
|
TextState,
|
||||||
UserService,
|
UserService,
|
||||||
UserServiceArgType,
|
UserServiceArgType,
|
||||||
VoiceAssistantCommand,
|
VoiceAssistantCommand,
|
||||||
@ -198,6 +202,7 @@ SUBSCRIBE_STATES_RESPONSE_TYPES: dict[Any, type[EntityState]] = {
|
|||||||
SensorStateResponse: SensorState,
|
SensorStateResponse: SensorState,
|
||||||
SirenStateResponse: SirenState,
|
SirenStateResponse: SirenState,
|
||||||
SwitchStateResponse: SwitchState,
|
SwitchStateResponse: SwitchState,
|
||||||
|
TextStateResponse: TextState,
|
||||||
TextSensorStateResponse: TextSensorState,
|
TextSensorStateResponse: TextSensorState,
|
||||||
ClimateStateResponse: ClimateState,
|
ClimateStateResponse: ClimateState,
|
||||||
LockStateResponse: LockEntityState,
|
LockStateResponse: LockEntityState,
|
||||||
@ -217,6 +222,7 @@ LIST_ENTITIES_SERVICES_RESPONSE_TYPES: dict[Any, type[EntityInfo] | None] = {
|
|||||||
ListEntitiesSensorResponse: SensorInfo,
|
ListEntitiesSensorResponse: SensorInfo,
|
||||||
ListEntitiesSirenResponse: SirenInfo,
|
ListEntitiesSirenResponse: SirenInfo,
|
||||||
ListEntitiesSwitchResponse: SwitchInfo,
|
ListEntitiesSwitchResponse: SwitchInfo,
|
||||||
|
ListEntitiesTextResponse: TextInfo,
|
||||||
ListEntitiesTextSensorResponse: TextSensorInfo,
|
ListEntitiesTextSensorResponse: TextSensorInfo,
|
||||||
ListEntitiesServicesResponse: None,
|
ListEntitiesServicesResponse: None,
|
||||||
ListEntitiesCameraResponse: CameraInfo,
|
ListEntitiesCameraResponse: CameraInfo,
|
||||||
@ -1338,6 +1344,15 @@ class APIClient:
|
|||||||
assert self._connection is not None
|
assert self._connection is not None
|
||||||
self._connection.send_message(req)
|
self._connection.send_message(req)
|
||||||
|
|
||||||
|
async def text_command(self, key: int, state: str) -> None:
|
||||||
|
self._check_authenticated()
|
||||||
|
|
||||||
|
req = TextCommandRequest()
|
||||||
|
req.key = key
|
||||||
|
req.state = state
|
||||||
|
assert self._connection is not None
|
||||||
|
self._connection.send_message(req)
|
||||||
|
|
||||||
async def execute_service(
|
async def execute_service(
|
||||||
self, service: UserService, data: ExecuteServiceDataType
|
self, service: UserService, data: ExecuteServiceDataType
|
||||||
) -> None:
|
) -> None:
|
||||||
|
@ -71,6 +71,7 @@ from .api_pb2 import ( # type: ignore
|
|||||||
ListEntitiesServicesResponse,
|
ListEntitiesServicesResponse,
|
||||||
ListEntitiesSirenResponse,
|
ListEntitiesSirenResponse,
|
||||||
ListEntitiesSwitchResponse,
|
ListEntitiesSwitchResponse,
|
||||||
|
ListEntitiesTextResponse,
|
||||||
ListEntitiesTextSensorResponse,
|
ListEntitiesTextSensorResponse,
|
||||||
LockCommandRequest,
|
LockCommandRequest,
|
||||||
LockStateResponse,
|
LockStateResponse,
|
||||||
@ -96,7 +97,9 @@ from .api_pb2 import ( # type: ignore
|
|||||||
SubscribeVoiceAssistantRequest,
|
SubscribeVoiceAssistantRequest,
|
||||||
SwitchCommandRequest,
|
SwitchCommandRequest,
|
||||||
SwitchStateResponse,
|
SwitchStateResponse,
|
||||||
|
TextCommandRequest,
|
||||||
TextSensorStateResponse,
|
TextSensorStateResponse,
|
||||||
|
TextStateResponse,
|
||||||
UnsubscribeBluetoothLEAdvertisementsRequest,
|
UnsubscribeBluetoothLEAdvertisementsRequest,
|
||||||
VoiceAssistantEventResponse,
|
VoiceAssistantEventResponse,
|
||||||
VoiceAssistantRequest,
|
VoiceAssistantRequest,
|
||||||
@ -340,4 +343,7 @@ MESSAGE_TYPE_TO_PROTO = {
|
|||||||
94: ListEntitiesAlarmControlPanelResponse,
|
94: ListEntitiesAlarmControlPanelResponse,
|
||||||
95: AlarmControlPanelStateResponse,
|
95: AlarmControlPanelStateResponse,
|
||||||
96: AlarmControlPanelCommandRequest,
|
96: AlarmControlPanelCommandRequest,
|
||||||
|
97: ListEntitiesTextResponse,
|
||||||
|
98: TextStateResponse,
|
||||||
|
99: TextCommandRequest,
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import enum
|
import enum
|
||||||
import sys
|
import sys
|
||||||
from collections.abc import Iterable
|
|
||||||
from dataclasses import asdict, dataclass, field, fields
|
from dataclasses import asdict, dataclass, field, fields
|
||||||
from functools import cache, lru_cache, partial
|
from functools import cache, lru_cache, partial
|
||||||
from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast
|
from typing import TYPE_CHECKING, Any, Callable, Iterable, Optional, TypeVar, cast
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from .util import fix_float_single_double_conversion
|
from .util import fix_float_single_double_conversion
|
||||||
@ -757,6 +756,28 @@ class AlarmControlPanelEntityState(EntityState):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== TEXT ====================
|
||||||
|
class TextMode(APIIntEnum):
|
||||||
|
TEXT = 0
|
||||||
|
PASSWORD = 1
|
||||||
|
|
||||||
|
|
||||||
|
@_frozen_dataclass_decorator
|
||||||
|
class TextInfo(EntityInfo):
|
||||||
|
min_length: int = 0
|
||||||
|
max_length: int = 255
|
||||||
|
pattern: str = ""
|
||||||
|
mode: Optional[TextMode] = converter_field(
|
||||||
|
default=TextMode.TEXT, converter=TextMode.convert
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@_frozen_dataclass_decorator
|
||||||
|
class TextState(EntityState):
|
||||||
|
state: str = ""
|
||||||
|
missing_state: bool = False
|
||||||
|
|
||||||
|
|
||||||
# ==================== INFO MAP ====================
|
# ==================== INFO MAP ====================
|
||||||
|
|
||||||
COMPONENT_TYPE_TO_INFO: dict[str, type[EntityInfo]] = {
|
COMPONENT_TYPE_TO_INFO: dict[str, type[EntityInfo]] = {
|
||||||
@ -776,6 +797,7 @@ COMPONENT_TYPE_TO_INFO: dict[str, type[EntityInfo]] = {
|
|||||||
"lock": LockInfo,
|
"lock": LockInfo,
|
||||||
"media_player": MediaPlayerInfo,
|
"media_player": MediaPlayerInfo,
|
||||||
"alarm_control_panel": AlarmControlPanelInfo,
|
"alarm_control_panel": AlarmControlPanelInfo,
|
||||||
|
"text": TextInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1126,6 +1148,7 @@ _TYPE_TO_NAME = {
|
|||||||
LockInfo: "lock",
|
LockInfo: "lock",
|
||||||
MediaPlayerInfo: "media_player",
|
MediaPlayerInfo: "media_player",
|
||||||
AlarmControlPanelInfo: "alarm_control_panel",
|
AlarmControlPanelInfo: "alarm_control_panel",
|
||||||
|
TextInfo: "text_info",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ from aioesphomeapi.api_pb2 import (
|
|||||||
NumberCommandRequest,
|
NumberCommandRequest,
|
||||||
SelectCommandRequest,
|
SelectCommandRequest,
|
||||||
SwitchCommandRequest,
|
SwitchCommandRequest,
|
||||||
|
TextCommandRequest,
|
||||||
)
|
)
|
||||||
from aioesphomeapi.client import APIClient
|
from aioesphomeapi.client import APIClient
|
||||||
from aioesphomeapi.model import (
|
from aioesphomeapi.model import (
|
||||||
@ -559,3 +560,18 @@ async def test_alarm_panel_command(auth_client, cmd, req):
|
|||||||
|
|
||||||
await auth_client.alarm_control_panel_command(**cmd)
|
await auth_client.alarm_control_panel_command(**cmd)
|
||||||
send.assert_called_once_with(AlarmControlPanelCommandRequest(**req))
|
send.assert_called_once_with(AlarmControlPanelCommandRequest(**req))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"cmd, req",
|
||||||
|
[
|
||||||
|
(dict(key=1, state="hello world"), dict(key=1, state="hello world")),
|
||||||
|
(dict(key=1, state="goodbye"), dict(key=1, state="goodbye")),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_text_command(auth_client, cmd, req):
|
||||||
|
send = patch_send(auth_client)
|
||||||
|
|
||||||
|
await auth_client.text_command(**cmd)
|
||||||
|
send.assert_called_once_with(TextCommandRequest(**req))
|
||||||
|
@ -37,6 +37,7 @@ from aioesphomeapi.api_pb2 import (
|
|||||||
ServiceArgType,
|
ServiceArgType,
|
||||||
SwitchStateResponse,
|
SwitchStateResponse,
|
||||||
TextSensorStateResponse,
|
TextSensorStateResponse,
|
||||||
|
TextStateResponse,
|
||||||
)
|
)
|
||||||
from aioesphomeapi.model import (
|
from aioesphomeapi.model import (
|
||||||
_TYPE_TO_NAME,
|
_TYPE_TO_NAME,
|
||||||
@ -74,8 +75,10 @@ from aioesphomeapi.model import (
|
|||||||
SirenInfo,
|
SirenInfo,
|
||||||
SwitchInfo,
|
SwitchInfo,
|
||||||
SwitchState,
|
SwitchState,
|
||||||
|
TextInfo,
|
||||||
TextSensorInfo,
|
TextSensorInfo,
|
||||||
TextSensorState,
|
TextSensorState,
|
||||||
|
TextState,
|
||||||
UserService,
|
UserService,
|
||||||
UserServiceArg,
|
UserServiceArg,
|
||||||
UserServiceArgType,
|
UserServiceArgType,
|
||||||
@ -236,6 +239,7 @@ def test_api_version_ord():
|
|||||||
(MediaPlayerEntityState, MediaPlayerStateResponse),
|
(MediaPlayerEntityState, MediaPlayerStateResponse),
|
||||||
(AlarmControlPanelInfo, ListEntitiesAlarmControlPanelResponse),
|
(AlarmControlPanelInfo, ListEntitiesAlarmControlPanelResponse),
|
||||||
(AlarmControlPanelEntityState, AlarmControlPanelStateResponse),
|
(AlarmControlPanelEntityState, AlarmControlPanelStateResponse),
|
||||||
|
(TextState, TextStateResponse),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_basic_pb_conversions(model, pb):
|
def test_basic_pb_conversions(model, pb):
|
||||||
@ -346,6 +350,7 @@ def test_user_service_conversion():
|
|||||||
LockInfo,
|
LockInfo,
|
||||||
MediaPlayerInfo,
|
MediaPlayerInfo,
|
||||||
AlarmControlPanelInfo,
|
AlarmControlPanelInfo,
|
||||||
|
TextInfo,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_build_unique_id(model):
|
def test_build_unique_id(model):
|
||||||
|
Loading…
Reference in New Issue
Block a user