Skip to content

Commit

Permalink
Update to Home Assistant 2022.2.9 (#31)
Browse files Browse the repository at this point in the history
Co-authored-by: rikroe <[email protected]>
  • Loading branch information
github-actions[bot] and rikroe authored Feb 20, 2022
1 parent e927295 commit c7f8ccb
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 32 deletions.
8 changes: 7 additions & 1 deletion custom_components/bmw_connected_drive/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@

PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.DEVICE_TRACKER,
Platform.LOCK,
Platform.NOTIFY,
Expand Down Expand Up @@ -163,7 +164,7 @@ def _update_all() -> None:
hass.async_create_task(
discovery.async_load_platform(
hass,
NOTIFY_DOMAIN,
Platform.NOTIFY,
DOMAIN,
{CONF_NAME: DOMAIN},
hass.data[DOMAIN][DATA_HASS_CONFIG],
Expand Down Expand Up @@ -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)

Expand Down
23 changes: 9 additions & 14 deletions custom_components/bmw_connected_drive/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,7 @@
)

from homeassistant.components.binary_sensor import (
DEVICE_CLASS_BATTERY_CHARGING,
DEVICE_CLASS_LIGHT,
DEVICE_CLASS_LOCK,
DEVICE_CLASS_OPENING,
DEVICE_CLASS_PLUG,
DEVICE_CLASS_PROBLEM,
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
Expand Down Expand Up @@ -158,57 +153,57 @@ class BMWBinarySensorEntityDescription(
BMWBinarySensorEntityDescription(
key="lids",
name="Doors",
device_class=DEVICE_CLASS_OPENING,
device_class=BinarySensorDeviceClass.OPENING,
icon="mdi:car-door-lock",
value_fn=_are_doors_closed,
),
BMWBinarySensorEntityDescription(
key="windows",
name="Windows",
device_class=DEVICE_CLASS_OPENING,
device_class=BinarySensorDeviceClass.OPENING,
icon="mdi:car-door",
value_fn=_are_windows_closed,
),
BMWBinarySensorEntityDescription(
key="door_lock_state",
name="Door lock state",
device_class=DEVICE_CLASS_LOCK,
device_class=BinarySensorDeviceClass.LOCK,
icon="mdi:car-key",
value_fn=_are_doors_locked,
),
BMWBinarySensorEntityDescription(
key="lights_parking",
name="Parking lights",
device_class=DEVICE_CLASS_LIGHT,
device_class=BinarySensorDeviceClass.LIGHT,
icon="mdi:car-parking-lights",
value_fn=_are_parking_lights_on,
),
BMWBinarySensorEntityDescription(
key="condition_based_services",
name="Condition based services",
device_class=DEVICE_CLASS_PROBLEM,
device_class=BinarySensorDeviceClass.PROBLEM,
icon="mdi:wrench",
value_fn=_are_problems_detected,
),
BMWBinarySensorEntityDescription(
key="check_control_messages",
name="Control messages",
device_class=DEVICE_CLASS_PROBLEM,
device_class=BinarySensorDeviceClass.PROBLEM,
icon="mdi:car-tire-alert",
value_fn=_check_control_messages,
),
# electric
BMWBinarySensorEntityDescription(
key="charging_status",
name="Charging status",
device_class=DEVICE_CLASS_BATTERY_CHARGING,
device_class=BinarySensorDeviceClass.BATTERY_CHARGING,
icon="mdi:ev-station",
value_fn=_is_vehicle_charging,
),
BMWBinarySensorEntityDescription(
key="connection_status",
name="Connection status",
device_class=DEVICE_CLASS_PLUG,
device_class=BinarySensorDeviceClass.PLUG,
icon="mdi:car-electric",
value_fn=_is_vehicle_plugged_in,
),
Expand Down
122 changes: 122 additions & 0 deletions custom_components/bmw_connected_drive/button.py
Original file line number Diff line number Diff line change
@@ -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)
2 changes: 2 additions & 0 deletions custom_components/bmw_connected_drive/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

ATTRIBUTION = "Data provided by BMW Connected Drive"

ATTR_DIRECTION = "direction"

CONF_ALLOWED_REGIONS = ["china", "north_america", "rest_of_world"]
CONF_READ_ONLY = "read_only"
CONF_USE_LOCATION = "use_location"
Expand Down
8 changes: 5 additions & 3 deletions custom_components/bmw_connected_drive/device_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
BMWConnectedDriveAccount,
BMWConnectedDriveBaseEntity,
)
from .const import CONF_ACCOUNT, DATA_ENTRIES
from .const import ATTR_DIRECTION, CONF_ACCOUNT, DATA_ENTRIES

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -78,9 +78,11 @@ def source_type(self) -> Literal["gps"]:
return SOURCE_TYPE_GPS

def update(self) -> None:
"""Update state of the decvice tracker."""
"""Update state of the device tracker."""
_LOGGER.debug("Updating device tracker of %s", self._vehicle.name)
self._attr_extra_state_attributes = self._attrs
state_attrs = self._attrs
state_attrs[ATTR_DIRECTION] = self._vehicle.status.gps_heading
self._attr_extra_state_attributes = state_attrs
self._location = (
self._vehicle.status.gps_position
if self._vehicle.is_vehicle_tracking_enabled
Expand Down
2 changes: 1 addition & 1 deletion custom_components/bmw_connected_drive/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
"requirements": ["bimmer_connected==0.8.10"],
"codeowners": ["@gerard33", "@rikroe"],
"config_flow": true,
"version": "2021.12.9-custom",
"version": "2022.2.9-custom",
"iot_class": "cloud_polling"
}
24 changes: 13 additions & 11 deletions custom_components/bmw_connected_drive/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@

from bimmer_connected.vehicle import ConnectedDriveVehicle

from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_UNIT_SYSTEM_IMPERIAL,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_TIMESTAMP,
LENGTH_KILOMETERS,
LENGTH_MILES,
PERCENTAGE,
Expand Down Expand Up @@ -48,12 +50,12 @@ class BMWSensorEntityDescription(SensorEntityDescription):
# --- Generic ---
"charging_start_time": BMWSensorEntityDescription(
key="charging_start_time",
device_class=DEVICE_CLASS_TIMESTAMP,
device_class=SensorDeviceClass.TIMESTAMP,
entity_registry_enabled_default=False,
),
"charging_end_time": BMWSensorEntityDescription(
key="charging_end_time",
device_class=DEVICE_CLASS_TIMESTAMP,
device_class=SensorDeviceClass.TIMESTAMP,
),
"charging_time_label": BMWSensorEntityDescription(
key="charging_time_label",
Expand All @@ -68,7 +70,7 @@ class BMWSensorEntityDescription(SensorEntityDescription):
key="charging_level_hv",
unit_metric=PERCENTAGE,
unit_imperial=PERCENTAGE,
device_class=DEVICE_CLASS_BATTERY,
device_class=SensorDeviceClass.BATTERY,
),
# --- Specific ---
"mileage": BMWSensorEntityDescription(
Expand All @@ -77,7 +79,7 @@ class BMWSensorEntityDescription(SensorEntityDescription):
unit_metric=LENGTH_KILOMETERS,
unit_imperial=LENGTH_MILES,
value=lambda x, hass: round(
hass.config.units.length(x[0], UNIT_MAP.get(x[1], x[1]))
hass.config.units.length(x[0], UNIT_MAP.get(x[1], x[1])), 2
),
),
"remaining_range_total": BMWSensorEntityDescription(
Expand All @@ -86,7 +88,7 @@ class BMWSensorEntityDescription(SensorEntityDescription):
unit_metric=LENGTH_KILOMETERS,
unit_imperial=LENGTH_MILES,
value=lambda x, hass: round(
hass.config.units.length(x[0], UNIT_MAP.get(x[1], x[1]))
hass.config.units.length(x[0], UNIT_MAP.get(x[1], x[1])), 2
),
),
"remaining_range_electric": BMWSensorEntityDescription(
Expand All @@ -95,7 +97,7 @@ class BMWSensorEntityDescription(SensorEntityDescription):
unit_metric=LENGTH_KILOMETERS,
unit_imperial=LENGTH_MILES,
value=lambda x, hass: round(
hass.config.units.length(x[0], UNIT_MAP.get(x[1], x[1]))
hass.config.units.length(x[0], UNIT_MAP.get(x[1], x[1])), 2
),
),
"remaining_range_fuel": BMWSensorEntityDescription(
Expand All @@ -104,7 +106,7 @@ class BMWSensorEntityDescription(SensorEntityDescription):
unit_metric=LENGTH_KILOMETERS,
unit_imperial=LENGTH_MILES,
value=lambda x, hass: round(
hass.config.units.length(x[0], UNIT_MAP.get(x[1], x[1]))
hass.config.units.length(x[0], UNIT_MAP.get(x[1], x[1])), 2
),
),
"remaining_fuel": BMWSensorEntityDescription(
Expand All @@ -113,7 +115,7 @@ class BMWSensorEntityDescription(SensorEntityDescription):
unit_metric=VOLUME_LITERS,
unit_imperial=VOLUME_GALLONS,
value=lambda x, hass: round(
hass.config.units.volume(x[0], UNIT_MAP.get(x[1], x[1]))
hass.config.units.volume(x[0], UNIT_MAP.get(x[1], x[1])), 2
),
),
"fuel_percent": BMWSensorEntityDescription(
Expand Down
2 changes: 1 addition & 1 deletion custom_components/bmw_connected_drive/translations/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"account_options": {
"data": {
"read_only": "Sola lettura (solo sensori e notifica, nessuna esecuzione di servizi, nessun blocco)",
"use_location": "Usa la posizione di Home Assistant per richieste sulla posizione dell'auto (richiesto per veicoli non i3/i8 prodotti prima del 7/2014)"
"use_location": "Usa la posizione di Home Assistant per le richieste di posizione dell'auto (richiesto per veicoli non i3/i8 prodotti prima del 7/2014)"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion custom_components/bmw_connected_drive/translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"account_options": {
"data": {
"read_only": "\u30ea\u30fc\u30c9\u30aa\u30f3\u30ea\u30fc(\u30bb\u30f3\u30b5\u30fc\u3068\u901a\u77e5\u306e\u307f\u3001\u30b5\u30fc\u30d3\u30b9\u306e\u5b9f\u884c\u306f\u4e0d\u53ef\u3001\u30ed\u30c3\u30af\u4e0d\u53ef)",
"use_location": "Home Assistant\u306e\u5834\u6240\u3092\u3001\u8eca\u306e\u4f4d\u7f6e\u3068\u3057\u3066\u30dd\u30fc\u30ea\u30f3\u30b0\u306b\u4f7f\u7528\u3059\u308b((2014\u5e747\u67087\u65e5\u4ee5\u524d\u306b\u751f\u7523\u3055\u308c\u305f\u3001i3/i8\u4ee5\u5916\u306e\u8eca\u4e21\u3067\u306f\u5fc5\u9808)"
"use_location": "Home Assistant\u306e\u5834\u6240\u3092\u3001\u8eca\u306e\u4f4d\u7f6e\u3068\u3057\u3066\u30dd\u30fc\u30ea\u30f3\u30b0\u306b\u4f7f\u7528\u3059\u308b(2014\u5e747\u67087\u65e5\u4ee5\u524d\u306b\u751f\u7523\u3055\u308c\u305f\u3001i3/i8\u4ee5\u5916\u306e\u8eca\u4e21\u3067\u306f\u5fc5\u9808)"
}
}
}
Expand Down

0 comments on commit c7f8ccb

Please sign in to comment.