Use entity class attributes for Bmw connected drive (#53054)

* Use entity class attributes for bmw_connected_driv

* forgot the icon
This commit is contained in:
Robert Hillis 2021-07-21 08:09:54 -04:00 committed by GitHub
parent 7fef87691a
commit 668437741a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 99 additions and 221 deletions

View file

@ -21,7 +21,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry, discovery
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import DeviceInfo, Entity
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import track_utc_time_change
from homeassistant.util import slugify
import homeassistant.util.dt as dt_util
@ -317,6 +317,8 @@ class BMWConnectedDriveAccount:
class BMWConnectedDriveBaseEntity(Entity):
"""Common base for BMW entities."""
_attr_should_poll = False
def __init__(self, account, vehicle):
"""Initialize sensor."""
self._account = account
@ -326,15 +328,11 @@ class BMWConnectedDriveBaseEntity(Entity):
"vin": self._vehicle.vin,
ATTR_ATTRIBUTION: ATTRIBUTION,
}
@property
def device_info(self) -> DeviceInfo:
"""Return info for device registry."""
return {
"identifiers": {(DOMAIN, self._vehicle.vin)},
"name": f'{self._vehicle.attributes.get("brand")} {self._vehicle.name}',
"model": self._vehicle.name,
"manufacturer": self._vehicle.attributes.get("brand"),
self._attr_device_info = {
"identifiers": {(DOMAIN, vehicle.vin)},
"name": f'{vehicle.attributes.get("brand")} {vehicle.name}',
"model": vehicle.name,
"manufacturer": vehicle.attributes.get("brand"),
}
@property
@ -342,14 +340,6 @@ class BMWConnectedDriveBaseEntity(Entity):
"""Return the state attributes of the sensor."""
return self._attrs
@property
def should_poll(self):
"""Do not poll this class.
Updates are triggered from BMWConnectedDriveAccount.
"""
return False
def update_callback(self):
"""Schedule a state update."""
self.schedule_update_ha_state(True)

View file

@ -76,41 +76,45 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, BinarySensorEntity):
super().__init__(account, vehicle)
self._attribute = attribute
self._name = f"{self._vehicle.name} {self._attribute}"
self._unique_id = f"{self._vehicle.vin}-{self._attribute}"
self._attr_name = f"{vehicle.name} {attribute}"
self._attr_unique_id = f"{vehicle.vin}-{attribute}"
self._sensor_name = sensor_name
self._device_class = device_class
self._icon = icon
self._state = None
self._attr_device_class = device_class
self._attr_icon = icon
@property
def unique_id(self):
"""Return the unique ID of the binary sensor."""
return self._unique_id
def update(self):
"""Read new state data from the library."""
vehicle_state = self._vehicle.state
@property
def name(self):
"""Return the name of the binary sensor."""
return self._name
# device class opening: On means open, Off means closed
if self._attribute == "lids":
_LOGGER.debug("Status of lid: %s", vehicle_state.all_lids_closed)
self._attr_state = not vehicle_state.all_lids_closed
if self._attribute == "windows":
self._attr_state = not vehicle_state.all_windows_closed
# device class lock: On means unlocked, Off means locked
if self._attribute == "door_lock_state":
# Possible values: LOCKED, SECURED, SELECTIVE_LOCKED, UNLOCKED
self._attr_state = vehicle_state.door_lock_state not in [
LockState.LOCKED,
LockState.SECURED,
]
# device class light: On means light detected, Off means no light
if self._attribute == "lights_parking":
self._attr_state = vehicle_state.are_parking_lights_on
# device class problem: On means problem detected, Off means no problem
if self._attribute == "condition_based_services":
self._attr_state = not vehicle_state.are_all_cbs_ok
if self._attribute == "check_control_messages":
self._attr_state = vehicle_state.has_check_control_messages
# device class power: On means power detected, Off means no power
if self._attribute == "charging_status":
self._attr_state = vehicle_state.charging_status in [ChargingState.CHARGING]
# device class plug: On means device is plugged in,
# Off means device is unplugged
if self._attribute == "connection_status":
self._attr_state = vehicle_state.connection_status == "CONNECTED"
@property
def icon(self):
"""Icon to use in the frontend, if any."""
return self._icon
@property
def device_class(self):
"""Return the class of the binary sensor."""
return self._device_class
@property
def is_on(self):
"""Return the state of the binary sensor."""
return self._state
@property
def extra_state_attributes(self):
"""Return the state attributes of the binary sensor."""
vehicle_state = self._vehicle.state
result = self._attrs.copy()
@ -144,40 +148,7 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, BinarySensorEntity):
elif self._attribute == "connection_status":
result["connection_status"] = vehicle_state.connection_status
return sorted(result.items())
def update(self):
"""Read new state data from the library."""
vehicle_state = self._vehicle.state
# device class opening: On means open, Off means closed
if self._attribute == "lids":
_LOGGER.debug("Status of lid: %s", vehicle_state.all_lids_closed)
self._state = not vehicle_state.all_lids_closed
if self._attribute == "windows":
self._state = not vehicle_state.all_windows_closed
# device class lock: On means unlocked, Off means locked
if self._attribute == "door_lock_state":
# Possible values: LOCKED, SECURED, SELECTIVE_LOCKED, UNLOCKED
self._state = vehicle_state.door_lock_state not in [
LockState.LOCKED,
LockState.SECURED,
]
# device class light: On means light detected, Off means no light
if self._attribute == "lights_parking":
self._state = vehicle_state.are_parking_lights_on
# device class problem: On means problem detected, Off means no problem
if self._attribute == "condition_based_services":
self._state = not vehicle_state.are_all_cbs_ok
if self._attribute == "check_control_messages":
self._state = vehicle_state.has_check_control_messages
# device class power: On means power detected, Off means no power
if self._attribute == "charging_status":
self._state = vehicle_state.charging_status in [ChargingState.CHARGING]
# device class plug: On means device is plugged in,
# Off means device is unplugged
if self._attribute == "connection_status":
self._state = vehicle_state.connection_status == "CONNECTED"
self._attr_extra_state_attributes = sorted(result.items())
def _format_cbs_report(self, report):
result = {}

View file

@ -29,15 +29,18 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
class BMWDeviceTracker(BMWConnectedDriveBaseEntity, TrackerEntity):
"""BMW Connected Drive device tracker."""
_attr_force_update = False
_attr_icon = "mdi:car"
def __init__(self, account, vehicle):
"""Initialize the Tracker."""
super().__init__(account, vehicle)
self._unique_id = vehicle.vin
self._attr_unique_id = vehicle.vin
self._location = (
vehicle.state.gps_position if vehicle.state.gps_position else (None, None)
)
self._name = vehicle.name
self._attr_name = vehicle.name
@property
def latitude(self):
@ -49,31 +52,11 @@ class BMWDeviceTracker(BMWConnectedDriveBaseEntity, TrackerEntity):
"""Return longitude value of the device."""
return self._location[1] if self._location else None
@property
def name(self):
"""Return the name of the device."""
return self._name
@property
def unique_id(self):
"""Return the unique ID."""
return self._unique_id
@property
def source_type(self):
"""Return the source type, eg gps or router, of the device."""
return SOURCE_TYPE_GPS
@property
def icon(self):
"""Return the icon to use in the frontend, if any."""
return "mdi:car"
@property
def force_update(self):
"""All updates do not need to be written to the state machine."""
return False
def update(self):
"""Update state of the decvice tracker."""
self._location = (

View file

@ -4,7 +4,6 @@ import logging
from bimmer_connected.state import LockState
from homeassistant.components.lock import LockEntity
from homeassistant.const import STATE_LOCKED, STATE_UNLOCKED
from . import DOMAIN as BMW_DOMAIN, BMWConnectedDriveBaseEntity
from .const import CONF_ACCOUNT, DATA_ENTRIES
@ -33,50 +32,17 @@ class BMWLock(BMWConnectedDriveBaseEntity, LockEntity):
super().__init__(account, vehicle)
self._attribute = attribute
self._name = f"{self._vehicle.name} {self._attribute}"
self._unique_id = f"{self._vehicle.vin}-{self._attribute}"
self._attr_name = f"{vehicle.name} {attribute}"
self._attr_unique_id = f"{vehicle.vin}-{attribute}"
self._sensor_name = sensor_name
self._state = None
self.door_lock_state_available = (
DOOR_LOCK_STATE in self._vehicle.available_attributes
)
@property
def unique_id(self):
"""Return the unique ID of the lock."""
return self._unique_id
@property
def name(self):
"""Return the name of the lock."""
return self._name
@property
def extra_state_attributes(self):
"""Return the state attributes of the lock."""
vehicle_state = self._vehicle.state
result = self._attrs.copy()
if self.door_lock_state_available:
result["door_lock_state"] = vehicle_state.door_lock_state.value
result["last_update_reason"] = vehicle_state.last_update_reason
return result
@property
def is_locked(self):
"""Return true if lock is locked."""
if self.door_lock_state_available:
result = self._state == STATE_LOCKED
else:
result = None
return result
self.door_lock_state_available = DOOR_LOCK_STATE in vehicle.available_attributes
def lock(self, **kwargs):
"""Lock the car."""
_LOGGER.debug("%s: locking doors", self._vehicle.name)
# Optimistic state set here because it takes some time before the
# update callback response
self._state = STATE_LOCKED
self._attr_is_locked = True
self.schedule_update_ha_state()
self._vehicle.remote_services.trigger_remote_door_lock()
@ -85,18 +51,23 @@ class BMWLock(BMWConnectedDriveBaseEntity, LockEntity):
_LOGGER.debug("%s: unlocking doors", self._vehicle.name)
# Optimistic state set here because it takes some time before the
# update callback response
self._state = STATE_UNLOCKED
self._attr_is_locked = False
self.schedule_update_ha_state()
self._vehicle.remote_services.trigger_remote_door_unlock()
def update(self):
"""Update state of the lock."""
_LOGGER.debug("%s: updating data for %s", self._vehicle.name, self._attribute)
vehicle_state = self._vehicle.state
if self._vehicle.state.door_lock_state in [LockState.LOCKED, LockState.SECURED]:
self._attr_is_locked = True
else:
self._attr_is_locked = False
if not self.door_lock_state_available:
self._attr_is_locked = None
# Possible values: LOCKED, SECURED, SELECTIVE_LOCKED, UNLOCKED
self._state = (
STATE_LOCKED
if vehicle_state.door_lock_state in [LockState.LOCKED, LockState.SECURED]
else STATE_UNLOCKED
)
vehicle_state = self._vehicle.state
result = self._attrs.copy()
if self.door_lock_state_available:
result["door_lock_state"] = vehicle_state.door_lock_state.value
result["last_update_reason"] = vehicle_state.last_update_reason
self._attr_extra_state_attributes = result

View file

@ -503,94 +503,46 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, SensorEntity):
self._attribute = attribute
self._service = service
self._state = None
if self._service:
self._name = (
f"{self._vehicle.name} {self._service.lower()}_{self._attribute}"
)
self._unique_id = (
f"{self._vehicle.vin}-{self._service.lower()}-{self._attribute}"
)
if service:
self._attr_name = f"{vehicle.name} {service.lower()}_{attribute}"
self._attr_unique_id = f"{vehicle.vin}-{service.lower()}-{attribute}"
else:
self._name = f"{self._vehicle.name} {self._attribute}"
self._unique_id = f"{self._vehicle.vin}-{self._attribute}"
self._attr_name = f"{vehicle.name} {attribute}"
self._attr_unique_id = f"{vehicle.vin}-{attribute}"
self._attribute_info = attribute_info
@property
def unique_id(self):
"""Return the unique ID of the sensor."""
return self._unique_id
@property
def name(self) -> str:
"""Return the name of the sensor."""
return self._name
@property
def icon(self):
"""Icon to use in the frontend, if any."""
vehicle_state = self._vehicle.state
charging_state = vehicle_state.charging_status in [ChargingState.CHARGING]
if self._attribute == "charging_level_hv":
return icon_for_battery_level(
battery_level=vehicle_state.charging_level_hv, charging=charging_state
)
icon = self._attribute_info.get(self._attribute, [None, None, None, None])[0]
return icon
@property
def entity_registry_enabled_default(self) -> bool:
"""Return if the entity should be enabled when first added to the entity registry."""
enabled_default = self._attribute_info.get(
self._attribute, [None, None, None, True]
self._attr_entity_registry_enabled_default = attribute_info.get(
attribute, [None, None, None, True]
)[3]
return enabled_default
@property
def state(self):
"""Return the state of the sensor.
The return type of this call depends on the attribute that
is configured.
"""
return self._state
@property
def device_class(self) -> str:
"""Get the device class."""
clss = self._attribute_info.get(self._attribute, [None, None, None, None])[1]
return clss
@property
def unit_of_measurement(self) -> str:
"""Get the unit of measurement."""
unit = self._attribute_info.get(self._attribute, [None, None, None, None])[2]
return unit
self._attr_device_class = attribute_info.get(
attribute, [None, None, None, None]
)[1]
self._attr_unit_of_measurement = attribute_info.get(
attribute, [None, None, None, None]
)[2]
def update(self) -> None:
"""Read new state data from the library."""
_LOGGER.debug("Updating %s", self._vehicle.name)
vehicle_state = self._vehicle.state
if self._attribute == "charging_status":
self._state = getattr(vehicle_state, self._attribute).value
self._attr_state = getattr(vehicle_state, self._attribute).value
elif self.unit_of_measurement == VOLUME_GALLONS:
value = getattr(vehicle_state, self._attribute)
value_converted = self.hass.config.units.volume(value, VOLUME_LITERS)
self._state = round(value_converted)
self._attr_state = round(value_converted)
elif self.unit_of_measurement == LENGTH_MILES:
value = getattr(vehicle_state, self._attribute)
value_converted = self.hass.config.units.length(value, LENGTH_KILOMETERS)
self._state = round(value_converted)
self._attr_state = round(value_converted)
elif self._service is None:
self._state = getattr(vehicle_state, self._attribute)
self._attr_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()
self._attr_state = dt_util.parse_datetime(date_str).isoformat()
else:
self._state = getattr(vehicle_last_trip, self._attribute)
self._attr_state = getattr(vehicle_last_trip, self._attribute)
elif self._service == SERVICE_ALL_TRIPS:
vehicle_all_trips = self._vehicle.state.all_trips
for attribute in (
@ -603,10 +555,21 @@ class BMWConnectedDriveSensor(BMWConnectedDriveBaseEntity, SensorEntity):
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)
self._attr_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()
self._attr_state = dt_util.parse_datetime(date_str).isoformat()
else:
self._state = getattr(vehicle_all_trips, self._attribute)
self._attr_state = getattr(vehicle_all_trips, self._attribute)
vehicle_state = self._vehicle.state
charging_state = vehicle_state.charging_status in [ChargingState.CHARGING]
if self._attribute == "charging_level_hv":
self._attr_icon = icon_for_battery_level(
battery_level=vehicle_state.charging_level_hv, charging=charging_state
)
self._attr_icon = self._attribute_info.get(
self._attribute, [None, None, None, None]
)[0]