diff --git a/homeassistant/components/binary_sensor/bmw_connected_drive.py b/homeassistant/components/binary_sensor/bmw_connected_drive.py index 0abf6eb1064..af3ebd53b80 100644 --- a/homeassistant/components/binary_sensor/bmw_connected_drive.py +++ b/homeassistant/components/binary_sensor/bmw_connected_drive.py @@ -17,9 +17,19 @@ _LOGGER = logging.getLogger(__name__) SENSOR_TYPES = { 'lids': ['Doors', 'opening'], 'windows': ['Windows', 'opening'], - 'door_lock_state': ['Door lock state', 'safety'] + 'door_lock_state': ['Door lock state', 'safety'], + 'lights_parking': ['Parking lights', 'light'], + 'condition_based_services': ['Condition based services', 'problem'], + 'check_control_messages': ['Control messages', 'problem'] } +SENSOR_TYPES_ELEC = { + 'charging_status': ['Charging status', 'power'], + 'connection_status': ['Connection status', 'plug'] +} + +SENSOR_TYPES_ELEC.update(SENSOR_TYPES) + def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the BMW sensors.""" @@ -29,10 +39,18 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices = [] for account in accounts: for vehicle in account.account.vehicles: - for key, value in sorted(SENSOR_TYPES.items()): - device = BMWConnectedDriveSensor(account, vehicle, key, - value[0], value[1]) - devices.append(device) + if vehicle.has_hv_battery: + _LOGGER.debug('BMW with a high voltage battery') + for key, value in sorted(SENSOR_TYPES_ELEC.items()): + device = BMWConnectedDriveSensor(account, vehicle, key, + value[0], value[1]) + devices.append(device) + elif vehicle.has_internal_combustion_engine: + _LOGGER.debug('BMW with an internal combustion engine') + for key, value in sorted(SENSOR_TYPES.items()): + device = BMWConnectedDriveSensor(account, vehicle, key, + value[0], value[1]) + devices.append(device) add_devices(devices, True) @@ -92,12 +110,41 @@ class BMWConnectedDriveSensor(BinarySensorDevice): result[window.name] = window.state.value elif self._attribute == 'door_lock_state': result['door_lock_state'] = vehicle_state.door_lock_state.value + result['last_update_reason'] = vehicle_state.last_update_reason + elif self._attribute == 'lights_parking': + result['lights_parking'] = vehicle_state.parking_lights.value + elif self._attribute == 'condition_based_services': + for report in vehicle_state.condition_based_services: + service_type = report.service_type.lower().replace('_', ' ') + result['{} status'.format(service_type)] = report.state.value + if report.due_date is not None: + result['{} date'.format(service_type)] = \ + report.due_date.strftime('%Y-%m-%d') + if report.due_distance is not None: + result['{} distance'.format(service_type)] = \ + '{} km'.format(report.due_distance) + elif self._attribute == 'check_control_messages': + check_control_messages = vehicle_state.check_control_messages + if not check_control_messages: + result['check_control_messages'] = 'OK' + else: + result['check_control_messages'] = check_control_messages + elif self._attribute == 'charging_status': + result['charging_status'] = vehicle_state.charging_status.value + # pylint: disable=W0212 + result['last_charging_end_result'] = \ + vehicle_state._attributes['lastChargingEndResult'] + if self._attribute == 'connection_status': + # pylint: disable=W0212 + result['connection_status'] = \ + vehicle_state._attributes['connectionStatus'] return result def update(self): """Read new state data from the library.""" from bimmer_connected.state import LockState + from bimmer_connected.state import ChargingState vehicle_state = self._vehicle.state # device class opening: On means open, Off means closed @@ -111,6 +158,24 @@ class BMWConnectedDriveSensor(BinarySensorDevice): # 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': + # pylint: disable=W0212 + self._state = (vehicle_state._attributes['connectionStatus'] == + 'CONNECTED') def update_callback(self): """Schedule a state update.""" diff --git a/homeassistant/components/bmw_connected_drive/__init__.py b/homeassistant/components/bmw_connected_drive/__init__.py index 347bab6f529..a7ed262ac2c 100644 --- a/homeassistant/components/bmw_connected_drive/__init__.py +++ b/homeassistant/components/bmw_connected_drive/__init__.py @@ -14,7 +14,7 @@ from homeassistant.helpers import discovery from homeassistant.helpers.event import track_utc_time_change import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['bimmer_connected==0.5.0'] +REQUIREMENTS = ['bimmer_connected==0.5.1'] _LOGGER = logging.getLogger(__name__) diff --git a/homeassistant/components/sensor/bmw_connected_drive.py b/homeassistant/components/sensor/bmw_connected_drive.py index ed75520c179..8e06836b102 100644 --- a/homeassistant/components/sensor/bmw_connected_drive.py +++ b/homeassistant/components/sensor/bmw_connected_drive.py @@ -9,22 +9,12 @@ import logging from homeassistant.components.bmw_connected_drive import DOMAIN as BMW_DOMAIN from homeassistant.helpers.entity import Entity +from homeassistant.helpers.icon import icon_for_battery_level DEPENDENCIES = ['bmw_connected_drive'] _LOGGER = logging.getLogger(__name__) -LENGTH_ATTRIBUTES = { - 'remaining_range_fuel': ['Range (fuel)', 'mdi:ruler'], - 'mileage': ['Mileage', 'mdi:speedometer'] -} - -VALID_ATTRIBUTES = { - 'remaining_fuel': ['Remaining Fuel', 'mdi:gas-station'] -} - -VALID_ATTRIBUTES.update(LENGTH_ATTRIBUTES) - def setup_platform(hass, config, add_devices, discovery_info=None): """Set up the BMW sensors.""" @@ -34,27 +24,26 @@ def setup_platform(hass, config, add_devices, discovery_info=None): devices = [] for account in accounts: for vehicle in account.account.vehicles: - for key, value in sorted(VALID_ATTRIBUTES.items()): - device = BMWConnectedDriveSensor(account, vehicle, key, - value[0], value[1]) + for attribute_name in vehicle.drive_train_attributes: + device = BMWConnectedDriveSensor(account, vehicle, + attribute_name) devices.append(device) + device = BMWConnectedDriveSensor(account, vehicle, 'mileage') + devices.append(device) add_devices(devices, True) class BMWConnectedDriveSensor(Entity): """Representation of a BMW vehicle sensor.""" - def __init__(self, account, vehicle, attribute: str, sensor_name, icon): + def __init__(self, account, vehicle, attribute: str): """Constructor.""" self._vehicle = vehicle self._account = account self._attribute = attribute self._state = None - self._unit_of_measurement = None self._name = '{} {}'.format(self._vehicle.name, self._attribute) self._unique_id = '{}-{}'.format(self._vehicle.vin, self._attribute) - self._sensor_name = sensor_name - self._icon = icon @property def should_poll(self) -> bool: @@ -74,7 +63,27 @@ class BMWConnectedDriveSensor(Entity): @property def icon(self): """Icon to use in the frontend, if any.""" - return self._icon + from bimmer_connected.state import ChargingState + vehicle_state = self._vehicle.state + charging_state = vehicle_state.charging_status in \ + [ChargingState.CHARGING] + + if self._attribute == 'mileage': + return 'mdi:speedometer' + elif self._attribute in ( + 'remaining_range_total', 'remaining_range_electric', + 'remaining_range_fuel', 'max_range_electric'): + return 'mdi:ruler' + elif self._attribute == 'remaining_fuel': + return 'mdi:gas-station' + elif self._attribute == 'charging_time_remaining': + return 'mdi:update' + elif self._attribute == 'charging_status': + return 'mdi:battery-charging' + elif self._attribute == 'charging_level_hv': + return icon_for_battery_level( + battery_level=vehicle_state.charging_level_hv, + charging=charging_state) @property def state(self): @@ -88,7 +97,17 @@ class BMWConnectedDriveSensor(Entity): @property def unit_of_measurement(self) -> str: """Get the unit of measurement.""" - return self._unit_of_measurement + if self._attribute in ( + 'mileage', 'remaining_range_total', 'remaining_range_electric', + 'remaining_range_fuel', 'max_range_electric'): + return 'km' + elif self._attribute == 'remaining_fuel': + return 'l' + elif self._attribute == 'charging_time_remaining': + return 'h' + elif self._attribute == 'charging_level_hv': + return '%' + return None @property def device_state_attributes(self): @@ -101,14 +120,10 @@ class BMWConnectedDriveSensor(Entity): """Read new state data from the library.""" _LOGGER.debug('Updating %s', self._vehicle.name) vehicle_state = self._vehicle.state - self._state = getattr(vehicle_state, self._attribute) - - if self._attribute in LENGTH_ATTRIBUTES: - self._unit_of_measurement = 'km' - elif self._attribute == 'remaining_fuel': - self._unit_of_measurement = 'l' + if self._attribute == 'charging_status': + self._state = getattr(vehicle_state, self._attribute).value else: - self._unit_of_measurement = None + self._state = getattr(vehicle_state, self._attribute) def update_callback(self): """Schedule a state update.""" diff --git a/requirements_all.txt b/requirements_all.txt index 66b011e0440..2db7a66d7b2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -149,7 +149,7 @@ beautifulsoup4==4.6.0 bellows==0.6.0 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.5.0 +bimmer_connected==0.5.1 # homeassistant.components.blink blinkpy==0.6.0