From 11d7efb785d210a8d8e9e197726b48fd126e6e9b Mon Sep 17 00:00:00 2001 From: EddyK69 Date: Sun, 11 Jul 2021 22:47:32 +0200 Subject: [PATCH] Add AllTrips sensors for BMW Connected Drive (#50420) * Add AllTrips sensors for BMW Connected Drive Added several new AllTrips sensors and some optional extra AllTrips sensors (disabled by default) * Fix for failed checks * Fix for failed check (black) * Code tidying Changed code after useful comments ;) --- .../components/bmw_connected_drive/sensor.py | 368 +++++++++++++++++- 1 file changed, 366 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/bmw_connected_drive/sensor.py b/homeassistant/components/bmw_connected_drive/sensor.py index 48d28e26f8a..053a5adff22 100644 --- a/homeassistant/components/bmw_connected_drive/sensor.py +++ b/homeassistant/components/bmw_connected_drive/sensor.py @@ -1,7 +1,7 @@ """Support for reading vehicle status from BMW connected drive portal.""" import logging -from bimmer_connected.const import 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 homeassistant.components.sensor import SensorEntity @@ -9,8 +9,10 @@ from homeassistant.const import ( CONF_UNIT_SYSTEM_IMPERIAL, DEVICE_CLASS_TIMESTAMP, ENERGY_KILO_WATT_HOUR, + ENERGY_WATT_HOUR, LENGTH_KILOMETERS, LENGTH_MILES, + MASS_KILOGRAMS, PERCENTAGE, TIME_HOURS, TIME_MINUTES, @@ -60,6 +62,146 @@ ATTR_TO_HA_METRIC = { "electric_distance": ["mdi:map-marker-distance", None, LENGTH_KILOMETERS, True], "saved_fuel": ["mdi:fuel", None, VOLUME_LITERS, False], "total_distance": ["mdi:map-marker-distance", None, LENGTH_KILOMETERS, True], + # AllTrips attributes + "average_combined_consumption_community_average": [ + "mdi:flash", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", + False, + ], + "average_combined_consumption_community_high": [ + "mdi:flash", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", + False, + ], + "average_combined_consumption_community_low": [ + "mdi:flash", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", + False, + ], + "average_combined_consumption_user_average": [ + "mdi:flash", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", + True, + ], + "average_electric_consumption_community_average": [ + "mdi:power-plug-outline", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", + False, + ], + "average_electric_consumption_community_high": [ + "mdi:power-plug-outline", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", + False, + ], + "average_electric_consumption_community_low": [ + "mdi:power-plug-outline", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", + False, + ], + "average_electric_consumption_user_average": [ + "mdi:power-plug-outline", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", + True, + ], + "average_recuperation_community_average": [ + "mdi:recycle-variant", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", + False, + ], + "average_recuperation_community_high": [ + "mdi:recycle-variant", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", + False, + ], + "average_recuperation_community_low": [ + "mdi:recycle-variant", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", + False, + ], + "average_recuperation_user_average": [ + "mdi:recycle-variant", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_KILOMETERS}", + True, + ], + "chargecycle_range_community_average": [ + "mdi:map-marker-distance", + None, + LENGTH_KILOMETERS, + False, + ], + "chargecycle_range_community_high": [ + "mdi:map-marker-distance", + None, + LENGTH_KILOMETERS, + False, + ], + "chargecycle_range_community_low": [ + "mdi:map-marker-distance", + None, + LENGTH_KILOMETERS, + False, + ], + "chargecycle_range_user_average": [ + "mdi:map-marker-distance", + None, + LENGTH_KILOMETERS, + True, + ], + "chargecycle_range_user_current_charge_cycle": [ + "mdi:map-marker-distance", + None, + LENGTH_KILOMETERS, + True, + ], + "chargecycle_range_user_high": [ + "mdi:map-marker-distance", + None, + LENGTH_KILOMETERS, + True, + ], + "total_electric_distance_community_average": [ + "mdi:map-marker-distance", + None, + LENGTH_KILOMETERS, + False, + ], + "total_electric_distance_community_high": [ + "mdi:map-marker-distance", + None, + LENGTH_KILOMETERS, + False, + ], + "total_electric_distance_community_low": [ + "mdi:map-marker-distance", + None, + LENGTH_KILOMETERS, + False, + ], + "total_electric_distance_user_average": [ + "mdi:map-marker-distance", + None, + LENGTH_KILOMETERS, + False, + ], + "total_electric_distance_user_total": [ + "mdi:map-marker-distance", + None, + LENGTH_KILOMETERS, + False, + ], + "total_saved_fuel": ["mdi:fuel", None, VOLUME_LITERS, False], } ATTR_TO_HA_IMPERIAL = { @@ -92,6 +234,146 @@ ATTR_TO_HA_IMPERIAL = { "electric_distance": ["mdi:map-marker-distance", None, LENGTH_MILES, True], "saved_fuel": ["mdi:fuel", None, VOLUME_GALLONS, False], "total_distance": ["mdi:map-marker-distance", None, LENGTH_MILES, True], + # AllTrips attributes + "average_combined_consumption_community_average": [ + "mdi:flash", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", + False, + ], + "average_combined_consumption_community_high": [ + "mdi:flash", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", + False, + ], + "average_combined_consumption_community_low": [ + "mdi:flash", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", + False, + ], + "average_combined_consumption_user_average": [ + "mdi:flash", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", + True, + ], + "average_electric_consumption_community_average": [ + "mdi:power-plug-outline", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", + False, + ], + "average_electric_consumption_community_high": [ + "mdi:power-plug-outline", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", + False, + ], + "average_electric_consumption_community_low": [ + "mdi:power-plug-outline", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", + False, + ], + "average_electric_consumption_user_average": [ + "mdi:power-plug-outline", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", + True, + ], + "average_recuperation_community_average": [ + "mdi:recycle-variant", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", + False, + ], + "average_recuperation_community_high": [ + "mdi:recycle-variant", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", + False, + ], + "average_recuperation_community_low": [ + "mdi:recycle-variant", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", + False, + ], + "average_recuperation_user_average": [ + "mdi:recycle-variant", + None, + f"{ENERGY_KILO_WATT_HOUR}/100{LENGTH_MILES}", + True, + ], + "chargecycle_range_community_average": [ + "mdi:map-marker-distance", + None, + LENGTH_MILES, + False, + ], + "chargecycle_range_community_high": [ + "mdi:map-marker-distance", + None, + LENGTH_MILES, + False, + ], + "chargecycle_range_community_low": [ + "mdi:map-marker-distance", + None, + LENGTH_MILES, + False, + ], + "chargecycle_range_user_average": [ + "mdi:map-marker-distance", + None, + LENGTH_MILES, + True, + ], + "chargecycle_range_user_current_charge_cycle": [ + "mdi:map-marker-distance", + None, + LENGTH_MILES, + True, + ], + "chargecycle_range_user_high": [ + "mdi:map-marker-distance", + None, + LENGTH_MILES, + True, + ], + "total_electric_distance_community_average": [ + "mdi:map-marker-distance", + None, + LENGTH_MILES, + False, + ], + "total_electric_distance_community_high": [ + "mdi:map-marker-distance", + None, + LENGTH_MILES, + False, + ], + "total_electric_distance_community_low": [ + "mdi:map-marker-distance", + None, + LENGTH_MILES, + False, + ], + "total_electric_distance_user_average": [ + "mdi:map-marker-distance", + None, + LENGTH_MILES, + False, + ], + "total_electric_distance_user_total": [ + "mdi:map-marker-distance", + None, + LENGTH_MILES, + False, + ], + "total_saved_fuel": ["mdi:fuel", None, VOLUME_GALLONS, False], } ATTR_TO_HA_GENERIC = { @@ -104,6 +386,11 @@ ATTR_TO_HA_GENERIC = { "date_utc": [None, DEVICE_CLASS_TIMESTAMP, None, True], "duration": ["mdi:timer-outline", None, TIME_MINUTES, True], "electric_distance_ratio": ["mdi:percent-outline", None, PERCENTAGE, False], + # AllTrips attributes + "battery_size_max": ["mdi:battery-charging-high", None, ENERGY_WATT_HOUR, False], + "reset_date_utc": [None, DEVICE_CLASS_TIMESTAMP, None, False], + "saved_co2": ["mdi:tree-outline", None, MASS_KILOGRAMS, False], + "saved_co2_green_energy": ["mdi:tree-outline", None, MASS_KILOGRAMS, False], } ATTR_TO_HA_METRIC.update(ATTR_TO_HA_GENERIC) @@ -112,6 +399,7 @@ ATTR_TO_HA_IMPERIAL.update(ATTR_TO_HA_GENERIC) async def async_setup_entry(hass, config_entry, async_add_entities): """Set up the BMW ConnectedDrive sensors from config entry.""" + # pylint: disable=too-many-nested-blocks if hass.config.units.name == CONF_UNIT_SYSTEM_IMPERIAL: attribute_info = ATTR_TO_HA_IMPERIAL else: @@ -145,6 +433,63 @@ async def async_setup_entry(hass, config_entry, async_add_entities): account, vehicle, attribute_name, attribute_info, service ) entities.append(device) + if service == SERVICE_ALL_TRIPS: + for attribute_name in vehicle.state.all_trips.available_attributes: + if attribute_name == "reset_date": + device = BMWConnectedDriveSensor( + account, + vehicle, + "reset_date_utc", + attribute_info, + service, + ) + entities.append(device) + elif attribute_name in ( + "average_combined_consumption", + "average_electric_consumption", + "average_recuperation", + "chargecycle_range", + "total_electric_distance", + ): + for attr in [ + "community_average", + "community_high", + "community_low", + "user_average", + ]: + device = BMWConnectedDriveSensor( + account, + vehicle, + f"{attribute_name}_{attr}", + attribute_info, + service, + ) + entities.append(device) + if attribute_name == "chargecycle_range": + for attr in ["user_current_charge_cycle", "user_high"]: + device = BMWConnectedDriveSensor( + account, + vehicle, + f"{attribute_name}_{attr}", + attribute_info, + service, + ) + entities.append(device) + if attribute_name == "total_electric_distance": + for attr in ["user_total"]: + device = BMWConnectedDriveSensor( + account, + vehicle, + f"{attribute_name}_{attr}", + attribute_info, + service, + ) + entities.append(device) + else: + device = BMWConnectedDriveSensor( + account, vehicle, attribute_name, attribute_info, service + ) + entities.append(device) async_add_entities(entities, True) @@ -227,7 +572,6 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, SensorEntity): """Read new state data from the library.""" _LOGGER.debug("Updating %s", self._vehicle.name) vehicle_state = self._vehicle.state - vehicle_last_trip = self._vehicle.state.last_trip if self._attribute == "charging_status": self._state = getattr(vehicle_state, self._attribute).value elif self.unit_of_measurement == VOLUME_GALLONS: @@ -241,8 +585,28 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, SensorEntity): elif self._service is None: self._state = getattr(vehicle_state, self._attribute) elif self._service == SERVICE_LAST_TRIP: + vehicle_last_trip = self._vehicle.state.last_trip if self._attribute == "date_utc": date_str = getattr(vehicle_last_trip, "date") self._state = dt_util.parse_datetime(date_str).isoformat() else: self._state = getattr(vehicle_last_trip, self._attribute) + elif self._service == SERVICE_ALL_TRIPS: + vehicle_all_trips = self._vehicle.state.all_trips + for attribute in [ + "average_combined_consumption", + "average_electric_consumption", + "average_recuperation", + "chargecycle_range", + "total_electric_distance", + ]: + if self._attribute.startswith(f"{attribute}_"): + attr = getattr(vehicle_all_trips, attribute) + sub_attr = self._attribute.replace(f"{attribute}_", "") + self._state = getattr(attr, sub_attr) + return + if self._attribute == "reset_date_utc": + date_str = getattr(vehicle_all_trips, "reset_date") + self._state = dt_util.parse_datetime(date_str).isoformat() + else: + self._state = getattr(vehicle_all_trips, self._attribute)