Use EntityDescription - bmw_connected_drive sensor (#57796)

This commit is contained in:
Marc Mueller 2021-10-17 21:05:06 +02:00 committed by GitHub
parent 4fd8b27ce6
commit 6ca23c67ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,12 +1,16 @@
"""Support for reading vehicle status from BMW connected drive portal.""" """Support for reading vehicle status from BMW connected drive portal."""
from __future__ import annotations from __future__ import annotations
from copy import copy
from dataclasses import dataclass
import logging import logging
from bimmer_connected.const import SERVICE_ALL_TRIPS, SERVICE_LAST_TRIP, SERVICE_STATUS from bimmer_connected.const import SERVICE_ALL_TRIPS, SERVICE_LAST_TRIP, SERVICE_STATUS
from bimmer_connected.state import ChargingState from bimmer_connected.state import ChargingState
from bimmer_connected.vehicle import ConnectedDriveVehicle
from homeassistant.components.sensor import SensorEntity from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
CONF_UNIT_SYSTEM_IMPERIAL, CONF_UNIT_SYSTEM_IMPERIAL,
DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_TIMESTAMP,
@ -21,390 +25,399 @@ from homeassistant.const import (
VOLUME_GALLONS, VOLUME_GALLONS,
VOLUME_LITERS, VOLUME_LITERS,
) )
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.icon import icon_for_battery_level from homeassistant.helpers.icon import icon_for_battery_level
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.util.unit_system import UnitSystem
from . import DOMAIN as BMW_DOMAIN, BMWConnectedDriveBaseEntity from . import (
DOMAIN as BMW_DOMAIN,
BMWConnectedDriveAccount,
BMWConnectedDriveBaseEntity,
)
from .const import CONF_ACCOUNT, DATA_ENTRIES from .const import CONF_ACCOUNT, DATA_ENTRIES
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
SENSOR_TYPES: dict[str, tuple[str | None, str | None, str | None, str | None, bool]] = {
# "<ID>": (<MDI_ICON>, <DEVICE_CLASS>, <UNIT_OF_MEASUREMENT (metric)>, <UNIT_OF_MEASUREMENT (imperial)>, <ENABLED_BY_DEFAULT>), @dataclass
class BMWSensorEntityDescription(SensorEntityDescription):
"""Describes BMW sensor entity."""
unit_metric: str | None = None
unit_imperial: str | None = None
SENSOR_TYPES: dict[str, BMWSensorEntityDescription] = {
# --- Generic --- # --- Generic ---
"charging_time_remaining": ( "charging_time_remaining": BMWSensorEntityDescription(
"mdi:update", key="charging_time_remaining",
None, icon="mdi:update",
TIME_HOURS, unit_metric=TIME_HOURS,
TIME_HOURS, unit_imperial=TIME_HOURS,
True,
), ),
"charging_status": ( "charging_status": BMWSensorEntityDescription(
"mdi:battery-charging", key="charging_status",
None, icon="mdi:battery-charging",
None,
None,
True,
), ),
# No icon as this is dealt with directly as a special case in icon() # No icon as this is dealt with directly as a special case in icon()
"charging_level_hv": ( "charging_level_hv": BMWSensorEntityDescription(
None, key="charging_level_hv",
None, unit_metric=PERCENTAGE,
PERCENTAGE, unit_imperial=PERCENTAGE,
PERCENTAGE,
True,
), ),
# LastTrip attributes # LastTrip attributes
"date_utc": ( "date_utc": BMWSensorEntityDescription(
None, key="date_utc",
DEVICE_CLASS_TIMESTAMP, device_class=DEVICE_CLASS_TIMESTAMP,
None,
None,
True,
), ),
"duration": ( "duration": BMWSensorEntityDescription(
"mdi:timer-outline", key="duration",
None, icon="mdi:timer-outline",
TIME_MINUTES, unit_metric=TIME_MINUTES,
TIME_MINUTES, unit_imperial=TIME_MINUTES,
True,
), ),
"electric_distance_ratio": ( "electric_distance_ratio": BMWSensorEntityDescription(
"mdi:percent-outline", key="electric_distance_ratio",
None, icon="mdi:percent-outline",
PERCENTAGE, unit_metric=PERCENTAGE,
PERCENTAGE, unit_imperial=PERCENTAGE,
False, entity_registry_enabled_default=False,
), ),
# AllTrips attributes # AllTrips attributes
"battery_size_max": ( "battery_size_max": BMWSensorEntityDescription(
"mdi:battery-charging-high", key="battery_size_max",
None, icon="mdi:battery-charging-high",
ENERGY_WATT_HOUR, unit_metric=ENERGY_WATT_HOUR,
ENERGY_WATT_HOUR, unit_imperial=ENERGY_WATT_HOUR,
False, entity_registry_enabled_default=False,
), ),
"reset_date_utc": ( "reset_date_utc": BMWSensorEntityDescription(
None, key="reset_date_utc",
DEVICE_CLASS_TIMESTAMP, device_class=DEVICE_CLASS_TIMESTAMP,
None, entity_registry_enabled_default=False,
None,
False,
), ),
"saved_co2": ( "saved_co2": BMWSensorEntityDescription(
"mdi:tree-outline", key="saved_co2",
None, icon="mdi:tree-outline",
MASS_KILOGRAMS, unit_metric=MASS_KILOGRAMS,
MASS_KILOGRAMS, unit_imperial=MASS_KILOGRAMS,
False, entity_registry_enabled_default=False,
), ),
"saved_co2_green_energy": ( "saved_co2_green_energy": BMWSensorEntityDescription(
"mdi:tree-outline", key="saved_co2_green_energy",
None, icon="mdi:tree-outline",
MASS_KILOGRAMS, unit_metric=MASS_KILOGRAMS,
MASS_KILOGRAMS, unit_imperial=MASS_KILOGRAMS,
False, entity_registry_enabled_default=False,
), ),
# --- Specific --- # --- Specific ---
"mileage": ( "mileage": BMWSensorEntityDescription(
"mdi:speedometer", key="mileage",
None, icon="mdi:speedometer",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
True,
), ),
"remaining_range_total": ( "remaining_range_total": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="remaining_range_total",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
True,
), ),
"remaining_range_electric": ( "remaining_range_electric": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="remaining_range_electric",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
True,
), ),
"remaining_range_fuel": ( "remaining_range_fuel": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="remaining_range_fuel",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
True,
), ),
"max_range_electric": ( "max_range_electric": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="max_range_electric",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
True,
), ),
"remaining_fuel": ( "remaining_fuel": BMWSensorEntityDescription(
"mdi:gas-station", key="remaining_fuel",
None, icon="mdi:gas-station",
VOLUME_LITERS, unit_metric=VOLUME_LITERS,
VOLUME_GALLONS, unit_imperial=VOLUME_GALLONS,
True,
), ),
# LastTrip attributes # LastTrip attributes
"average_combined_consumption": ( "average_combined_consumption": BMWSensorEntityDescription(
"mdi:flash", key="average_combined_consumption",
None, icon="mdi:flash",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
True,
), ),
"average_electric_consumption": ( "average_electric_consumption": BMWSensorEntityDescription(
"mdi:power-plug-outline", key="average_electric_consumption",
None, icon="mdi:power-plug-outline",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
True,
), ),
"average_recuperation": ( "average_recuperation": BMWSensorEntityDescription(
"mdi:recycle-variant", key="average_recuperation",
None, icon="mdi:recycle-variant",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
True,
), ),
"electric_distance": ( "electric_distance": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="electric_distance",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
True,
), ),
"saved_fuel": ( "saved_fuel": BMWSensorEntityDescription(
"mdi:fuel", key="saved_fuel",
None, icon="mdi:fuel",
VOLUME_LITERS, unit_metric=VOLUME_LITERS,
VOLUME_GALLONS, unit_imperial=VOLUME_GALLONS,
False, entity_registry_enabled_default=False,
), ),
"total_distance": ( "total_distance": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="total_distance",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
True,
), ),
# AllTrips attributes # AllTrips attributes
"average_combined_consumption_community_average": ( "average_combined_consumption_community_average": BMWSensorEntityDescription(
"mdi:flash", key="average_combined_consumption_community_average",
None, icon="mdi:flash",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
False, entity_registry_enabled_default=False,
), ),
"average_combined_consumption_community_high": ( "average_combined_consumption_community_high": BMWSensorEntityDescription(
"mdi:flash", key="average_combined_consumption_community_high",
None, icon="mdi:flash",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
False, entity_registry_enabled_default=False,
), ),
"average_combined_consumption_community_low": ( "average_combined_consumption_community_low": BMWSensorEntityDescription(
"mdi:flash", key="average_combined_consumption_community_low",
None, icon="mdi:flash",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
False, entity_registry_enabled_default=False,
), ),
"average_combined_consumption_user_average": ( "average_combined_consumption_user_average": BMWSensorEntityDescription(
"mdi:flash", key="average_combined_consumption_user_average",
None, icon="mdi:flash",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
True,
), ),
"average_electric_consumption_community_average": ( "average_electric_consumption_community_average": BMWSensorEntityDescription(
"mdi:power-plug-outline", key="average_electric_consumption_community_average",
None, icon="mdi:power-plug-outline",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
False, entity_registry_enabled_default=False,
), ),
"average_electric_consumption_community_high": ( "average_electric_consumption_community_high": BMWSensorEntityDescription(
"mdi:power-plug-outline", key="average_electric_consumption_community_high",
None, icon="mdi:power-plug-outline",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
False, entity_registry_enabled_default=False,
), ),
"average_electric_consumption_community_low": ( "average_electric_consumption_community_low": BMWSensorEntityDescription(
"mdi:power-plug-outline", key="average_electric_consumption_community_low",
None, icon="mdi:power-plug-outline",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
False, entity_registry_enabled_default=False,
), ),
"average_electric_consumption_user_average": ( "average_electric_consumption_user_average": BMWSensorEntityDescription(
"mdi:power-plug-outline", key="average_electric_consumption_user_average",
None, icon="mdi:power-plug-outline",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
True,
), ),
"average_recuperation_community_average": ( "average_recuperation_community_average": BMWSensorEntityDescription(
"mdi:recycle-variant", key="average_recuperation_community_average",
None, icon="mdi:recycle-variant",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
False, entity_registry_enabled_default=False,
), ),
"average_recuperation_community_high": ( "average_recuperation_community_high": BMWSensorEntityDescription(
"mdi:recycle-variant", key="average_recuperation_community_high",
None, icon="mdi:recycle-variant",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
False, entity_registry_enabled_default=False,
), ),
"average_recuperation_community_low": ( "average_recuperation_community_low": BMWSensorEntityDescription(
"mdi:recycle-variant", key="average_recuperation_community_low",
None, icon="mdi:recycle-variant",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
False, entity_registry_enabled_default=False,
), ),
"average_recuperation_user_average": ( "average_recuperation_user_average": BMWSensorEntityDescription(
"mdi:recycle-variant", key="average_recuperation_user_average",
None, icon="mdi:recycle-variant",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", unit_metric=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}",
f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", unit_imperial=f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}",
True,
), ),
"chargecycle_range_community_average": ( "chargecycle_range_community_average": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="chargecycle_range_community_average",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
False, entity_registry_enabled_default=False,
), ),
"chargecycle_range_community_high": ( "chargecycle_range_community_high": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="chargecycle_range_community_high",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
False, entity_registry_enabled_default=False,
), ),
"chargecycle_range_community_low": ( "chargecycle_range_community_low": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="chargecycle_range_community_low",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
False, entity_registry_enabled_default=False,
), ),
"chargecycle_range_user_average": ( "chargecycle_range_user_average": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="chargecycle_range_user_average",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
True,
), ),
"chargecycle_range_user_current_charge_cycle": ( "chargecycle_range_user_current_charge_cycle": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="chargecycle_range_user_current_charge_cycle",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
True,
), ),
"chargecycle_range_user_high": ( "chargecycle_range_user_high": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="chargecycle_range_user_high",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
True,
), ),
"total_electric_distance_community_average": ( "total_electric_distance_community_average": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="total_electric_distance_community_average",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
False, entity_registry_enabled_default=False,
), ),
"total_electric_distance_community_high": ( "total_electric_distance_community_high": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="total_electric_distance_community_high",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
False, entity_registry_enabled_default=False,
), ),
"total_electric_distance_community_low": ( "total_electric_distance_community_low": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="total_electric_distance_community_low",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
False, entity_registry_enabled_default=False,
), ),
"total_electric_distance_user_average": ( "total_electric_distance_user_average": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="total_electric_distance_user_average",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
False, entity_registry_enabled_default=False,
), ),
"total_electric_distance_user_total": ( "total_electric_distance_user_total": BMWSensorEntityDescription(
"mdi:map-marker-distance", key="total_electric_distance_user_total",
None, icon="mdi:map-marker-distance",
LENGTH_KILOMETERS, unit_metric=LENGTH_KILOMETERS,
LENGTH_MILES, unit_imperial=LENGTH_MILES,
False, entity_registry_enabled_default=False,
), ),
"total_saved_fuel": ( "total_saved_fuel": BMWSensorEntityDescription(
"mdi:fuel", key="total_saved_fuel",
None, icon="mdi:fuel",
VOLUME_LITERS, unit_metric=VOLUME_LITERS,
VOLUME_GALLONS, unit_imperial=VOLUME_GALLONS,
False, entity_registry_enabled_default=False,
), ),
} }
async def async_setup_entry(hass, config_entry, async_add_entities): 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.""" """Set up the BMW ConnectedDrive sensors from config entry."""
# pylint: disable=too-many-nested-blocks # pylint: disable=too-many-nested-blocks
account = hass.data[BMW_DOMAIN][DATA_ENTRIES][config_entry.entry_id][CONF_ACCOUNT] unit_system = hass.config.units
entities = [] account: BMWConnectedDriveAccount = hass.data[BMW_DOMAIN][DATA_ENTRIES][
config_entry.entry_id
][CONF_ACCOUNT]
entities: list[BMWConnectedDriveSensor] = []
for vehicle in account.account.vehicles: for vehicle in account.account.vehicles:
for service in vehicle.available_state_services: for service in vehicle.available_state_services:
if service == SERVICE_STATUS: if service == SERVICE_STATUS:
for attribute_name in vehicle.drive_train_attributes: entities.extend(
if attribute_name in vehicle.available_attributes: [
device = BMWConnectedDriveSensor( BMWConnectedDriveSensor(
hass, account, vehicle, attribute_name account, vehicle, description, unit_system
) )
entities.append(device) for attribute_name in vehicle.drive_train_attributes
if attribute_name in vehicle.available_attributes
and (description := SENSOR_TYPES.get(attribute_name))
]
)
if service == SERVICE_LAST_TRIP: if service == SERVICE_LAST_TRIP:
for attribute_name in vehicle.state.last_trip.available_attributes: entities.extend(
if attribute_name == "date": [
device = BMWConnectedDriveSensor( BMWConnectedDriveSensor(
hass, account, vehicle, description, unit_system, service
)
for attribute_name in vehicle.state.last_trip.available_attributes
if attribute_name != "date"
and (description := SENSOR_TYPES.get(attribute_name))
]
)
if "date" in vehicle.state.last_trip.available_attributes:
entities.append(
BMWConnectedDriveSensor(
account, account,
vehicle, vehicle,
"date_utc", SENSOR_TYPES["date_utc"],
unit_system,
service, service,
) )
entities.append(device) )
else:
device = BMWConnectedDriveSensor(
hass, account, vehicle, attribute_name, service
)
entities.append(device)
if service == SERVICE_ALL_TRIPS: if service == SERVICE_ALL_TRIPS:
for attribute_name in vehicle.state.all_trips.available_attributes: for attribute_name in vehicle.state.all_trips.available_attributes:
if attribute_name == "reset_date": if attribute_name == "reset_date":
device = BMWConnectedDriveSensor( entities.append(
hass, BMWConnectedDriveSensor(
account, account,
vehicle, vehicle,
"reset_date_utc", SENSOR_TYPES["reset_date_utc"],
service, unit_system,
service,
)
) )
entities.append(device)
elif attribute_name in ( elif attribute_name in (
"average_combined_consumption", "average_combined_consumption",
"average_electric_consumption", "average_electric_consumption",
@ -412,45 +425,60 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
"chargecycle_range", "chargecycle_range",
"total_electric_distance", "total_electric_distance",
): ):
for attr in ( entities.extend(
"community_average", [
"community_high", BMWConnectedDriveSensor(
"community_low", account,
"user_average", vehicle,
): SENSOR_TYPES[f"{attribute_name}_{attr}"],
device = BMWConnectedDriveSensor( unit_system,
hass, service,
)
for attr in (
"community_average",
"community_high",
"community_low",
"user_average",
)
]
)
if attribute_name == "chargecycle_range":
entities.extend(
BMWConnectedDriveSensor(
account,
vehicle,
SENSOR_TYPES[f"{attribute_name}_{attr}"],
unit_system,
service,
)
for attr in ("user_current_charge_cycle", "user_high")
)
elif attribute_name == "total_electric_distance":
entities.extend(
[
BMWConnectedDriveSensor(
account,
vehicle,
SENSOR_TYPES[f"{attribute_name}_{attr}"],
unit_system,
service,
)
for attr in ("user_total",)
]
)
else:
if (description := SENSOR_TYPES.get(attribute_name)) is None:
description = copy(DEFAULT_BMW_DESCRIPTION)
description.key = attribute_name
entities.append(
BMWConnectedDriveSensor(
account, account,
vehicle, vehicle,
f"{attribute_name}_{attr}", description,
unit_system,
service, service,
) )
entities.append(device)
if attribute_name == "chargecycle_range":
for attr in ("user_current_charge_cycle", "user_high"):
device = BMWConnectedDriveSensor(
hass,
account,
vehicle,
f"{attribute_name}_{attr}",
service,
)
entities.append(device)
if attribute_name == "total_electric_distance":
for attr in ("user_total",):
device = BMWConnectedDriveSensor(
hass,
account,
vehicle,
f"{attribute_name}_{attr}",
service,
)
entities.append(device)
else:
device = BMWConnectedDriveSensor(
hass, account, vehicle, attribute_name, service
) )
entities.append(device)
async_add_entities(entities, True) async_add_entities(entities, True)
@ -458,52 +486,57 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, SensorEntity): class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, SensorEntity):
"""Representation of a BMW vehicle sensor.""" """Representation of a BMW vehicle sensor."""
def __init__(self, hass, account, vehicle, attribute: str, service=None): entity_description: BMWSensorEntityDescription
def __init__(
self,
account: BMWConnectedDriveAccount,
vehicle: ConnectedDriveVehicle,
description: BMWSensorEntityDescription,
unit_system: UnitSystem,
service: str | None = None,
) -> None:
"""Initialize BMW vehicle sensor.""" """Initialize BMW vehicle sensor."""
super().__init__(account, vehicle) super().__init__(account, vehicle)
self.entity_description = description
self._attribute = attribute
self._service = service self._service = service
if service: if service:
self._attr_name = f"{vehicle.name} {service.lower()}_{attribute}" self._attr_name = f"{vehicle.name} {service.lower()}_{description.key}"
self._attr_unique_id = f"{vehicle.vin}-{service.lower()}-{attribute}" self._attr_unique_id = f"{vehicle.vin}-{service.lower()}-{description.key}"
else: else:
self._attr_name = f"{vehicle.name} {attribute}" self._attr_name = f"{vehicle.name} {description.key}"
self._attr_unique_id = f"{vehicle.vin}-{attribute}" self._attr_unique_id = f"{vehicle.vin}-{description.key}"
self._attribute_info = SENSOR_TYPES.get(
attribute, (None, None, None, None, True) if unit_system.name == CONF_UNIT_SYSTEM_IMPERIAL:
) self._attr_native_unit_of_measurement = description.unit_imperial
self._attr_entity_registry_enabled_default = self._attribute_info[4]
self._attr_icon = self._attribute_info[0]
self._attr_device_class = self._attribute_info[1]
if hass.config.units.name == CONF_UNIT_SYSTEM_IMPERIAL:
self._attr_native_unit_of_measurement = self._attribute_info[3]
else: else:
self._attr_native_unit_of_measurement = self._attribute_info[2] self._attr_native_unit_of_measurement = description.unit_metric
def update(self) -> None: def update(self) -> None:
"""Read new state data from the library.""" """Read new state data from the library."""
_LOGGER.debug("Updating %s", self._vehicle.name) _LOGGER.debug("Updating %s", self._vehicle.name)
vehicle_state = self._vehicle.state vehicle_state = self._vehicle.state
if self._attribute == "charging_status": sensor_key = self.entity_description.key
self._attr_native_value = getattr(vehicle_state, self._attribute).value if sensor_key == "charging_status":
self._attr_native_value = getattr(vehicle_state, sensor_key).value
elif self.unit_of_measurement == VOLUME_GALLONS: elif self.unit_of_measurement == VOLUME_GALLONS:
value = getattr(vehicle_state, self._attribute) value = getattr(vehicle_state, sensor_key)
value_converted = self.hass.config.units.volume(value, VOLUME_LITERS) value_converted = self.hass.config.units.volume(value, VOLUME_LITERS)
self._attr_native_value = round(value_converted) self._attr_native_value = round(value_converted)
elif self.unit_of_measurement == LENGTH_MILES: elif self.unit_of_measurement == LENGTH_MILES:
value = getattr(vehicle_state, self._attribute) value = getattr(vehicle_state, sensor_key)
value_converted = self.hass.config.units.length(value, LENGTH_KILOMETERS) value_converted = self.hass.config.units.length(value, LENGTH_KILOMETERS)
self._attr_native_value = round(value_converted) self._attr_native_value = round(value_converted)
elif self._service is None: elif self._service is None:
self._attr_native_value = getattr(vehicle_state, self._attribute) self._attr_native_value = getattr(vehicle_state, sensor_key)
elif self._service == SERVICE_LAST_TRIP: elif self._service == SERVICE_LAST_TRIP:
vehicle_last_trip = self._vehicle.state.last_trip vehicle_last_trip = self._vehicle.state.last_trip
if self._attribute == "date_utc": if sensor_key == "date_utc":
date_str = getattr(vehicle_last_trip, "date") date_str = getattr(vehicle_last_trip, "date")
self._attr_native_value = dt_util.parse_datetime(date_str).isoformat() self._attr_native_value = dt_util.parse_datetime(date_str).isoformat()
else: else:
self._attr_native_value = getattr(vehicle_last_trip, self._attribute) self._attr_native_value = getattr(vehicle_last_trip, sensor_key)
elif self._service == SERVICE_ALL_TRIPS: elif self._service == SERVICE_ALL_TRIPS:
vehicle_all_trips = self._vehicle.state.all_trips vehicle_all_trips = self._vehicle.state.all_trips
for attribute in ( for attribute in (
@ -513,21 +546,21 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, SensorEntity):
"chargecycle_range", "chargecycle_range",
"total_electric_distance", "total_electric_distance",
): ):
if self._attribute.startswith(f"{attribute}_"): if sensor_key.startswith(f"{attribute}_"):
attr = getattr(vehicle_all_trips, attribute) attr = getattr(vehicle_all_trips, attribute)
sub_attr = self._attribute.replace(f"{attribute}_", "") sub_attr = sensor_key.replace(f"{attribute}_", "")
self._attr_native_value = getattr(attr, sub_attr) self._attr_native_value = getattr(attr, sub_attr)
return return
if self._attribute == "reset_date_utc": if sensor_key == "reset_date_utc":
date_str = getattr(vehicle_all_trips, "reset_date") date_str = getattr(vehicle_all_trips, "reset_date")
self._attr_native_value = dt_util.parse_datetime(date_str).isoformat() self._attr_native_value = dt_util.parse_datetime(date_str).isoformat()
else: else:
self._attr_native_value = getattr(vehicle_all_trips, self._attribute) self._attr_native_value = getattr(vehicle_all_trips, sensor_key)
vehicle_state = self._vehicle.state vehicle_state = self._vehicle.state
charging_state = vehicle_state.charging_status in [ChargingState.CHARGING] charging_state = vehicle_state.charging_status in [ChargingState.CHARGING]
if self._attribute == "charging_level_hv": if sensor_key == "charging_level_hv":
self._attr_icon = icon_for_battery_level( self._attr_icon = icon_for_battery_level(
battery_level=vehicle_state.charging_level_hv, charging=charging_state battery_level=vehicle_state.charging_level_hv, charging=charging_state
) )