mirror of
https://github.com/home-assistant/core.git
synced 2026-07-03 20:56:06 +01:00
Add seat coolers to Teslemetry (#175422)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -176,6 +176,33 @@ VEHICLE_DESCRIPTIONS: tuple[TeslemetrySelectEntityDescription, ...] = (
|
||||
HIGH,
|
||||
],
|
||||
),
|
||||
TeslemetrySelectEntityDescription(
|
||||
# remote_seat_cooler_request uses 1-indexed positions (front-left=1,
|
||||
# front-right=2), unlike the 0-indexed Seat enum used for heaters.
|
||||
# Polled state comes from the seat_fan_front_* vehicle_data fields.
|
||||
key="climate_state_seat_fan_front_left",
|
||||
select_fn=lambda api, level: api.remote_seat_cooler_request(1, level),
|
||||
supported_fn=lambda data: bool(data.get("has_seat_cooling")),
|
||||
streaming_listener=lambda x, y: x.listen_ClimateSeatCoolingFrontLeft(y),
|
||||
options=[
|
||||
OFF,
|
||||
LOW,
|
||||
MEDIUM,
|
||||
HIGH,
|
||||
],
|
||||
),
|
||||
TeslemetrySelectEntityDescription(
|
||||
key="climate_state_seat_fan_front_right",
|
||||
select_fn=lambda api, level: api.remote_seat_cooler_request(2, level),
|
||||
supported_fn=lambda data: bool(data.get("has_seat_cooling")),
|
||||
streaming_listener=lambda x, y: x.listen_ClimateSeatCoolingFrontRight(y),
|
||||
options=[
|
||||
OFF,
|
||||
LOW,
|
||||
MEDIUM,
|
||||
HIGH,
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -363,6 +363,24 @@
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"climate_state_seat_fan_front_left": {
|
||||
"name": "Seat cooler front left",
|
||||
"state": {
|
||||
"high": "[%key:common::state::high%]",
|
||||
"low": "[%key:common::state::low%]",
|
||||
"medium": "[%key:common::state::medium%]",
|
||||
"off": "[%key:common::state::off%]"
|
||||
}
|
||||
},
|
||||
"climate_state_seat_fan_front_right": {
|
||||
"name": "Seat cooler front right",
|
||||
"state": {
|
||||
"high": "[%key:common::state::high%]",
|
||||
"low": "[%key:common::state::low%]",
|
||||
"medium": "[%key:common::state::medium%]",
|
||||
"off": "[%key:common::state::off%]"
|
||||
}
|
||||
},
|
||||
"climate_state_seat_heater_left": {
|
||||
"name": "Seat heater front left",
|
||||
"state": {
|
||||
|
||||
@@ -16,7 +16,7 @@ from homeassistant.components.select import (
|
||||
SERVICE_SELECT_OPTION,
|
||||
)
|
||||
from homeassistant.components.teslemetry.coordinator import ENERGY_INFO_INTERVAL
|
||||
from homeassistant.components.teslemetry.select import LOW
|
||||
from homeassistant.components.teslemetry.select import LEVEL, LOW, MEDIUM, OFF
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
@@ -183,6 +183,68 @@ async def test_select_services(hass: HomeAssistant, mock_vehicle_data) -> None:
|
||||
call.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("entity_id", "seat_position"),
|
||||
[
|
||||
("select.test_seat_cooler_front_left", 1),
|
||||
("select.test_seat_cooler_front_right", 2),
|
||||
],
|
||||
)
|
||||
async def test_seat_cooler_services(
|
||||
hass: HomeAssistant,
|
||||
mock_metadata: AsyncMock,
|
||||
mock_vehicle_data: AsyncMock,
|
||||
entity_id: str,
|
||||
seat_position: int,
|
||||
) -> None:
|
||||
"""Test the seat cooler entities send the 1-indexed seat position.
|
||||
|
||||
remote_seat_cooler_request is 1-indexed (front-left=1, front-right=2),
|
||||
unlike the 0-indexed Seat enum used for the seat heaters.
|
||||
"""
|
||||
mock_vehicle_data.return_value = VEHICLE_DATA_ALT
|
||||
metadata = deepcopy(METADATA)
|
||||
metadata["vehicles"][VEHICLE_VIN]["config"] = {"has_seat_cooling": True}
|
||||
mock_metadata.return_value = metadata
|
||||
|
||||
await setup_platform(hass, [Platform.SELECT])
|
||||
|
||||
with patch(
|
||||
"tesla_fleet_api.teslemetry.Vehicle.remote_seat_cooler_request",
|
||||
return_value=COMMAND_OK,
|
||||
) as call:
|
||||
await hass.services.async_call(
|
||||
SELECT_DOMAIN,
|
||||
SERVICE_SELECT_OPTION,
|
||||
{ATTR_ENTITY_ID: entity_id, ATTR_OPTION: LOW},
|
||||
blocking=True,
|
||||
)
|
||||
assert hass.states.get(entity_id).state == LOW
|
||||
call.assert_called_once_with(seat_position, LEVEL[LOW])
|
||||
|
||||
|
||||
async def test_seat_cooler_polling(
|
||||
hass: HomeAssistant,
|
||||
mock_metadata: AsyncMock,
|
||||
mock_vehicle_data: AsyncMock,
|
||||
) -> None:
|
||||
"""Test the seat cooler entities read polled state from seat_fan_front_*."""
|
||||
metadata = deepcopy(METADATA)
|
||||
metadata["vehicles"][VEHICLE_VIN]["polling"] = True
|
||||
metadata["vehicles"][VEHICLE_VIN]["config"] = {"has_seat_cooling": True}
|
||||
mock_metadata.return_value = metadata
|
||||
|
||||
data = deepcopy(VEHICLE_DATA_ALT)
|
||||
data["response"]["climate_state"]["seat_fan_front_left"] = 2
|
||||
data["response"]["climate_state"]["seat_fan_front_right"] = 0
|
||||
mock_vehicle_data.return_value = data
|
||||
|
||||
await setup_platform(hass, [Platform.SELECT])
|
||||
|
||||
assert hass.states.get("select.test_seat_cooler_front_left").state == MEDIUM
|
||||
assert hass.states.get("select.test_seat_cooler_front_right").state == OFF
|
||||
|
||||
|
||||
@pytest.mark.parametrize("response", COMMAND_ERRORS)
|
||||
async def test_select_command_errors(
|
||||
hass: HomeAssistant, mock_vehicle_data: AsyncMock, response: dict
|
||||
|
||||
Reference in New Issue
Block a user