Skip to content

Commit

Permalink
Update to Home Assistant 2023.6.0b4 (#66)
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 Jun 4, 2023
1 parent bca9e82 commit 5f3a27c
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 17 deletions.
1 change: 1 addition & 0 deletions custom_components/bmw_connected_drive/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
Platform.NUMBER,
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
]

SERVICE_UPDATE_STATE = "update_state"
Expand Down
9 changes: 0 additions & 9 deletions custom_components/bmw_connected_drive/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,6 @@ class BMWButtonEntityDescription(ButtonEntityDescription):
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",
Expand Down Expand Up @@ -128,7 +122,4 @@ async def async_press(self) -> None:
)
await self.entity_description.account_function(self.coordinator)

# Always update HA states after a button was executed.
# BMW remote services that change the vehicle's state update the local object
# when executing the service, so only the HA state machine needs further updates.
self.coordinator.async_update_listeners()
4 changes: 4 additions & 0 deletions custom_components/bmw_connected_drive/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ async def async_lock(self, **kwargs: Any) -> None:
self.async_write_ha_state()
await self.vehicle.remote_services.trigger_remote_door_lock()

self.coordinator.async_update_listeners()

async def async_unlock(self, **kwargs: Any) -> None:
"""Unlock the car."""
_LOGGER.debug("%s: unlocking doors", self.vehicle.name)
Expand All @@ -79,6 +81,8 @@ async def async_unlock(self, **kwargs: Any) -> None:
self.async_write_ha_state()
await self.vehicle.remote_services.trigger_remote_door_unlock()

self.coordinator.async_update_listeners()

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
Expand Down
3 changes: 2 additions & 1 deletion custom_components/bmw_connected_drive/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
"iot_class": "cloud_polling",
"loggers": ["bimmer_connected"],
"requirements": ["bimmer_connected==0.13.5"]
"version": "2023.6.0b4",
"requirements": ["bimmer_connected==0.13.6"]
}
2 changes: 2 additions & 0 deletions custom_components/bmw_connected_drive/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,5 @@ async def async_set_native_value(self, value: float) -> None:
await self.entity_description.remote_service(self.vehicle, value)
except MyBMWAPIError as ex:
raise HomeAssistantError(ex) from ex

self.coordinator.async_update_listeners()
2 changes: 2 additions & 0 deletions custom_components/bmw_connected_drive/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,5 @@ async def async_select_option(self, option: str) -> None:
option,
)
await self.entity_description.remote_service(self.vehicle, option)

self.coordinator.async_update_listeners()
7 changes: 0 additions & 7 deletions custom_components/bmw_connected_drive/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,6 @@ def convert_and_round(
unit_type=PERCENTAGE,
device_class=SensorDeviceClass.BATTERY,
),
"charging_mode": BMWSensorEntityDescription(
key="charging_mode",
name="Charging mode",
key_class="charging_profile",
icon="mdi:ev-station",
value=lambda x, y: x.value,
),
# --- Specific ---
"mileage": BMWSensorEntityDescription(
key="mileage",
Expand Down
132 changes: 132 additions & 0 deletions custom_components/bmw_connected_drive/switch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
"""Switch platform for BMW."""

from collections.abc import Callable, Coroutine
from dataclasses import dataclass
import logging
from typing import Any

from bimmer_connected.models import MyBMWAPIError
from bimmer_connected.vehicle import MyBMWVehicle
from bimmer_connected.vehicle.fuel_and_battery import ChargingState

from homeassistant.components.switch import SwitchEntity, SwitchEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import BMWBaseEntity
from .const import DOMAIN
from .coordinator import BMWDataUpdateCoordinator

_LOGGER = logging.getLogger(__name__)


@dataclass
class BMWRequiredKeysMixin:
"""Mixin for required keys."""

value_fn: Callable[[MyBMWVehicle], bool]
remote_service_on: Callable[[MyBMWVehicle], Coroutine[Any, Any, Any]]
remote_service_off: Callable[[MyBMWVehicle], Coroutine[Any, Any, Any]]


@dataclass
class BMWSwitchEntityDescription(SwitchEntityDescription, BMWRequiredKeysMixin):
"""Describes BMW switch entity."""

is_available: Callable[[MyBMWVehicle], bool] = lambda _: False
dynamic_options: Callable[[MyBMWVehicle], list[str]] | None = None


CHARGING_STATE_ON = {
ChargingState.CHARGING,
ChargingState.COMPLETE,
ChargingState.FULLY_CHARGED,
ChargingState.FINISHED_FULLY_CHARGED,
ChargingState.FINISHED_NOT_FULL,
ChargingState.TARGET_REACHED,
}

NUMBER_TYPES: list[BMWSwitchEntityDescription] = [
BMWSwitchEntityDescription(
key="climate",
name="Climate",
is_available=lambda v: v.is_remote_climate_stop_enabled,
value_fn=lambda v: v.climate.is_climate_on,
remote_service_on=lambda v: v.remote_services.trigger_remote_air_conditioning(),
remote_service_off=lambda v: v.remote_services.trigger_remote_air_conditioning_stop(),
icon="mdi:fan",
),
BMWSwitchEntityDescription(
key="charging",
name="Charging",
is_available=lambda v: v.is_remote_charge_stop_enabled,
value_fn=lambda v: v.fuel_and_battery.charging_status in CHARGING_STATE_ON,
remote_service_on=lambda v: v.remote_services.trigger_charge_start(),
remote_service_off=lambda v: v.remote_services.trigger_charge_stop(),
icon="mdi:ev-station",
),
]


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the MyBMW switch from config entry."""
coordinator: BMWDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]

entities: list[BMWSwitch] = []

for vehicle in coordinator.account.vehicles:
if not coordinator.read_only:
entities.extend(
[
BMWSwitch(coordinator, vehicle, description)
for description in NUMBER_TYPES
if description.is_available(vehicle)
]
)
async_add_entities(entities)


class BMWSwitch(BMWBaseEntity, SwitchEntity):
"""Representation of BMW Switch entity."""

entity_description: BMWSwitchEntityDescription

def __init__(
self,
coordinator: BMWDataUpdateCoordinator,
vehicle: MyBMWVehicle,
description: BMWSwitchEntityDescription,
) -> None:
"""Initialize an BMW Switch."""
super().__init__(coordinator, vehicle)
self.entity_description = description
self._attr_unique_id = f"{vehicle.vin}-{description.key}"

@property
def is_on(self) -> bool:
"""Return the entity value to represent the entity state."""
return self.entity_description.value_fn(self.vehicle)

async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
try:
await self.entity_description.remote_service_on(self.vehicle)
except MyBMWAPIError as ex:
raise HomeAssistantError(ex) from ex

self.coordinator.async_update_listeners()

async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
try:
await self.entity_description.remote_service_off(self.vehicle)
except MyBMWAPIError as ex:
raise HomeAssistantError(ex) from ex

self.coordinator.async_update_listeners()

0 comments on commit 5f3a27c

Please sign in to comment.