parent
08f6e9cd12
commit
c89d8edb3c
|
@ -17,7 +17,6 @@ from tesla_fleet_api.exceptions import (
|
||||||
TeslaFleetError,
|
TeslaFleetError,
|
||||||
VehicleOffline,
|
VehicleOffline,
|
||||||
)
|
)
|
||||||
from tesla_fleet_api.ratecalculator import RateCalculator
|
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||||
|
@ -66,7 +65,6 @@ class TeslaFleetVehicleDataCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||||
updated_once: bool
|
updated_once: bool
|
||||||
pre2021: bool
|
pre2021: bool
|
||||||
last_active: datetime
|
last_active: datetime
|
||||||
rate: RateCalculator
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -87,44 +85,36 @@ class TeslaFleetVehicleDataCoordinator(DataUpdateCoordinator[dict[str, Any]]):
|
||||||
self.data = flatten(product)
|
self.data = flatten(product)
|
||||||
self.updated_once = False
|
self.updated_once = False
|
||||||
self.last_active = datetime.now()
|
self.last_active = datetime.now()
|
||||||
self.rate = RateCalculator(100, 86400, VEHICLE_INTERVAL_SECONDS, 3600, 5)
|
|
||||||
|
|
||||||
async def _async_update_data(self) -> dict[str, Any]:
|
async def _async_update_data(self) -> dict[str, Any]:
|
||||||
"""Update vehicle data using TeslaFleet API."""
|
"""Update vehicle data using TeslaFleet API."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Check if the vehicle is awake using a non-rate limited API call
|
# Check if the vehicle is awake using a free API call
|
||||||
if self.data["state"] != TeslaFleetState.ONLINE:
|
response = await self.api.vehicle()
|
||||||
response = await self.api.vehicle()
|
self.data["state"] = response["response"]["state"]
|
||||||
self.data["state"] = response["response"]["state"]
|
|
||||||
|
|
||||||
if self.data["state"] != TeslaFleetState.ONLINE:
|
if self.data["state"] != TeslaFleetState.ONLINE:
|
||||||
return self.data
|
return self.data
|
||||||
|
|
||||||
# This is a rated limited API call
|
|
||||||
self.rate.consume()
|
|
||||||
response = await self.api.vehicle_data(endpoints=ENDPOINTS)
|
response = await self.api.vehicle_data(endpoints=ENDPOINTS)
|
||||||
data = response["response"]
|
data = response["response"]
|
||||||
|
|
||||||
except VehicleOffline:
|
except VehicleOffline:
|
||||||
self.data["state"] = TeslaFleetState.ASLEEP
|
self.data["state"] = TeslaFleetState.ASLEEP
|
||||||
return self.data
|
return self.data
|
||||||
except RateLimited as e:
|
except RateLimited:
|
||||||
LOGGER.warning(
|
LOGGER.warning(
|
||||||
"%s rate limited, will retry in %s seconds",
|
"%s rate limited, will skip refresh",
|
||||||
self.name,
|
self.name,
|
||||||
e.data.get("after"),
|
|
||||||
)
|
)
|
||||||
if "after" in e.data:
|
|
||||||
self.update_interval = timedelta(seconds=int(e.data["after"]))
|
|
||||||
return self.data
|
return self.data
|
||||||
except (InvalidToken, OAuthExpired, LoginRequired) as e:
|
except (InvalidToken, OAuthExpired, LoginRequired) as e:
|
||||||
raise ConfigEntryAuthFailed from e
|
raise ConfigEntryAuthFailed from e
|
||||||
except TeslaFleetError as e:
|
except TeslaFleetError as e:
|
||||||
raise UpdateFailed(e.message) from e
|
raise UpdateFailed(e.message) from e
|
||||||
|
|
||||||
# Calculate ideal refresh interval
|
self.update_interval = VEHICLE_INTERVAL
|
||||||
self.update_interval = timedelta(seconds=self.rate.calculate())
|
|
||||||
|
|
||||||
self.updated_once = True
|
self.updated_once = True
|
||||||
|
|
||||||
|
|
|
@ -156,14 +156,15 @@ async def test_vehicle_refresh_offline(
|
||||||
mock_vehicle_state.reset_mock()
|
mock_vehicle_state.reset_mock()
|
||||||
mock_vehicle_data.reset_mock()
|
mock_vehicle_data.reset_mock()
|
||||||
|
|
||||||
# Then the vehicle goes offline
|
# Then the vehicle goes offline despite saying its online
|
||||||
mock_vehicle_data.side_effect = VehicleOffline
|
mock_vehicle_data.side_effect = VehicleOffline
|
||||||
freezer.tick(VEHICLE_INTERVAL)
|
freezer.tick(VEHICLE_INTERVAL)
|
||||||
async_fire_time_changed(hass)
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
mock_vehicle_state.assert_not_called()
|
mock_vehicle_state.assert_called_once()
|
||||||
mock_vehicle_data.assert_called_once()
|
mock_vehicle_data.assert_called_once()
|
||||||
|
mock_vehicle_state.reset_mock()
|
||||||
mock_vehicle_data.reset_mock()
|
mock_vehicle_data.reset_mock()
|
||||||
|
|
||||||
# And stays offline
|
# And stays offline
|
||||||
|
@ -212,20 +213,15 @@ async def test_vehicle_refresh_ratelimited(
|
||||||
|
|
||||||
assert (state := hass.states.get("sensor.test_battery_level"))
|
assert (state := hass.states.get("sensor.test_battery_level"))
|
||||||
assert state.state == "unknown"
|
assert state.state == "unknown"
|
||||||
assert mock_vehicle_data.call_count == 1
|
|
||||||
|
mock_vehicle_data.reset_mock()
|
||||||
|
|
||||||
freezer.tick(VEHICLE_INTERVAL)
|
freezer.tick(VEHICLE_INTERVAL)
|
||||||
async_fire_time_changed(hass)
|
async_fire_time_changed(hass)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
# Should not call for another 10 seconds
|
assert (state := hass.states.get("sensor.test_battery_level"))
|
||||||
assert mock_vehicle_data.call_count == 1
|
assert state.state == "unknown"
|
||||||
|
|
||||||
freezer.tick(VEHICLE_INTERVAL)
|
|
||||||
async_fire_time_changed(hass)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert mock_vehicle_data.call_count == 2
|
|
||||||
|
|
||||||
|
|
||||||
async def test_vehicle_sleep(
|
async def test_vehicle_sleep(
|
||||||
|
|
Loading…
Reference in New Issue