Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions aioesphomeapi/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ service APIConnection {
option (needs_authentication) = false;
}
rpc execute_service (ExecuteServiceRequest) returns (void) {}
rpc noise_encryption_set_key (NoiseEncryptionSetKeyRequest) returns (NoiseEncryptionSetKeyResponse) {}

rpc cover_command (CoverCommandRequest) returns (void) {}
rpc fan_command (FanCommandRequest) returns (void) {}
Expand Down Expand Up @@ -224,9 +225,12 @@ message DeviceInfoResponse {
uint32 voice_assistant_feature_flags = 17;

string suggested_area = 16;

// The Bluetooth mac address of the device. For example "AC:BC:32:89:0E:AA"
string bluetooth_mac_address = 18;
string bluetooth_mac_address = 18;

// Supports receiving and saving api encryption key
bool api_encryption_supported = 19;
}

message ListEntitiesRequest {
Expand Down Expand Up @@ -638,6 +642,23 @@ message SubscribeLogsResponse {
bool send_failed = 4;
}

// ==================== NOISE ENCRYPTION ====================
message NoiseEncryptionSetKeyRequest {
option (id) = 124;
option (source) = SOURCE_CLIENT;
option (ifdef) = "USE_API_NOISE";

bytes key = 1;
}

message NoiseEncryptionSetKeyResponse {
option (id) = 125;
option (source) = SOURCE_SERVER;
option (ifdef) = "USE_API_NOISE";

bool success = 1;
}

// ==================== HOMEASSISTANT.SERVICE ====================
message SubscribeHomeassistantServicesRequest {
option (id) = 34;
Expand Down
636 changes: 322 additions & 314 deletions aioesphomeapi/api_pb2.py

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions aioesphomeapi/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
ListEntitiesServicesResponse,
LockCommandRequest,
MediaPlayerCommandRequest,
NoiseEncryptionSetKeyRequest,
NoiseEncryptionSetKeyResponse,
NumberCommandRequest,
SelectCommandRequest,
SirenCommandRequest,
Expand Down Expand Up @@ -130,6 +132,7 @@
LockCommand,
LogLevel,
MediaPlayerCommand,
NoiseEncryptionSetKeyResponse as NoiseEncryptionSetKeyResponseModel,
UpdateCommand,
UserService,
UserServiceArgType,
Expand Down Expand Up @@ -1373,3 +1376,14 @@ def alarm_control_panel_command(
if code is not None:
req.code = code
self._get_connection().send_message(req)

async def noise_encryption_set_key(
self,
key: bytes,
) -> bool:
"""Set the noise encryption key."""
req = NoiseEncryptionSetKeyRequest(key=key)
resp = await self._get_connection().send_message_await_response(
req, NoiseEncryptionSetKeyResponse
)
return NoiseEncryptionSetKeyResponseModel.from_pb(resp).success
4 changes: 4 additions & 0 deletions aioesphomeapi/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@
LockStateResponse,
MediaPlayerCommandRequest,
MediaPlayerStateResponse,
NoiseEncryptionSetKeyRequest,
NoiseEncryptionSetKeyResponse,
NumberCommandRequest,
NumberStateResponse,
PingRequest,
Expand Down Expand Up @@ -438,6 +440,8 @@ def __init__(self, error: BluetoothGATTError) -> None:
121: VoiceAssistantConfigurationRequest,
122: VoiceAssistantConfigurationResponse,
123: VoiceAssistantSetConfiguration,
124: NoiseEncryptionSetKeyRequest,
125: NoiseEncryptionSetKeyResponse,
}

MESSAGE_NUMBER_TO_PROTO = tuple(MESSAGE_TYPE_TO_PROTO.values())
10 changes: 10 additions & 0 deletions aioesphomeapi/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1335,6 +1335,16 @@ class VoiceAssistantSetConfiguration(APIModelBase):
active_wake_words: list[int] = converter_field(default_factory=list, converter=list)


@_frozen_dataclass_decorator
class NoiseEncryptionSetKeyRequest(APIModelBase):
key: bytes = field(default_factory=bytes) # pylint: disable=invalid-field-call


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


class LogLevel(APIIntEnum):
LOG_LEVEL_NONE = 0
LOG_LEVEL_ERROR = 1
Expand Down
24 changes: 24 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
ListEntitiesServicesResponse,
LockCommandRequest,
MediaPlayerCommandRequest,
NoiseEncryptionSetKeyRequest,
NoiseEncryptionSetKeyResponse,
NumberCommandRequest,
SelectCommandRequest,
SirenCommandRequest,
Expand Down Expand Up @@ -2733,3 +2735,25 @@ async def test_calls_after_connection_closed(

with pytest.raises(APIConnectionError):
await client.update_command(1, True)


async def test_noise_encryption_set_key(
api_client: tuple[
APIClient, APIConnection, asyncio.Transport, APIPlaintextFrameHelper
],
) -> None:
"""Test set_noise_encryption_key."""
client, connection, transport, protocol = api_client
original_send_message = connection.send_message

def send_message(msg):
assert msg == NoiseEncryptionSetKeyRequest(key=b"1234")
original_send_message(msg)

with patch.object(connection, "send_message", new=send_message):
set_task = asyncio.create_task(client.noise_encryption_set_key(b"1234"))
await asyncio.sleep(0)
response: message.Message = NoiseEncryptionSetKeyResponse(success=True)
mock_data_received(protocol, generate_plaintext_packet(response))
success = await set_task
assert success is True
3 changes: 3 additions & 0 deletions tests/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
LockStateResponse,
MediaPlayerStateResponse,
MediaPlayerSupportedFormat,
NoiseEncryptionSetKeyResponse,
NumberStateResponse,
SelectStateResponse,
SensorStateResponse,
Expand Down Expand Up @@ -96,6 +97,7 @@
LockInfo,
MediaPlayerEntityState,
MediaPlayerInfo,
NoiseEncryptionSetKeyResponse as NoiseEncryptionSetKeyResponseModel,
NumberInfo,
NumberState,
SelectInfo,
Expand Down Expand Up @@ -291,6 +293,7 @@ def test_api_version_ord():
(Event, EventResponse),
(UpdateInfo, ListEntitiesUpdateResponse),
(UpdateState, UpdateStateResponse),
(NoiseEncryptionSetKeyResponseModel, NoiseEncryptionSetKeyResponse),
],
)
def test_basic_pb_conversions(model, pb):
Expand Down