mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Remove tesla integration (#55988)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
675426dc25
commit
98ecf2888c
@ -1049,14 +1049,6 @@ omit =
|
||||
homeassistant/components/telnet/switch.py
|
||||
homeassistant/components/temper/sensor.py
|
||||
homeassistant/components/tensorflow/image_processing.py
|
||||
homeassistant/components/tesla/__init__.py
|
||||
homeassistant/components/tesla/binary_sensor.py
|
||||
homeassistant/components/tesla/climate.py
|
||||
homeassistant/components/tesla/const.py
|
||||
homeassistant/components/tesla/device_tracker.py
|
||||
homeassistant/components/tesla/lock.py
|
||||
homeassistant/components/tesla/sensor.py
|
||||
homeassistant/components/tesla/switch.py
|
||||
homeassistant/components/tfiac/climate.py
|
||||
homeassistant/components/thermoworks_smoke/sensor.py
|
||||
homeassistant/components/thethingsnetwork/*
|
||||
|
@ -518,7 +518,6 @@ homeassistant/components/tasmota/* @emontnemery
|
||||
homeassistant/components/tautulli/* @ludeeus
|
||||
homeassistant/components/tellduslive/* @fredrike
|
||||
homeassistant/components/template/* @PhracturedBlue @tetienne @home-assistant/core
|
||||
homeassistant/components/tesla/* @zabuldon @alandtse
|
||||
homeassistant/components/tfiac/* @fredrike @mellado
|
||||
homeassistant/components/thethingsnetwork/* @fabaff
|
||||
homeassistant/components/threshold/* @fabaff
|
||||
|
@ -1,357 +0,0 @@
|
||||
"""Support for Tesla cars."""
|
||||
import asyncio
|
||||
from collections import defaultdict
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
import async_timeout
|
||||
import httpx
|
||||
from teslajsonpy import Controller as TeslaAPI
|
||||
from teslajsonpy.exceptions import IncompleteCredentials, TeslaException
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import SOURCE_IMPORT
|
||||
from homeassistant.const import (
|
||||
ATTR_BATTERY_CHARGING,
|
||||
ATTR_BATTERY_LEVEL,
|
||||
CONF_ACCESS_TOKEN,
|
||||
CONF_PASSWORD,
|
||||
CONF_SCAN_INTERVAL,
|
||||
CONF_TOKEN,
|
||||
CONF_USERNAME,
|
||||
EVENT_HOMEASSISTANT_CLOSE,
|
||||
HTTP_UNAUTHORIZED,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.httpx_client import SERVER_SOFTWARE, USER_AGENT
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
CoordinatorEntity,
|
||||
DataUpdateCoordinator,
|
||||
UpdateFailed,
|
||||
)
|
||||
from homeassistant.util import slugify
|
||||
|
||||
from .config_flow import CannotConnect, InvalidAuth, validate_input
|
||||
from .const import (
|
||||
CONF_EXPIRATION,
|
||||
CONF_WAKE_ON_START,
|
||||
DATA_LISTENER,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DEFAULT_WAKE_ON_START,
|
||||
DOMAIN,
|
||||
ICONS,
|
||||
MIN_SCAN_INTERVAL,
|
||||
PLATFORMS,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
vol.Optional(
|
||||
CONF_SCAN_INTERVAL, default=DEFAULT_SCAN_INTERVAL
|
||||
): vol.All(cv.positive_int, vol.Clamp(min=MIN_SCAN_INTERVAL)),
|
||||
}
|
||||
)
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def _async_save_tokens(hass, config_entry, access_token, refresh_token):
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry,
|
||||
data={
|
||||
**config_entry.data,
|
||||
CONF_ACCESS_TOKEN: access_token,
|
||||
CONF_TOKEN: refresh_token,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def _async_configured_emails(hass):
|
||||
"""Return a set of configured Tesla emails."""
|
||||
return {
|
||||
entry.data[CONF_USERNAME]
|
||||
for entry in hass.config_entries.async_entries(DOMAIN)
|
||||
if CONF_USERNAME in entry.data
|
||||
}
|
||||
|
||||
|
||||
async def async_setup(hass, base_config):
|
||||
"""Set up of Tesla component."""
|
||||
|
||||
def _update_entry(email, data=None, options=None):
|
||||
data = data or {}
|
||||
options = options or {
|
||||
CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL,
|
||||
CONF_WAKE_ON_START: DEFAULT_WAKE_ON_START,
|
||||
}
|
||||
for entry in hass.config_entries.async_entries(DOMAIN):
|
||||
if email != entry.title:
|
||||
continue
|
||||
hass.config_entries.async_update_entry(entry, data=data, options=options)
|
||||
|
||||
config = base_config.get(DOMAIN)
|
||||
if not config:
|
||||
return True
|
||||
email = config[CONF_USERNAME]
|
||||
password = config[CONF_PASSWORD]
|
||||
scan_interval = config[CONF_SCAN_INTERVAL]
|
||||
if email in _async_configured_emails(hass):
|
||||
try:
|
||||
info = await validate_input(hass, config)
|
||||
except (CannotConnect, InvalidAuth):
|
||||
return False
|
||||
_update_entry(
|
||||
email,
|
||||
data={
|
||||
CONF_USERNAME: email,
|
||||
CONF_PASSWORD: password,
|
||||
CONF_ACCESS_TOKEN: info[CONF_ACCESS_TOKEN],
|
||||
CONF_TOKEN: info[CONF_TOKEN],
|
||||
CONF_EXPIRATION: info[CONF_EXPIRATION],
|
||||
},
|
||||
options={CONF_SCAN_INTERVAL: scan_interval},
|
||||
)
|
||||
else:
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data={CONF_USERNAME: email, CONF_PASSWORD: password},
|
||||
)
|
||||
)
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][email] = {CONF_SCAN_INTERVAL: scan_interval}
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry):
|
||||
"""Set up Tesla as config entry."""
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
config = config_entry.data
|
||||
# Because users can have multiple accounts, we always create a new session so they have separate cookies
|
||||
async_client = httpx.AsyncClient(headers={USER_AGENT: SERVER_SOFTWARE}, timeout=60)
|
||||
email = config_entry.title
|
||||
if email in hass.data[DOMAIN] and CONF_SCAN_INTERVAL in hass.data[DOMAIN][email]:
|
||||
scan_interval = hass.data[DOMAIN][email][CONF_SCAN_INTERVAL]
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry, options={CONF_SCAN_INTERVAL: scan_interval}
|
||||
)
|
||||
hass.data[DOMAIN].pop(email)
|
||||
try:
|
||||
controller = TeslaAPI(
|
||||
async_client,
|
||||
email=config.get(CONF_USERNAME),
|
||||
password=config.get(CONF_PASSWORD),
|
||||
refresh_token=config[CONF_TOKEN],
|
||||
access_token=config[CONF_ACCESS_TOKEN],
|
||||
expiration=config.get(CONF_EXPIRATION, 0),
|
||||
update_interval=config_entry.options.get(
|
||||
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL
|
||||
),
|
||||
)
|
||||
result = await controller.connect(
|
||||
wake_if_asleep=config_entry.options.get(
|
||||
CONF_WAKE_ON_START, DEFAULT_WAKE_ON_START
|
||||
)
|
||||
)
|
||||
refresh_token = result["refresh_token"]
|
||||
access_token = result["access_token"]
|
||||
except IncompleteCredentials as ex:
|
||||
await async_client.aclose()
|
||||
raise ConfigEntryAuthFailed from ex
|
||||
except httpx.ConnectTimeout as ex:
|
||||
await async_client.aclose()
|
||||
raise ConfigEntryNotReady from ex
|
||||
except TeslaException as ex:
|
||||
await async_client.aclose()
|
||||
if ex.code == HTTP_UNAUTHORIZED:
|
||||
raise ConfigEntryAuthFailed from ex
|
||||
if ex.message in [
|
||||
"VEHICLE_UNAVAILABLE",
|
||||
"TOO_MANY_REQUESTS",
|
||||
"SERVICE_MAINTENANCE",
|
||||
"UPSTREAM_TIMEOUT",
|
||||
]:
|
||||
raise ConfigEntryNotReady(
|
||||
f"Temporarily unable to communicate with Tesla API: {ex.message}"
|
||||
) from ex
|
||||
_LOGGER.error("Unable to communicate with Tesla API: %s", ex.message)
|
||||
return False
|
||||
|
||||
async def _async_close_client(*_):
|
||||
await async_client.aclose()
|
||||
|
||||
@callback
|
||||
def _async_create_close_task():
|
||||
asyncio.create_task(_async_close_client())
|
||||
|
||||
config_entry.async_on_unload(
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_CLOSE, _async_close_client)
|
||||
)
|
||||
config_entry.async_on_unload(_async_create_close_task)
|
||||
|
||||
_async_save_tokens(hass, config_entry, access_token, refresh_token)
|
||||
coordinator = TeslaDataUpdateCoordinator(
|
||||
hass, config_entry=config_entry, controller=controller
|
||||
)
|
||||
# Fetch initial data so we have data when entities subscribe
|
||||
entry_data = hass.data[DOMAIN][config_entry.entry_id] = {
|
||||
"coordinator": coordinator,
|
||||
"devices": defaultdict(list),
|
||||
DATA_LISTENER: [config_entry.add_update_listener(update_listener)],
|
||||
}
|
||||
_LOGGER.debug("Connected to the Tesla API")
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
all_devices = controller.get_homeassistant_components()
|
||||
|
||||
if not all_devices:
|
||||
return False
|
||||
|
||||
for device in all_devices:
|
||||
entry_data["devices"][device.hass_type].append(device)
|
||||
|
||||
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass, config_entry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
for listener in hass.data[DOMAIN][config_entry.entry_id][DATA_LISTENER]:
|
||||
listener()
|
||||
username = config_entry.title
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN].pop(config_entry.entry_id)
|
||||
_LOGGER.debug("Unloaded entry for %s", username)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
async def update_listener(hass, config_entry):
|
||||
"""Update when config_entry options update."""
|
||||
controller = hass.data[DOMAIN][config_entry.entry_id]["coordinator"].controller
|
||||
old_update_interval = controller.update_interval
|
||||
controller.update_interval = config_entry.options.get(CONF_SCAN_INTERVAL)
|
||||
if old_update_interval != controller.update_interval:
|
||||
_LOGGER.debug(
|
||||
"Changing scan_interval from %s to %s",
|
||||
old_update_interval,
|
||||
controller.update_interval,
|
||||
)
|
||||
|
||||
|
||||
class TeslaDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
"""Class to manage fetching Tesla data."""
|
||||
|
||||
def __init__(self, hass, *, config_entry, controller):
|
||||
"""Initialize global Tesla data updater."""
|
||||
self.controller = controller
|
||||
self.config_entry = config_entry
|
||||
|
||||
update_interval = timedelta(seconds=MIN_SCAN_INTERVAL)
|
||||
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=DOMAIN,
|
||||
update_interval=update_interval,
|
||||
)
|
||||
|
||||
async def _async_update_data(self):
|
||||
"""Fetch data from API endpoint."""
|
||||
if self.controller.is_token_refreshed():
|
||||
result = self.controller.get_tokens()
|
||||
refresh_token = result["refresh_token"]
|
||||
access_token = result["access_token"]
|
||||
_async_save_tokens(
|
||||
self.hass, self.config_entry, access_token, refresh_token
|
||||
)
|
||||
_LOGGER.debug("Saving new tokens in config_entry")
|
||||
|
||||
try:
|
||||
# Note: asyncio.TimeoutError and aiohttp.ClientError are already
|
||||
# handled by the data update coordinator.
|
||||
async with async_timeout.timeout(30):
|
||||
return await self.controller.update()
|
||||
except TeslaException as err:
|
||||
raise UpdateFailed(f"Error communicating with API: {err}") from err
|
||||
|
||||
|
||||
class TeslaDevice(CoordinatorEntity):
|
||||
"""Representation of a Tesla device."""
|
||||
|
||||
def __init__(self, tesla_device, coordinator):
|
||||
"""Initialise the Tesla device."""
|
||||
super().__init__(coordinator)
|
||||
self.tesla_device = tesla_device
|
||||
self._name = self.tesla_device.name
|
||||
self._unique_id = slugify(self.tesla_device.uniq_name)
|
||||
self._attributes = self.tesla_device.attrs.copy()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique ID."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon of the sensor."""
|
||||
if self.device_class:
|
||||
return None
|
||||
|
||||
return ICONS.get(self.tesla_device.type)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return the state attributes of the device."""
|
||||
attr = self._attributes
|
||||
if self.tesla_device.has_battery():
|
||||
attr[ATTR_BATTERY_LEVEL] = self.tesla_device.battery_level()
|
||||
attr[ATTR_BATTERY_CHARGING] = self.tesla_device.battery_charging()
|
||||
return attr
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return the device_info of the device."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.tesla_device.id())},
|
||||
"name": self.tesla_device.car_name(),
|
||||
"manufacturer": "Tesla",
|
||||
"model": self.tesla_device.car_type,
|
||||
"sw_version": self.tesla_device.car_version,
|
||||
}
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register state update callback."""
|
||||
self.async_on_remove(self.coordinator.async_add_listener(self.refresh))
|
||||
|
||||
@callback
|
||||
def refresh(self) -> None:
|
||||
"""Refresh the state of the device.
|
||||
|
||||
This assumes the coordinator has updated the controller.
|
||||
"""
|
||||
self.tesla_device.refresh()
|
||||
self._attributes = self.tesla_device.attrs.copy()
|
||||
self.async_write_ha_state()
|
@ -1,39 +0,0 @@
|
||||
"""Support for Tesla binary sensor."""
|
||||
|
||||
from homeassistant.components.binary_sensor import DEVICE_CLASSES, BinarySensorEntity
|
||||
|
||||
from . import DOMAIN as TESLA_DOMAIN, TeslaDevice
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Tesla binary_sensors by config_entry."""
|
||||
async_add_entities(
|
||||
[
|
||||
TeslaBinarySensor(
|
||||
device,
|
||||
hass.data[TESLA_DOMAIN][config_entry.entry_id]["coordinator"],
|
||||
)
|
||||
for device in hass.data[TESLA_DOMAIN][config_entry.entry_id]["devices"][
|
||||
"binary_sensor"
|
||||
]
|
||||
],
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
class TeslaBinarySensor(TeslaDevice, BinarySensorEntity):
|
||||
"""Implement an Tesla binary sensor for parking and charger."""
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the class of this binary sensor."""
|
||||
return (
|
||||
self.tesla_device.sensor_type
|
||||
if self.tesla_device.sensor_type in DEVICE_CLASSES
|
||||
else None
|
||||
)
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return the state of the binary sensor."""
|
||||
return self.tesla_device.get_value()
|
@ -1,120 +0,0 @@
|
||||
"""Support for Tesla HVAC system."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from teslajsonpy.exceptions import UnknownPresetMode
|
||||
|
||||
from homeassistant.components.climate import ClimateEntity
|
||||
from homeassistant.components.climate.const import (
|
||||
HVAC_MODE_HEAT_COOL,
|
||||
HVAC_MODE_OFF,
|
||||
SUPPORT_PRESET_MODE,
|
||||
SUPPORT_TARGET_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||
|
||||
from . import DOMAIN as TESLA_DOMAIN, TeslaDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SUPPORT_HVAC = [HVAC_MODE_HEAT_COOL, HVAC_MODE_OFF]
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Tesla binary_sensors by config_entry."""
|
||||
async_add_entities(
|
||||
[
|
||||
TeslaThermostat(
|
||||
device,
|
||||
hass.data[TESLA_DOMAIN][config_entry.entry_id]["coordinator"],
|
||||
)
|
||||
for device in hass.data[TESLA_DOMAIN][config_entry.entry_id]["devices"][
|
||||
"climate"
|
||||
]
|
||||
],
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
class TeslaThermostat(TeslaDevice, ClimateEntity):
|
||||
"""Representation of a Tesla climate."""
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Return the list of supported features."""
|
||||
return SUPPORT_TARGET_TEMPERATURE | SUPPORT_PRESET_MODE
|
||||
|
||||
@property
|
||||
def hvac_mode(self):
|
||||
"""Return hvac operation ie. heat, cool mode.
|
||||
|
||||
Need to be one of HVAC_MODE_*.
|
||||
"""
|
||||
if self.tesla_device.is_hvac_enabled():
|
||||
return HVAC_MODE_HEAT_COOL
|
||||
return HVAC_MODE_OFF
|
||||
|
||||
@property
|
||||
def hvac_modes(self):
|
||||
"""Return the list of available hvac operation modes.
|
||||
|
||||
Need to be a subset of HVAC_MODES.
|
||||
"""
|
||||
return SUPPORT_HVAC
|
||||
|
||||
@property
|
||||
def temperature_unit(self):
|
||||
"""Return the unit of measurement."""
|
||||
if self.tesla_device.measurement == "F":
|
||||
return TEMP_FAHRENHEIT
|
||||
return TEMP_CELSIUS
|
||||
|
||||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
return self.tesla_device.get_current_temp()
|
||||
|
||||
@property
|
||||
def target_temperature(self):
|
||||
"""Return the temperature we try to reach."""
|
||||
return self.tesla_device.get_goal_temp()
|
||||
|
||||
async def async_set_temperature(self, **kwargs):
|
||||
"""Set new target temperatures."""
|
||||
temperature = kwargs.get(ATTR_TEMPERATURE)
|
||||
if temperature:
|
||||
_LOGGER.debug("%s: Setting temperature to %s", self.name, temperature)
|
||||
await self.tesla_device.set_temperature(temperature)
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode):
|
||||
"""Set new target hvac mode."""
|
||||
_LOGGER.debug("%s: Setting hvac mode to %s", self.name, hvac_mode)
|
||||
if hvac_mode == HVAC_MODE_OFF:
|
||||
await self.tesla_device.set_status(False)
|
||||
elif hvac_mode == HVAC_MODE_HEAT_COOL:
|
||||
await self.tesla_device.set_status(True)
|
||||
|
||||
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
||||
"""Set new preset mode."""
|
||||
_LOGGER.debug("%s: Setting preset_mode to: %s", self.name, preset_mode)
|
||||
try:
|
||||
await self.tesla_device.set_preset_mode(preset_mode)
|
||||
except UnknownPresetMode as ex:
|
||||
_LOGGER.error("%s", ex.message)
|
||||
|
||||
@property
|
||||
def preset_mode(self) -> str | None:
|
||||
"""Return the current preset mode, e.g., home, away, temp.
|
||||
|
||||
Requires SUPPORT_PRESET_MODE.
|
||||
"""
|
||||
return self.tesla_device.preset_mode
|
||||
|
||||
@property
|
||||
def preset_modes(self) -> list[str] | None:
|
||||
"""Return a list of available preset modes.
|
||||
|
||||
Requires SUPPORT_PRESET_MODE.
|
||||
"""
|
||||
return self.tesla_device.preset_modes
|
@ -1,191 +0,0 @@
|
||||
"""Tesla Config Flow."""
|
||||
import logging
|
||||
|
||||
import httpx
|
||||
from teslajsonpy import Controller as TeslaAPI, TeslaException
|
||||
from teslajsonpy.exceptions import IncompleteCredentials
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries, core, exceptions
|
||||
from homeassistant.const import (
|
||||
CONF_ACCESS_TOKEN,
|
||||
CONF_PASSWORD,
|
||||
CONF_SCAN_INTERVAL,
|
||||
CONF_TOKEN,
|
||||
CONF_USERNAME,
|
||||
HTTP_UNAUTHORIZED,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.httpx_client import SERVER_SOFTWARE, USER_AGENT
|
||||
|
||||
from .const import (
|
||||
CONF_EXPIRATION,
|
||||
CONF_MFA,
|
||||
CONF_WAKE_ON_START,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DEFAULT_WAKE_ON_START,
|
||||
DOMAIN,
|
||||
MIN_SCAN_INTERVAL,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TeslaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Tesla."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the tesla flow."""
|
||||
self.username = None
|
||||
self.reauth = False
|
||||
|
||||
async def async_step_import(self, import_config):
|
||||
"""Import a config entry from configuration.yaml."""
|
||||
return await self.async_step_user(import_config)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle the start of the config flow."""
|
||||
errors = {}
|
||||
|
||||
if user_input is not None:
|
||||
existing_entry = self._async_entry_for_username(user_input[CONF_USERNAME])
|
||||
if existing_entry and not self.reauth:
|
||||
return self.async_abort(reason="already_configured")
|
||||
|
||||
try:
|
||||
info = await validate_input(self.hass, user_input)
|
||||
except CannotConnect:
|
||||
errors["base"] = "cannot_connect"
|
||||
except InvalidAuth:
|
||||
errors["base"] = "invalid_auth"
|
||||
|
||||
if not errors:
|
||||
if existing_entry:
|
||||
self.hass.config_entries.async_update_entry(
|
||||
existing_entry, data=info
|
||||
)
|
||||
await self.hass.config_entries.async_reload(existing_entry.entry_id)
|
||||
return self.async_abort(reason="reauth_successful")
|
||||
|
||||
return self.async_create_entry(
|
||||
title=user_input[CONF_USERNAME], data=info
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=self._async_schema(),
|
||||
errors=errors,
|
||||
description_placeholders={},
|
||||
)
|
||||
|
||||
async def async_step_reauth(self, data):
|
||||
"""Handle configuration by re-auth."""
|
||||
self.username = data[CONF_USERNAME]
|
||||
self.reauth = True
|
||||
return await self.async_step_user()
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry):
|
||||
"""Get the options flow for this handler."""
|
||||
return OptionsFlowHandler(config_entry)
|
||||
|
||||
@callback
|
||||
def _async_schema(self):
|
||||
"""Fetch schema with defaults."""
|
||||
return vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_USERNAME, default=self.username): str,
|
||||
vol.Required(CONF_PASSWORD): str,
|
||||
vol.Optional(CONF_MFA): str,
|
||||
}
|
||||
)
|
||||
|
||||
@callback
|
||||
def _async_entry_for_username(self, username):
|
||||
"""Find an existing entry for a username."""
|
||||
for entry in self._async_current_entries():
|
||||
if entry.data.get(CONF_USERNAME) == username:
|
||||
return entry
|
||||
return None
|
||||
|
||||
|
||||
class OptionsFlowHandler(config_entries.OptionsFlow):
|
||||
"""Handle a option flow for Tesla."""
|
||||
|
||||
def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
|
||||
"""Initialize options flow."""
|
||||
self.config_entry = config_entry
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
"""Handle options flow."""
|
||||
if user_input is not None:
|
||||
return self.async_create_entry(title="", data=user_input)
|
||||
|
||||
data_schema = vol.Schema(
|
||||
{
|
||||
vol.Optional(
|
||||
CONF_SCAN_INTERVAL,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL
|
||||
),
|
||||
): vol.All(cv.positive_int, vol.Clamp(min=MIN_SCAN_INTERVAL)),
|
||||
vol.Optional(
|
||||
CONF_WAKE_ON_START,
|
||||
default=self.config_entry.options.get(
|
||||
CONF_WAKE_ON_START, DEFAULT_WAKE_ON_START
|
||||
),
|
||||
): bool,
|
||||
}
|
||||
)
|
||||
return self.async_show_form(step_id="init", data_schema=data_schema)
|
||||
|
||||
|
||||
async def validate_input(hass: core.HomeAssistant, data):
|
||||
"""Validate the user input allows us to connect.
|
||||
|
||||
Data has the keys from DATA_SCHEMA with values provided by the user.
|
||||
"""
|
||||
|
||||
config = {}
|
||||
async_client = httpx.AsyncClient(headers={USER_AGENT: SERVER_SOFTWARE}, timeout=60)
|
||||
|
||||
try:
|
||||
controller = TeslaAPI(
|
||||
async_client,
|
||||
email=data[CONF_USERNAME],
|
||||
password=data[CONF_PASSWORD],
|
||||
update_interval=DEFAULT_SCAN_INTERVAL,
|
||||
)
|
||||
result = await controller.connect(
|
||||
test_login=True, mfa_code=(data[CONF_MFA] if CONF_MFA in data else "")
|
||||
)
|
||||
config[CONF_TOKEN] = result["refresh_token"]
|
||||
config[CONF_ACCESS_TOKEN] = result["access_token"]
|
||||
config[CONF_EXPIRATION] = result[CONF_EXPIRATION]
|
||||
config[CONF_USERNAME] = data[CONF_USERNAME]
|
||||
config[CONF_PASSWORD] = data[CONF_PASSWORD]
|
||||
except IncompleteCredentials as ex:
|
||||
_LOGGER.error("Authentication error: %s %s", ex.message, ex)
|
||||
raise InvalidAuth() from ex
|
||||
except TeslaException as ex:
|
||||
if ex.code == HTTP_UNAUTHORIZED:
|
||||
_LOGGER.error("Invalid credentials: %s", ex)
|
||||
raise InvalidAuth() from ex
|
||||
_LOGGER.error("Unable to communicate with Tesla API: %s", ex.message)
|
||||
raise CannotConnect() from ex
|
||||
finally:
|
||||
await async_client.aclose()
|
||||
_LOGGER.debug("Credentials successfully connected to the Tesla API")
|
||||
return config
|
||||
|
||||
|
||||
class CannotConnect(exceptions.HomeAssistantError):
|
||||
"""Error to indicate we cannot connect."""
|
||||
|
||||
|
||||
class InvalidAuth(exceptions.HomeAssistantError):
|
||||
"""Error to indicate there is invalid auth."""
|
@ -1,33 +0,0 @@
|
||||
"""Const file for Tesla cars."""
|
||||
CONF_EXPIRATION = "expiration"
|
||||
CONF_WAKE_ON_START = "enable_wake_on_start"
|
||||
CONF_MFA = "mfa"
|
||||
DOMAIN = "tesla"
|
||||
DATA_LISTENER = "listener"
|
||||
DEFAULT_SCAN_INTERVAL = 660
|
||||
DEFAULT_WAKE_ON_START = False
|
||||
MIN_SCAN_INTERVAL = 60
|
||||
|
||||
PLATFORMS = [
|
||||
"sensor",
|
||||
"lock",
|
||||
"climate",
|
||||
"binary_sensor",
|
||||
"device_tracker",
|
||||
"switch",
|
||||
]
|
||||
|
||||
ICONS = {
|
||||
"battery sensor": "mdi:battery",
|
||||
"range sensor": "mdi:gauge",
|
||||
"mileage sensor": "mdi:counter",
|
||||
"parking brake sensor": "mdi:car-brake-parking",
|
||||
"charger sensor": "mdi:ev-station",
|
||||
"charger switch": "mdi:battery-charging",
|
||||
"update switch": "mdi:update",
|
||||
"maxrange switch": "mdi:gauge-full",
|
||||
"temperature sensor": "mdi:thermometer",
|
||||
"location tracker": "mdi:crosshairs-gps",
|
||||
"charging rate sensor": "mdi:speedometer",
|
||||
"sentry mode switch": "mdi:shield-car",
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
"""Support for tracking Tesla cars."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.device_tracker import SOURCE_TYPE_GPS
|
||||
from homeassistant.components.device_tracker.config_entry import TrackerEntity
|
||||
|
||||
from . import DOMAIN as TESLA_DOMAIN, TeslaDevice
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Tesla binary_sensors by config_entry."""
|
||||
entities = [
|
||||
TeslaDeviceEntity(
|
||||
device,
|
||||
hass.data[TESLA_DOMAIN][config_entry.entry_id]["coordinator"],
|
||||
)
|
||||
for device in hass.data[TESLA_DOMAIN][config_entry.entry_id]["devices"][
|
||||
"devices_tracker"
|
||||
]
|
||||
]
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
||||
class TeslaDeviceEntity(TeslaDevice, TrackerEntity):
|
||||
"""A class representing a Tesla device."""
|
||||
|
||||
@property
|
||||
def latitude(self) -> float | None:
|
||||
"""Return latitude value of the device."""
|
||||
location = self.tesla_device.get_location()
|
||||
return self.tesla_device.get_location().get("latitude") if location else None
|
||||
|
||||
@property
|
||||
def longitude(self) -> float | None:
|
||||
"""Return longitude value of the device."""
|
||||
location = self.tesla_device.get_location()
|
||||
return self.tesla_device.get_location().get("longitude") if location else None
|
||||
|
||||
@property
|
||||
def source_type(self):
|
||||
"""Return the source type, eg gps or router, of the device."""
|
||||
return SOURCE_TYPE_GPS
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return the state attributes of the device."""
|
||||
attr = super().extra_state_attributes.copy()
|
||||
location = self.tesla_device.get_location()
|
||||
if location:
|
||||
attr.update(
|
||||
{
|
||||
"trackr_id": self.unique_id,
|
||||
"heading": location["heading"],
|
||||
"speed": location["speed"],
|
||||
}
|
||||
)
|
||||
return attr
|
@ -1,41 +0,0 @@
|
||||
"""Support for Tesla door locks."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.lock import LockEntity
|
||||
|
||||
from . import DOMAIN as TESLA_DOMAIN, TeslaDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Tesla binary_sensors by config_entry."""
|
||||
entities = [
|
||||
TeslaLock(
|
||||
device,
|
||||
hass.data[TESLA_DOMAIN][config_entry.entry_id]["coordinator"],
|
||||
)
|
||||
for device in hass.data[TESLA_DOMAIN][config_entry.entry_id]["devices"]["lock"]
|
||||
]
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
||||
class TeslaLock(TeslaDevice, LockEntity):
|
||||
"""Representation of a Tesla door lock."""
|
||||
|
||||
async def async_lock(self, **kwargs):
|
||||
"""Send the lock command."""
|
||||
_LOGGER.debug("Locking doors for: %s", self.name)
|
||||
await self.tesla_device.lock()
|
||||
|
||||
async def async_unlock(self, **kwargs):
|
||||
"""Send the unlock command."""
|
||||
_LOGGER.debug("Unlocking doors for: %s", self.name)
|
||||
await self.tesla_device.unlock()
|
||||
|
||||
@property
|
||||
def is_locked(self):
|
||||
"""Get whether the lock is in locked state."""
|
||||
if self.tesla_device.is_locked() is None:
|
||||
return None
|
||||
return self.tesla_device.is_locked()
|
@ -1,23 +0,0 @@
|
||||
{
|
||||
"domain": "tesla",
|
||||
"name": "Tesla",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/tesla",
|
||||
"requirements": ["teslajsonpy==0.18.3"],
|
||||
"codeowners": ["@zabuldon", "@alandtse"],
|
||||
"dhcp": [
|
||||
{
|
||||
"hostname": "tesla_*",
|
||||
"macaddress": "4CFCAA*"
|
||||
},
|
||||
{
|
||||
"hostname": "tesla_*",
|
||||
"macaddress": "044EAF*"
|
||||
},
|
||||
{
|
||||
"hostname": "tesla_*",
|
||||
"macaddress": "98ED5C*"
|
||||
}
|
||||
],
|
||||
"iot_class": "cloud_polling"
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
"""Support for the Tesla sensors."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import DEVICE_CLASSES, SensorEntity
|
||||
from homeassistant.const import (
|
||||
LENGTH_KILOMETERS,
|
||||
LENGTH_MILES,
|
||||
TEMP_CELSIUS,
|
||||
TEMP_FAHRENHEIT,
|
||||
)
|
||||
from homeassistant.util.distance import convert
|
||||
|
||||
from . import DOMAIN as TESLA_DOMAIN, TeslaDevice
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Tesla binary_sensors by config_entry."""
|
||||
coordinator = hass.data[TESLA_DOMAIN][config_entry.entry_id]["coordinator"]
|
||||
entities = []
|
||||
for device in hass.data[TESLA_DOMAIN][config_entry.entry_id]["devices"]["sensor"]:
|
||||
if device.type == "temperature sensor":
|
||||
entities.append(TeslaSensor(device, coordinator, "inside"))
|
||||
entities.append(TeslaSensor(device, coordinator, "outside"))
|
||||
else:
|
||||
entities.append(TeslaSensor(device, coordinator))
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
||||
class TeslaSensor(TeslaDevice, SensorEntity):
|
||||
"""Representation of Tesla sensors."""
|
||||
|
||||
def __init__(self, tesla_device, coordinator, sensor_type=None):
|
||||
"""Initialize of the sensor."""
|
||||
super().__init__(tesla_device, coordinator)
|
||||
self.type = sensor_type
|
||||
if self.type:
|
||||
self._name = f"{super().name} ({self.type})"
|
||||
self._unique_id = f"{super().unique_id}_{self.type}"
|
||||
|
||||
@property
|
||||
def native_value(self) -> float | None:
|
||||
"""Return the state of the sensor."""
|
||||
if self.tesla_device.type == "temperature sensor":
|
||||
if self.type == "outside":
|
||||
return self.tesla_device.get_outside_temp()
|
||||
return self.tesla_device.get_inside_temp()
|
||||
if self.tesla_device.type in ("range sensor", "mileage sensor"):
|
||||
units = self.tesla_device.measurement
|
||||
if units == "LENGTH_MILES":
|
||||
return self.tesla_device.get_value()
|
||||
return round(
|
||||
convert(self.tesla_device.get_value(), LENGTH_MILES, LENGTH_KILOMETERS),
|
||||
2,
|
||||
)
|
||||
if self.tesla_device.type == "charging rate sensor":
|
||||
return self.tesla_device.charging_rate
|
||||
return self.tesla_device.get_value()
|
||||
|
||||
@property
|
||||
def native_unit_of_measurement(self) -> str | None:
|
||||
"""Return the unit_of_measurement of the device."""
|
||||
units = self.tesla_device.measurement
|
||||
if units == "F":
|
||||
return TEMP_FAHRENHEIT
|
||||
if units == "C":
|
||||
return TEMP_CELSIUS
|
||||
if units == "LENGTH_MILES":
|
||||
return LENGTH_MILES
|
||||
if units == "LENGTH_KILOMETERS":
|
||||
return LENGTH_KILOMETERS
|
||||
return units
|
||||
|
||||
@property
|
||||
def device_class(self) -> str | None:
|
||||
"""Return the device_class of the device."""
|
||||
return (
|
||||
self.tesla_device.device_class
|
||||
if self.tesla_device.device_class in DEVICE_CLASSES
|
||||
else None
|
||||
)
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self):
|
||||
"""Return the state attributes of the device."""
|
||||
attr = self._attributes.copy()
|
||||
if self.tesla_device.type == "charging rate sensor":
|
||||
attr.update(
|
||||
{
|
||||
"time_left": self.tesla_device.time_left,
|
||||
"added_range": self.tesla_device.added_range,
|
||||
"charge_energy_added": self.tesla_device.charge_energy_added,
|
||||
"charge_current_request": self.tesla_device.charge_current_request,
|
||||
"charger_actual_current": self.tesla_device.charger_actual_current,
|
||||
"charger_voltage": self.tesla_device.charger_voltage,
|
||||
}
|
||||
)
|
||||
return attr
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
|
||||
},
|
||||
"abort": {
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"username": "[%key:common::config_flow::data::email%]",
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"mfa": "MFA Code (optional)"
|
||||
},
|
||||
"description": "Please enter your information.",
|
||||
"title": "Tesla - Configuration"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"scan_interval": "Seconds between scans",
|
||||
"enable_wake_on_start": "Force cars awake on startup"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
"""Support for Tesla charger switches."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
|
||||
from . import DOMAIN as TESLA_DOMAIN, TeslaDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Tesla binary_sensors by config_entry."""
|
||||
coordinator = hass.data[TESLA_DOMAIN][config_entry.entry_id]["coordinator"]
|
||||
entities = []
|
||||
for device in hass.data[TESLA_DOMAIN][config_entry.entry_id]["devices"]["switch"]:
|
||||
if device.type == "charger switch":
|
||||
entities.append(ChargerSwitch(device, coordinator))
|
||||
entities.append(UpdateSwitch(device, coordinator))
|
||||
elif device.type == "maxrange switch":
|
||||
entities.append(RangeSwitch(device, coordinator))
|
||||
elif device.type == "sentry mode switch":
|
||||
entities.append(SentryModeSwitch(device, coordinator))
|
||||
async_add_entities(entities, True)
|
||||
|
||||
|
||||
class ChargerSwitch(TeslaDevice, SwitchEntity):
|
||||
"""Representation of a Tesla charger switch."""
|
||||
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Send the on command."""
|
||||
_LOGGER.debug("Enable charging: %s", self.name)
|
||||
await self.tesla_device.start_charge()
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Send the off command."""
|
||||
_LOGGER.debug("Disable charging for: %s", self.name)
|
||||
await self.tesla_device.stop_charge()
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Get whether the switch is in on state."""
|
||||
if self.tesla_device.is_charging() is None:
|
||||
return None
|
||||
return self.tesla_device.is_charging()
|
||||
|
||||
|
||||
class RangeSwitch(TeslaDevice, SwitchEntity):
|
||||
"""Representation of a Tesla max range charging switch."""
|
||||
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Send the on command."""
|
||||
_LOGGER.debug("Enable max range charging: %s", self.name)
|
||||
await self.tesla_device.set_max()
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Send the off command."""
|
||||
_LOGGER.debug("Disable max range charging: %s", self.name)
|
||||
await self.tesla_device.set_standard()
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Get whether the switch is in on state."""
|
||||
if self.tesla_device.is_maxrange() is None:
|
||||
return None
|
||||
return bool(self.tesla_device.is_maxrange())
|
||||
|
||||
|
||||
class UpdateSwitch(TeslaDevice, SwitchEntity):
|
||||
"""Representation of a Tesla update switch."""
|
||||
|
||||
def __init__(self, tesla_device, coordinator):
|
||||
"""Initialise the switch."""
|
||||
super().__init__(tesla_device, coordinator)
|
||||
self.controller = coordinator.controller
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return super().name.replace("charger", "update")
|
||||
|
||||
@property
|
||||
def unique_id(self) -> str:
|
||||
"""Return a unique ID."""
|
||||
return super().unique_id.replace("charger", "update")
|
||||
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Send the on command."""
|
||||
_LOGGER.debug("Enable updates: %s %s", self.name, self.tesla_device.id())
|
||||
self.controller.set_updates(self.tesla_device.id(), True)
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Send the off command."""
|
||||
_LOGGER.debug("Disable updates: %s %s", self.name, self.tesla_device.id())
|
||||
self.controller.set_updates(self.tesla_device.id(), False)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Get whether the switch is in on state."""
|
||||
if self.controller.get_updates(self.tesla_device.id()) is None:
|
||||
return None
|
||||
return bool(self.controller.get_updates(self.tesla_device.id()))
|
||||
|
||||
|
||||
class SentryModeSwitch(TeslaDevice, SwitchEntity):
|
||||
"""Representation of a Tesla sentry mode switch."""
|
||||
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Send the on command."""
|
||||
_LOGGER.debug("Enable sentry mode: %s", self.name)
|
||||
await self.tesla_device.enable_sentry_mode()
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Send the off command."""
|
||||
_LOGGER.debug("Disable sentry mode: %s", self.name)
|
||||
await self.tesla_device.disable_sentry_mode()
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Get whether the switch is in on state."""
|
||||
if self.tesla_device.is_on() is None:
|
||||
return None
|
||||
return self.tesla_device.is_on()
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "El compte ja ha estat configurat",
|
||||
"reauth_successful": "Re-autenticaci\u00f3 realitzada correctament"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "El compte ja ha estat configurat",
|
||||
"cannot_connect": "Ha fallat la connexi\u00f3",
|
||||
"invalid_auth": "Autenticaci\u00f3 inv\u00e0lida"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "Codi MFA (opcional)",
|
||||
"password": "Contrasenya",
|
||||
"username": "Correu electr\u00f2nic"
|
||||
},
|
||||
"description": "Introdueix la teva informaci\u00f3.",
|
||||
"title": "Configuraci\u00f3 de Tesla"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "For\u00e7a el despertar del cotxe en la posada en marxa",
|
||||
"scan_interval": "Segons entre escanejos"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u00da\u010det je ji\u017e nastaven",
|
||||
"reauth_successful": "Op\u011btovn\u00e9 ov\u011b\u0159en\u00ed bylo \u00fasp\u011b\u0161n\u00e9"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "\u00da\u010det je ji\u017e nastaven",
|
||||
"cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit",
|
||||
"invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Heslo",
|
||||
"username": "E-mail"
|
||||
},
|
||||
"description": "Zadejte sv\u00e9 \u00fadaje.",
|
||||
"title": "Tesla - Nastaven\u00ed"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"scan_interval": "Po\u010det sekund mezi sledov\u00e1n\u00edm"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Adgangskode",
|
||||
"username": "Email-adresse"
|
||||
},
|
||||
"description": "Indtast dine oplysninger.",
|
||||
"title": "Tesla - Konfiguration"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"scan_interval": "Sekunder mellem scanninger"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Konto wurde bereits konfiguriert",
|
||||
"reauth_successful": "Die erneute Authentifizierung war erfolgreich"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "Konto wurde bereits konfiguriert",
|
||||
"cannot_connect": "Verbindung fehlgeschlagen",
|
||||
"invalid_auth": "Ung\u00fcltige Authentifizierung"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "MFA-Code (optional)",
|
||||
"password": "Passwort",
|
||||
"username": "E-Mail"
|
||||
},
|
||||
"description": "Bitte gib deine Daten ein.",
|
||||
"title": "Tesla - Konfiguration"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Aufwachen des Autos beim Start erzwingen",
|
||||
"scan_interval": "Sekunden zwischen den Scans"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Account is already configured",
|
||||
"reauth_successful": "Re-authentication was successful"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "Account is already configured",
|
||||
"cannot_connect": "Failed to connect",
|
||||
"invalid_auth": "Invalid authentication"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "MFA Code (optional)",
|
||||
"password": "Password",
|
||||
"username": "Email"
|
||||
},
|
||||
"description": "Please enter your information.",
|
||||
"title": "Tesla - Configuration"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Force cars awake on startup",
|
||||
"scan_interval": "Seconds between scans"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Contrase\u00f1a",
|
||||
"username": "Direcci\u00f3n de correo electr\u00f3nico"
|
||||
},
|
||||
"description": "Por favor ingrese su informaci\u00f3n.",
|
||||
"title": "Tesla - Configuraci\u00f3n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Forzar a autom\u00f3viles despertar al inicio",
|
||||
"scan_interval": "Segundos entre escaneos"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "La cuenta ya ha sido configurada",
|
||||
"reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "La cuenta ya ha sido configurada",
|
||||
"cannot_connect": "No se pudo conectar",
|
||||
"invalid_auth": "Autenticaci\u00f3n no v\u00e1lida"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "C\u00f3digo MFA (opcional)",
|
||||
"password": "Contrase\u00f1a",
|
||||
"username": "Correo electr\u00f3nico"
|
||||
},
|
||||
"description": "Por favor, introduzca su informaci\u00f3n.",
|
||||
"title": "Tesla - Configuraci\u00f3n"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Forzar autom\u00f3viles despiertos al inicio",
|
||||
"scan_interval": "Segundos entre escaneos"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Kasutaja on juba seadistatud",
|
||||
"reauth_successful": "Taastuvastamine \u00f5nnestus"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "Konto on juba h\u00e4\u00e4lestatud",
|
||||
"cannot_connect": "\u00dchendamine nurjus",
|
||||
"invalid_auth": "Tuvastamise viga"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "MFA kood (valikuline)",
|
||||
"password": "Salas\u00f5na",
|
||||
"username": "E-post"
|
||||
},
|
||||
"description": "Palun sisesta oma andmed.",
|
||||
"title": "Tesla - seadistamine"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Sunni autod k\u00e4ivitamisel \u00e4rkama (?)",
|
||||
"scan_interval": "P\u00e4ringute vahe sekundites"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "MFA-koodi (valinnainen)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9",
|
||||
"reauth_successful": "La r\u00e9-authentification a r\u00e9ussi"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "Le compte est d\u00e9j\u00e0 configur\u00e9",
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification invalide"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "Code MFA (facultatif)",
|
||||
"password": "Mot de passe",
|
||||
"username": "Email"
|
||||
},
|
||||
"description": "Veuillez saisir vos informations.",
|
||||
"title": "Tesla - Configuration"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Forcer les voitures \u00e0 se r\u00e9veiller au d\u00e9marrage",
|
||||
"scan_interval": "Secondes entre les scans"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4",
|
||||
"reauth_successful": "\u05d4\u05d0\u05d9\u05de\u05d5\u05ea \u05de\u05d7\u05d3\u05e9 \u05d4\u05e6\u05dc\u05d9\u05d7"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d7\u05e9\u05d1\u05d5\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4",
|
||||
"cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4",
|
||||
"invalid_auth": "\u05d0\u05d9\u05de\u05d5\u05ea \u05dc\u05d0 \u05d7\u05d5\u05e7\u05d9"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "\u05e1\u05d9\u05e1\u05de\u05d4",
|
||||
"username": "\u05d3\u05d5\u05d0\"\u05dc"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van",
|
||||
"reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "A fi\u00f3k m\u00e1r konfigur\u00e1lva van",
|
||||
"cannot_connect": "Sikertelen csatlakoz\u00e1s",
|
||||
"invalid_auth": "\u00c9rv\u00e9nytelen hiteles\u00edt\u00e9s"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "MFA k\u00f3d (opcion\u00e1lis)",
|
||||
"password": "Jelsz\u00f3",
|
||||
"username": "E-mail"
|
||||
},
|
||||
"description": "K\u00e9rlek, add meg az adataidat.",
|
||||
"title": "Tesla - Konfigur\u00e1ci\u00f3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Az aut\u00f3k \u00e9bred\u00e9sre k\u00e9nyszer\u00edt\u00e9se ind\u00edt\u00e1skor",
|
||||
"scan_interval": "Szkennel\u00e9sek k\u00f6z\u00f6tti m\u00e1sodpercek"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Akun sudah dikonfigurasi",
|
||||
"reauth_successful": "Autentikasi ulang berhasil"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "Akun sudah dikonfigurasi",
|
||||
"cannot_connect": "Gagal terhubung",
|
||||
"invalid_auth": "Autentikasi tidak valid"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Kata Sandi",
|
||||
"username": "Email"
|
||||
},
|
||||
"description": "Masukkan informasi Anda.",
|
||||
"title": "Tesla - Konfigurasi"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Paksa mobil bangun saat dinyalakan",
|
||||
"scan_interval": "Interval pemindaian dalam detik"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "L'account \u00e8 gi\u00e0 configurato",
|
||||
"reauth_successful": "La nuova autenticazione \u00e8 stata eseguita correttamente"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "L'account \u00e8 gi\u00e0 configurato",
|
||||
"cannot_connect": "Impossibile connettersi",
|
||||
"invalid_auth": "Autenticazione non valida"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "Codice autenticazione a pi\u00f9 fattori MFA (facoltativo)",
|
||||
"password": "Password",
|
||||
"username": "E-mail"
|
||||
},
|
||||
"description": "Si prega di inserire le tue informazioni.",
|
||||
"title": "Tesla - Configurazione"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Forza il risveglio delle auto all'avvio",
|
||||
"scan_interval": "Secondi tra le scansioni"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"already_configured": "\u10d0\u10dc\u10d2\u10d0\u10e0\u10d8\u10e8\u10d8 \u10e3\u10d9\u10d5\u10d4 \u10d9\u10dd\u10dc\u10e4\u10d8\u10d2\u10e3\u10e0\u10d8\u10e0\u10d4\u10d1\u10e3\u10da\u10d8\u10d0"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4",
|
||||
"reauth_successful": "\uc7ac\uc778\uc99d\uc5d0 \uc131\uacf5\ud588\uc2b5\ub2c8\ub2e4"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "\uacc4\uc815\uc774 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4",
|
||||
"cannot_connect": "\uc5f0\uacb0\ud558\uc9c0 \ubabb\ud588\uc2b5\ub2c8\ub2e4",
|
||||
"invalid_auth": "\uc778\uc99d\uc774 \uc798\ubabb\ub418\uc5c8\uc2b5\ub2c8\ub2e4"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "\ube44\ubc00\ubc88\ud638",
|
||||
"username": "\uc774\uba54\uc77c"
|
||||
},
|
||||
"description": "\uc0ac\uc6a9\uc790 \uc815\ubcf4\ub97c \uc785\ub825\ud574\uc8fc\uc138\uc694",
|
||||
"title": "Tesla - \uad6c\uc131"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "\uc2dc\ub3d9 \uc2dc \ucc28\ub7c9 \uae68\uc6b0\uae30",
|
||||
"scan_interval": "\uc2a4\uce94 \uac04\uaca9 (\ucd08)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"already_configured": "Kont ass scho konfigur\u00e9iert",
|
||||
"cannot_connect": "Feeler beim verbannen",
|
||||
"invalid_auth": "Ong\u00eblteg Authentifikatioun"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Passwuert",
|
||||
"username": "E-Mail"
|
||||
},
|
||||
"description": "F\u00ebllt \u00e4r Informatiounen aus.",
|
||||
"title": "Tesla - Konfiguratioun"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Forc\u00e9ier d'Erw\u00e4chen vun den Autoen beim starten",
|
||||
"scan_interval": "Sekonnen t\u00ebscht Scannen"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Parole",
|
||||
"username": "E-pasta adrese"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Account is al geconfigureerd",
|
||||
"reauth_successful": "Herauthenticatie was succesvol"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "Account is al geconfigureerd",
|
||||
"cannot_connect": "Kan geen verbinding maken",
|
||||
"invalid_auth": "Ongeldige authenticatie"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "MFA Code (optioneel)",
|
||||
"password": "Wachtwoord",
|
||||
"username": "E-mail"
|
||||
},
|
||||
"description": "Vul alstublieft uw gegevens in.",
|
||||
"title": "Tesla - Configuratie"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Forceer auto's wakker bij het opstarten",
|
||||
"scan_interval": "Seconden tussen scans"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Kontoen er allerede konfigurert",
|
||||
"reauth_successful": "Godkjenning p\u00e5 nytt var vellykket"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "Kontoen er allerede konfigurert",
|
||||
"cannot_connect": "Tilkobling mislyktes",
|
||||
"invalid_auth": "Ugyldig godkjenning"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "MFA -kode (valgfritt)",
|
||||
"password": "Passord",
|
||||
"username": "E-post"
|
||||
},
|
||||
"description": "Vennligst fyll inn din informasjonen.",
|
||||
"title": "Tesla - Konfigurasjon"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Tving biler til \u00e5 v\u00e5kne ved oppstart",
|
||||
"scan_interval": "Sekunder mellom skanninger"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Konto jest ju\u017c skonfigurowane",
|
||||
"reauth_successful": "Ponowne uwierzytelnienie powiod\u0142o si\u0119"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "Konto jest ju\u017c skonfigurowane",
|
||||
"cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia",
|
||||
"invalid_auth": "Niepoprawne uwierzytelnienie"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "Kod uwierzytelniania wielosk\u0142adnikowego (opcjonalnie)",
|
||||
"password": "Has\u0142o",
|
||||
"username": "Adres e-mail"
|
||||
},
|
||||
"description": "Wprowad\u017a dane",
|
||||
"title": "Tesla \u2014 konfiguracja"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Wymu\u015b wybudzenie samochod\u00f3w podczas uruchamiania",
|
||||
"scan_interval": "Cz\u0119stotliwo\u015b\u0107 aktualizacji"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Senha",
|
||||
"username": "Endere\u00e7o de e-mail"
|
||||
},
|
||||
"description": "Por favor, insira suas informa\u00e7\u00f5es.",
|
||||
"title": "Tesla - Configura\u00e7\u00e3o"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "For\u00e7ar carros a acordar na inicializa\u00e7\u00e3o"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"already_configured": "Conta j\u00e1 configurada",
|
||||
"cannot_connect": "Falha na liga\u00e7\u00e3o",
|
||||
"invalid_auth": "Autentica\u00e7\u00e3o inv\u00e1lida"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Palavra-passe",
|
||||
"username": "Endere\u00e7o de e-mail"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.",
|
||||
"reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430 \u0443\u0441\u043f\u0435\u0448\u043d\u043e."
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "\u042d\u0442\u0430 \u0443\u0447\u0451\u0442\u043d\u0430\u044f \u0437\u0430\u043f\u0438\u0441\u044c \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0432 Home Assistant.",
|
||||
"cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f.",
|
||||
"invalid_auth": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "\u041a\u043e\u0434 MFA (\u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e)",
|
||||
"password": "\u041f\u0430\u0440\u043e\u043b\u044c",
|
||||
"username": "\u0410\u0434\u0440\u0435\u0441 \u044d\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0439 \u043f\u043e\u0447\u0442\u044b"
|
||||
},
|
||||
"description": "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0443\u0447\u0451\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438.",
|
||||
"title": "Tesla"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "\u041f\u0440\u0438\u043d\u0443\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0430\u0437\u0431\u0443\u0434\u0438\u0442\u044c \u043c\u0430\u0448\u0438\u043d\u0443 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0435",
|
||||
"scan_interval": "\u0418\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043c\u0435\u0436\u0434\u0443 \u0441\u043a\u0430\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\u043c\u0438 (\u0441\u0435\u043a.)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Geslo",
|
||||
"username": "E-po\u0161tni naslov"
|
||||
},
|
||||
"description": "Prosimo, vnesite svoje podatke.",
|
||||
"title": "Tesla - konfiguracija"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "Vsili zbujanje avtomobila ob zagonu",
|
||||
"scan_interval": "Sekund med skeniranjem"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "L\u00f6senord",
|
||||
"username": "E-postadress"
|
||||
},
|
||||
"description": "V\u00e4nligen ange din information.",
|
||||
"title": "Tesla - Konfiguration"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"scan_interval": "Sekunder mellan skanningar"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"already_configured": "Hesap zaten yap\u0131land\u0131r\u0131lm\u0131\u015f",
|
||||
"cannot_connect": "Ba\u011flanma hatas\u0131",
|
||||
"invalid_auth": "Ge\u00e7ersiz kimlik do\u011frulama"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "Parola",
|
||||
"username": "E-posta"
|
||||
},
|
||||
"description": "L\u00fctfen bilgilerinizi giriniz."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"already_configured": "\u0426\u0435\u0439 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u0438\u0439 \u0437\u0430\u043f\u0438\u0441 \u0432\u0436\u0435 \u0434\u043e\u0434\u0430\u043d\u043e \u0432 Home Assistant.",
|
||||
"cannot_connect": "\u041d\u0435 \u0432\u0434\u0430\u043b\u043e\u0441\u044f \u043f\u0456\u0434'\u0454\u0434\u043d\u0430\u0442\u0438\u0441\u044f",
|
||||
"invalid_auth": "\u041d\u0435\u0432\u0456\u0440\u043d\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0456\u043a\u0430\u0446\u0456\u044f."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "\u041f\u0430\u0440\u043e\u043b\u044c",
|
||||
"username": "\u0410\u0434\u0440\u0435\u0441\u0430 \u0435\u043b\u0435\u043a\u0442\u0440\u043e\u043d\u043d\u043e\u0457 \u043f\u043e\u0448\u0442\u0438"
|
||||
},
|
||||
"description": "\u0412\u0432\u0435\u0434\u0456\u0442\u044c \u0434\u0430\u043d\u0456 \u043e\u0431\u043b\u0456\u043a\u043e\u0432\u043e\u0433\u043e \u0437\u0430\u043f\u0438\u0441\u0443.",
|
||||
"title": "Tesla"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "\u041f\u0440\u0438\u043c\u0443\u0441\u043e\u0432\u043e \u0440\u043e\u0437\u0431\u0443\u0434\u0438\u0442\u0438 \u043c\u0430\u0448\u0438\u043d\u0443 \u043f\u0440\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0443",
|
||||
"scan_interval": "\u0406\u043d\u0442\u0435\u0440\u0432\u0430\u043b \u043c\u0456\u0436 \u0441\u043a\u0430\u043d\u0443\u0432\u0430\u043d\u043d\u044f\u043c\u0438 (\u0441\u0435\u043a.)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "MFA \u4ee3\u7801\uff08\u53ef\u9009\uff09"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210",
|
||||
"reauth_successful": "\u91cd\u65b0\u8a8d\u8b49\u6210\u529f"
|
||||
},
|
||||
"error": {
|
||||
"already_configured": "\u5e33\u865f\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210",
|
||||
"cannot_connect": "\u9023\u7dda\u5931\u6557",
|
||||
"invalid_auth": "\u9a57\u8b49\u78bc\u7121\u6548"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"mfa": "MFA \u78bc\uff08\u9078\u9805\uff09",
|
||||
"password": "\u5bc6\u78bc",
|
||||
"username": "\u96fb\u5b50\u90f5\u4ef6"
|
||||
},
|
||||
"description": "\u8acb\u8f38\u5165\u8cc7\u8a0a\u3002",
|
||||
"title": "Tesla - \u8a2d\u5b9a"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"enable_wake_on_start": "\u65bc\u555f\u52d5\u6642\u5f37\u5236\u559a\u9192\u6c7d\u8eca",
|
||||
"scan_interval": "\u6383\u63cf\u9593\u9694\u79d2\u6578"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -272,7 +272,6 @@ FLOWS = [
|
||||
"tado",
|
||||
"tasmota",
|
||||
"tellduslive",
|
||||
"tesla",
|
||||
"tibber",
|
||||
"tile",
|
||||
"toon",
|
||||
|
@ -259,21 +259,6 @@ DHCP = [
|
||||
"domain": "tado",
|
||||
"hostname": "tado*"
|
||||
},
|
||||
{
|
||||
"domain": "tesla",
|
||||
"hostname": "tesla_*",
|
||||
"macaddress": "4CFCAA*"
|
||||
},
|
||||
{
|
||||
"domain": "tesla",
|
||||
"hostname": "tesla_*",
|
||||
"macaddress": "044EAF*"
|
||||
},
|
||||
{
|
||||
"domain": "tesla",
|
||||
"hostname": "tesla_*",
|
||||
"macaddress": "98ED5C*"
|
||||
},
|
||||
{
|
||||
"domain": "toon",
|
||||
"hostname": "eneco-*",
|
||||
|
3
mypy.ini
3
mypy.ini
@ -1618,9 +1618,6 @@ ignore_errors = true
|
||||
[mypy-homeassistant.components.template.*]
|
||||
ignore_errors = true
|
||||
|
||||
[mypy-homeassistant.components.tesla.*]
|
||||
ignore_errors = true
|
||||
|
||||
[mypy-homeassistant.components.toon.*]
|
||||
ignore_errors = true
|
||||
|
||||
|
@ -2283,9 +2283,6 @@ temperusb==1.5.3
|
||||
# homeassistant.components.powerwall
|
||||
tesla-powerwall==0.3.10
|
||||
|
||||
# homeassistant.components.tesla
|
||||
teslajsonpy==0.18.3
|
||||
|
||||
# homeassistant.components.tensorflow
|
||||
# tf-models-official==2.3.0
|
||||
|
||||
|
@ -1275,9 +1275,6 @@ tellduslive==0.10.11
|
||||
# homeassistant.components.powerwall
|
||||
tesla-powerwall==0.3.10
|
||||
|
||||
# homeassistant.components.tesla
|
||||
teslajsonpy==0.18.3
|
||||
|
||||
# homeassistant.components.toon
|
||||
toonapi==0.2.0
|
||||
|
||||
|
@ -128,7 +128,6 @@ IGNORED_MODULES: Final[list[str]] = [
|
||||
"homeassistant.components.tado.*",
|
||||
"homeassistant.components.telegram_bot.*",
|
||||
"homeassistant.components.template.*",
|
||||
"homeassistant.components.tesla.*",
|
||||
"homeassistant.components.toon.*",
|
||||
"homeassistant.components.tplink.*",
|
||||
"homeassistant.components.unifi.*",
|
||||
|
@ -1 +0,0 @@
|
||||
"""Tests for the Tesla integration."""
|
@ -1,270 +0,0 @@
|
||||
"""Test the Tesla config flow."""
|
||||
import datetime
|
||||
from unittest.mock import patch
|
||||
|
||||
from teslajsonpy.exceptions import IncompleteCredentials, TeslaException
|
||||
|
||||
from homeassistant import config_entries, data_entry_flow, setup
|
||||
from homeassistant.components.tesla.const import (
|
||||
CONF_EXPIRATION,
|
||||
CONF_WAKE_ON_START,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DEFAULT_WAKE_ON_START,
|
||||
DOMAIN,
|
||||
MIN_SCAN_INTERVAL,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_ACCESS_TOKEN,
|
||||
CONF_PASSWORD,
|
||||
CONF_SCAN_INTERVAL,
|
||||
CONF_TOKEN,
|
||||
CONF_USERNAME,
|
||||
HTTP_NOT_FOUND,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
TEST_USERNAME = "test-username"
|
||||
TEST_TOKEN = "test-token"
|
||||
TEST_PASSWORD = "test-password"
|
||||
TEST_ACCESS_TOKEN = "test-access-token"
|
||||
TEST_VALID_EXPIRATION = datetime.datetime.now().timestamp() * 2
|
||||
|
||||
|
||||
async def test_form(hass):
|
||||
"""Test we get the form."""
|
||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == "form"
|
||||
assert result["errors"] == {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.tesla.config_flow.TeslaAPI.connect",
|
||||
return_value={
|
||||
"refresh_token": TEST_TOKEN,
|
||||
CONF_ACCESS_TOKEN: TEST_ACCESS_TOKEN,
|
||||
CONF_EXPIRATION: TEST_VALID_EXPIRATION,
|
||||
},
|
||||
), patch(
|
||||
"homeassistant.components.tesla.async_setup", return_value=True
|
||||
) as mock_setup, patch(
|
||||
"homeassistant.components.tesla.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], {CONF_PASSWORD: "test", CONF_USERNAME: "test@email.com"}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result2["title"] == "test@email.com"
|
||||
assert result2["data"] == {
|
||||
CONF_USERNAME: "test@email.com",
|
||||
CONF_PASSWORD: "test",
|
||||
CONF_TOKEN: TEST_TOKEN,
|
||||
CONF_ACCESS_TOKEN: TEST_ACCESS_TOKEN,
|
||||
CONF_EXPIRATION: TEST_VALID_EXPIRATION,
|
||||
}
|
||||
assert len(mock_setup.mock_calls) == 1
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_form_invalid_auth(hass):
|
||||
"""Test we handle invalid auth."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.tesla.config_flow.TeslaAPI.connect",
|
||||
side_effect=TeslaException(401),
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_USERNAME: TEST_USERNAME, CONF_PASSWORD: TEST_PASSWORD},
|
||||
)
|
||||
|
||||
assert result2["type"] == "form"
|
||||
assert result2["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
|
||||
async def test_form_invalid_auth_incomplete_credentials(hass):
|
||||
"""Test we handle invalid auth with incomplete credentials."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.tesla.config_flow.TeslaAPI.connect",
|
||||
side_effect=IncompleteCredentials(401),
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_USERNAME: TEST_USERNAME, CONF_PASSWORD: TEST_PASSWORD},
|
||||
)
|
||||
|
||||
assert result2["type"] == "form"
|
||||
assert result2["errors"] == {"base": "invalid_auth"}
|
||||
|
||||
|
||||
async def test_form_cannot_connect(hass):
|
||||
"""Test we handle cannot connect error."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.tesla.config_flow.TeslaAPI.connect",
|
||||
side_effect=TeslaException(code=HTTP_NOT_FOUND),
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_PASSWORD: TEST_PASSWORD, CONF_USERNAME: TEST_USERNAME},
|
||||
)
|
||||
|
||||
assert result2["type"] == "form"
|
||||
assert result2["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_form_repeat_identifier(hass):
|
||||
"""Test we handle repeat identifiers."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title=TEST_USERNAME,
|
||||
data={"username": TEST_USERNAME, "password": TEST_PASSWORD},
|
||||
options=None,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
with patch(
|
||||
"homeassistant.components.tesla.config_flow.TeslaAPI.connect",
|
||||
return_value={
|
||||
"refresh_token": TEST_TOKEN,
|
||||
CONF_ACCESS_TOKEN: TEST_ACCESS_TOKEN,
|
||||
CONF_EXPIRATION: TEST_VALID_EXPIRATION,
|
||||
},
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_USERNAME: TEST_USERNAME, CONF_PASSWORD: TEST_PASSWORD},
|
||||
)
|
||||
|
||||
assert result2["type"] == "abort"
|
||||
assert result2["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_form_reauth(hass):
|
||||
"""Test we handle reauth."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
title=TEST_USERNAME,
|
||||
data={"username": TEST_USERNAME, "password": "same"},
|
||||
options=None,
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_REAUTH},
|
||||
data={"username": TEST_USERNAME},
|
||||
)
|
||||
with patch(
|
||||
"homeassistant.components.tesla.config_flow.TeslaAPI.connect",
|
||||
return_value={
|
||||
"refresh_token": TEST_TOKEN,
|
||||
CONF_ACCESS_TOKEN: TEST_ACCESS_TOKEN,
|
||||
CONF_EXPIRATION: TEST_VALID_EXPIRATION,
|
||||
},
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{CONF_USERNAME: TEST_USERNAME, CONF_PASSWORD: "new-password"},
|
||||
)
|
||||
|
||||
assert result2["type"] == "abort"
|
||||
assert result2["reason"] == "reauth_successful"
|
||||
|
||||
|
||||
async def test_import(hass):
|
||||
"""Test import step."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.tesla.config_flow.TeslaAPI.connect",
|
||||
return_value={
|
||||
"refresh_token": TEST_TOKEN,
|
||||
CONF_ACCESS_TOKEN: TEST_ACCESS_TOKEN,
|
||||
CONF_EXPIRATION: TEST_VALID_EXPIRATION,
|
||||
},
|
||||
):
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={CONF_PASSWORD: TEST_PASSWORD, CONF_USERNAME: TEST_USERNAME},
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == TEST_USERNAME
|
||||
assert result["data"][CONF_ACCESS_TOKEN] == TEST_ACCESS_TOKEN
|
||||
assert result["data"][CONF_TOKEN] == TEST_TOKEN
|
||||
assert result["description_placeholders"] is None
|
||||
|
||||
|
||||
async def test_option_flow(hass):
|
||||
"""Test config flow options."""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data={}, options=None)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={CONF_SCAN_INTERVAL: 350, CONF_WAKE_ON_START: True},
|
||||
)
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["data"] == {CONF_SCAN_INTERVAL: 350, CONF_WAKE_ON_START: True}
|
||||
|
||||
|
||||
async def test_option_flow_defaults(hass):
|
||||
"""Test config flow options."""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data={}, options=None)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], user_input={}
|
||||
)
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["data"] == {
|
||||
CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL,
|
||||
CONF_WAKE_ON_START: DEFAULT_WAKE_ON_START,
|
||||
}
|
||||
|
||||
|
||||
async def test_option_flow_input_floor(hass):
|
||||
"""Test config flow options."""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data={}, options=None)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
assert result["type"] == "form"
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"], user_input={CONF_SCAN_INTERVAL: 1}
|
||||
)
|
||||
assert result["type"] == "create_entry"
|
||||
assert result["data"] == {
|
||||
CONF_SCAN_INTERVAL: MIN_SCAN_INTERVAL,
|
||||
CONF_WAKE_ON_START: DEFAULT_WAKE_ON_START,
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user