Skip to content

Commit

Permalink
Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Bre77 committed May 15, 2024
1 parent ad8069c commit e9935bd
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 51 deletions.
2 changes: 1 addition & 1 deletion homeassistant/components/teslemetry/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@
"components_disallow_charge_from_grid_with_solar_installed": {
"name": "Allow charging from grid"
},
"storm_mode_enabled": {
"user_settings_storm_mode_enabled": {
"name": "Storm mode"
},
"vehicle_state_sentry_mode": {
Expand Down
76 changes: 36 additions & 40 deletions homeassistant/components/teslemetry/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .entity import (
TeslemetryEnergyInfoEntity,
TeslemetryEnergyLiveEntity,
TeslemetryVehicleEntity,
)
from .entity import TeslemetryEnergyInfoEntity, TeslemetryVehicleEntity
from .models import TeslemetryEnergyData, TeslemetryVehicleData


Expand Down Expand Up @@ -114,11 +110,6 @@ async def async_setup_entry(
)
for vehicle in entry.runtime_data.vehicles
),
(
TeslemetryStormModeSwitchEntity(energysite, entry.runtime_data.scopes)
for energysite in entry.runtime_data.energysites
if energysite.info_coordinator.data.get("components_storm_mode_capable")
),
(
TeslemetryChargeFromGridSwitchEntity(
energysite,
Expand All @@ -128,6 +119,11 @@ async def async_setup_entry(
if energysite.info_coordinator.data.get("components_battery")
and energysite.info_coordinator.data.get("components_solar")
),
(
TeslemetryStormModeSwitchEntity(energysite, entry.runtime_data.scopes)
for energysite in entry.runtime_data.energysites
if energysite.info_coordinator.data.get("components_storm_mode_capable")
),
)
)

Expand Down Expand Up @@ -157,7 +153,8 @@ def _async_update_attrs(self) -> None:
"""Update the attributes of the sensor."""
if self._value is None:
self._attr_is_on = None
self._attr_is_on = bool(self._value)
else:
self._attr_is_on = bool(self._value)

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the Switch."""
Expand Down Expand Up @@ -187,81 +184,80 @@ def _async_update_attrs(self) -> None:
self._attr_is_on = self._value


class TeslemetryStormModeSwitchEntity(
TeslemetryEnergyLiveEntity, TeslemetrySwitchEntity
class TeslemetryChargeFromGridSwitchEntity(
TeslemetryEnergyInfoEntity, TeslemetrySwitchEntity
):
"""Entity class for Storm Mode switch."""
"""Entity class for Charge From Grid switch."""

def __init__(
self,
data: TeslemetryEnergyData,
scopes: list[Scope],
) -> None:
"""Initialize the Switch."""
super().__init__(data, "storm_mode_enabled")
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 sensor."""
if self._value is None:
self._attr_is_on = None
self._attr_is_on = bool(self._value)
"""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_scope()
await self.handle_command(self.api.storm_mode(enabled=True))
await self.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_scope()
await self.handle_command(self.api.storm_mode(enabled=False))
await self.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 TeslemetryChargeFromGridSwitchEntity(
class TeslemetryStormModeSwitchEntity(
TeslemetryEnergyInfoEntity, TeslemetrySwitchEntity
):
"""Entity class for Charge From Grid switch."""
"""Entity class for Storm Mode switch."""

def __init__(
self,
data: TeslemetryEnergyData,
scopes: list[Scope],
) -> None:
"""Initialize the Switch."""
super().__init__(data, "user_settings_storm_mode_enabled")
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)
"""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_scope()
await self.handle_command(
self.api.grid_import_export(
disallow_charge_from_grid_with_solar_installed=False
)
)
await self.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_scope()
await self.handle_command(
self.api.grid_import_export(
disallow_charge_from_grid_with_solar_installed=True
)
)
await self.handle_command(self.api.storm_mode(enabled=False))
self._attr_is_on = False
self.async_write_ha_state()
10 changes: 7 additions & 3 deletions tests/components/teslemetry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,20 @@ def assert_entities(
entry_id: str,
entity_registry: er.EntityRegistry,
snapshot: SnapshotAssertion,
assert_entry: bool = True,
assert_state: bool = True,
) -> None:
"""Test that all entities match their snapshot."""

entity_entries = er.async_entries_for_config_entry(entity_registry, entry_id)

assert entity_entries
for entity_entry in entity_entries:
assert entity_entry == snapshot(name=f"{entity_entry.entity_id}-entry")
assert (state := hass.states.get(entity_entry.entity_id))
assert state == snapshot(name=f"{entity_entry.entity_id}-state")
if assert_entry:
assert entity_entry == snapshot(name=f"{entity_entry.entity_id}-entry")
if assert_state:
assert (state := hass.states.get(entity_entry.entity_id))
assert state == snapshot(name=f"{entity_entry.entity_id}-state")


def assert_entities_alt(
Expand Down
2 changes: 1 addition & 1 deletion tests/components/teslemetry/fixtures/vehicle_data_alt.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"timestamp": null,
"trip_charging": false,
"usable_battery_level": 77,
"user_charge_enable_request": null
"user_charge_enable_request": true
},
"climate_state": {
"allow_cabin_overheat_protection": true,
Expand Down
132 changes: 129 additions & 3 deletions tests/components/teslemetry/snapshots/test_switch.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@
'platform': 'teslemetry',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'storm_mode_enabled',
'unique_id': '123456-storm_mode_enabled',
'translation_key': 'user_settings_storm_mode_enabled',
'unique_id': '123456-user_settings_storm_mode_enabled',
'unit_of_measurement': None,
})
# ---
Expand All @@ -90,7 +90,7 @@
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
'state': 'on',
})
# ---
# name: test_switch[switch.test_auto_seat_climate_left-entry]
Expand Down Expand Up @@ -422,3 +422,129 @@
'state': 'off',
})
# ---
# name: test_switch_alt[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_alt[switch.energy_site_storm_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'switch',
'friendly_name': 'Energy Site Storm mode',
}),
'context': <ANY>,
'entity_id': 'switch.energy_site_storm_mode',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'on',
})
# ---
# name: test_switch_alt[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': 'off',
})
# ---
# name: test_switch_alt[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': 'off',
})
# ---
# name: test_switch_alt[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_alt[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_alt[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_alt[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.test_valet_mode-state]
StateSnapshot({
'attributes': ReadOnlyDict({
'device_class': 'switch',
'friendly_name': 'Test Valet mode',
}),
'context': <ANY>,
'entity_id': 'switch.test_valet_mode',
'last_changed': <ANY>,
'last_reported': <ANY>,
'last_updated': <ANY>,
'state': 'off',
})
# ---
19 changes: 16 additions & 3 deletions tests/components/teslemetry/test_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from homeassistant.helpers import entity_registry as er

from . import assert_entities, setup_platform
from .const import COMMAND_OK
from .const import COMMAND_OK, VEHICLE_DATA_ALT


async def test_switch(
Expand All @@ -36,6 +36,19 @@ async def test_switch(
assert_entities(hass, entry.entry_id, entity_registry, snapshot)


async def test_switch_alt(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
mock_vehicle_data,
) -> None:
"""Tests that the switch entities are correct."""

mock_vehicle_data.return_value = VEHICLE_DATA_ALT
entry = await setup_platform(hass, [Platform.SWITCH])
assert_entities(hass, entry.entry_id, entity_registry, snapshot, assert_entry=False)


async def test_switch_offline(
hass: HomeAssistant,
mock_vehicle_data,
Expand Down Expand Up @@ -74,12 +87,12 @@ async def test_switch_offline(
"VehicleSpecific.set_preconditioning_max",
),
(
"energy_site_allow_charging_from_grid",
"energy_site_storm_mode",
"EnergySpecific.storm_mode",
"EnergySpecific.storm_mode",
),
(
"energy_site_storm_mode",
"energy_site_allow_charging_from_grid",
"EnergySpecific.grid_import_export",
"EnergySpecific.grid_import_export",
),
Expand Down

0 comments on commit e9935bd

Please sign in to comment.