Remove HVAC Modes when no scopes in Teslemetry (#124612)

* Remove modes when not scoped

* Fix inits

* Re-add raise

* Remove unused raise_for_scope

* Set hvac_modes when not scoped

* tests
This commit is contained in:
Brett Adams 2024-09-01 00:28:35 +10:00 committed by GitHub
parent 2a8feda691
commit 65f007ace7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 112 additions and 18 deletions

View File

@ -84,8 +84,10 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity):
) -> None: ) -> None:
"""Initialize the climate.""" """Initialize the climate."""
self.scoped = Scope.VEHICLE_CMDS in scopes self.scoped = Scope.VEHICLE_CMDS in scopes
if not self.scoped: if not self.scoped:
self._attr_supported_features = ClimateEntityFeature(0) self._attr_supported_features = ClimateEntityFeature(0)
self._attr_hvac_modes = []
super().__init__( super().__init__(
data, data,
@ -102,6 +104,10 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity):
else: else:
self._attr_hvac_mode = HVACMode.OFF self._attr_hvac_mode = HVACMode.OFF
# If not scoped, prevent the user from changing the HVAC mode by making it the only option
if self._attr_hvac_mode and not self.scoped:
self._attr_hvac_modes = [self._attr_hvac_mode]
self._attr_current_temperature = self.get("climate_state_inside_temp") self._attr_current_temperature = self.get("climate_state_inside_temp")
self._attr_target_temperature = self.get(f"climate_state_{self.key}_setting") self._attr_target_temperature = self.get(f"climate_state_{self.key}_setting")
self._attr_preset_mode = self.get("climate_state_climate_keeper_mode") self._attr_preset_mode = self.get("climate_state_climate_keeper_mode")
@ -114,7 +120,6 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity):
async def async_turn_on(self) -> None: async def async_turn_on(self) -> None:
"""Set the climate state to on.""" """Set the climate state to on."""
self.raise_for_scope() self.raise_for_scope()
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command(self.api.auto_conditioning_start()) await handle_vehicle_command(self.api.auto_conditioning_start())
@ -124,7 +129,6 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity):
async def async_turn_off(self) -> None: async def async_turn_off(self) -> None:
"""Set the climate state to off.""" """Set the climate state to off."""
self.raise_for_scope() self.raise_for_scope()
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command(self.api.auto_conditioning_stop()) await handle_vehicle_command(self.api.auto_conditioning_stop())
@ -135,7 +139,6 @@ class TeslemetryClimateEntity(TeslemetryVehicleEntity, ClimateEntity):
async def async_set_temperature(self, **kwargs: Any) -> None: async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the climate temperature.""" """Set the climate temperature."""
if temp := kwargs.get(ATTR_TEMPERATURE): if temp := kwargs.get(ATTR_TEMPERATURE):
await self.wake_up_if_asleep() await self.wake_up_if_asleep()
await handle_vehicle_command( await handle_vehicle_command(
@ -206,19 +209,20 @@ class TeslemetryCabinOverheatProtectionEntity(TeslemetryVehicleEntity, ClimateEn
) -> None: ) -> None:
"""Initialize the climate.""" """Initialize the climate."""
super().__init__(data, "climate_state_cabin_overheat_protection") self.scoped = Scope.VEHICLE_CMDS in scopes
if self.scoped:
# Supported Features
self._attr_supported_features = ( self._attr_supported_features = (
ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF
) )
if self.get("vehicle_config_cop_user_set_temp_supported"): else:
self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE
# Scopes
self.scoped = Scope.VEHICLE_CMDS in scopes
if not self.scoped:
self._attr_supported_features = ClimateEntityFeature(0) self._attr_supported_features = ClimateEntityFeature(0)
self._attr_hvac_modes = []
super().__init__(data, "climate_state_cabin_overheat_protection")
# Supported Features from data
if self.scoped and self.get("vehicle_config_cop_user_set_temp_supported"):
self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE
def _async_update_attrs(self) -> None: def _async_update_attrs(self) -> None:
"""Update the attributes of the entity.""" """Update the attributes of the entity."""
@ -228,6 +232,10 @@ class TeslemetryCabinOverheatProtectionEntity(TeslemetryVehicleEntity, ClimateEn
else: else:
self._attr_hvac_mode = COP_MODES.get(state) self._attr_hvac_mode = COP_MODES.get(state)
# If not scoped, prevent the user from changing the HVAC mode by making it the only option
if self._attr_hvac_mode and not self.scoped:
self._attr_hvac_modes = [self._attr_hvac_mode]
if (level := self.get("climate_state_cop_activation_temperature")) is None: if (level := self.get("climate_state_cop_activation_temperature")) is None:
self._attr_target_temperature = None self._attr_target_temperature = None
else: else:
@ -245,8 +253,6 @@ class TeslemetryCabinOverheatProtectionEntity(TeslemetryVehicleEntity, ClimateEn
async def async_set_temperature(self, **kwargs: Any) -> None: async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the climate temperature.""" """Set the climate temperature."""
self.raise_for_scope()
if not (temp := kwargs.get(ATTR_TEMPERATURE)): if not (temp := kwargs.get(ATTR_TEMPERATURE)):
return return

View File

@ -280,6 +280,85 @@
'state': 'off', 'state': 'off',
}) })
# --- # ---
# name: test_climate_noscope[climate.test_cabin_overheat_protection-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'hvac_modes': list([
<HVACMode.COOL: 'cool'>,
]),
'max_temp': 40,
'min_temp': 30,
'target_temp_step': 5,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': <RegistryEntryDisabler.INTEGRATION: 'integration'>,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.test_cabin_overheat_protection',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Cabin overheat protection',
'platform': 'teslemetry',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': 'climate_state_cabin_overheat_protection',
'unique_id': 'LRWXF7EK4KC700000-climate_state_cabin_overheat_protection',
'unit_of_measurement': None,
})
# ---
# name: test_climate_noscope[climate.test_climate-entry]
EntityRegistryEntrySnapshot({
'aliases': set({
}),
'area_id': None,
'capabilities': dict({
'hvac_modes': list([
<HVACMode.HEAT_COOL: 'heat_cool'>,
]),
'max_temp': 28.0,
'min_temp': 15.0,
}),
'config_entry_id': <ANY>,
'device_class': None,
'device_id': <ANY>,
'disabled_by': None,
'domain': 'climate',
'entity_category': None,
'entity_id': 'climate.test_climate',
'has_entity_name': True,
'hidden_by': None,
'icon': None,
'id': <ANY>,
'labels': set({
}),
'name': None,
'options': dict({
}),
'original_device_class': None,
'original_icon': None,
'original_name': 'Climate',
'platform': 'teslemetry',
'previous_unique_id': None,
'supported_features': 0,
'translation_key': <TeslemetryClimateSide.DRIVER: 'driver_temp'>,
'unique_id': 'LRWXF7EK4KC700000-driver_temp',
'unit_of_measurement': None,
})
# ---
# name: test_climate_offline[climate.test_cabin_overheat_protection-entry] # name: test_climate_offline[climate.test_cabin_overheat_protection-entry]
EntityRegistryEntrySnapshot({ EntityRegistryEntrySnapshot({
'aliases': set({ 'aliases': set({

View File

@ -1,6 +1,6 @@
"""Test the Teslemetry climate platform.""" """Test the Teslemetry climate platform."""
from unittest.mock import patch from unittest.mock import AsyncMock, patch
from freezegun.api import FrozenDateTimeFactory from freezegun.api import FrozenDateTimeFactory
import pytest import pytest
@ -371,12 +371,21 @@ async def test_asleep_or_offline(
async def test_climate_noscope( async def test_climate_noscope(
hass: HomeAssistant, hass: HomeAssistant,
mock_metadata, snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
mock_metadata: AsyncMock,
) -> None: ) -> None:
"""Tests that the climate entity is correct.""" """Tests that the climate entity is correct."""
mock_metadata.return_value = METADATA_NOSCOPE mock_metadata.return_value = METADATA_NOSCOPE
await setup_platform(hass, [Platform.CLIMATE]) entry = await setup_platform(hass, [Platform.CLIMATE])
entity_entries = er.async_entries_for_config_entry(entity_registry, entry.entry_id)
assert entity_entries
for entity_entry in entity_entries:
assert entity_entry == snapshot(name=f"{entity_entry.entity_id}-entry")
entity_id = "climate.test_climate" entity_id = "climate.test_climate"
with pytest.raises(ServiceValidationError): with pytest.raises(ServiceValidationError):