Add Select platform to Tessie (#105423)
* Add select platform * Add error coverage * Fix case * fix value * Remove virtual key issue * Add TessieSeatHeaterOptions enum and update TessieSeatHeaterSelectEntity options * use ENUM in tests * Porting other fixes * Update entity
This commit is contained in:
parent
126f0e4047
commit
dbb726f41f
5 changed files with 204 additions and 1 deletions
|
@ -14,7 +14,13 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
|||
from .const import DOMAIN
|
||||
from .coordinator import TessieDataUpdateCoordinator
|
||||
|
||||
PLATFORMS = [Platform.BINARY_SENSOR, Platform.CLIMATE, Platform.SENSOR, Platform.SWITCH]
|
||||
PLATFORMS = [
|
||||
Platform.BINARY_SENSOR,
|
||||
Platform.CLIMATE,
|
||||
Platform.SELECT,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
@ -20,6 +20,15 @@ class TessieStatus(StrEnum):
|
|||
ONLINE = "online"
|
||||
|
||||
|
||||
class TessieSeatHeaterOptions(StrEnum):
|
||||
"""Tessie seat heater options."""
|
||||
|
||||
OFF = "off"
|
||||
LOW = "low"
|
||||
MEDIUM = "medium"
|
||||
HIGH = "high"
|
||||
|
||||
|
||||
class TessieClimateKeeper(StrEnum):
|
||||
"""Tessie Climate Keeper Modes."""
|
||||
|
||||
|
|
58
homeassistant/components/tessie/select.py
Normal file
58
homeassistant/components/tessie/select.py
Normal file
|
@ -0,0 +1,58 @@
|
|||
"""Select platform for Tessie integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from tessie_api import set_seat_heat
|
||||
|
||||
from homeassistant.components.select import SelectEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import DOMAIN, TessieSeatHeaterOptions
|
||||
from .entity import TessieEntity
|
||||
|
||||
SEAT_HEATERS = {
|
||||
"climate_state_seat_heater_left": "front_left",
|
||||
"climate_state_seat_heater_right": "front_right",
|
||||
"climate_state_seat_heater_rear_left": "rear_left",
|
||||
"climate_state_seat_heater_rear_center": "rear_center",
|
||||
"climate_state_seat_heater_rear_right": "rear_right",
|
||||
"climate_state_seat_heater_third_row_left": "third_row_left",
|
||||
"climate_state_seat_heater_third_row_right": "third_row_right",
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up the Tessie select platform from a config entry."""
|
||||
coordinators = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
TessieSeatHeaterSelectEntity(coordinator, key)
|
||||
for coordinator in coordinators
|
||||
for key in SEAT_HEATERS
|
||||
if key in coordinator.data
|
||||
)
|
||||
|
||||
|
||||
class TessieSeatHeaterSelectEntity(TessieEntity, SelectEntity):
|
||||
"""Select entity for current charge."""
|
||||
|
||||
_attr_options = [
|
||||
TessieSeatHeaterOptions.OFF,
|
||||
TessieSeatHeaterOptions.LOW,
|
||||
TessieSeatHeaterOptions.MEDIUM,
|
||||
TessieSeatHeaterOptions.HIGH,
|
||||
]
|
||||
|
||||
@property
|
||||
def current_option(self) -> str | None:
|
||||
"""Return the current selected option."""
|
||||
return self._attr_options[self._value]
|
||||
|
||||
async def async_select_option(self, option: str) -> None:
|
||||
"""Change the selected option."""
|
||||
level = self._attr_options.index(option)
|
||||
await self.run(set_seat_heat, seat=SEAT_HEATERS[self.key], level=level)
|
||||
self.set((self.key, level))
|
|
@ -102,6 +102,71 @@
|
|||
"name": "Passenger temperature setting"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"climate_state_seat_heater_left": {
|
||||
"name": "Seat heater left",
|
||||
"state": {
|
||||
"off": "[%key:common::state::off%]",
|
||||
"low": "Low",
|
||||
"medium": "Medium",
|
||||
"high": "High"
|
||||
}
|
||||
},
|
||||
"climate_state_seat_heater_right": {
|
||||
"name": "Seat heater right",
|
||||
"state": {
|
||||
"off": "[%key:common::state::off%]",
|
||||
"low": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::low%]",
|
||||
"medium": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::medium%]",
|
||||
"high": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::high%]"
|
||||
}
|
||||
},
|
||||
"climate_state_seat_heater_rear_left": {
|
||||
"name": "Seat heater rear left",
|
||||
"state": {
|
||||
"off": "[%key:common::state::off%]",
|
||||
"low": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::low%]",
|
||||
"medium": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::medium%]",
|
||||
"high": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::high%]"
|
||||
}
|
||||
},
|
||||
"climate_state_seat_heater_rear_center": {
|
||||
"name": "Seat heater rear center",
|
||||
"state": {
|
||||
"off": "[%key:common::state::off%]",
|
||||
"low": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::low%]",
|
||||
"medium": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::medium%]",
|
||||
"high": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::high%]"
|
||||
}
|
||||
},
|
||||
"climate_state_seat_heater_rear_right": {
|
||||
"name": "Seat heater rear right",
|
||||
"state": {
|
||||
"off": "[%key:common::state::off%]",
|
||||
"low": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::low%]",
|
||||
"medium": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::medium%]",
|
||||
"high": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::high%]"
|
||||
}
|
||||
},
|
||||
"climate_state_seat_heater_third_row_left": {
|
||||
"name": "Seat heater third row left",
|
||||
"state": {
|
||||
"off": "[%key:common::state::off%]",
|
||||
"low": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::low%]",
|
||||
"medium": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::medium%]",
|
||||
"high": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::high%]"
|
||||
}
|
||||
},
|
||||
"climate_state_seat_heater_third_row_right": {
|
||||
"name": "Seat heater third row right",
|
||||
"state": {
|
||||
"off": "[%key:common::state::off%]",
|
||||
"low": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::low%]",
|
||||
"medium": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::medium%]",
|
||||
"high": "[%key:component::tessie::entity::select::climate_state_seat_heater_left::state::high%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"binary_sensor": {
|
||||
"state": {
|
||||
"name": "Status"
|
||||
|
|
65
tests/components/tessie/test_select.py
Normal file
65
tests/components/tessie/test_select.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
"""Test the Tessie select platform."""
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.select import (
|
||||
DOMAIN as SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
)
|
||||
from homeassistant.components.tessie.const import TessieSeatHeaterOptions
|
||||
from homeassistant.const import ATTR_ENTITY_ID, ATTR_OPTION, STATE_OFF
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from .common import ERROR_UNKNOWN, TEST_RESPONSE, setup_platform
|
||||
|
||||
|
||||
async def test_select(hass: HomeAssistant) -> None:
|
||||
"""Tests that the select entity is correct."""
|
||||
|
||||
assert len(hass.states.async_all(SELECT_DOMAIN)) == 0
|
||||
|
||||
await setup_platform(hass)
|
||||
|
||||
assert len(hass.states.async_all(SELECT_DOMAIN)) == 5
|
||||
|
||||
entity_id = "select.test_seat_heater_left"
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
|
||||
# Test changing select
|
||||
with patch(
|
||||
"homeassistant.components.tessie.select.set_seat_heat",
|
||||
return_value=TEST_RESPONSE,
|
||||
) as mock_set:
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{ATTR_ENTITY_ID: [entity_id], ATTR_OPTION: TessieSeatHeaterOptions.LOW},
|
||||
blocking=True,
|
||||
)
|
||||
mock_set.assert_called_once()
|
||||
assert mock_set.call_args[1]["seat"] == "front_left"
|
||||
assert mock_set.call_args[1]["level"] == 1
|
||||
assert hass.states.get(entity_id).state == TessieSeatHeaterOptions.LOW
|
||||
|
||||
|
||||
async def test_errors(hass: HomeAssistant) -> None:
|
||||
"""Tests unknown error is handled."""
|
||||
|
||||
await setup_platform(hass)
|
||||
entity_id = "select.test_seat_heater_left"
|
||||
|
||||
# Test setting cover open with unknown error
|
||||
with patch(
|
||||
"homeassistant.components.tessie.select.set_seat_heat",
|
||||
side_effect=ERROR_UNKNOWN,
|
||||
) as mock_set, pytest.raises(HomeAssistantError) as error:
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{ATTR_ENTITY_ID: [entity_id], ATTR_OPTION: TessieSeatHeaterOptions.LOW},
|
||||
blocking=True,
|
||||
)
|
||||
mock_set.assert_called_once()
|
||||
assert error.from_exception == ERROR_UNKNOWN
|
Loading…
Add table
Reference in a new issue