Skip to content

Commit

Permalink
Updated fork of PR for Text input components (#532)
Browse files Browse the repository at this point in the history
Co-authored-by: Maurits <[email protected]>
Co-authored-by: Daniel Dunn <[email protected]>
Co-authored-by: J. Nick Koston <[email protected]>
Co-authored-by: Jesse Hills <[email protected]>
  • Loading branch information
5 people authored Oct 25, 2023
1 parent ae03a83 commit 5a8c0d8
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 55 deletions.
46 changes: 46 additions & 0 deletions aioesphomeapi/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ service APIConnection {
rpc climate_command (ClimateCommandRequest) returns (void) {}
rpc number_command (NumberCommandRequest) returns (void) {}
rpc select_command (SelectCommandRequest) returns (void) {}
rpc text_command (TextCommandRequest) returns (void) {}
rpc siren_command (SirenCommandRequest) returns (void) {}
rpc button_command (ButtonCommandRequest) returns (void) {}
rpc lock_command (LockCommandRequest) returns (void) {}
Expand Down Expand Up @@ -1555,3 +1556,48 @@ message AlarmControlPanelCommandRequest {
AlarmControlPanelStateCommand command = 2;
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;
}
144 changes: 93 additions & 51 deletions aioesphomeapi/api_pb2.py

Large diffs are not rendered by default.

19 changes: 17 additions & 2 deletions aioesphomeapi/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

import asyncio
import logging
from collections.abc import Awaitable, Coroutine
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

Expand Down Expand Up @@ -69,6 +68,7 @@
ListEntitiesServicesResponse,
ListEntitiesSirenResponse,
ListEntitiesSwitchResponse,
ListEntitiesTextResponse,
ListEntitiesTextSensorResponse,
LockCommandRequest,
LockStateResponse,
Expand All @@ -92,7 +92,9 @@
SubscribeVoiceAssistantRequest,
SwitchCommandRequest,
SwitchStateResponse,
TextCommandRequest,
TextSensorStateResponse,
TextStateResponse,
UnsubscribeBluetoothLEAdvertisementsRequest,
VoiceAssistantAudioSettings,
VoiceAssistantEventData,
Expand Down Expand Up @@ -165,8 +167,10 @@
SirenState,
SwitchInfo,
SwitchState,
TextInfo,
TextSensorInfo,
TextSensorState,
TextState,
UserService,
UserServiceArgType,
VoiceAssistantCommand,
Expand Down Expand Up @@ -198,6 +202,7 @@
SensorStateResponse: SensorState,
SirenStateResponse: SirenState,
SwitchStateResponse: SwitchState,
TextStateResponse: TextState,
TextSensorStateResponse: TextSensorState,
ClimateStateResponse: ClimateState,
LockStateResponse: LockEntityState,
Expand All @@ -217,6 +222,7 @@
ListEntitiesSensorResponse: SensorInfo,
ListEntitiesSirenResponse: SirenInfo,
ListEntitiesSwitchResponse: SwitchInfo,
ListEntitiesTextResponse: TextInfo,
ListEntitiesTextSensorResponse: TextSensorInfo,
ListEntitiesServicesResponse: None,
ListEntitiesCameraResponse: CameraInfo,
Expand Down Expand Up @@ -1338,6 +1344,15 @@ async def media_player_command(
assert self._connection is not None
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(
self, service: UserService, data: ExecuteServiceDataType
) -> None:
Expand Down
6 changes: 6 additions & 0 deletions aioesphomeapi/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
ListEntitiesServicesResponse,
ListEntitiesSirenResponse,
ListEntitiesSwitchResponse,
ListEntitiesTextResponse,
ListEntitiesTextSensorResponse,
LockCommandRequest,
LockStateResponse,
Expand All @@ -96,7 +97,9 @@
SubscribeVoiceAssistantRequest,
SwitchCommandRequest,
SwitchStateResponse,
TextCommandRequest,
TextSensorStateResponse,
TextStateResponse,
UnsubscribeBluetoothLEAdvertisementsRequest,
VoiceAssistantEventResponse,
VoiceAssistantRequest,
Expand Down Expand Up @@ -340,4 +343,7 @@ def __init__(self, error: BluetoothGATTError) -> None:
94: ListEntitiesAlarmControlPanelResponse,
95: AlarmControlPanelStateResponse,
96: AlarmControlPanelCommandRequest,
97: ListEntitiesTextResponse,
98: TextStateResponse,
99: TextCommandRequest,
}
27 changes: 25 additions & 2 deletions aioesphomeapi/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@

import enum
import sys
from collections.abc import Iterable
from dataclasses import asdict, dataclass, field, fields
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 .util import fix_float_single_double_conversion
Expand Down Expand Up @@ -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 ====================

COMPONENT_TYPE_TO_INFO: dict[str, type[EntityInfo]] = {
Expand All @@ -776,6 +797,7 @@ class AlarmControlPanelEntityState(EntityState):
"lock": LockInfo,
"media_player": MediaPlayerInfo,
"alarm_control_panel": AlarmControlPanelInfo,
"text": TextInfo,
}


Expand Down Expand Up @@ -1126,6 +1148,7 @@ class VoiceAssistantEventType(APIIntEnum):
LockInfo: "lock",
MediaPlayerInfo: "media_player",
AlarmControlPanelInfo: "alarm_control_panel",
TextInfo: "text_info",
}


Expand Down
16 changes: 16 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
NumberCommandRequest,
SelectCommandRequest,
SwitchCommandRequest,
TextCommandRequest,
)
from aioesphomeapi.client import APIClient
from aioesphomeapi.model import (
Expand Down Expand Up @@ -559,3 +560,18 @@ async def test_alarm_panel_command(auth_client, cmd, req):

await auth_client.alarm_control_panel_command(**cmd)
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))
5 changes: 5 additions & 0 deletions tests/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
ServiceArgType,
SwitchStateResponse,
TextSensorStateResponse,
TextStateResponse,
)
from aioesphomeapi.model import (
_TYPE_TO_NAME,
Expand Down Expand Up @@ -74,8 +75,10 @@
SirenInfo,
SwitchInfo,
SwitchState,
TextInfo,
TextSensorInfo,
TextSensorState,
TextState,
UserService,
UserServiceArg,
UserServiceArgType,
Expand Down Expand Up @@ -236,6 +239,7 @@ def test_api_version_ord():
(MediaPlayerEntityState, MediaPlayerStateResponse),
(AlarmControlPanelInfo, ListEntitiesAlarmControlPanelResponse),
(AlarmControlPanelEntityState, AlarmControlPanelStateResponse),
(TextState, TextStateResponse),
],
)
def test_basic_pb_conversions(model, pb):
Expand Down Expand Up @@ -346,6 +350,7 @@ def test_user_service_conversion():
LockInfo,
MediaPlayerInfo,
AlarmControlPanelInfo,
TextInfo,
],
)
def test_build_unique_id(model):
Expand Down

0 comments on commit 5a8c0d8

Please sign in to comment.