From e9dec5d03a95762d030e04b3b6b57aef68c887c2 Mon Sep 17 00:00:00 2001 From: rikroe <42204099+rikroe@users.noreply.github.com> Date: Thu, 6 Jan 2022 12:05:25 +0100 Subject: [PATCH] Add button entities to bmw_connected_drive (#63136) Co-authored-by: Gerard Co-authored-by: Franck Nijhof Co-authored-by: rikroe --- .coveragerc | 1 + .../bmw_connected_drive/__init__.py | 6 + .../components/bmw_connected_drive/button.py | 122 ++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 homeassistant/components/bmw_connected_drive/button.py diff --git a/.coveragerc b/.coveragerc index 199efa7d3dc..d987c63b516 100644 --- a/.coveragerc +++ b/.coveragerc @@ -120,6 +120,7 @@ omit = homeassistant/components/bmp280/sensor.py homeassistant/components/bmw_connected_drive/__init__.py homeassistant/components/bmw_connected_drive/binary_sensor.py + homeassistant/components/bmw_connected_drive/button.py homeassistant/components/bmw_connected_drive/device_tracker.py homeassistant/components/bmw_connected_drive/lock.py homeassistant/components/bmw_connected_drive/notify.py diff --git a/homeassistant/components/bmw_connected_drive/__init__.py b/homeassistant/components/bmw_connected_drive/__init__.py index e681cac8223..d5c3e527a5b 100644 --- a/homeassistant/components/bmw_connected_drive/__init__.py +++ b/homeassistant/components/bmw_connected_drive/__init__.py @@ -68,6 +68,7 @@ DEFAULT_OPTIONS = { PLATFORMS = [ Platform.BINARY_SENSOR, + Platform.BUTTON, Platform.DEVICE_TRACKER, Platform.LOCK, Platform.NOTIFY, @@ -223,6 +224,11 @@ def setup_account( def execute_service(call: ServiceCall) -> None: """Execute a service for a vehicle.""" + _LOGGER.warning( + "BMW Connected Drive services are deprecated. Please migrate to the dedicated button entities. " + "See https://www.home-assistant.io/integrations/bmw_connected_drive/#buttons for details" + ) + vin: str | None = call.data.get(ATTR_VIN) device_id: str | None = call.data.get(CONF_DEVICE_ID) diff --git a/homeassistant/components/bmw_connected_drive/button.py b/homeassistant/components/bmw_connected_drive/button.py new file mode 100644 index 00000000000..72d66d746ab --- /dev/null +++ b/homeassistant/components/bmw_connected_drive/button.py @@ -0,0 +1,122 @@ +"""Support for BMW connected drive button entities.""" +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass + +from bimmer_connected.remote_services import RemoteServiceStatus +from bimmer_connected.vehicle import ConnectedDriveVehicle + +from homeassistant.components.button import ButtonEntity, ButtonEntityDescription +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import ( + DOMAIN as BMW_DOMAIN, + BMWConnectedDriveAccount, + BMWConnectedDriveBaseEntity, +) +from .const import CONF_ACCOUNT, DATA_ENTRIES + + +@dataclass +class BMWButtonEntityDescription(ButtonEntityDescription): + """Class describing BMW button entities.""" + + enabled_when_read_only: bool = False + remote_function: Callable[ + [ConnectedDriveVehicle], RemoteServiceStatus + ] | None = None + account_function: Callable[[BMWConnectedDriveAccount], None] | None = None + + +BUTTON_TYPES: tuple[BMWButtonEntityDescription, ...] = ( + BMWButtonEntityDescription( + key="light_flash", + icon="mdi:car-light-alert", + name="Flash Lights", + remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_light_flash(), + ), + BMWButtonEntityDescription( + key="sound_horn", + icon="mdi:bullhorn", + name="Sound Horn", + remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_horn(), + ), + BMWButtonEntityDescription( + key="activate_air_conditioning", + icon="mdi:hvac", + name="Activate 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(), + ), + BMWButtonEntityDescription( + key="find_vehicle", + icon="mdi:crosshairs-question", + name="Find Vehicle", + remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_vehicle_finder(), + ), + BMWButtonEntityDescription( + key="refresh", + icon="mdi:refresh", + name="Refresh from cloud", + account_function=lambda account: account.update(), + enabled_when_read_only=True, + ), +) + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the BMW ConnectedDrive buttons from config entry.""" + account: BMWConnectedDriveAccount = hass.data[BMW_DOMAIN][DATA_ENTRIES][ + config_entry.entry_id + ][CONF_ACCOUNT] + entities: list[BMWButton] = [] + + for vehicle in account.account.vehicles: + entities.extend( + [ + BMWButton(account, vehicle, description) + for description in BUTTON_TYPES + if not account.read_only + or (account.read_only and description.enabled_when_read_only) + ] + ) + + async_add_entities(entities) + + +class BMWButton(BMWConnectedDriveBaseEntity, ButtonEntity): + """Representation of a BMW Connected Drive button.""" + + entity_description: BMWButtonEntityDescription + + def __init__( + self, + account: BMWConnectedDriveAccount, + vehicle: ConnectedDriveVehicle, + description: BMWButtonEntityDescription, + ) -> None: + """Initialize BMW vehicle sensor.""" + super().__init__(account, vehicle) + self.entity_description = description + + self._attr_name = f"{vehicle.name} {description.name}" + self._attr_unique_id = f"{vehicle.vin}-{description.key}" + + def press(self) -> None: + """Process the button press.""" + if self.entity_description.remote_function: + self.entity_description.remote_function(self._vehicle) + elif self.entity_description.account_function: + self.entity_description.account_function(self._account)