1 """Support for reading vehicle status from MyBMW portal."""
3 from __future__
import annotations
5 from collections.abc
import Callable
6 from dataclasses
import dataclass
10 from bimmer_connected.models
import StrEnum, ValueWithUnit
11 from bimmer_connected.vehicle
import MyBMWVehicle
12 from bimmer_connected.vehicle.climate
import ClimateActivityState
13 from bimmer_connected.vehicle.fuel_and_battery
import ChargingState
18 SensorEntityDescription,
24 UnitOfElectricCurrent,
33 from .
import BMWConfigEntry
34 from .coordinator
import BMWDataUpdateCoordinator
35 from .entity
import BMWBaseEntity
37 _LOGGER = logging.getLogger(__name__)
40 @dataclass(frozen=True)
42 """Describes BMW sensor entity."""
44 key_class: str |
None =
None
45 is_available: Callable[[MyBMWVehicle], bool] =
lambda v: v.is_lsc_enabled
48 TIRES = [
"front_left",
"front_right",
"rear_left",
"rear_right"]
50 SENSOR_TYPES: list[BMWSensorEntityDescription] = [
52 key=
"charging_profile.ac_current_limit",
53 translation_key=
"ac_current_limit",
54 device_class=SensorDeviceClass.CURRENT,
55 native_unit_of_measurement=UnitOfElectricCurrent.AMPERE,
56 entity_registry_enabled_default=
False,
57 suggested_display_precision=0,
58 is_available=
lambda v: v.is_lsc_enabled
and v.has_electric_drivetrain,
61 key=
"fuel_and_battery.charging_start_time",
62 translation_key=
"charging_start_time",
63 device_class=SensorDeviceClass.TIMESTAMP,
64 entity_registry_enabled_default=
False,
65 is_available=
lambda v: v.is_lsc_enabled
and v.has_electric_drivetrain,
68 key=
"fuel_and_battery.charging_end_time",
69 translation_key=
"charging_end_time",
70 device_class=SensorDeviceClass.TIMESTAMP,
71 is_available=
lambda v: v.is_lsc_enabled
and v.has_electric_drivetrain,
74 key=
"fuel_and_battery.charging_status",
75 translation_key=
"charging_status",
76 device_class=SensorDeviceClass.ENUM,
77 options=[s.value.lower()
for s
in ChargingState
if s != ChargingState.UNKNOWN],
78 is_available=
lambda v: v.is_lsc_enabled
and v.has_electric_drivetrain,
81 key=
"fuel_and_battery.charging_target",
82 translation_key=
"charging_target",
83 native_unit_of_measurement=PERCENTAGE,
84 suggested_display_precision=0,
85 is_available=
lambda v: v.is_lsc_enabled
and v.has_electric_drivetrain,
88 key=
"fuel_and_battery.remaining_battery_percent",
89 translation_key=
"remaining_battery_percent",
90 device_class=SensorDeviceClass.BATTERY,
91 native_unit_of_measurement=PERCENTAGE,
92 state_class=SensorStateClass.MEASUREMENT,
93 suggested_display_precision=0,
94 is_available=
lambda v: v.is_lsc_enabled
and v.has_electric_drivetrain,
98 translation_key=
"mileage",
99 device_class=SensorDeviceClass.DISTANCE,
100 native_unit_of_measurement=UnitOfLength.KILOMETERS,
101 state_class=SensorStateClass.TOTAL_INCREASING,
102 suggested_display_precision=0,
105 key=
"fuel_and_battery.remaining_range_total",
106 translation_key=
"remaining_range_total",
107 device_class=SensorDeviceClass.DISTANCE,
108 native_unit_of_measurement=UnitOfLength.KILOMETERS,
109 state_class=SensorStateClass.MEASUREMENT,
110 suggested_display_precision=0,
113 key=
"fuel_and_battery.remaining_range_electric",
114 translation_key=
"remaining_range_electric",
115 device_class=SensorDeviceClass.DISTANCE,
116 native_unit_of_measurement=UnitOfLength.KILOMETERS,
117 state_class=SensorStateClass.MEASUREMENT,
118 suggested_display_precision=0,
119 is_available=
lambda v: v.is_lsc_enabled
and v.has_electric_drivetrain,
122 key=
"fuel_and_battery.remaining_range_fuel",
123 translation_key=
"remaining_range_fuel",
124 device_class=SensorDeviceClass.DISTANCE,
125 native_unit_of_measurement=UnitOfLength.KILOMETERS,
126 state_class=SensorStateClass.MEASUREMENT,
127 suggested_display_precision=0,
128 is_available=
lambda v: v.is_lsc_enabled
and v.has_combustion_drivetrain,
131 key=
"fuel_and_battery.remaining_fuel",
132 translation_key=
"remaining_fuel",
133 device_class=SensorDeviceClass.VOLUME_STORAGE,
134 native_unit_of_measurement=UnitOfVolume.LITERS,
135 state_class=SensorStateClass.MEASUREMENT,
136 suggested_display_precision=0,
137 is_available=
lambda v: v.is_lsc_enabled
and v.has_combustion_drivetrain,
140 key=
"fuel_and_battery.remaining_fuel_percent",
141 translation_key=
"remaining_fuel_percent",
142 native_unit_of_measurement=PERCENTAGE,
143 state_class=SensorStateClass.MEASUREMENT,
144 suggested_display_precision=0,
145 is_available=
lambda v: v.is_lsc_enabled
and v.has_combustion_drivetrain,
148 key=
"climate.activity",
149 translation_key=
"climate_status",
150 device_class=SensorDeviceClass.ENUM,
153 for s
in ClimateActivityState
154 if s != ClimateActivityState.UNKNOWN
156 is_available=
lambda v: v.is_remote_climate_stop_enabled,
160 key=f
"tires.{tire}.current_pressure",
161 translation_key=f
"{tire}_current_pressure",
162 device_class=SensorDeviceClass.PRESSURE,
163 native_unit_of_measurement=UnitOfPressure.KPA,
164 suggested_unit_of_measurement=UnitOfPressure.BAR,
165 state_class=SensorStateClass.MEASUREMENT,
166 suggested_display_precision=2,
167 is_available=
lambda v: v.is_lsc_enabled
and v.tires
is not None,
173 key=f
"tires.{tire}.target_pressure",
174 translation_key=f
"{tire}_target_pressure",
175 device_class=SensorDeviceClass.PRESSURE,
176 native_unit_of_measurement=UnitOfPressure.KPA,
177 suggested_unit_of_measurement=UnitOfPressure.BAR,
178 state_class=SensorStateClass.MEASUREMENT,
179 suggested_display_precision=2,
180 entity_registry_enabled_default=
False,
181 is_available=
lambda v: v.is_lsc_enabled
and v.tires
is not None,
190 config_entry: BMWConfigEntry,
191 async_add_entities: AddEntitiesCallback,
193 """Set up the MyBMW sensors from config entry."""
194 coordinator = config_entry.runtime_data.coordinator
197 BMWSensor(coordinator, vehicle, description)
198 for vehicle
in coordinator.account.vehicles
199 for description
in SENSOR_TYPES
200 if description.is_available(vehicle)
207 """Representation of a BMW vehicle sensor."""
209 entity_description: BMWSensorEntityDescription
213 coordinator: BMWDataUpdateCoordinator,
214 vehicle: MyBMWVehicle,
215 description: BMWSensorEntityDescription,
217 """Initialize BMW vehicle sensor."""
218 super().
__init__(coordinator, vehicle)
224 """Handle updated data from the coordinator."""
230 state = getattr(self.
vehiclevehicle, key_path.pop(0))
233 state = getattr(state, key)
236 if isinstance(state, datetime.datetime)
and state.tzinfo
is None:
237 state = state.replace(tzinfo=dt_util.get_default_time_zone())
239 elif isinstance(state, ValueWithUnit):
242 elif isinstance(state, StrEnum):
243 state = state.value.lower()
244 if state == STATE_UNKNOWN:
None _handle_coordinator_update(self)
None __init__(self, BMWDataUpdateCoordinator coordinator, MyBMWVehicle vehicle, BMWSensorEntityDescription description)
None async_setup_entry(HomeAssistant hass, BMWConfigEntry config_entry, AddEntitiesCallback async_add_entities)