Merge branch 'main' into feature/fan_presets
This commit is contained in:
commit
a1766c939c
|
@ -846,6 +846,10 @@ message ListEntitiesClimateResponse {
|
|||
string icon = 19;
|
||||
EntityCategory entity_category = 20;
|
||||
float visual_current_temperature_step = 21;
|
||||
bool supports_current_humidity = 22;
|
||||
bool supports_target_humidity = 23;
|
||||
float visual_min_humidity = 24;
|
||||
float visual_max_humidity = 25;
|
||||
}
|
||||
message ClimateStateResponse {
|
||||
option (id) = 47;
|
||||
|
@ -867,6 +871,8 @@ message ClimateStateResponse {
|
|||
string custom_fan_mode = 11;
|
||||
ClimatePreset preset = 12;
|
||||
string custom_preset = 13;
|
||||
float current_humidity = 14;
|
||||
float target_humidity = 15;
|
||||
}
|
||||
message ClimateCommandRequest {
|
||||
option (id) = 48;
|
||||
|
@ -896,6 +902,8 @@ message ClimateCommandRequest {
|
|||
ClimatePreset preset = 19;
|
||||
bool has_custom_preset = 20;
|
||||
string custom_preset = 21;
|
||||
bool has_target_humidity = 22;
|
||||
float target_humidity = 23;
|
||||
}
|
||||
|
||||
// ==================== NUMBER ====================
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -349,7 +349,7 @@ class APIClient:
|
|||
"""Execute a coroutine and reset the _connection if it fails."""
|
||||
try:
|
||||
await coro
|
||||
except Exception: # pylint: disable=broad-except
|
||||
except (Exception, asyncio.CancelledError): # pylint: disable=broad-except
|
||||
self._connection = None
|
||||
raise
|
||||
|
||||
|
@ -1039,7 +1039,7 @@ class APIClient:
|
|||
async def switch_command(self, key: int, state: bool) -> None:
|
||||
self._get_connection().send_message(SwitchCommandRequest(key=key, state=state))
|
||||
|
||||
async def climate_command(
|
||||
async def climate_command( # pylint: disable=too-many-branches
|
||||
self,
|
||||
key: int,
|
||||
mode: ClimateMode | None = None,
|
||||
|
@ -1051,6 +1051,7 @@ class APIClient:
|
|||
custom_fan_mode: str | None = None,
|
||||
preset: ClimatePreset | None = None,
|
||||
custom_preset: str | None = None,
|
||||
target_humidity: float | None = None,
|
||||
) -> None:
|
||||
req = ClimateCommandRequest(key=key)
|
||||
if mode is not None:
|
||||
|
@ -1087,6 +1088,9 @@ class APIClient:
|
|||
if custom_preset is not None:
|
||||
req.has_custom_preset = True
|
||||
req.custom_preset = custom_preset
|
||||
if target_humidity is not None:
|
||||
req.has_target_humidity = True
|
||||
req.target_humidity = target_humidity
|
||||
self._get_connection().send_message(req)
|
||||
|
||||
async def number_command(self, key: int, state: float) -> None:
|
||||
|
|
|
@ -548,6 +548,10 @@ class ClimateInfo(EntityInfo):
|
|||
supported_custom_presets: list[str] = converter_field(
|
||||
default_factory=list, converter=list
|
||||
)
|
||||
supports_current_humidity: bool = False
|
||||
supports_target_humidity: bool = False
|
||||
visual_min_humidity: float = 0
|
||||
visual_max_humidity: float = 0
|
||||
|
||||
def supported_presets_compat(self, api_version: APIVersion) -> list[ClimatePreset]:
|
||||
if api_version < APIVersion(1, 5):
|
||||
|
@ -591,6 +595,8 @@ class ClimateState(EntityState):
|
|||
default=ClimatePreset.NONE, converter=ClimatePreset.convert
|
||||
)
|
||||
custom_preset: str = ""
|
||||
current_humidity: float = 0
|
||||
target_humidity: float = 0
|
||||
|
||||
def preset_compat(self, api_version: APIVersion) -> ClimatePreset | None:
|
||||
if api_version < APIVersion(1, 5):
|
||||
|
|
2
setup.py
2
setup.py
|
@ -11,7 +11,7 @@ with open(os.path.join(here, "README.rst"), encoding="utf-8") as readme_file:
|
|||
long_description = readme_file.read()
|
||||
|
||||
|
||||
VERSION = "19.1.8"
|
||||
VERSION = "19.2.2"
|
||||
PROJECT_NAME = "aioesphomeapi"
|
||||
PROJECT_PACKAGE_NAME = "aioesphomeapi"
|
||||
PROJECT_LICENSE = "MIT"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import contextlib
|
||||
import itertools
|
||||
import logging
|
||||
from functools import partial
|
||||
|
@ -212,6 +213,37 @@ async def test_finish_connection_wraps_exceptions_as_unhandled_api_error() -> No
|
|||
await cli.finish_connection(False)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_connection_released_if_connecting_is_cancelled() -> None:
|
||||
"""Verify connection is unset if connecting is cancelled."""
|
||||
cli = APIClient("1.2.3.4", 1234, None)
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
with patch.object(loop, "sock_connect", side_effect=partial(asyncio.sleep, 1)):
|
||||
start_task = asyncio.create_task(cli.start_connection())
|
||||
await asyncio.sleep(0)
|
||||
assert cli._connection is not None
|
||||
|
||||
start_task.cancel()
|
||||
with contextlib.suppress(BaseException):
|
||||
await start_task
|
||||
assert cli._connection is None
|
||||
|
||||
with patch(
|
||||
"aioesphomeapi.client.APIConnection", PatchableAPIConnection
|
||||
), patch.object(loop, "sock_connect"):
|
||||
await cli.start_connection()
|
||||
await asyncio.sleep(0)
|
||||
|
||||
assert cli._connection is not None
|
||||
task = asyncio.create_task(cli.finish_connection(False))
|
||||
await asyncio.sleep(0)
|
||||
task.cancel()
|
||||
with contextlib.suppress(BaseException):
|
||||
await task
|
||||
assert cli._connection is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_request_while_handshaking(event_loop) -> None:
|
||||
"""Test trying a request while handshaking raises."""
|
||||
|
@ -528,6 +560,10 @@ async def test_climate_command_legacy(
|
|||
dict(key=1, custom_preset="asdf"),
|
||||
dict(key=1, has_custom_preset=True, custom_preset="asdf"),
|
||||
),
|
||||
(
|
||||
dict(key=1, target_humidity=60.0),
|
||||
dict(key=1, has_target_humidity=True, target_humidity=60.0),
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_climate_command(
|
||||
|
@ -1825,6 +1861,15 @@ async def test_bluetooth_device_connect(
|
|||
await asyncio.sleep(0)
|
||||
assert states == [(True, 23, 0), (False, 23, 7)]
|
||||
|
||||
# Make sure cancel is safe to call again
|
||||
cancel()
|
||||
|
||||
await client.disconnect(force=True)
|
||||
await asyncio.sleep(0)
|
||||
assert not client._connection
|
||||
# Make sure cancel is safe to call after disconnect
|
||||
cancel()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bluetooth_device_connect_and_disconnect_times_out(
|
||||
|
|
Loading…
Reference in New Issue