Skip to content

Commit 7504d2b

Browse files
authored
Poll for pending HVAC states [fixes #21] (#22)
When setting mode=AUTO while the car is sleeping, the target state might not be reached until more than 30 seconds later (the delay we use after sending an HVAC command). In that case, what happens is: 1. Set hvac_mode=AUTO -> send hvac command -> write state 2. After 30 seconds, poll TeslaFi 3. If the state has not been updated yet, this will write state=OFF 4. Some time later (3 min default), TeslaFi has the updated state and will report state=AUTO The transition from 2->3->4 causes the climate entity to flip states unexpectedly. For `lock` and `alarm_control_panel` platforms, we retain a pending or target state, and on the next refresh if the pending state has not been met yet, we schedule another refresh, until the target state is met. This adds the same technique for climate mode. We only do this for mode, because it's the one that is most likely to be set from a sleeping state. In addition, we'll also ask TeslaFi to add the `wake=` parameter which will cause the API to wake the car first and wait a few seconds for the car to wake up before sending the real command. Long term it would be great to have a better (and more reusable) way of doing this. Ideally the HA service call should not return until the target state is fully verified, but given the polling nature of both the integration and TeslaFi itself, this could cause problems in HA.
1 parent 6480a6a commit 7504d2b

File tree

4 files changed

+21
-4
lines changed

4 files changed

+21
-4
lines changed

custom_components/teslafi/client.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ async def _request(self, command: str = "", **kwargs) -> dict:
4646
"""
4747
:param command: The command to send. Can be empty string, `lastGood`, etc. See
4848
"""
49+
timeout = kwargs.get("wake", 0) + REQUEST_TIMEOUT
4950
response = await self._client.get(
5051
url="https://www.teslafi.com/feed.php",
5152
headers={"Authorization": "Bearer " + self._api_key},
5253
params={"command": command} | kwargs,
53-
timeout=REQUEST_TIMEOUT,
54+
timeout=timeout,
5455
)
5556
assert response.status_code < 400
5657

custom_components/teslafi/climate.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ class TeslaFiClimate(TeslaFiEntity[TeslaFiClimateEntityDescription], ClimateEnti
6969
_attr_preset_mode = None
7070
_attr_is_aux_heat = False
7171

72+
_pending_mode = None
73+
7274
def _handle_coordinator_update(self) -> None:
7375
# These are in Celsius, despite user settings
7476
self._attr_target_temperature = (
@@ -81,10 +83,19 @@ def _handle_coordinator_update(self) -> None:
8183
)
8284

8385
is_on = _convert_to_bool(self.coordinator.data.get("is_climate_on"))
84-
if not is_on:
85-
self._attr_hvac_mode = HVACMode.OFF
86+
87+
new_mode = None if is_on is None else HVACMode.AUTO if is_on else HVACMode.OFF
88+
want_mode = self._pending_mode
89+
90+
if want_mode is None:
91+
self._attr_hvac_mode = new_mode
92+
elif new_mode == want_mode:
93+
self._attr_hvac_mode = new_mode
94+
self._pending_mode = None
95+
LOGGER.info("Target state succeeded: %s", want_mode)
8696
else:
87-
self._attr_hvac_mode = HVACMode.AUTO
97+
LOGGER.debug("Still waiting for %s", want_mode)
98+
return self.coordinator.schedule_refresh_in(DELAY_WAKEUP)
8899

89100
self._attr_fan_mode = (
90101
FAN_AUTO if self.coordinator.data.get("fan_status") == "2" else FAN_OFF
@@ -141,6 +152,7 @@ async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
141152
else:
142153
raise f"Mode '{hvac_mode}' not supported."
143154

155+
self._pending_mode = hvac_mode
144156
await self.coordinator.execute_command(cmd)
145157
self._attr_hvac_mode = hvac_mode
146158
self._attr_hvac_action = None

custom_components/teslafi/const.py

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
POLLING_INTERVAL_SLEEPING = timedelta(minutes=10)
1717

1818
DELAY_CLIMATE = timedelta(seconds=30)
19+
DELAY_CMD_WAKE = timedelta(seconds=10)
1920
DELAY_LOCKS = timedelta(seconds=15)
2021
DELAY_WAKEUP = timedelta(seconds=30)
2122

custom_components/teslafi/coordinator.py

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from .client import TeslaFiClient
99
from .const import (
10+
DELAY_CMD_WAKE,
1011
DOMAIN,
1112
LOGGER,
1213
POLLING_INTERVAL_DEFAULT,
@@ -44,6 +45,8 @@ def __init__(
4445

4546
async def execute_command(self, cmd: str, **kwargs) -> dict:
4647
"""Execute the remote command."""
48+
if self.data.is_sleeping:
49+
kwargs["wake"] = DELAY_CMD_WAKE.seconds
4750
LOGGER.debug(">> executing command %s; args=%s", cmd, kwargs)
4851
result = await self._client.command(cmd, **kwargs)
4952
LOGGER.debug("<< command %s response: %s", cmd, result)

0 commit comments

Comments
 (0)