mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 05:07:41 +00:00
Add switch platform to Tesla Fleet (#125798)
* Add switch platform * Add tests
This commit is contained in:
parent
8af6ffdb49
commit
e71709f0ec
@ -44,6 +44,7 @@ PLATFORMS: Final = [
|
|||||||
Platform.CLIMATE,
|
Platform.CLIMATE,
|
||||||
Platform.DEVICE_TRACKER,
|
Platform.DEVICE_TRACKER,
|
||||||
Platform.SENSOR,
|
Platform.SENSOR,
|
||||||
|
Platform.SWITCH,
|
||||||
]
|
]
|
||||||
|
|
||||||
type TeslaFleetConfigEntry = ConfigEntry[TeslaFleetData]
|
type TeslaFleetConfigEntry = ConfigEntry[TeslaFleetData]
|
||||||
|
@ -4,7 +4,9 @@ from abc import abstractmethod
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from tesla_fleet_api import EnergySpecific, VehicleSpecific
|
from tesla_fleet_api import EnergySpecific, VehicleSpecific
|
||||||
|
from tesla_fleet_api.const import Scope
|
||||||
|
|
||||||
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
from homeassistant.helpers.device_registry import DeviceInfo
|
from homeassistant.helpers.device_registry import DeviceInfo
|
||||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||||
|
|
||||||
@ -29,6 +31,7 @@ class TeslaFleetEntity(
|
|||||||
|
|
||||||
_attr_has_entity_name = True
|
_attr_has_entity_name = True
|
||||||
read_only: bool
|
read_only: bool
|
||||||
|
scoped: bool
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -78,6 +81,14 @@ class TeslaFleetEntity(
|
|||||||
def _async_update_attrs(self) -> None:
|
def _async_update_attrs(self) -> None:
|
||||||
"""Update the attributes of the entity."""
|
"""Update the attributes of the entity."""
|
||||||
|
|
||||||
|
def raise_for_read_only(self, scope: Scope) -> None:
|
||||||
|
"""Raise an error if a scope is not available."""
|
||||||
|
if not self.scoped:
|
||||||
|
raise ServiceValidationError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key=f"missing_scope_{scope.name.lower()}",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TeslaFleetVehicleEntity(TeslaFleetEntity):
|
class TeslaFleetVehicleEntity(TeslaFleetEntity):
|
||||||
"""Parent class for TeslaFleet Vehicle entities."""
|
"""Parent class for TeslaFleet Vehicle entities."""
|
||||||
@ -106,6 +117,14 @@ class TeslaFleetVehicleEntity(TeslaFleetEntity):
|
|||||||
"""Wake up the vehicle if its asleep."""
|
"""Wake up the vehicle if its asleep."""
|
||||||
await wake_up_vehicle(self.vehicle)
|
await wake_up_vehicle(self.vehicle)
|
||||||
|
|
||||||
|
def raise_for_read_only(self, scope: Scope) -> None:
|
||||||
|
"""Raise an error if no command signing or a scope is not available."""
|
||||||
|
if self.vehicle.signing:
|
||||||
|
raise ServiceValidationError(
|
||||||
|
translation_domain=DOMAIN, translation_key="command_signing"
|
||||||
|
)
|
||||||
|
super().raise_for_read_only(scope)
|
||||||
|
|
||||||
|
|
||||||
class TeslaFleetEnergyLiveEntity(TeslaFleetEntity):
|
class TeslaFleetEnergyLiveEntity(TeslaFleetEntity):
|
||||||
"""Parent class for TeslaFleet Energy Site Live entities."""
|
"""Parent class for TeslaFleet Energy Site Live entities."""
|
||||||
|
@ -121,6 +121,41 @@
|
|||||||
"wall_connector_state": {
|
"wall_connector_state": {
|
||||||
"default": "mdi:ev-station"
|
"default": "mdi:ev-station"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"switch": {
|
||||||
|
"charge_state_user_charge_enable_request": {
|
||||||
|
"default": "mdi:ev-station"
|
||||||
|
},
|
||||||
|
"climate_state_auto_seat_climate_left": {
|
||||||
|
"default": "mdi:car-seat-heater",
|
||||||
|
"state": {
|
||||||
|
"off": "mdi:car-seat"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"climate_state_auto_seat_climate_right": {
|
||||||
|
"default": "mdi:car-seat-heater",
|
||||||
|
"state": {
|
||||||
|
"off": "mdi:car-seat"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"climate_state_auto_steering_wheel_heat": {
|
||||||
|
"default": "mdi:steering"
|
||||||
|
},
|
||||||
|
"climate_state_defrost_mode": {
|
||||||
|
"default": "mdi:snowflake-melt"
|
||||||
|
},
|
||||||
|
"components_disallow_charge_from_grid_with_solar_installed": {
|
||||||
|
"state": {
|
||||||
|
"false": "mdi:transmission-tower",
|
||||||
|
"true": "mdi:solar-power"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"vehicle_state_sentry_mode": {
|
||||||
|
"default": "mdi:shield-car"
|
||||||
|
},
|
||||||
|
"vehicle_state_valet_mode": {
|
||||||
|
"default": "mdi:speedometer-slow"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,6 +286,35 @@
|
|||||||
"wall_connector_state": {
|
"wall_connector_state": {
|
||||||
"name": "State code"
|
"name": "State code"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"switch": {
|
||||||
|
"charge_state_user_charge_enable_request": {
|
||||||
|
"name": "Charge"
|
||||||
|
},
|
||||||
|
"climate_state_auto_seat_climate_left": {
|
||||||
|
"name": "Auto seat climate left"
|
||||||
|
},
|
||||||
|
"climate_state_auto_seat_climate_right": {
|
||||||
|
"name": "Auto seat climate right"
|
||||||
|
},
|
||||||
|
"climate_state_auto_steering_wheel_heat": {
|
||||||
|
"name": "Auto steering wheel heater"
|
||||||
|
},
|
||||||
|
"climate_state_defrost_mode": {
|
||||||
|
"name": "Defrost"
|
||||||
|
},
|
||||||
|
"components_disallow_charge_from_grid_with_solar_installed": {
|
||||||
|
"name": "Allow charging from grid"
|
||||||
|
},
|
||||||
|
"user_settings_storm_mode_enabled": {
|
||||||
|
"name": "Storm watch"
|
||||||
|
},
|
||||||
|
"vehicle_state_sentry_mode": {
|
||||||
|
"name": "Sentry mode"
|
||||||
|
},
|
||||||
|
"vehicle_state_valet_mode": {
|
||||||
|
"name": "Valet mode"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"exceptions": {
|
"exceptions": {
|
||||||
@ -304,6 +333,9 @@
|
|||||||
"command_no_reason": {
|
"command_no_reason": {
|
||||||
"message": "Command was unsuccessful but did not return a reason why."
|
"message": "Command was unsuccessful but did not return a reason why."
|
||||||
},
|
},
|
||||||
|
"command_signing": {
|
||||||
|
"message": "Vehicle requires command signing. Please see documentation for more details."
|
||||||
|
},
|
||||||
"invalid_cop_temp": {
|
"invalid_cop_temp": {
|
||||||
"message": "Cabin overheat protection does not support that temperature."
|
"message": "Cabin overheat protection does not support that temperature."
|
||||||
},
|
},
|
||||||
@ -312,6 +344,15 @@
|
|||||||
},
|
},
|
||||||
"missing_temperature": {
|
"missing_temperature": {
|
||||||
"message": "Temperature is required for this action."
|
"message": "Temperature is required for this action."
|
||||||
|
},
|
||||||
|
"missing_scope_vehicle_cmds": {
|
||||||
|
"message": "Missing vehicle commands scope."
|
||||||
|
},
|
||||||
|
"missing_scope_vehicle_charging_cmds": {
|
||||||
|
"message": "Missing vehicle charging commands scope."
|
||||||
|
},
|
||||||
|
"missing_scope_energy_cmds": {
|
||||||
|
"message": "Missing energy commands scope."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
262
homeassistant/components/tesla_fleet/switch.py
Normal file
262
homeassistant/components/tesla_fleet/switch.py
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
"""Switch platform for Tesla Fleet integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from itertools import chain
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from tesla_fleet_api.const import Scope, Seat
|
||||||
|
|
||||||
|
from homeassistant.components.switch import (
|
||||||
|
SwitchDeviceClass,
|
||||||
|
SwitchEntity,
|
||||||
|
SwitchEntityDescription,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
|
from . import TeslaFleetConfigEntry
|
||||||
|
from .entity import TeslaFleetEnergyInfoEntity, TeslaFleetVehicleEntity
|
||||||
|
from .helpers import handle_command, handle_vehicle_command
|
||||||
|
from .models import TeslaFleetEnergyData, TeslaFleetVehicleData
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class TeslaFleetSwitchEntityDescription(SwitchEntityDescription):
|
||||||
|
"""Describes TeslaFleet Switch entity."""
|
||||||
|
|
||||||
|
on_func: Callable
|
||||||
|
off_func: Callable
|
||||||
|
scopes: list[Scope]
|
||||||
|
|
||||||
|
|
||||||
|
VEHICLE_DESCRIPTIONS: tuple[TeslaFleetSwitchEntityDescription, ...] = (
|
||||||
|
TeslaFleetSwitchEntityDescription(
|
||||||
|
key="vehicle_state_sentry_mode",
|
||||||
|
on_func=lambda api: api.set_sentry_mode(on=True),
|
||||||
|
off_func=lambda api: api.set_sentry_mode(on=False),
|
||||||
|
scopes=[Scope.VEHICLE_CMDS],
|
||||||
|
),
|
||||||
|
TeslaFleetSwitchEntityDescription(
|
||||||
|
key="climate_state_auto_seat_climate_left",
|
||||||
|
on_func=lambda api: api.remote_auto_seat_climate_request(Seat.FRONT_LEFT, True),
|
||||||
|
off_func=lambda api: api.remote_auto_seat_climate_request(
|
||||||
|
Seat.FRONT_LEFT, False
|
||||||
|
),
|
||||||
|
scopes=[Scope.VEHICLE_CMDS],
|
||||||
|
),
|
||||||
|
TeslaFleetSwitchEntityDescription(
|
||||||
|
key="climate_state_auto_seat_climate_right",
|
||||||
|
on_func=lambda api: api.remote_auto_seat_climate_request(
|
||||||
|
Seat.FRONT_RIGHT, True
|
||||||
|
),
|
||||||
|
off_func=lambda api: api.remote_auto_seat_climate_request(
|
||||||
|
Seat.FRONT_RIGHT, False
|
||||||
|
),
|
||||||
|
scopes=[Scope.VEHICLE_CMDS],
|
||||||
|
),
|
||||||
|
TeslaFleetSwitchEntityDescription(
|
||||||
|
key="climate_state_auto_steering_wheel_heat",
|
||||||
|
on_func=lambda api: api.remote_auto_steering_wheel_heat_climate_request(
|
||||||
|
on=True
|
||||||
|
),
|
||||||
|
off_func=lambda api: api.remote_auto_steering_wheel_heat_climate_request(
|
||||||
|
on=False
|
||||||
|
),
|
||||||
|
scopes=[Scope.VEHICLE_CMDS],
|
||||||
|
),
|
||||||
|
TeslaFleetSwitchEntityDescription(
|
||||||
|
key="climate_state_defrost_mode",
|
||||||
|
on_func=lambda api: api.set_preconditioning_max(on=True, manual_override=False),
|
||||||
|
off_func=lambda api: api.set_preconditioning_max(
|
||||||
|
on=False, manual_override=False
|
||||||
|
),
|
||||||
|
scopes=[Scope.VEHICLE_CMDS],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
VEHICLE_CHARGE_DESCRIPTION = TeslaFleetSwitchEntityDescription(
|
||||||
|
key="charge_state_user_charge_enable_request",
|
||||||
|
on_func=lambda api: api.charge_start(),
|
||||||
|
off_func=lambda api: api.charge_stop(),
|
||||||
|
scopes=[Scope.VEHICLE_CHARGING_CMDS, Scope.VEHICLE_CMDS],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entry: TeslaFleetConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up the TeslaFleet Switch platform from a config entry."""
|
||||||
|
|
||||||
|
async_add_entities(
|
||||||
|
chain(
|
||||||
|
(
|
||||||
|
TeslaFleetVehicleSwitchEntity(
|
||||||
|
vehicle, description, entry.runtime_data.scopes
|
||||||
|
)
|
||||||
|
for vehicle in entry.runtime_data.vehicles
|
||||||
|
for description in VEHICLE_DESCRIPTIONS
|
||||||
|
),
|
||||||
|
(
|
||||||
|
TeslaFleetChargeSwitchEntity(
|
||||||
|
vehicle, VEHICLE_CHARGE_DESCRIPTION, entry.runtime_data.scopes
|
||||||
|
)
|
||||||
|
for vehicle in entry.runtime_data.vehicles
|
||||||
|
),
|
||||||
|
(
|
||||||
|
TeslaFleetChargeFromGridSwitchEntity(
|
||||||
|
energysite,
|
||||||
|
entry.runtime_data.scopes,
|
||||||
|
)
|
||||||
|
for energysite in entry.runtime_data.energysites
|
||||||
|
if energysite.info_coordinator.data.get("components_battery")
|
||||||
|
and energysite.info_coordinator.data.get("components_solar")
|
||||||
|
),
|
||||||
|
(
|
||||||
|
TeslaFleetStormModeSwitchEntity(energysite, entry.runtime_data.scopes)
|
||||||
|
for energysite in entry.runtime_data.energysites
|
||||||
|
if energysite.info_coordinator.data.get("components_storm_mode_capable")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TeslaFleetSwitchEntity(SwitchEntity):
|
||||||
|
"""Base class for all TeslaFleet switch entities."""
|
||||||
|
|
||||||
|
_attr_device_class = SwitchDeviceClass.SWITCH
|
||||||
|
entity_description: TeslaFleetSwitchEntityDescription
|
||||||
|
|
||||||
|
|
||||||
|
class TeslaFleetVehicleSwitchEntity(TeslaFleetVehicleEntity, TeslaFleetSwitchEntity):
|
||||||
|
"""Base class for TeslaFleet vehicle switch entities."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
data: TeslaFleetVehicleData,
|
||||||
|
description: TeslaFleetSwitchEntityDescription,
|
||||||
|
scopes: list[Scope],
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the Switch."""
|
||||||
|
super().__init__(data, description.key)
|
||||||
|
self.entity_description = description
|
||||||
|
self.scoped = any(scope in scopes for scope in description.scopes)
|
||||||
|
|
||||||
|
def _async_update_attrs(self) -> None:
|
||||||
|
"""Update the attributes of the sensor."""
|
||||||
|
if self._value is None:
|
||||||
|
self._attr_is_on = None
|
||||||
|
else:
|
||||||
|
self._attr_is_on = bool(self._value)
|
||||||
|
|
||||||
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn on the Switch."""
|
||||||
|
self.raise_for_read_only(self.entity_description.scopes[0])
|
||||||
|
await self.wake_up_if_asleep()
|
||||||
|
await handle_vehicle_command(self.entity_description.on_func(self.api))
|
||||||
|
self._attr_is_on = True
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn off the Switch."""
|
||||||
|
self.raise_for_read_only(self.entity_description.scopes[0])
|
||||||
|
await self.wake_up_if_asleep()
|
||||||
|
await handle_vehicle_command(self.entity_description.off_func(self.api))
|
||||||
|
self._attr_is_on = False
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
|
class TeslaFleetChargeSwitchEntity(TeslaFleetVehicleSwitchEntity):
|
||||||
|
"""Entity class for TeslaFleet charge switch."""
|
||||||
|
|
||||||
|
def _async_update_attrs(self) -> None:
|
||||||
|
"""Update the attributes of the entity."""
|
||||||
|
if self._value is None:
|
||||||
|
self._attr_is_on = self.get("charge_state_charge_enable_request")
|
||||||
|
else:
|
||||||
|
self._attr_is_on = self._value
|
||||||
|
|
||||||
|
|
||||||
|
class TeslaFleetChargeFromGridSwitchEntity(
|
||||||
|
TeslaFleetEnergyInfoEntity, TeslaFleetSwitchEntity
|
||||||
|
):
|
||||||
|
"""Entity class for Charge From Grid switch."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
data: TeslaFleetEnergyData,
|
||||||
|
scopes: list[Scope],
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the Switch."""
|
||||||
|
self.scoped = Scope.ENERGY_CMDS in scopes
|
||||||
|
super().__init__(
|
||||||
|
data, "components_disallow_charge_from_grid_with_solar_installed"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _async_update_attrs(self) -> None:
|
||||||
|
"""Update the attributes of the entity."""
|
||||||
|
# When disallow_charge_from_grid_with_solar_installed is missing, its Off.
|
||||||
|
# But this sensor is flipped to match how the Tesla app works.
|
||||||
|
self._attr_is_on = not self.get(self.key, False)
|
||||||
|
|
||||||
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn on the Switch."""
|
||||||
|
self.raise_for_read_only(Scope.ENERGY_CMDS)
|
||||||
|
await handle_command(
|
||||||
|
self.api.grid_import_export(
|
||||||
|
disallow_charge_from_grid_with_solar_installed=False
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._attr_is_on = True
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn off the Switch."""
|
||||||
|
self.raise_for_read_only(Scope.ENERGY_CMDS)
|
||||||
|
await handle_command(
|
||||||
|
self.api.grid_import_export(
|
||||||
|
disallow_charge_from_grid_with_solar_installed=True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self._attr_is_on = False
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
|
||||||
|
class TeslaFleetStormModeSwitchEntity(
|
||||||
|
TeslaFleetEnergyInfoEntity, TeslaFleetSwitchEntity
|
||||||
|
):
|
||||||
|
"""Entity class for Storm Mode switch."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
data: TeslaFleetEnergyData,
|
||||||
|
scopes: list[Scope],
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the Switch."""
|
||||||
|
super().__init__(data, "user_settings_storm_mode_enabled")
|
||||||
|
self.scoped = Scope.ENERGY_CMDS in scopes
|
||||||
|
|
||||||
|
def _async_update_attrs(self) -> None:
|
||||||
|
"""Update the attributes of the sensor."""
|
||||||
|
self._attr_available = self._value is not None
|
||||||
|
self._attr_is_on = bool(self._value)
|
||||||
|
|
||||||
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn on the Switch."""
|
||||||
|
self.raise_for_read_only(Scope.ENERGY_CMDS)
|
||||||
|
await handle_command(self.api.storm_mode(enabled=True))
|
||||||
|
self._attr_is_on = True
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
|
"""Turn off the Switch."""
|
||||||
|
self.raise_for_read_only(Scope.ENERGY_CMDS)
|
||||||
|
await handle_command(self.api.storm_mode(enabled=False))
|
||||||
|
self._attr_is_on = False
|
||||||
|
self.async_write_ha_state()
|
489
tests/components/tesla_fleet/snapshots/test_switch.ambr
Normal file
489
tests/components/tesla_fleet/snapshots/test_switch.ambr
Normal file
@ -0,0 +1,489 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_switch[switch.energy_site_allow_charging_from_grid-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.energy_site_allow_charging_from_grid',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Allow charging from grid',
|
||||||
|
'platform': 'tesla_fleet',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'components_disallow_charge_from_grid_with_solar_installed',
|
||||||
|
'unique_id': '123456-components_disallow_charge_from_grid_with_solar_installed',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.energy_site_allow_charging_from_grid-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Energy Site Allow charging from grid',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.energy_site_allow_charging_from_grid',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.energy_site_storm_watch-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.energy_site_storm_watch',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Storm watch',
|
||||||
|
'platform': 'tesla_fleet',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'user_settings_storm_mode_enabled',
|
||||||
|
'unique_id': '123456-user_settings_storm_mode_enabled',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.energy_site_storm_watch-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Energy Site Storm watch',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.energy_site_storm_watch',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.test_auto_seat_climate_left-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.test_auto_seat_climate_left',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Auto seat climate left',
|
||||||
|
'platform': 'tesla_fleet',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'climate_state_auto_seat_climate_left',
|
||||||
|
'unique_id': 'LRWXF7EK4KC700000-climate_state_auto_seat_climate_left',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.test_auto_seat_climate_left-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Test Auto seat climate left',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.test_auto_seat_climate_left',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.test_auto_seat_climate_right-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.test_auto_seat_climate_right',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Auto seat climate right',
|
||||||
|
'platform': 'tesla_fleet',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'climate_state_auto_seat_climate_right',
|
||||||
|
'unique_id': 'LRWXF7EK4KC700000-climate_state_auto_seat_climate_right',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.test_auto_seat_climate_right-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Test Auto seat climate right',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.test_auto_seat_climate_right',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.test_auto_steering_wheel_heater-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.test_auto_steering_wheel_heater',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Auto steering wheel heater',
|
||||||
|
'platform': 'tesla_fleet',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'climate_state_auto_steering_wheel_heat',
|
||||||
|
'unique_id': 'LRWXF7EK4KC700000-climate_state_auto_steering_wheel_heat',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.test_auto_steering_wheel_heater-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Test Auto steering wheel heater',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.test_auto_steering_wheel_heater',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.test_charge-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.test_charge',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Charge',
|
||||||
|
'platform': 'tesla_fleet',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'charge_state_user_charge_enable_request',
|
||||||
|
'unique_id': 'LRWXF7EK4KC700000-charge_state_user_charge_enable_request',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.test_charge-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Test Charge',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.test_charge',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.test_defrost-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.test_defrost',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Defrost',
|
||||||
|
'platform': 'tesla_fleet',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'climate_state_defrost_mode',
|
||||||
|
'unique_id': 'LRWXF7EK4KC700000-climate_state_defrost_mode',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.test_defrost-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Test Defrost',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.test_defrost',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.test_sentry_mode-entry]
|
||||||
|
EntityRegistryEntrySnapshot({
|
||||||
|
'aliases': set({
|
||||||
|
}),
|
||||||
|
'area_id': None,
|
||||||
|
'capabilities': None,
|
||||||
|
'config_entry_id': <ANY>,
|
||||||
|
'device_class': None,
|
||||||
|
'device_id': <ANY>,
|
||||||
|
'disabled_by': None,
|
||||||
|
'domain': 'switch',
|
||||||
|
'entity_category': None,
|
||||||
|
'entity_id': 'switch.test_sentry_mode',
|
||||||
|
'has_entity_name': True,
|
||||||
|
'hidden_by': None,
|
||||||
|
'icon': None,
|
||||||
|
'id': <ANY>,
|
||||||
|
'labels': set({
|
||||||
|
}),
|
||||||
|
'name': None,
|
||||||
|
'options': dict({
|
||||||
|
}),
|
||||||
|
'original_device_class': <SwitchDeviceClass.SWITCH: 'switch'>,
|
||||||
|
'original_icon': None,
|
||||||
|
'original_name': 'Sentry mode',
|
||||||
|
'platform': 'tesla_fleet',
|
||||||
|
'previous_unique_id': None,
|
||||||
|
'supported_features': 0,
|
||||||
|
'translation_key': 'vehicle_state_sentry_mode',
|
||||||
|
'unique_id': 'LRWXF7EK4KC700000-vehicle_state_sentry_mode',
|
||||||
|
'unit_of_measurement': None,
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch[switch.test_sentry_mode-state]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Test Sentry mode',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.test_sentry_mode',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch_alt[switch.energy_site_allow_charging_from_grid-statealt]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Energy Site Allow charging from grid',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.energy_site_allow_charging_from_grid',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch_alt[switch.energy_site_storm_watch-statealt]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Energy Site Storm watch',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.energy_site_storm_watch',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch_alt[switch.test_auto_seat_climate_left-statealt]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Test Auto seat climate left',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.test_auto_seat_climate_left',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch_alt[switch.test_auto_seat_climate_right-statealt]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Test Auto seat climate right',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.test_auto_seat_climate_right',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch_alt[switch.test_auto_steering_wheel_heater-statealt]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Test Auto steering wheel heater',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.test_auto_steering_wheel_heater',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch_alt[switch.test_charge-statealt]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Test Charge',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.test_charge',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'on',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch_alt[switch.test_defrost-statealt]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Test Defrost',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.test_defrost',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_switch_alt[switch.test_sentry_mode-statealt]
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'device_class': 'switch',
|
||||||
|
'friendly_name': 'Test Sentry mode',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'switch.test_sentry_mode',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_reported': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'off',
|
||||||
|
})
|
||||||
|
# ---
|
194
tests/components/tesla_fleet/test_switch.py
Normal file
194
tests/components/tesla_fleet/test_switch.py
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
"""Test the tesla_fleet switch platform."""
|
||||||
|
|
||||||
|
from copy import deepcopy
|
||||||
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from syrupy import SnapshotAssertion
|
||||||
|
from tesla_fleet_api.exceptions import VehicleOffline
|
||||||
|
|
||||||
|
from homeassistant.components.switch import (
|
||||||
|
DOMAIN as SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
)
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_ENTITY_ID,
|
||||||
|
STATE_OFF,
|
||||||
|
STATE_ON,
|
||||||
|
STATE_UNKNOWN,
|
||||||
|
Platform,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
|
from homeassistant.helpers import entity_registry as er
|
||||||
|
|
||||||
|
from . import assert_entities, assert_entities_alt, setup_platform
|
||||||
|
from .const import COMMAND_OK, VEHICLE_DATA_ALT
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_switch(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
normal_config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Tests that the switch entities are correct."""
|
||||||
|
|
||||||
|
await setup_platform(hass, normal_config_entry, [Platform.SWITCH])
|
||||||
|
assert_entities(hass, normal_config_entry.entry_id, entity_registry, snapshot)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_switch_alt(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
mock_vehicle_data: AsyncMock,
|
||||||
|
normal_config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Tests that the switch entities are correct."""
|
||||||
|
|
||||||
|
mock_vehicle_data.return_value = VEHICLE_DATA_ALT
|
||||||
|
await setup_platform(hass, normal_config_entry, [Platform.SWITCH])
|
||||||
|
assert_entities_alt(hass, normal_config_entry.entry_id, entity_registry, snapshot)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_switch_offline(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_vehicle_data: AsyncMock,
|
||||||
|
normal_config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Tests that the switch entities are correct when offline."""
|
||||||
|
|
||||||
|
mock_vehicle_data.side_effect = VehicleOffline
|
||||||
|
await setup_platform(hass, normal_config_entry, [Platform.SWITCH])
|
||||||
|
state = hass.states.get("switch.test_auto_seat_climate_left")
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("name", "on", "off"),
|
||||||
|
[
|
||||||
|
("test_charge", "VehicleSpecific.charge_start", "VehicleSpecific.charge_stop"),
|
||||||
|
(
|
||||||
|
"test_auto_seat_climate_left",
|
||||||
|
"VehicleSpecific.remote_auto_seat_climate_request",
|
||||||
|
"VehicleSpecific.remote_auto_seat_climate_request",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"test_auto_seat_climate_right",
|
||||||
|
"VehicleSpecific.remote_auto_seat_climate_request",
|
||||||
|
"VehicleSpecific.remote_auto_seat_climate_request",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"test_auto_steering_wheel_heater",
|
||||||
|
"VehicleSpecific.remote_auto_steering_wheel_heat_climate_request",
|
||||||
|
"VehicleSpecific.remote_auto_steering_wheel_heat_climate_request",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"test_defrost",
|
||||||
|
"VehicleSpecific.set_preconditioning_max",
|
||||||
|
"VehicleSpecific.set_preconditioning_max",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"energy_site_storm_watch",
|
||||||
|
"EnergySpecific.storm_mode",
|
||||||
|
"EnergySpecific.storm_mode",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"energy_site_allow_charging_from_grid",
|
||||||
|
"EnergySpecific.grid_import_export",
|
||||||
|
"EnergySpecific.grid_import_export",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"test_sentry_mode",
|
||||||
|
"VehicleSpecific.set_sentry_mode",
|
||||||
|
"VehicleSpecific.set_sentry_mode",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_switch_services(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
name: str,
|
||||||
|
on: str,
|
||||||
|
off: str,
|
||||||
|
normal_config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Tests that the switch service calls work."""
|
||||||
|
|
||||||
|
await setup_platform(hass, normal_config_entry, [Platform.SWITCH])
|
||||||
|
|
||||||
|
entity_id = f"switch.{name}"
|
||||||
|
with patch(
|
||||||
|
f"homeassistant.components.tesla_fleet.{on}",
|
||||||
|
return_value=COMMAND_OK,
|
||||||
|
) as call:
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_ON,
|
||||||
|
{ATTR_ENTITY_ID: entity_id},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.state == STATE_ON
|
||||||
|
call.assert_called_once()
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
f"homeassistant.components.tesla_fleet.{off}",
|
||||||
|
return_value=COMMAND_OK,
|
||||||
|
) as call:
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: entity_id},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
state = hass.states.get(entity_id)
|
||||||
|
assert state.state == STATE_OFF
|
||||||
|
call.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_switch_no_scope(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
readonly_config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Tests that the switch entities are correct."""
|
||||||
|
|
||||||
|
await setup_platform(hass, readonly_config_entry, [Platform.SWITCH])
|
||||||
|
with pytest.raises(ServiceValidationError, match="Missing vehicle commands scope"):
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: "switch.test_auto_steering_wheel_heater"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_switch_no_signing(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entity_registry: er.EntityRegistry,
|
||||||
|
normal_config_entry: MockConfigEntry,
|
||||||
|
mock_products: AsyncMock,
|
||||||
|
) -> None:
|
||||||
|
"""Tests that the switch entities are correct."""
|
||||||
|
|
||||||
|
# Make the vehicle require command signing
|
||||||
|
products = deepcopy(mock_products.return_value)
|
||||||
|
products["response"][0]["command_signing"] = "required"
|
||||||
|
mock_products.return_value = products
|
||||||
|
|
||||||
|
await setup_platform(hass, normal_config_entry, [Platform.SWITCH])
|
||||||
|
with pytest.raises(
|
||||||
|
ServiceValidationError,
|
||||||
|
match="Vehicle requires command signing. Please see documentation for more details",
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
SWITCH_DOMAIN,
|
||||||
|
SERVICE_TURN_OFF,
|
||||||
|
{ATTR_ENTITY_ID: "switch.test_auto_steering_wheel_heater"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user