1 """Tesla Fleet integration."""
4 from typing
import Final
6 from aiohttp.client_exceptions
import ClientResponseError
8 from tesla_fleet_api
import (
14 from tesla_fleet_api.const
import Scope
15 from tesla_fleet_api.exceptions
import (
32 async_get_config_entry_implementation,
37 from .config_flow
import OAuth2FlowHandler
38 from .const
import DOMAIN, LOGGER, MODELS
39 from .coordinator
import (
40 TeslaFleetEnergySiteInfoCoordinator,
41 TeslaFleetEnergySiteLiveCoordinator,
42 TeslaFleetVehicleDataCoordinator,
44 from .models
import TeslaFleetData, TeslaFleetEnergyData, TeslaFleetVehicleData
45 from .oauth
import TeslaSystemImplementation
48 Platform.BINARY_SENSOR,
52 Platform.DEVICE_TRACKER,
54 Platform.MEDIA_PLAYER,
61 type TeslaFleetConfigEntry = ConfigEntry[TeslaFleetData]
63 CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
67 """Set up TeslaFleet config."""
69 access_token = entry.data[CONF_TOKEN][CONF_ACCESS_TOKEN]
72 token = jwt.decode(access_token, options={
"verify_signature":
False})
73 scopes: list[Scope] = [Scope(s)
for s
in token[
"scp"]]
74 region: str = token[
"ou_code"].lower()
76 OAuth2FlowHandler.async_register_implementation(
83 refresh_lock = asyncio.Lock()
85 async
def _refresh_token() -> str:
86 async
with refresh_lock:
88 await oauth_session.async_ensure_token_valid()
89 except ClientResponseError
as e:
91 raise ConfigEntryAuthFailed
from e
92 raise ConfigEntryNotReady
from e
93 token: str = oauth_session.token[CONF_ACCESS_TOKEN]
97 tesla = TeslaFleetApi(
99 access_token=access_token,
101 charging_scope=
False,
103 energy_scope=Scope.ENERGY_DEVICE_DATA
in scopes,
104 vehicle_scope=Scope.VEHICLE_DEVICE_DATA
in scopes,
105 refresh_hook=_refresh_token,
108 products = (await tesla.products())[
"response"]
109 except (InvalidToken, OAuthExpired, LoginRequired)
as e:
110 raise ConfigEntryAuthFailed
from e
111 except InvalidRegion:
113 LOGGER.warning(
"Region is invalid, trying to find the correct region")
114 await tesla.find_server()
116 products = (await tesla.products())[
"response"]
117 except TeslaFleetError
as e:
118 raise ConfigEntryNotReady
from e
119 except LibraryError
as e:
120 raise ConfigEntryAuthFailed
from e
121 except TeslaFleetError
as e:
122 raise ConfigEntryNotReady
from e
124 device_registry = dr.async_get(hass)
127 vehicles: list[TeslaFleetVehicleData] = []
128 energysites: list[TeslaFleetEnergyData] = []
129 for product
in products:
130 if "vin" in product
and hasattr(tesla,
"vehicle"):
132 product.pop(
"cached_data",
None)
134 signing = product[
"command_signing"] ==
"required"
136 if not tesla.private_key:
137 await tesla.get_private_key(hass.config.path(
"tesla_fleet.key"))
138 api = VehicleSigned(tesla.vehicle, vin)
140 api = VehicleSpecific(tesla.vehicle, vin)
143 await coordinator.async_config_entry_first_refresh()
146 identifiers={(DOMAIN, vin)},
147 manufacturer=
"Tesla",
148 name=product[
"display_name"],
149 model=MODELS.get(vin[3]),
156 coordinator=coordinator,
162 elif "energy_site_id" in product
and hasattr(tesla,
"energy"):
163 site_id = product[
"energy_site_id"]
165 product[
"components"][
"battery"]
166 or product[
"components"][
"solar"]
167 or "wall_connectors" in product[
"components"]
170 "Skipping Energy Site %s as it has no components",
175 api = EnergySpecific(tesla.energy, site_id)
180 await live_coordinator.async_config_entry_first_refresh()
181 await info_coordinator.async_config_entry_first_refresh()
186 for gateway
in info_coordinator.data.get(
"components_gateways", []):
187 if gateway.get(
"part_name"):
188 models.add(gateway[
"part_name"])
189 for battery
in info_coordinator.data.get(
"components_batteries", []):
190 if battery.get(
"part_name"):
191 models.add(battery[
"part_name"])
193 model =
", ".join(sorted(models))
196 identifiers={(DOMAIN,
str(site_id))},
197 manufacturer=
"Tesla",
198 name=product.get(
"site_name",
"Energy Site"),
200 serial_number=
str(site_id),
205 device_registry.async_get_or_create(
206 config_entry_id=entry.entry_id, **device
212 live_coordinator=live_coordinator,
213 info_coordinator=info_coordinator,
220 entry.runtime_data =
TeslaFleetData(vehicles, energysites, scopes)
221 await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
227 """Unload TeslaFleet Config."""
228 return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
bool async_setup_entry(HomeAssistant hass, TeslaFleetConfigEntry entry)
bool async_unload_entry(HomeAssistant hass, TeslaFleetConfigEntry entry)
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)
AbstractOAuth2Implementation async_get_config_entry_implementation(HomeAssistant hass, config_entries.ConfigEntry config_entry)