Skip to content

Commit

Permalink
Rebase to HA-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
rikroe committed Nov 26, 2021
1 parent e677700 commit 25a9ae5
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 97 deletions.
9 changes: 6 additions & 3 deletions custom_components/bmw_connected_drive/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
)

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,
Expand Down Expand Up @@ -169,14 +172,14 @@ class BMWBinarySensorEntityDescription(
BMWBinarySensorEntityDescription(
key="door_lock_state",
name="Door lock state",
device_class="lock",
device_class=DEVICE_CLASS_LOCK,
icon="mdi:car-key",
value_fn=_are_doors_locked,
),
BMWBinarySensorEntityDescription(
key="lights_parking",
name="Parking lights",
device_class="light",
device_class=DEVICE_CLASS_LIGHT,
icon="mdi:car-parking-lights",
value_fn=_are_parking_lights_on,
),
Expand All @@ -198,7 +201,7 @@ class BMWBinarySensorEntityDescription(
BMWBinarySensorEntityDescription(
key="charging_status",
name="Charging status",
device_class="power",
device_class=DEVICE_CLASS_BATTERY_CHARGING,
icon="mdi:ev-station",
value_fn=_is_vehicle_charging,
),
Expand Down
4 changes: 3 additions & 1 deletion custom_components/bmw_connected_drive/lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ def unlock(self, **kwargs: Any) -> None:

def update(self) -> None:
"""Update state of the lock."""
_LOGGER.debug("Updating lock data for '%s' of %s", self._attribute, self._vehicle.name)
_LOGGER.debug(
"Updating lock data for '%s' of %s", self._attribute, self._vehicle.name
)
vehicle_state = self._vehicle.status
if not self.door_lock_state_available:
self._attr_is_locked = None
Expand Down
7 changes: 1 addition & 6 deletions custom_components/bmw_connected_drive/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
from homeassistant.components.notify import (
ATTR_DATA,
ATTR_TARGET,
ATTR_TITLE,
ATTR_TITLE_DEFAULT,
BaseNotificationService,
)
from homeassistant.const import ATTR_LATITUDE, ATTR_LOCATION, ATTR_LONGITUDE, ATTR_NAME
Expand Down Expand Up @@ -63,7 +61,6 @@ def send_message(self, message: str = "", **kwargs: Any) -> None:
_LOGGER.debug("Sending message to %s", vehicle.name)

# Extract params from data dict
title: str = kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)
data = kwargs.get(ATTR_DATA)

# Check if message is a POI
Expand All @@ -84,6 +81,4 @@ def send_message(self, message: str = "", **kwargs: Any) -> None:

vehicle.remote_services.trigger_send_poi(location_dict)
else:
vehicle.remote_services.trigger_send_message(
{ATTR_TEXT: message, ATTR_SUBJECT: title}
)
raise ValueError(f"'data.{ATTR_LOCATION}' is required.")
124 changes: 37 additions & 87 deletions custom_components/bmw_connected_drive/sensor.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,28 @@
"""Support for reading vehicle status from BMW connected drive portal."""
from __future__ import annotations

from copy import copy
from collections.abc import Callable
from dataclasses import dataclass
import logging
from typing import cast

from bimmer_connected.const import SERVICE_STATUS
from bimmer_connected.vehicle import ConnectedDriveVehicle
from bimmer_connected.vehicle_status import ChargingState

from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_UNIT_SYSTEM_IMPERIAL,
DEVICE_CLASS_TIMESTAMP,
ENERGY_KILO_WATT_HOUR,
ENERGY_WATT_HOUR,
DEVICE_CLASS_BATTERY,
LENGTH_KILOMETERS,
LENGTH_MILES,
MASS_KILOGRAMS,
PERCENTAGE,
TIME_HOURS,
TIME_MINUTES,
VOLUME_GALLONS,
VOLUME_LITERS,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.icon import icon_for_battery_level
import homeassistant.util.dt as dt_util
from homeassistant.helpers.typing import StateType
from homeassistant.util.unit_system import UnitSystem

from . import (
Expand All @@ -47,6 +41,7 @@ class BMWSensorEntityDescription(SensorEntityDescription):

unit_metric: str | None = None
unit_imperial: str | None = None
value: Callable = lambda x, y: x


SENSOR_TYPES: dict[str, BMWSensorEntityDescription] = {
Expand All @@ -59,50 +54,60 @@ class BMWSensorEntityDescription(SensorEntityDescription):
),
"charging_status": BMWSensorEntityDescription(
key="charging_status",
icon="mdi:battery-charging",
icon="mdi:ev-station",
value=lambda x, y: x.value,
),
# No icon as this is dealt with directly as a special case in icon()
"charging_level_hv": BMWSensorEntityDescription(
key="charging_level_hv",
unit_metric=PERCENTAGE,
unit_imperial=PERCENTAGE,
device_class=DEVICE_CLASS_BATTERY,
),
# --- Specific ---
"mileage": BMWSensorEntityDescription(
key="mileage",
icon="mdi:speedometer",
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]))
),
),
"remaining_range_total": BMWSensorEntityDescription(
key="remaining_range_total",
icon="mdi:map-marker-distance",
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]))
),
),
"remaining_range_electric": BMWSensorEntityDescription(
key="remaining_range_electric",
icon="mdi:map-marker-distance",
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]))
),
),
"remaining_range_fuel": BMWSensorEntityDescription(
key="remaining_range_fuel",
icon="mdi:map-marker-distance",
unit_metric=LENGTH_KILOMETERS,
unit_imperial=LENGTH_MILES,
),
"max_range_electric": BMWSensorEntityDescription(
key="max_range_electric",
icon="mdi:map-marker-distance",
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]))
),
),
"remaining_fuel": BMWSensorEntityDescription(
key="remaining_fuel",
icon="mdi:gas-station",
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]))
),
),
"fuel_percent": BMWSensorEntityDescription(
key="fuel_percent",
Expand All @@ -113,37 +118,26 @@ class BMWSensorEntityDescription(SensorEntityDescription):
}


DEFAULT_BMW_DESCRIPTION = BMWSensorEntityDescription(
key="",
entity_registry_enabled_default=True,
)


async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up the BMW ConnectedDrive sensors from config entry."""
# pylint: disable=too-many-nested-blocks
unit_system = hass.config.units
account: BMWConnectedDriveAccount = hass.data[BMW_DOMAIN][DATA_ENTRIES][
config_entry.entry_id
][CONF_ACCOUNT]
entities: list[BMWConnectedDriveSensor] = []

for vehicle in account.account.vehicles:
for service in vehicle.available_state_services:
if service == SERVICE_STATUS:
entities.extend(
[
BMWConnectedDriveSensor(
account, vehicle, description, unit_system
)
for attribute_name in vehicle.available_attributes
if (description := SENSOR_TYPES.get(attribute_name))
]
)
entities.extend(
[
BMWConnectedDriveSensor(account, vehicle, description, unit_system)
for attribute_name in vehicle.available_attributes
if (description := SENSOR_TYPES.get(attribute_name))
]
)

async_add_entities(entities, True)

Expand All @@ -159,65 +153,21 @@ def __init__(
vehicle: ConnectedDriveVehicle,
description: BMWSensorEntityDescription,
unit_system: UnitSystem,
service: str | None = None,
) -> None:
"""Initialize BMW vehicle sensor."""
super().__init__(account, vehicle)
self.entity_description = description

self._service = service
if service:
self._attr_name = f"{vehicle.name} {service.lower()}_{description.key}"
self._attr_unique_id = f"{vehicle.vin}-{service.lower()}-{description.key}"
else:
self._attr_name = f"{vehicle.name} {description.key}"
self._attr_unique_id = f"{vehicle.vin}-{description.key}"
self._attr_name = f"{vehicle.name} {description.key}"
self._attr_unique_id = f"{vehicle.vin}-{description.key}"

if unit_system.name == CONF_UNIT_SYSTEM_IMPERIAL:
self._attr_native_unit_of_measurement = description.unit_imperial
else:
self._attr_native_unit_of_measurement = description.unit_metric

def update(self) -> None:
"""Read new state data from the library."""
_LOGGER.debug("Updating sensors of %s", self._vehicle.name)
vehicle_state = self._vehicle.status
sensor_key = self.entity_description.key
sensor_value = None

if sensor_key == "charging_status":
sensor_value = getattr(vehicle_state, sensor_key).value
elif self._service is None:
sensor_value = getattr(vehicle_state, sensor_key)

if isinstance(sensor_value, tuple):
sensor_unit = UNIT_MAP.get(sensor_value[1], sensor_value[1])
sensor_value = sensor_value[0]
sensor_value_org = sensor_value

if self.unit_of_measurement in [LENGTH_KILOMETERS, LENGTH_MILES]:
sensor_value = round(
self.hass.config.units.length(sensor_value, sensor_unit)
)
elif self.unit_of_measurement in [VOLUME_LITERS, VOLUME_GALLONS]:
sensor_value = round(
self.hass.config.units.volume(sensor_value, sensor_unit)
)

_LOGGER.debug(
"UoM Conversion for %s. HA: %s %s, Vehicle: %s %s.",
sensor_key,
sensor_value, self.unit_of_measurement,
sensor_value_org, sensor_unit
)

self._attr_native_value = sensor_value

if sensor_key == "charging_level_hv":
charging_state = self._vehicle.status.charging_status in {
ChargingState.CHARGING
}
self._attr_icon = icon_for_battery_level(
battery_level=vehicle_state.charging_level_hv, charging=charging_state
)

@property
def native_value(self) -> StateType:
"""Return the state."""
state = getattr(self._vehicle.status, self.entity_description.key)
return cast(StateType, self.entity_description.value(state, self.hass))

0 comments on commit 25a9ae5

Please sign in to comment.