mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 04:07:08 +00:00
Re-add "deactivate air conditioning" button to bmw_connected_drive (#94765)
This commit is contained in:
parent
2092bd225d
commit
f1a54a510c
@ -124,7 +124,6 @@ omit =
|
|||||||
homeassistant/components/bluetooth_tracker/*
|
homeassistant/components/bluetooth_tracker/*
|
||||||
homeassistant/components/bmw_connected_drive/__init__.py
|
homeassistant/components/bmw_connected_drive/__init__.py
|
||||||
homeassistant/components/bmw_connected_drive/binary_sensor.py
|
homeassistant/components/bmw_connected_drive/binary_sensor.py
|
||||||
homeassistant/components/bmw_connected_drive/button.py
|
|
||||||
homeassistant/components/bmw_connected_drive/coordinator.py
|
homeassistant/components/bmw_connected_drive/coordinator.py
|
||||||
homeassistant/components/bmw_connected_drive/lock.py
|
homeassistant/components/bmw_connected_drive/lock.py
|
||||||
homeassistant/components/bmw_connected_drive/notify.py
|
homeassistant/components/bmw_connected_drive/notify.py
|
||||||
|
@ -34,6 +34,7 @@ class BMWButtonEntityDescription(ButtonEntityDescription):
|
|||||||
[MyBMWVehicle], Coroutine[Any, Any, RemoteServiceStatus]
|
[MyBMWVehicle], Coroutine[Any, Any, RemoteServiceStatus]
|
||||||
] | None = None
|
] | None = None
|
||||||
account_function: Callable[[BMWDataUpdateCoordinator], Coroutine] | None = None
|
account_function: Callable[[BMWDataUpdateCoordinator], Coroutine] | None = None
|
||||||
|
is_available: Callable[[MyBMWVehicle], bool] = lambda _: True
|
||||||
|
|
||||||
|
|
||||||
BUTTON_TYPES: tuple[BMWButtonEntityDescription, ...] = (
|
BUTTON_TYPES: tuple[BMWButtonEntityDescription, ...] = (
|
||||||
@ -55,6 +56,13 @@ BUTTON_TYPES: tuple[BMWButtonEntityDescription, ...] = (
|
|||||||
icon="mdi:hvac",
|
icon="mdi:hvac",
|
||||||
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning(),
|
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning(),
|
||||||
),
|
),
|
||||||
|
BMWButtonEntityDescription(
|
||||||
|
key="deactivate_air_conditioning",
|
||||||
|
icon="mdi:hvac-off",
|
||||||
|
name="Deactivate air conditioning",
|
||||||
|
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning_stop(),
|
||||||
|
is_available=lambda vehicle: vehicle.is_remote_climate_stop_enabled,
|
||||||
|
),
|
||||||
BMWButtonEntityDescription(
|
BMWButtonEntityDescription(
|
||||||
key="find_vehicle",
|
key="find_vehicle",
|
||||||
translation_key="find_vehicle",
|
translation_key="find_vehicle",
|
||||||
@ -86,7 +94,7 @@ async def async_setup_entry(
|
|||||||
[
|
[
|
||||||
BMWButton(coordinator, vehicle, description)
|
BMWButton(coordinator, vehicle, description)
|
||||||
for description in BUTTON_TYPES
|
for description in BUTTON_TYPES
|
||||||
if not coordinator.read_only
|
if (not coordinator.read_only and description.is_available(vehicle))
|
||||||
or (coordinator.read_only and description.enabled_when_read_only)
|
or (coordinator.read_only and description.enabled_when_read_only)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
"""Tests for the for the BMW Connected Drive integration."""
|
"""Tests for the for the BMW Connected Drive integration."""
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from bimmer_connected.api.authentication import MyBMWAuthentication
|
from bimmer_connected.api.authentication import MyBMWAuthentication
|
||||||
from bimmer_connected.const import (
|
from bimmer_connected.const import (
|
||||||
|
REMOTE_SERVICE_POSITION_URL,
|
||||||
VEHICLE_CHARGING_DETAILS_URL,
|
VEHICLE_CHARGING_DETAILS_URL,
|
||||||
VEHICLE_STATE_URL,
|
VEHICLE_STATE_URL,
|
||||||
VEHICLES_URL,
|
VEHICLES_URL,
|
||||||
@ -115,6 +117,18 @@ def mock_vehicles() -> respx.Router:
|
|||||||
router.get(VEHICLE_CHARGING_DETAILS_URL).mock(
|
router.get(VEHICLE_CHARGING_DETAILS_URL).mock(
|
||||||
side_effect=vehicle_charging_sideeffect
|
side_effect=vehicle_charging_sideeffect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Get vehicle position after remote service
|
||||||
|
router.post(urlparse(REMOTE_SERVICE_POSITION_URL).netloc).mock(
|
||||||
|
httpx.Response(
|
||||||
|
200,
|
||||||
|
json=load_json_object_fixture(
|
||||||
|
FIXTURE_PATH / "remote_service" / "eventposition.json",
|
||||||
|
integration=BMW_DOMAIN,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return router
|
return router
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"positionData": {
|
||||||
|
"status": "OK",
|
||||||
|
"position": {
|
||||||
|
"latitude": 123.456,
|
||||||
|
"longitude": 34.5678,
|
||||||
|
"formattedAddress": "some_formatted_address",
|
||||||
|
"heading": 121
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"errorDetails": null
|
||||||
|
}
|
137
tests/components/bmw_connected_drive/snapshots/test_button.ambr
Normal file
137
tests/components/bmw_connected_drive/snapshots/test_button.ambr
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_entity_state_attrs
|
||||||
|
list([
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by MyBMW',
|
||||||
|
'friendly_name': 'i4 eDrive40 Flash lights',
|
||||||
|
'icon': 'mdi:car-light-alert',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'button.i4_edrive40_flash_lights',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
}),
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by MyBMW',
|
||||||
|
'friendly_name': 'i4 eDrive40 Sound horn',
|
||||||
|
'icon': 'mdi:bullhorn',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'button.i4_edrive40_sound_horn',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
}),
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by MyBMW',
|
||||||
|
'friendly_name': 'i4 eDrive40 Activate air conditioning',
|
||||||
|
'icon': 'mdi:hvac',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'button.i4_edrive40_activate_air_conditioning',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
}),
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by MyBMW',
|
||||||
|
'friendly_name': 'i4 eDrive40 Deactivate air conditioning',
|
||||||
|
'icon': 'mdi:hvac-off',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'button.i4_edrive40_deactivate_air_conditioning',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
}),
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by MyBMW',
|
||||||
|
'friendly_name': 'i4 eDrive40 Find vehicle',
|
||||||
|
'icon': 'mdi:crosshairs-question',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'button.i4_edrive40_find_vehicle',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
}),
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by MyBMW',
|
||||||
|
'friendly_name': 'i4 eDrive40 Refresh from cloud',
|
||||||
|
'icon': 'mdi:refresh',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'button.i4_edrive40_refresh_from_cloud',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
}),
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by MyBMW',
|
||||||
|
'friendly_name': 'i3 (+ REX) Flash lights',
|
||||||
|
'icon': 'mdi:car-light-alert',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'button.i3_rex_flash_lights',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
}),
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by MyBMW',
|
||||||
|
'friendly_name': 'i3 (+ REX) Sound horn',
|
||||||
|
'icon': 'mdi:bullhorn',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'button.i3_rex_sound_horn',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
}),
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by MyBMW',
|
||||||
|
'friendly_name': 'i3 (+ REX) Activate air conditioning',
|
||||||
|
'icon': 'mdi:hvac',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'button.i3_rex_activate_air_conditioning',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
}),
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by MyBMW',
|
||||||
|
'friendly_name': 'i3 (+ REX) Find vehicle',
|
||||||
|
'icon': 'mdi:crosshairs-question',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'button.i3_rex_find_vehicle',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
}),
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'attribution': 'Data provided by MyBMW',
|
||||||
|
'friendly_name': 'i3 (+ REX) Refresh from cloud',
|
||||||
|
'icon': 'mdi:refresh',
|
||||||
|
}),
|
||||||
|
'context': <ANY>,
|
||||||
|
'entity_id': 'button.i3_rex_refresh_from_cloud',
|
||||||
|
'last_changed': <ANY>,
|
||||||
|
'last_updated': <ANY>,
|
||||||
|
'state': 'unknown',
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
# ---
|
79
tests/components/bmw_connected_drive/test_button.py
Normal file
79
tests/components/bmw_connected_drive/test_button.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
"""Test BMW buttons."""
|
||||||
|
from bimmer_connected.vehicle.remote_services import RemoteServices
|
||||||
|
import pytest
|
||||||
|
import respx
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.components.bmw_connected_drive.coordinator import (
|
||||||
|
BMWDataUpdateCoordinator,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from . import setup_mocked_integration
|
||||||
|
|
||||||
|
|
||||||
|
async def test_entity_state_attrs(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
bmw_fixture: respx.Router,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Test button options and values."""
|
||||||
|
|
||||||
|
# Setup component
|
||||||
|
assert await setup_mocked_integration(hass)
|
||||||
|
|
||||||
|
# Get all button entities
|
||||||
|
assert hass.states.async_all("button") == snapshot
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("entity_id"),
|
||||||
|
[
|
||||||
|
("button.i4_edrive40_flash_lights"),
|
||||||
|
("button.i4_edrive40_sound_horn"),
|
||||||
|
("button.i4_edrive40_activate_air_conditioning"),
|
||||||
|
("button.i4_edrive40_deactivate_air_conditioning"),
|
||||||
|
("button.i4_edrive40_find_vehicle"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_update_triggers_success(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
entity_id: str,
|
||||||
|
bmw_fixture: respx.Router,
|
||||||
|
) -> None:
|
||||||
|
"""Test button press."""
|
||||||
|
|
||||||
|
# Setup component
|
||||||
|
assert await setup_mocked_integration(hass)
|
||||||
|
BMWDataUpdateCoordinator.async_update_listeners.reset_mock()
|
||||||
|
|
||||||
|
# Test
|
||||||
|
await hass.services.async_call(
|
||||||
|
"button",
|
||||||
|
"press",
|
||||||
|
blocking=True,
|
||||||
|
target={"entity_id": entity_id},
|
||||||
|
)
|
||||||
|
assert RemoteServices.trigger_remote_service.call_count == 1
|
||||||
|
assert BMWDataUpdateCoordinator.async_update_listeners.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
|
async def test_refresh_from_cloud(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
bmw_fixture: respx.Router,
|
||||||
|
) -> None:
|
||||||
|
"""Test button press for deprecated service."""
|
||||||
|
|
||||||
|
# Setup component
|
||||||
|
assert await setup_mocked_integration(hass)
|
||||||
|
BMWDataUpdateCoordinator.async_update_listeners.reset_mock()
|
||||||
|
|
||||||
|
# Test
|
||||||
|
await hass.services.async_call(
|
||||||
|
"button",
|
||||||
|
"press",
|
||||||
|
blocking=True,
|
||||||
|
target={"entity_id": "button.i4_edrive40_refresh_from_cloud"},
|
||||||
|
)
|
||||||
|
assert RemoteServices.trigger_remote_service.call_count == 0
|
||||||
|
assert BMWDataUpdateCoordinator.async_update_listeners.call_count == 2
|
Loading…
x
Reference in New Issue
Block a user