mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Convert Tesla to Async (#28748)
* build: bump teslajsonpy to 0.2.0 * feat: add async * perf: convert unnecessary async calls to sync * fix: force real login * Revert change to HVAC_MODE_HEAT * Remove charging rate sensor * Remove tests * Remove tests * Address requested changes * Add missing sensors * Move update to async_setup_scanner * Align wtih prior update behavior
This commit is contained in:
parent
806b96ef73
commit
2aad150419
@ -2,9 +2,8 @@
|
||||
from collections import defaultdict
|
||||
import logging
|
||||
|
||||
import voluptuous as vol
|
||||
from teslajsonpy import Controller as teslaAPI, TeslaException
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.const import (
|
||||
ATTR_BATTERY_LEVEL,
|
||||
@ -12,18 +11,14 @@ from homeassistant.const import (
|
||||
CONF_SCAN_INTERVAL,
|
||||
CONF_USERNAME,
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers import discovery
|
||||
from homeassistant.helpers import aiohttp_client, config_validation as cv, discovery
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.util import slugify
|
||||
|
||||
DOMAIN = "tesla"
|
||||
from .const import DOMAIN, TESLA_COMPONENTS
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
TESLA_ID_FORMAT = "{}_{}"
|
||||
TESLA_ID_LIST_SCHEMA = vol.Schema([int])
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
@ -42,17 +37,8 @@ CONFIG_SCHEMA = vol.Schema(
|
||||
NOTIFICATION_ID = "tesla_integration_notification"
|
||||
NOTIFICATION_TITLE = "Tesla integration setup"
|
||||
|
||||
TESLA_COMPONENTS = [
|
||||
"sensor",
|
||||
"lock",
|
||||
"climate",
|
||||
"binary_sensor",
|
||||
"device_tracker",
|
||||
"switch",
|
||||
]
|
||||
|
||||
|
||||
def setup(hass, base_config):
|
||||
async def async_setup(hass, base_config):
|
||||
"""Set up of Tesla component."""
|
||||
config = base_config.get(DOMAIN)
|
||||
|
||||
@ -61,10 +47,15 @@ def setup(hass, base_config):
|
||||
update_interval = config.get(CONF_SCAN_INTERVAL)
|
||||
if hass.data.get(DOMAIN) is None:
|
||||
try:
|
||||
hass.data[DOMAIN] = {
|
||||
"controller": teslaAPI(email, password, update_interval),
|
||||
"devices": defaultdict(list),
|
||||
}
|
||||
websession = aiohttp_client.async_get_clientsession(hass)
|
||||
controller = teslaAPI(
|
||||
websession,
|
||||
email=email,
|
||||
password=password,
|
||||
update_interval=update_interval,
|
||||
)
|
||||
await controller.connect(test_login=False)
|
||||
hass.data[DOMAIN] = {"controller": controller, "devices": defaultdict(list)}
|
||||
_LOGGER.debug("Connected to the Tesla API.")
|
||||
except TeslaException as ex:
|
||||
if ex.code == 401:
|
||||
@ -85,9 +76,7 @@ def setup(hass, base_config):
|
||||
)
|
||||
_LOGGER.error("Unable to communicate with Tesla API: %s", ex.message)
|
||||
return False
|
||||
|
||||
all_devices = hass.data[DOMAIN]["controller"].list_vehicles()
|
||||
|
||||
all_devices = controller.get_homeassistant_components()
|
||||
if not all_devices:
|
||||
return False
|
||||
|
||||
@ -95,8 +84,9 @@ def setup(hass, base_config):
|
||||
hass.data[DOMAIN]["devices"][device.hass_type].append(device)
|
||||
|
||||
for component in TESLA_COMPONENTS:
|
||||
discovery.load_platform(hass, component, DOMAIN, {}, base_config)
|
||||
|
||||
hass.async_create_task(
|
||||
discovery.async_load_platform(hass, component, DOMAIN, {}, base_config)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
@ -104,11 +94,12 @@ class TeslaDevice(Entity):
|
||||
"""Representation of a Tesla device."""
|
||||
|
||||
def __init__(self, tesla_device, controller):
|
||||
"""Initialise of the Tesla device."""
|
||||
"""Initialise the Tesla device."""
|
||||
self.tesla_device = tesla_device
|
||||
self.controller = controller
|
||||
self._name = self.tesla_device.name
|
||||
self.tesla_id = slugify(self.tesla_device.uniq_name)
|
||||
self._attributes = {}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@ -128,8 +119,19 @@ class TeslaDevice(Entity):
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes of the device."""
|
||||
attr = {}
|
||||
|
||||
attr = self._attributes
|
||||
if self.tesla_device.has_battery():
|
||||
attr[ATTR_BATTERY_LEVEL] = self.tesla_device.battery_level()
|
||||
return attr
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register state update callback."""
|
||||
pass
|
||||
|
||||
async def async_will_remove_from_hass(self):
|
||||
"""Prepare for unload."""
|
||||
pass
|
||||
|
||||
async def async_update(self):
|
||||
"""Update the state of the device."""
|
||||
await self.tesla_device.async_update()
|
||||
|
@ -8,7 +8,7 @@ from . import DOMAIN as TESLA_DOMAIN, TeslaDevice
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the Tesla binary sensor."""
|
||||
devices = [
|
||||
TeslaBinarySensor(device, hass.data[TESLA_DOMAIN]["controller"], "connectivity")
|
||||
@ -41,8 +41,8 @@ class TeslaBinarySensor(TeslaDevice, BinarySensorDevice):
|
||||
"""Return the state of the binary sensor."""
|
||||
return self._state
|
||||
|
||||
def update(self):
|
||||
async def async_update(self):
|
||||
"""Update the state of the device."""
|
||||
_LOGGER.debug("Updating sensor: %s", self._name)
|
||||
self.tesla_device.update()
|
||||
await super().async_update()
|
||||
self._state = self.tesla_device.get_value()
|
||||
|
@ -16,7 +16,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
SUPPORT_HVAC = [HVAC_MODE_HEAT, HVAC_MODE_OFF]
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the Tesla climate platform."""
|
||||
devices = [
|
||||
TeslaThermostat(device, hass.data[TESLA_DOMAIN]["controller"])
|
||||
@ -57,10 +57,10 @@ class TeslaThermostat(TeslaDevice, ClimateDevice):
|
||||
"""
|
||||
return SUPPORT_HVAC
|
||||
|
||||
def update(self):
|
||||
async def async_update(self):
|
||||
"""Call by the Tesla device callback to update state."""
|
||||
_LOGGER.debug("Updating: %s", self._name)
|
||||
self.tesla_device.update()
|
||||
await super().async_update()
|
||||
self._target_temperature = self.tesla_device.get_goal_temp()
|
||||
self._temperature = self.tesla_device.get_current_temp()
|
||||
|
||||
@ -83,17 +83,17 @@ class TeslaThermostat(TeslaDevice, ClimateDevice):
|
||||
"""Return the temperature we try to reach."""
|
||||
return self._target_temperature
|
||||
|
||||
def set_temperature(self, **kwargs):
|
||||
async def async_set_temperature(self, **kwargs):
|
||||
"""Set new target temperatures."""
|
||||
_LOGGER.debug("Setting temperature for: %s", self._name)
|
||||
temperature = kwargs.get(ATTR_TEMPERATURE)
|
||||
if temperature:
|
||||
self.tesla_device.set_temperature(temperature)
|
||||
await self.tesla_device.set_temperature(temperature)
|
||||
|
||||
def set_hvac_mode(self, hvac_mode):
|
||||
async def async_set_hvac_mode(self, hvac_mode):
|
||||
"""Set new target hvac mode."""
|
||||
_LOGGER.debug("Setting mode for: %s", self._name)
|
||||
if hvac_mode == HVAC_MODE_OFF:
|
||||
self.tesla_device.set_status(False)
|
||||
await self.tesla_device.set_status(False)
|
||||
elif hvac_mode == HVAC_MODE_HEAT:
|
||||
self.tesla_device.set_status(True)
|
||||
await self.tesla_device.set_status(True)
|
||||
|
24
homeassistant/components/tesla/const.py
Normal file
24
homeassistant/components/tesla/const.py
Normal file
@ -0,0 +1,24 @@
|
||||
"""Const file for Tesla cars."""
|
||||
DOMAIN = "tesla"
|
||||
DATA_LISTENER = "listener"
|
||||
TESLA_COMPONENTS = [
|
||||
"sensor",
|
||||
"lock",
|
||||
"climate",
|
||||
"binary_sensor",
|
||||
"device_tracker",
|
||||
"switch",
|
||||
]
|
||||
SENSOR_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",
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
"""Support for tracking Tesla cars."""
|
||||
import logging
|
||||
|
||||
from homeassistant.helpers.event import track_utc_time_change
|
||||
from homeassistant.helpers.event import async_track_utc_time_change
|
||||
from homeassistant.util import slugify
|
||||
|
||||
from . import DOMAIN as TESLA_DOMAIN
|
||||
@ -9,11 +9,13 @@ from . import DOMAIN as TESLA_DOMAIN
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_scanner(hass, config, see, discovery_info=None):
|
||||
async def async_setup_scanner(hass, config, async_see, discovery_info=None):
|
||||
"""Set up the Tesla tracker."""
|
||||
TeslaDeviceTracker(
|
||||
hass, config, see, hass.data[TESLA_DOMAIN]["devices"]["devices_tracker"]
|
||||
tracker = TeslaDeviceTracker(
|
||||
hass, config, async_see, hass.data[TESLA_DOMAIN]["devices"]["devices_tracker"]
|
||||
)
|
||||
await tracker.update_info()
|
||||
async_track_utc_time_change(hass, tracker.update_info, second=range(0, 60, 30))
|
||||
return True
|
||||
|
||||
|
||||
@ -25,14 +27,11 @@ class TeslaDeviceTracker:
|
||||
self.hass = hass
|
||||
self.see = see
|
||||
self.devices = tesla_devices
|
||||
self._update_info()
|
||||
|
||||
track_utc_time_change(self.hass, self._update_info, second=range(0, 60, 30))
|
||||
|
||||
def _update_info(self, now=None):
|
||||
async def update_info(self, now=None):
|
||||
"""Update the device info."""
|
||||
for device in self.devices:
|
||||
device.update()
|
||||
await device.async_update()
|
||||
name = device.name
|
||||
_LOGGER.debug("Updating device position: %s", name)
|
||||
dev_id = slugify(device.uniq_name)
|
||||
@ -41,6 +40,6 @@ class TeslaDeviceTracker:
|
||||
lat = location["latitude"]
|
||||
lon = location["longitude"]
|
||||
attrs = {"trackr_id": dev_id, "id": dev_id, "name": name}
|
||||
self.see(
|
||||
await self.see(
|
||||
dev_id=dev_id, host_name=name, gps=(lat, lon), attributes=attrs
|
||||
)
|
||||
|
@ -9,7 +9,7 @@ from . import DOMAIN as TESLA_DOMAIN, TeslaDevice
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the Tesla lock platform."""
|
||||
devices = [
|
||||
TeslaLock(device, hass.data[TESLA_DOMAIN]["controller"])
|
||||
@ -26,23 +26,23 @@ class TeslaLock(TeslaDevice, LockDevice):
|
||||
self._state = None
|
||||
super().__init__(tesla_device, controller)
|
||||
|
||||
def lock(self, **kwargs):
|
||||
async def async_lock(self, **kwargs):
|
||||
"""Send the lock command."""
|
||||
_LOGGER.debug("Locking doors for: %s", self._name)
|
||||
self.tesla_device.lock()
|
||||
await self.tesla_device.lock()
|
||||
|
||||
def unlock(self, **kwargs):
|
||||
async def async_unlock(self, **kwargs):
|
||||
"""Send the unlock command."""
|
||||
_LOGGER.debug("Unlocking doors for: %s", self._name)
|
||||
self.tesla_device.unlock()
|
||||
await self.tesla_device.unlock()
|
||||
|
||||
@property
|
||||
def is_locked(self):
|
||||
"""Get whether the lock is in locked state."""
|
||||
return self._state == STATE_LOCKED
|
||||
|
||||
def update(self):
|
||||
async def async_update(self):
|
||||
"""Update state of the lock."""
|
||||
_LOGGER.debug("Updating state for: %s", self._name)
|
||||
self.tesla_device.update()
|
||||
await super().async_update()
|
||||
self._state = STATE_LOCKED if self.tesla_device.is_locked() else STATE_UNLOCKED
|
||||
|
@ -2,7 +2,7 @@
|
||||
"domain": "tesla",
|
||||
"name": "Tesla",
|
||||
"documentation": "https://www.home-assistant.io/integrations/tesla",
|
||||
"requirements": ["teslajsonpy==0.0.26"],
|
||||
"requirements": ["teslajsonpy==0.2.0"],
|
||||
"dependencies": [],
|
||||
"codeowners": ["@zabuldon"]
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
"""Support for the Tesla sensors."""
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
|
||||
from homeassistant.const import (
|
||||
@ -14,10 +13,8 @@ from . import DOMAIN as TESLA_DOMAIN, TeslaDevice
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
SCAN_INTERVAL = timedelta(minutes=5)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the Tesla sensor platform."""
|
||||
controller = hass.data[TESLA_DOMAIN]["devices"]["controller"]
|
||||
devices = []
|
||||
@ -26,7 +23,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
if device.bin_type == 0x4:
|
||||
devices.append(TeslaSensor(device, controller, "inside"))
|
||||
devices.append(TeslaSensor(device, controller, "outside"))
|
||||
else:
|
||||
elif device.bin_type in [0xA, 0xB, 0x5]:
|
||||
devices.append(TeslaSensor(device, controller))
|
||||
add_entities(devices, True)
|
||||
|
||||
@ -62,10 +59,10 @@ class TeslaSensor(TeslaDevice, Entity):
|
||||
"""Return the unit_of_measurement of the device."""
|
||||
return self._unit
|
||||
|
||||
def update(self):
|
||||
async def async_update(self):
|
||||
"""Update the state from the sensor."""
|
||||
_LOGGER.debug("Updating sensor: %s", self._name)
|
||||
self.tesla_device.update()
|
||||
await super().async_update()
|
||||
units = self.tesla_device.measurement
|
||||
|
||||
if self.tesla_device.bin_type == 0x4:
|
||||
|
@ -9,7 +9,7 @@ from . import DOMAIN as TESLA_DOMAIN, TeslaDevice
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
async def async_setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
"""Set up the Tesla switch platform."""
|
||||
controller = hass.data[TESLA_DOMAIN]["controller"]
|
||||
devices = []
|
||||
@ -30,25 +30,25 @@ class ChargerSwitch(TeslaDevice, SwitchDevice):
|
||||
self._state = None
|
||||
super().__init__(tesla_device, controller)
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Send the on command."""
|
||||
_LOGGER.debug("Enable charging: %s", self._name)
|
||||
self.tesla_device.start_charge()
|
||||
await self.tesla_device.start_charge()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Send the off command."""
|
||||
_LOGGER.debug("Disable charging for: %s", self._name)
|
||||
self.tesla_device.stop_charge()
|
||||
await self.tesla_device.stop_charge()
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Get whether the switch is in on state."""
|
||||
return self._state == STATE_ON
|
||||
|
||||
def update(self):
|
||||
async def async_update(self):
|
||||
"""Update the state of the switch."""
|
||||
_LOGGER.debug("Updating state for: %s", self._name)
|
||||
self.tesla_device.update()
|
||||
await super().async_update()
|
||||
self._state = STATE_ON if self.tesla_device.is_charging() else STATE_OFF
|
||||
|
||||
|
||||
@ -56,29 +56,29 @@ class RangeSwitch(TeslaDevice, SwitchDevice):
|
||||
"""Representation of a Tesla max range charging switch."""
|
||||
|
||||
def __init__(self, tesla_device, controller):
|
||||
"""Initialise of the switch."""
|
||||
"""Initialise the switch."""
|
||||
self._state = None
|
||||
super().__init__(tesla_device, controller)
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
async def async_turn_on(self, **kwargs):
|
||||
"""Send the on command."""
|
||||
_LOGGER.debug("Enable max range charging: %s", self._name)
|
||||
self.tesla_device.set_max()
|
||||
await self.tesla_device.set_max()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
async def async_turn_off(self, **kwargs):
|
||||
"""Send the off command."""
|
||||
_LOGGER.debug("Disable max range charging: %s", self._name)
|
||||
self.tesla_device.set_standard()
|
||||
await self.tesla_device.set_standard()
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Get whether the switch is in on state."""
|
||||
return self._state
|
||||
|
||||
def update(self):
|
||||
async def async_update(self):
|
||||
"""Update the state of the switch."""
|
||||
_LOGGER.debug("Updating state for: %s", self._name)
|
||||
self.tesla_device.update()
|
||||
await super().async_update()
|
||||
self._state = bool(self.tesla_device.is_maxrange())
|
||||
|
||||
|
||||
@ -86,18 +86,19 @@ class UpdateSwitch(TeslaDevice, SwitchDevice):
|
||||
"""Representation of a Tesla update switch."""
|
||||
|
||||
def __init__(self, tesla_device, controller):
|
||||
"""Initialise of the switch."""
|
||||
"""Initialise the switch."""
|
||||
self._state = None
|
||||
tesla_device.type = "update switch"
|
||||
super().__init__(tesla_device, controller)
|
||||
self._name = self._name.replace("charger", "update")
|
||||
self.tesla_id = self.tesla_id.replace("charger", "update")
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
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)
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
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)
|
||||
@ -107,8 +108,9 @@ class UpdateSwitch(TeslaDevice, SwitchDevice):
|
||||
"""Get whether the switch is in on state."""
|
||||
return self._state
|
||||
|
||||
def update(self):
|
||||
async def async_update(self):
|
||||
"""Update the state of the switch."""
|
||||
car_id = self.tesla_device.id()
|
||||
_LOGGER.debug("Updating state for: %s %s", self._name, car_id)
|
||||
await super().async_update()
|
||||
self._state = bool(self.controller.get_updates(car_id))
|
||||
|
@ -1898,7 +1898,7 @@ temperusb==1.5.3
|
||||
# tensorflow==1.13.2
|
||||
|
||||
# homeassistant.components.tesla
|
||||
teslajsonpy==0.0.26
|
||||
teslajsonpy==0.2.0
|
||||
|
||||
# homeassistant.components.thermoworks_smoke
|
||||
thermoworks_smoke==0.1.8
|
||||
|
Loading…
x
Reference in New Issue
Block a user