aioesphomeapi 45.2.2__tar.gz → 45.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {aioesphomeapi-45.2.2/aioesphomeapi.egg-info → aioesphomeapi-45.3.1}/PKG-INFO +3 -3
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/client.py +18 -29
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/client_base.py +1 -8
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/connection.py +22 -2
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/model.py +164 -90
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/timezone.py +6 -10
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1/aioesphomeapi.egg-info}/PKG-INFO +3 -3
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi.egg-info/SOURCES.txt +1 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi.egg-info/requires.txt +1 -1
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/pyproject.toml +1 -1
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/requirements/base.txt +1 -1
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/setup.py +1 -1
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_client.py +190 -3
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_connection.py +56 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_model.py +111 -0
- aioesphomeapi-45.3.1/tests/test_public_api.py +86 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_timezone.py +77 -20
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/LICENSE +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/MANIFEST.in +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/README.rst +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/__init__.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/_frame_helper/__init__.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/_frame_helper/base.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/_frame_helper/noise.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/_frame_helper/noise_encryption.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/_frame_helper/packets.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/_frame_helper/plain_text.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/_sanitize.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/api_options_pb2.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/api_pb2.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/ble_defs.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/core.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/discover.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/host_resolver.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/log_parser.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/log_reader.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/log_runner.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/model_conversions.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/object_id.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/posix_tz.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/py.typed +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/reconnect_logic.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/singleton.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/state_log_formatter.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/util.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi/zeroconf.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi.egg-info/dependency_links.txt +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi.egg-info/entry_points.txt +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi.egg-info/not-zip-safe +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/aioesphomeapi.egg-info/top_level.txt +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/bench/__init__.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/bench/raw_ble_plain_text.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/bench/raw_ble_plain_text_with_callback.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/setup.cfg +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test__frame_helper.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test__sanitize.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_core.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_discover.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_host_resolver.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_log_parser.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_log_reader.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_log_runner.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_model_conversions.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_object_id.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_posix_tz.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_provide_time.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_reconnect_logic.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_singleton.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_state_log_formatter.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_util.py +0 -0
- {aioesphomeapi-45.2.2 → aioesphomeapi-45.3.1}/tests/test_zeroconf.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aioesphomeapi
|
|
3
|
-
Version: 45.
|
|
3
|
+
Version: 45.3.1
|
|
4
4
|
Summary: Python API for interacting with ESPHome devices.
|
|
5
5
|
Home-page: https://esphome.io/
|
|
6
|
-
Download-URL: https://github.com/esphome/aioesphomeapi/archive/45.
|
|
6
|
+
Download-URL: https://github.com/esphome/aioesphomeapi/archive/45.3.1.zip
|
|
7
7
|
Author: Otto Winter
|
|
8
8
|
Author-email: esphome@nabucasa.com
|
|
9
9
|
License: MIT
|
|
@@ -12,7 +12,7 @@ License-File: LICENSE
|
|
|
12
12
|
Requires-Dist: aiohappyeyeballs>=2.6.2
|
|
13
13
|
Requires-Dist: async-interrupt>=1.2.2
|
|
14
14
|
Requires-Dist: protobuf<8,>=6
|
|
15
|
-
Requires-Dist: tzdata>=
|
|
15
|
+
Requires-Dist: tzdata>=2026.2
|
|
16
16
|
Requires-Dist: tzlocal<6,>=5.3.1
|
|
17
17
|
Requires-Dist: zeroconf<2.0,>=0.149.16
|
|
18
18
|
Requires-Dist: chacha20poly1305-reuseable>=0.13.2
|
|
@@ -859,7 +859,7 @@ class APIClient(APIClientBase):
|
|
|
859
859
|
"""Set the Bluetooth scanner mode."""
|
|
860
860
|
self._get_connection().send_message(BluetoothScannerSetModeRequest(mode=mode))
|
|
861
861
|
|
|
862
|
-
async def bluetooth_device_connect( # noqa: C901 # pylint: disable=too-many-locals
|
|
862
|
+
async def bluetooth_device_connect( # noqa: C901 # pylint: disable=too-many-locals
|
|
863
863
|
self,
|
|
864
864
|
address: int,
|
|
865
865
|
on_bluetooth_connection_state: Callable[[bool, int, int], None],
|
|
@@ -923,22 +923,16 @@ class APIClient(APIClientBase):
|
|
|
923
923
|
timeout_handle = loop.call_at(
|
|
924
924
|
loop.time() + timeout, handle_timeout, connect_future
|
|
925
925
|
)
|
|
926
|
-
timeout_expired = False
|
|
927
|
-
connect_ok = False
|
|
928
|
-
unhandled_exception = False
|
|
929
926
|
try:
|
|
930
927
|
await connect_future
|
|
931
|
-
connect_ok = True
|
|
932
928
|
except TimeoutError as err:
|
|
933
|
-
#
|
|
934
|
-
# to
|
|
935
|
-
#
|
|
936
|
-
# since we are going to raise a TimeoutAPIError.
|
|
929
|
+
# Unsub before disconnecting so the disconnect message is not
|
|
930
|
+
# propagated back to the caller — we are going to raise a
|
|
931
|
+
# TimeoutAPIError instead.
|
|
937
932
|
unsub()
|
|
938
|
-
|
|
939
|
-
#
|
|
940
|
-
#
|
|
941
|
-
# to avoid race were we run out even though we have a slot.
|
|
933
|
+
# Disconnect before raising the exception so the slot is
|
|
934
|
+
# recovered before the timeout is raised, avoiding a race
|
|
935
|
+
# where we run out of slots even though we have one.
|
|
942
936
|
addr = to_human_readable_address(address)
|
|
943
937
|
if self._debug_enabled:
|
|
944
938
|
_LOGGER.debug("%s: Connecting timed out, waiting for disconnect", addr)
|
|
@@ -954,13 +948,14 @@ class APIClient(APIClientBase):
|
|
|
954
948
|
)
|
|
955
949
|
raise TimeoutAPIError(msg) from err
|
|
956
950
|
except asyncio.CancelledError:
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
#
|
|
960
|
-
#
|
|
961
|
-
#
|
|
962
|
-
#
|
|
963
|
-
#
|
|
951
|
+
unsub()
|
|
952
|
+
self._bluetooth_disconnect_no_wait(address)
|
|
953
|
+
# Distinguish an outside cancellation of our task from a
|
|
954
|
+
# cancellation of the connect_future itself. If the current
|
|
955
|
+
# task is not actually being cancelled, convert the
|
|
956
|
+
# CancelledError into an APIConnectionError so callers (and
|
|
957
|
+
# their retry logic) can treat it as a normal connection
|
|
958
|
+
# failure instead of aborting.
|
|
964
959
|
current_task = asyncio.current_task()
|
|
965
960
|
if current_task is None or not current_task.cancelling():
|
|
966
961
|
addr = to_human_readable_address(address)
|
|
@@ -968,17 +963,11 @@ class APIClient(APIClientBase):
|
|
|
968
963
|
raise APIConnectionError(msg) from None
|
|
969
964
|
raise
|
|
970
965
|
except BaseException:
|
|
971
|
-
|
|
966
|
+
unsub()
|
|
967
|
+
self._bluetooth_disconnect_no_wait(address)
|
|
972
968
|
raise
|
|
973
969
|
finally:
|
|
974
|
-
|
|
975
|
-
unsub()
|
|
976
|
-
if not timeout_expired:
|
|
977
|
-
timeout_handle.cancel()
|
|
978
|
-
if unhandled_exception:
|
|
979
|
-
# Make sure to disconnect if we had an unhandled exception
|
|
980
|
-
# as otherwise the connection will be left open.
|
|
981
|
-
self._bluetooth_disconnect_no_wait(address)
|
|
970
|
+
timeout_handle.cancel()
|
|
982
971
|
|
|
983
972
|
return unsub
|
|
984
973
|
|
|
@@ -166,14 +166,7 @@ def on_bluetooth_gatt_notify_data_response(
|
|
|
166
166
|
) -> None:
|
|
167
167
|
"""Handle a BluetoothGATTNotifyDataResponse message."""
|
|
168
168
|
if address == msg.address and handle == msg.handle:
|
|
169
|
-
|
|
170
|
-
on_bluetooth_gatt_notify(handle, bytearray(msg.data))
|
|
171
|
-
except Exception:
|
|
172
|
-
_LOGGER.exception(
|
|
173
|
-
"Unexpected error in Bluetooth GATT notify callback for address %s, handle %s",
|
|
174
|
-
address,
|
|
175
|
-
handle,
|
|
176
|
-
)
|
|
169
|
+
on_bluetooth_gatt_notify(handle, bytearray(msg.data))
|
|
177
170
|
|
|
178
171
|
|
|
179
172
|
def on_bluetooth_scanner_state_response(
|
|
@@ -1121,7 +1121,19 @@ class APIConnection:
|
|
|
1121
1121
|
# type.
|
|
1122
1122
|
handlers_copy = handlers.copy()
|
|
1123
1123
|
for handler in handlers_copy:
|
|
1124
|
-
|
|
1124
|
+
# Isolate user-callback exceptions so a buggy
|
|
1125
|
+
# handler does not propagate through asyncio's
|
|
1126
|
+
# data_received path and tear the whole session
|
|
1127
|
+
# down. See issue #1755.
|
|
1128
|
+
try:
|
|
1129
|
+
handler(msg)
|
|
1130
|
+
except Exception:
|
|
1131
|
+
_LOGGER.exception(
|
|
1132
|
+
"%s: Unexpected error in message handler %r for %s",
|
|
1133
|
+
self.log_name,
|
|
1134
|
+
handler,
|
|
1135
|
+
type(msg).__name__,
|
|
1136
|
+
)
|
|
1125
1137
|
return
|
|
1126
1138
|
|
|
1127
1139
|
# Most common case, only one handler:
|
|
@@ -1130,7 +1142,15 @@ class APIConnection:
|
|
|
1130
1142
|
# only one handler because Cython will
|
|
1131
1143
|
# poorly optimize next(iter(handlers))
|
|
1132
1144
|
for handler in handlers:
|
|
1133
|
-
|
|
1145
|
+
try:
|
|
1146
|
+
handler(msg)
|
|
1147
|
+
except Exception:
|
|
1148
|
+
_LOGGER.exception(
|
|
1149
|
+
"%s: Unexpected error in message handler %r for %s",
|
|
1150
|
+
self.log_name,
|
|
1151
|
+
handler,
|
|
1152
|
+
type(msg).__name__,
|
|
1153
|
+
)
|
|
1134
1154
|
break
|
|
1135
1155
|
|
|
1136
1156
|
def _register_internal_message_handlers(self) -> None:
|
|
@@ -84,6 +84,12 @@ class APIModelBase:
|
|
|
84
84
|
def from_pb(cls, data: Any) -> Self:
|
|
85
85
|
return cls(**{f.name: getattr(data, f.name) for f in cached_fields(cls)}) # type: ignore[arg-type]
|
|
86
86
|
|
|
87
|
+
@classmethod
|
|
88
|
+
def convert_list(cls, value: list[Any]) -> list[Self]:
|
|
89
|
+
return [
|
|
90
|
+
cls.from_dict(x) if isinstance(x, dict) else cls.from_pb(x) for x in value
|
|
91
|
+
]
|
|
92
|
+
|
|
87
93
|
|
|
88
94
|
def converter_field(*, converter: Callable[[Any], _V], **kwargs: Any) -> _V:
|
|
89
95
|
metadata = kwargs.pop("metadata", {})
|
|
@@ -185,16 +191,6 @@ class AreaInfo(APIModelBase):
|
|
|
185
191
|
area_id: int = 0
|
|
186
192
|
name: str = ""
|
|
187
193
|
|
|
188
|
-
@classmethod
|
|
189
|
-
def convert_list(cls, value: list[Any]) -> list[AreaInfo]:
|
|
190
|
-
ret = []
|
|
191
|
-
for x in value:
|
|
192
|
-
if isinstance(x, dict):
|
|
193
|
-
ret.append(AreaInfo.from_dict(x))
|
|
194
|
-
else:
|
|
195
|
-
ret.append(AreaInfo.from_pb(x))
|
|
196
|
-
return ret
|
|
197
|
-
|
|
198
194
|
@classmethod
|
|
199
195
|
def convert(cls, value: Any) -> AreaInfo:
|
|
200
196
|
if isinstance(value, dict):
|
|
@@ -208,16 +204,6 @@ class SubDeviceInfo(APIModelBase):
|
|
|
208
204
|
name: str = ""
|
|
209
205
|
area_id: int = 0
|
|
210
206
|
|
|
211
|
-
@classmethod
|
|
212
|
-
def convert_list(cls, value: list[Any]) -> list[SubDeviceInfo]:
|
|
213
|
-
ret = []
|
|
214
|
-
for x in value:
|
|
215
|
-
if isinstance(x, dict):
|
|
216
|
-
ret.append(SubDeviceInfo.from_dict(x))
|
|
217
|
-
else:
|
|
218
|
-
ret.append(SubDeviceInfo.from_pb(x))
|
|
219
|
-
return ret
|
|
220
|
-
|
|
221
207
|
|
|
222
208
|
class SerialProxyPortType(APIIntEnum):
|
|
223
209
|
TTL = 0
|
|
@@ -232,16 +218,6 @@ class SerialProxyInfo(APIModelBase):
|
|
|
232
218
|
default=SerialProxyPortType.TTL, converter=SerialProxyPortType.convert
|
|
233
219
|
)
|
|
234
220
|
|
|
235
|
-
@classmethod
|
|
236
|
-
def convert_list(cls, value: list[Any]) -> list[SerialProxyInfo]:
|
|
237
|
-
ret = []
|
|
238
|
-
for x in value:
|
|
239
|
-
if isinstance(x, dict):
|
|
240
|
-
ret.append(SerialProxyInfo.from_dict(x))
|
|
241
|
-
else:
|
|
242
|
-
ret.append(SerialProxyInfo.from_pb(x))
|
|
243
|
-
return ret
|
|
244
|
-
|
|
245
221
|
|
|
246
222
|
@_frozen_dataclass_decorator
|
|
247
223
|
class DeviceInfo(APIModelBase):
|
|
@@ -1061,16 +1037,6 @@ class MediaPlayerSupportedFormat(APIModelBase):
|
|
|
1061
1037
|
)
|
|
1062
1038
|
sample_bytes: int = 0
|
|
1063
1039
|
|
|
1064
|
-
@classmethod
|
|
1065
|
-
def convert_list(cls, value: list[Any]) -> list[MediaPlayerSupportedFormat]:
|
|
1066
|
-
ret = []
|
|
1067
|
-
for x in value:
|
|
1068
|
-
if isinstance(x, dict):
|
|
1069
|
-
ret.append(MediaPlayerSupportedFormat.from_dict(x))
|
|
1070
|
-
else:
|
|
1071
|
-
ret.append(MediaPlayerSupportedFormat.from_pb(x))
|
|
1072
|
-
return ret
|
|
1073
|
-
|
|
1074
1040
|
|
|
1075
1041
|
@_frozen_dataclass_decorator
|
|
1076
1042
|
class MediaPlayerInfo(EntityInfo):
|
|
@@ -1668,16 +1634,6 @@ class BluetoothGATTDescriptor(APIModelBase):
|
|
|
1668
1634
|
data["uuid"] = _join_split_uuid(data["uuid"])
|
|
1669
1635
|
return APIModelBase.from_dict.__func__(cls, data, ignore_missing=ignore_missing) # type: ignore[attr-defined, no-any-return]
|
|
1670
1636
|
|
|
1671
|
-
@classmethod
|
|
1672
|
-
def convert_list(cls, value: list[Any]) -> list[BluetoothGATTDescriptor]:
|
|
1673
|
-
ret = []
|
|
1674
|
-
for x in value:
|
|
1675
|
-
if isinstance(x, dict):
|
|
1676
|
-
ret.append(cls.from_dict(x))
|
|
1677
|
-
else:
|
|
1678
|
-
ret.append(cls.from_pb(x))
|
|
1679
|
-
return ret
|
|
1680
|
-
|
|
1681
1637
|
|
|
1682
1638
|
@_frozen_dataclass_decorator
|
|
1683
1639
|
class BluetoothGATTCharacteristic(APIModelBase):
|
|
@@ -1713,16 +1669,6 @@ class BluetoothGATTCharacteristic(APIModelBase):
|
|
|
1713
1669
|
data["uuid"] = _join_split_uuid(data["uuid"])
|
|
1714
1670
|
return APIModelBase.from_dict.__func__(cls, data, ignore_missing=ignore_missing) # type: ignore[attr-defined, no-any-return]
|
|
1715
1671
|
|
|
1716
|
-
@classmethod
|
|
1717
|
-
def convert_list(cls, value: list[Any]) -> list[BluetoothGATTCharacteristic]:
|
|
1718
|
-
ret = []
|
|
1719
|
-
for x in value:
|
|
1720
|
-
if isinstance(x, dict):
|
|
1721
|
-
ret.append(cls.from_dict(x))
|
|
1722
|
-
else:
|
|
1723
|
-
ret.append(cls.from_pb(x))
|
|
1724
|
-
return ret
|
|
1725
|
-
|
|
1726
1672
|
|
|
1727
1673
|
@_frozen_dataclass_decorator
|
|
1728
1674
|
class BluetoothGATTService(APIModelBase):
|
|
@@ -1757,16 +1703,6 @@ class BluetoothGATTService(APIModelBase):
|
|
|
1757
1703
|
data["uuid"] = _join_split_uuid(data["uuid"])
|
|
1758
1704
|
return APIModelBase.from_dict.__func__(cls, data, ignore_missing=ignore_missing) # type: ignore[attr-defined, no-any-return]
|
|
1759
1705
|
|
|
1760
|
-
@classmethod
|
|
1761
|
-
def convert_list(cls, value: list[Any]) -> list[BluetoothGATTService]:
|
|
1762
|
-
ret = []
|
|
1763
|
-
for x in value:
|
|
1764
|
-
if isinstance(x, dict):
|
|
1765
|
-
ret.append(cls.from_dict(x))
|
|
1766
|
-
else:
|
|
1767
|
-
ret.append(cls.from_pb(x))
|
|
1768
|
-
return ret
|
|
1769
|
-
|
|
1770
1706
|
|
|
1771
1707
|
@_frozen_dataclass_decorator
|
|
1772
1708
|
class BluetoothGATTServices(APIModelBase):
|
|
@@ -1877,16 +1813,6 @@ class VoiceAssistantWakeWord(APIModelBase):
|
|
|
1877
1813
|
wake_word: str
|
|
1878
1814
|
trained_languages: list[str]
|
|
1879
1815
|
|
|
1880
|
-
@classmethod
|
|
1881
|
-
def convert_list(cls, value: list[Any]) -> list[VoiceAssistantWakeWord]:
|
|
1882
|
-
ret = []
|
|
1883
|
-
for x in value:
|
|
1884
|
-
if isinstance(x, dict):
|
|
1885
|
-
ret.append(VoiceAssistantWakeWord.from_dict(x))
|
|
1886
|
-
else:
|
|
1887
|
-
ret.append(VoiceAssistantWakeWord.from_pb(x))
|
|
1888
|
-
return ret
|
|
1889
|
-
|
|
1890
1816
|
|
|
1891
1817
|
@_frozen_dataclass_decorator
|
|
1892
1818
|
class VoiceAssistantExternalWakeWord(APIModelBase):
|
|
@@ -1898,16 +1824,6 @@ class VoiceAssistantExternalWakeWord(APIModelBase):
|
|
|
1898
1824
|
model_hash: str
|
|
1899
1825
|
url: str
|
|
1900
1826
|
|
|
1901
|
-
@classmethod
|
|
1902
|
-
def convert_list(cls, value: list[Any]) -> list[VoiceAssistantExternalWakeWord]:
|
|
1903
|
-
ret = []
|
|
1904
|
-
for x in value:
|
|
1905
|
-
if isinstance(x, dict):
|
|
1906
|
-
ret.append(VoiceAssistantExternalWakeWord.from_dict(x))
|
|
1907
|
-
else:
|
|
1908
|
-
ret.append(VoiceAssistantExternalWakeWord.from_pb(x))
|
|
1909
|
-
return ret
|
|
1910
|
-
|
|
1911
1827
|
|
|
1912
1828
|
@_frozen_dataclass_decorator
|
|
1913
1829
|
class VoiceAssistantConfigurationResponse(APIModelBase):
|
|
@@ -2066,3 +1982,161 @@ def build_unique_id(
|
|
|
2066
1982
|
|
|
2067
1983
|
def message_types_to_names(msg_types: Iterable[type[message.Message]]) -> str:
|
|
2068
1984
|
return ", ".join(t.__name__ for t in msg_types)
|
|
1985
|
+
|
|
1986
|
+
|
|
1987
|
+
__all__ = (
|
|
1988
|
+
"COMPONENT_TYPE_TO_INFO",
|
|
1989
|
+
"APIIntEnum",
|
|
1990
|
+
"APIModelBase",
|
|
1991
|
+
"APIVersion",
|
|
1992
|
+
"AlarmControlPanelCommand",
|
|
1993
|
+
"AlarmControlPanelEntityFeature",
|
|
1994
|
+
"AlarmControlPanelEntityState",
|
|
1995
|
+
"AlarmControlPanelInfo",
|
|
1996
|
+
"AlarmControlPanelState",
|
|
1997
|
+
"AreaInfo",
|
|
1998
|
+
"BinarySensorInfo",
|
|
1999
|
+
"BinarySensorState",
|
|
2000
|
+
"BluetoothConnectionsFree",
|
|
2001
|
+
"BluetoothDeviceClearCache",
|
|
2002
|
+
"BluetoothDeviceConnection",
|
|
2003
|
+
"BluetoothDevicePairing",
|
|
2004
|
+
"BluetoothDeviceRequestType",
|
|
2005
|
+
"BluetoothDeviceUnpairing",
|
|
2006
|
+
"BluetoothGATTCharacteristic",
|
|
2007
|
+
"BluetoothGATTDescriptor",
|
|
2008
|
+
"BluetoothGATTError",
|
|
2009
|
+
"BluetoothGATTRead",
|
|
2010
|
+
"BluetoothGATTService",
|
|
2011
|
+
"BluetoothGATTServices",
|
|
2012
|
+
"BluetoothLEAdvertisement",
|
|
2013
|
+
"BluetoothProxyFeature",
|
|
2014
|
+
"BluetoothProxySubscriptionFlag",
|
|
2015
|
+
"BluetoothScannerMode",
|
|
2016
|
+
"BluetoothScannerState",
|
|
2017
|
+
"BluetoothScannerStateResponse",
|
|
2018
|
+
"ButtonInfo",
|
|
2019
|
+
"CameraInfo",
|
|
2020
|
+
"CameraState",
|
|
2021
|
+
"ClimateAction",
|
|
2022
|
+
"ClimateFanMode",
|
|
2023
|
+
"ClimateFeature",
|
|
2024
|
+
"ClimateInfo",
|
|
2025
|
+
"ClimateMode",
|
|
2026
|
+
"ClimatePreset",
|
|
2027
|
+
"ClimateState",
|
|
2028
|
+
"ClimateSwingMode",
|
|
2029
|
+
"ColorMode",
|
|
2030
|
+
"CommandProtoMessage",
|
|
2031
|
+
"CoverInfo",
|
|
2032
|
+
"CoverOperation",
|
|
2033
|
+
"CoverState",
|
|
2034
|
+
"DateInfo",
|
|
2035
|
+
"DateState",
|
|
2036
|
+
"DateTimeInfo",
|
|
2037
|
+
"DateTimeState",
|
|
2038
|
+
"DeviceInfo",
|
|
2039
|
+
"ESPHomeBluetoothGATTServices",
|
|
2040
|
+
"EntityCategory",
|
|
2041
|
+
"EntityInfo",
|
|
2042
|
+
"EntityState",
|
|
2043
|
+
"Event",
|
|
2044
|
+
"EventInfo",
|
|
2045
|
+
"ExecuteServiceResponse",
|
|
2046
|
+
"FanDirection",
|
|
2047
|
+
"FanInfo",
|
|
2048
|
+
"FanSpeed",
|
|
2049
|
+
"FanState",
|
|
2050
|
+
"HomeassistantActionResponse",
|
|
2051
|
+
"HomeassistantServiceCall",
|
|
2052
|
+
"InfraredCapability",
|
|
2053
|
+
"InfraredInfo",
|
|
2054
|
+
"InfraredRFReceiveEvent",
|
|
2055
|
+
"LastResetType",
|
|
2056
|
+
"LegacyCoverCommand",
|
|
2057
|
+
"LegacyCoverState",
|
|
2058
|
+
"LightColorCapability",
|
|
2059
|
+
"LightInfo",
|
|
2060
|
+
"LightState",
|
|
2061
|
+
"LockCommand",
|
|
2062
|
+
"LockEntityState",
|
|
2063
|
+
"LockInfo",
|
|
2064
|
+
"LockState",
|
|
2065
|
+
"LogLevel",
|
|
2066
|
+
"MediaPlayerCommand",
|
|
2067
|
+
"MediaPlayerEntityFeature",
|
|
2068
|
+
"MediaPlayerEntityState",
|
|
2069
|
+
"MediaPlayerFormatPurpose",
|
|
2070
|
+
"MediaPlayerInfo",
|
|
2071
|
+
"MediaPlayerState",
|
|
2072
|
+
"MediaPlayerSupportedFormat",
|
|
2073
|
+
"NoiseEncryptionSetKeyRequest",
|
|
2074
|
+
"NoiseEncryptionSetKeyResponse",
|
|
2075
|
+
"NumberInfo",
|
|
2076
|
+
"NumberMode",
|
|
2077
|
+
"NumberState",
|
|
2078
|
+
"RadioFrequencyCapability",
|
|
2079
|
+
"RadioFrequencyInfo",
|
|
2080
|
+
"RadioFrequencyModulation",
|
|
2081
|
+
"SelectInfo",
|
|
2082
|
+
"SelectState",
|
|
2083
|
+
"SensorInfo",
|
|
2084
|
+
"SensorState",
|
|
2085
|
+
"SensorStateClass",
|
|
2086
|
+
"SerialProxyDataReceived",
|
|
2087
|
+
"SerialProxyInfo",
|
|
2088
|
+
"SerialProxyModemPins",
|
|
2089
|
+
"SerialProxyParity",
|
|
2090
|
+
"SerialProxyPortType",
|
|
2091
|
+
"SerialProxyRequestResponse",
|
|
2092
|
+
"SerialProxyRequestType",
|
|
2093
|
+
"SerialProxyStatus",
|
|
2094
|
+
"SirenInfo",
|
|
2095
|
+
"SirenState",
|
|
2096
|
+
"SubDeviceInfo",
|
|
2097
|
+
"SupportsResponseType",
|
|
2098
|
+
"SwitchInfo",
|
|
2099
|
+
"SwitchState",
|
|
2100
|
+
"TemperatureUnit",
|
|
2101
|
+
"TextInfo",
|
|
2102
|
+
"TextMode",
|
|
2103
|
+
"TextSensorInfo",
|
|
2104
|
+
"TextSensorState",
|
|
2105
|
+
"TextState",
|
|
2106
|
+
"TimeInfo",
|
|
2107
|
+
"TimeState",
|
|
2108
|
+
"UpdateCommand",
|
|
2109
|
+
"UpdateInfo",
|
|
2110
|
+
"UpdateState",
|
|
2111
|
+
"UserService",
|
|
2112
|
+
"UserServiceArg",
|
|
2113
|
+
"UserServiceArgType",
|
|
2114
|
+
"ValveInfo",
|
|
2115
|
+
"ValveOperation",
|
|
2116
|
+
"ValveState",
|
|
2117
|
+
"VoiceAssistantAnnounceFinished",
|
|
2118
|
+
"VoiceAssistantAudioData",
|
|
2119
|
+
"VoiceAssistantAudioSettings",
|
|
2120
|
+
"VoiceAssistantCommand",
|
|
2121
|
+
"VoiceAssistantCommandFlag",
|
|
2122
|
+
"VoiceAssistantConfigurationRequest",
|
|
2123
|
+
"VoiceAssistantConfigurationResponse",
|
|
2124
|
+
"VoiceAssistantEventType",
|
|
2125
|
+
"VoiceAssistantExternalWakeWord",
|
|
2126
|
+
"VoiceAssistantFeature",
|
|
2127
|
+
"VoiceAssistantSetConfiguration",
|
|
2128
|
+
"VoiceAssistantSubscriptionFlag",
|
|
2129
|
+
"VoiceAssistantTimerEventType",
|
|
2130
|
+
"VoiceAssistantWakeWord",
|
|
2131
|
+
"WaterHeaterCommandField",
|
|
2132
|
+
"WaterHeaterFeature",
|
|
2133
|
+
"WaterHeaterInfo",
|
|
2134
|
+
"WaterHeaterMode",
|
|
2135
|
+
"WaterHeaterState",
|
|
2136
|
+
"WaterHeaterStateFlag",
|
|
2137
|
+
"ZWaveProxyFeature",
|
|
2138
|
+
"ZWaveProxyFrame",
|
|
2139
|
+
"ZWaveProxyRequest",
|
|
2140
|
+
"ZWaveProxyRequestType",
|
|
2141
|
+
"build_unique_id",
|
|
2142
|
+
)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
-
from functools import cache
|
|
6
|
+
from functools import cache, lru_cache
|
|
7
7
|
from importlib import resources
|
|
8
8
|
import logging
|
|
9
9
|
|
|
@@ -71,6 +71,7 @@ def _get_local_timezone() -> str:
|
|
|
71
71
|
return ""
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
@lru_cache(maxsize=64)
|
|
74
75
|
def iana_to_posix_tz(iana_key: str) -> str:
|
|
75
76
|
"""Convert IANA timezone key to POSIX TZ string.
|
|
76
77
|
|
|
@@ -115,12 +116,7 @@ async def get_timezone(iana_key: str | None) -> str:
|
|
|
115
116
|
Returns empty string if timezone cannot be determined.
|
|
116
117
|
|
|
117
118
|
"""
|
|
118
|
-
if iana_key:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
loop = asyncio.get_running_loop()
|
|
123
|
-
return await loop.run_in_executor(None, iana_to_posix_tz, iana_key)
|
|
124
|
-
|
|
125
|
-
return await _get_iana_timezone()
|
|
126
|
-
return await get_local_timezone()
|
|
119
|
+
if not iana_key:
|
|
120
|
+
return await get_local_timezone()
|
|
121
|
+
loop = asyncio.get_running_loop()
|
|
122
|
+
return await loop.run_in_executor(None, iana_to_posix_tz, iana_key)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aioesphomeapi
|
|
3
|
-
Version: 45.
|
|
3
|
+
Version: 45.3.1
|
|
4
4
|
Summary: Python API for interacting with ESPHome devices.
|
|
5
5
|
Home-page: https://esphome.io/
|
|
6
|
-
Download-URL: https://github.com/esphome/aioesphomeapi/archive/45.
|
|
6
|
+
Download-URL: https://github.com/esphome/aioesphomeapi/archive/45.3.1.zip
|
|
7
7
|
Author: Otto Winter
|
|
8
8
|
Author-email: esphome@nabucasa.com
|
|
9
9
|
License: MIT
|
|
@@ -12,7 +12,7 @@ License-File: LICENSE
|
|
|
12
12
|
Requires-Dist: aiohappyeyeballs>=2.6.2
|
|
13
13
|
Requires-Dist: async-interrupt>=1.2.2
|
|
14
14
|
Requires-Dist: protobuf<8,>=6
|
|
15
|
-
Requires-Dist: tzdata>=
|
|
15
|
+
Requires-Dist: tzdata>=2026.2
|
|
16
16
|
Requires-Dist: tzlocal<6,>=5.3.1
|
|
17
17
|
Requires-Dist: zeroconf<2.0,>=0.149.16
|
|
18
18
|
Requires-Dist: chacha20poly1305-reuseable>=0.13.2
|
|
@@ -128,7 +128,7 @@ combine-as-imports = true
|
|
|
128
128
|
split-on-trailing-comma = false
|
|
129
129
|
|
|
130
130
|
[build-system]
|
|
131
|
-
requires = ['setuptools>=82.0.1', 'wheel', 'Cython>=3.2.
|
|
131
|
+
requires = ['setuptools>=82.0.1', 'wheel', 'Cython>=3.2.5']
|
|
132
132
|
|
|
133
133
|
[tool.pytest.ini_options]
|
|
134
134
|
asyncio_mode = "auto"
|