Add missing application credential to Tesla Fleet (#123271)

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
Brett Adams 2024-08-07 21:11:03 +10:00 committed by GitHub
parent 45ce0fed0a
commit 764166342e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 61 additions and 36 deletions

View File

@ -13,6 +13,7 @@ from tesla_fleet_api.exceptions import (
TeslaFleetError, TeslaFleetError,
) )
from homeassistant.components.application_credentials import ClientCredential
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN, Platform from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -26,7 +27,9 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.device_registry import DeviceInfo
from .const import DOMAIN, LOGGER, MODELS from .application_credentials import TeslaOAuth2Implementation
from .config_flow import OAuth2FlowHandler
from .const import CLIENT_ID, DOMAIN, LOGGER, MODELS, NAME
from .coordinator import ( from .coordinator import (
TeslaFleetEnergySiteInfoCoordinator, TeslaFleetEnergySiteInfoCoordinator,
TeslaFleetEnergySiteLiveCoordinator, TeslaFleetEnergySiteLiveCoordinator,
@ -51,6 +54,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslaFleetConfigEntry) -
scopes = token["scp"] scopes = token["scp"]
region = token["ou_code"].lower() region = token["ou_code"].lower()
OAuth2FlowHandler.async_register_implementation(
hass,
TeslaOAuth2Implementation(hass, DOMAIN, ClientCredential(CLIENT_ID, "", NAME)),
)
implementation = await async_get_config_entry_implementation(hass, entry) implementation = await async_get_config_entry_implementation(hass, entry)
oauth_session = OAuth2Session(hass, entry, implementation) oauth_session = OAuth2Session(hass, entry, implementation)
refresh_lock = asyncio.Lock() refresh_lock = asyncio.Lock()

View File

@ -5,15 +5,17 @@ import hashlib
import secrets import secrets
from typing import Any from typing import Any
from homeassistant.components.application_credentials import ClientCredential from homeassistant.components.application_credentials import (
AuthImplementation,
AuthorizationServer,
ClientCredential,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers import config_entry_oauth2_flow
from .const import DOMAIN, SCOPES from .const import AUTHORIZE_URL, DOMAIN, SCOPES, TOKEN_URL
CLIENT_ID = "71b813eb-4a2e-483a-b831-4dec5cb9bf0d" AUTH_SERVER = AuthorizationServer(AUTHORIZE_URL, TOKEN_URL)
AUTHORIZE_URL = "https://auth.tesla.com/oauth2/v3/authorize"
TOKEN_URL = "https://auth.tesla.com/oauth2/v3/token"
async def async_get_auth_implementation( async def async_get_auth_implementation(
@ -23,15 +25,16 @@ async def async_get_auth_implementation(
return TeslaOAuth2Implementation( return TeslaOAuth2Implementation(
hass, hass,
DOMAIN, DOMAIN,
credential,
) )
class TeslaOAuth2Implementation(config_entry_oauth2_flow.LocalOAuth2Implementation): class TeslaOAuth2Implementation(AuthImplementation):
"""Tesla Fleet API Open Source Oauth2 implementation.""" """Tesla Fleet API Open Source Oauth2 implementation."""
_name = "Tesla Fleet API" def __init__(
self, hass: HomeAssistant, domain: str, credential: ClientCredential
def __init__(self, hass: HomeAssistant, domain: str) -> None: ) -> None:
"""Initialize local auth implementation.""" """Initialize local auth implementation."""
self.hass = hass self.hass = hass
self._domain = domain self._domain = domain
@ -45,10 +48,8 @@ class TeslaOAuth2Implementation(config_entry_oauth2_flow.LocalOAuth2Implementati
super().__init__( super().__init__(
hass, hass,
domain, domain,
CLIENT_ID, credential,
"", # Implementation has no client secret AUTH_SERVER,
AUTHORIZE_URL,
TOKEN_URL,
) )
@property @property

View File

@ -8,10 +8,12 @@ from typing import Any
import jwt import jwt
from homeassistant.components.application_credentials import ClientCredential
from homeassistant.config_entries import ConfigEntry, ConfigFlowResult from homeassistant.config_entries import ConfigEntry, ConfigFlowResult
from homeassistant.helpers import config_entry_oauth2_flow from homeassistant.helpers import config_entry_oauth2_flow
from .const import DOMAIN, LOGGER from .application_credentials import TeslaOAuth2Implementation
from .const import CLIENT_ID, DOMAIN, LOGGER, NAME
class OAuth2FlowHandler( class OAuth2FlowHandler(
@ -27,6 +29,19 @@ class OAuth2FlowHandler(
"""Return logger.""" """Return logger."""
return LOGGER return LOGGER
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Handle a flow start."""
self.async_register_implementation(
self.hass,
TeslaOAuth2Implementation(
self.hass, DOMAIN, ClientCredential(CLIENT_ID, "", NAME)
),
)
return await super().async_step_user()
async def async_oauth_create_entry( async def async_oauth_create_entry(
self, self,
data: dict[str, Any], data: dict[str, Any],

View File

@ -13,6 +13,11 @@ CONF_REFRESH_TOKEN = "refresh_token"
LOGGER = logging.getLogger(__package__) LOGGER = logging.getLogger(__package__)
NAME = "Home Assistant"
CLIENT_ID = "71b813eb-4a2e-483a-b831-4dec5cb9bf0d"
AUTHORIZE_URL = "https://auth.tesla.com/oauth2/v3/authorize"
TOKEN_URL = "https://auth.tesla.com/oauth2/v3/token"
SCOPES = [ SCOPES = [
Scope.OPENID, Scope.OPENID,
Scope.OFFLINE_ACCESS, Scope.OFFLINE_ACCESS,

View File

@ -4,9 +4,15 @@ from unittest.mock import patch
from syrupy import SnapshotAssertion from syrupy import SnapshotAssertion
from homeassistant.components.application_credentials import (
ClientCredential,
async_import_client_credential,
)
from homeassistant.components.tesla_fleet.const import CLIENT_ID, DOMAIN
from homeassistant.const import Platform from homeassistant.const import Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -18,6 +24,14 @@ async def setup_platform(
) -> None: ) -> None:
"""Set up the Tesla Fleet platform.""" """Set up the Tesla Fleet platform."""
assert await async_setup_component(hass, "application_credentials", {})
await async_import_client_credential(
hass,
DOMAIN,
ClientCredential(CLIENT_ID, "", "Home Assistant"),
DOMAIN,
)
config_entry.add_to_hass(hass) config_entry.add_to_hass(hass)
if platforms is None: if platforms is None:

View File

@ -10,14 +10,7 @@ from unittest.mock import AsyncMock, patch
import jwt import jwt
import pytest import pytest
from homeassistant.components.application_credentials import (
ClientCredential,
async_import_client_credential,
)
from homeassistant.components.tesla_fleet.application_credentials import CLIENT_ID
from homeassistant.components.tesla_fleet.const import DOMAIN, SCOPES from homeassistant.components.tesla_fleet.const import DOMAIN, SCOPES
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from .const import LIVE_STATUS, PRODUCTS, SITE_INFO, VEHICLE_DATA, VEHICLE_ONLINE from .const import LIVE_STATUS, PRODUCTS, SITE_INFO, VEHICLE_DATA, VEHICLE_ONLINE
@ -71,18 +64,6 @@ def normal_config_entry(expires_at: int, scopes: list[str]) -> MockConfigEntry:
) )
@pytest.fixture(autouse=True)
async def setup_credentials(hass: HomeAssistant) -> None:
"""Fixture to setup credentials."""
assert await async_setup_component(hass, "application_credentials", {})
await async_import_client_credential(
hass,
DOMAIN,
ClientCredential(CLIENT_ID, ""),
DOMAIN,
)
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def mock_products() -> Generator[AsyncMock]: def mock_products() -> Generator[AsyncMock]:
"""Mock Tesla Fleet Api products method.""" """Mock Tesla Fleet Api products method."""

View File

@ -5,12 +5,13 @@ from urllib.parse import parse_qs, urlparse
import pytest import pytest
from homeassistant.components.tesla_fleet.application_credentials import ( from homeassistant.components.tesla_fleet.const import (
AUTHORIZE_URL, AUTHORIZE_URL,
CLIENT_ID, CLIENT_ID,
DOMAIN,
SCOPES,
TOKEN_URL, TOKEN_URL,
) )
from homeassistant.components.tesla_fleet.const import DOMAIN, SCOPES
from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER from homeassistant.config_entries import SOURCE_REAUTH, SOURCE_USER
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType from homeassistant.data_entry_flow import FlowResultType