Skip to content

Commit

Permalink
Update to Home Assistant 2023.2.2 (#55)
Browse files Browse the repository at this point in the history
Co-authored-by: rikroe <[email protected]>
  • Loading branch information
github-actions[bot] and rikroe authored Feb 5, 2023
1 parent 85f9a89 commit 385baa8
Show file tree
Hide file tree
Showing 15 changed files with 106 additions and 101 deletions.
5 changes: 3 additions & 2 deletions custom_components/bmw_connected_drive/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data[DOMAIN][entry.entry_id] = coordinator

# Set up all platforms except notify
hass.config_entries.async_setup_platforms(
await hass.config_entries.async_forward_entry_setups(
entry, [platform for platform in PLATFORMS if platform != Platform.NOTIFY]
)

Expand Down Expand Up @@ -163,6 +163,7 @@ class BMWBaseEntity(CoordinatorEntity[BMWDataUpdateCoordinator]):

coordinator: BMWDataUpdateCoordinator
_attr_attribution = ATTRIBUTION
_attr_has_entity_name = True

def __init__(
self,
Expand All @@ -182,7 +183,7 @@ def __init__(
identifiers={(DOMAIN, self.vehicle.vin)},
manufacturer=vehicle.brand.name,
model=vehicle.name,
name=f"{vehicle.brand.name} {vehicle.name}",
name=vehicle.name,
)

async def async_added_to_hass(self) -> None:
Expand Down
11 changes: 5 additions & 6 deletions custom_components/bmw_connected_drive/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,18 @@

ALLOWED_CONDITION_BASED_SERVICE_KEYS = {
"BRAKE_FLUID",
"EMISSION_CHECK",
"ENGINE_OIL",
"OIL",
"TIRE_WEAR_FRONT",
"TIRE_WEAR_REAR",
"VEHICLE_CHECK",
"VEHICLE_TUV",
}
LOGGED_CONDITION_BASED_SERVICE_WARNINGS = set()
LOGGED_CONDITION_BASED_SERVICE_WARNINGS: set[str] = set()

ALLOWED_CHECK_CONTROL_MESSAGE_KEYS = {"ENGINE_OIL", "TIRE_PRESSURE"}
LOGGED_CHECK_CONTROL_MESSAGE_WARNINGS = set()
LOGGED_CHECK_CONTROL_MESSAGE_WARNINGS: set[str] = set()


def _condition_based_services(
Expand Down Expand Up @@ -121,7 +122,7 @@ class BMWBinarySensorEntityDescription(
SENSOR_TYPES: tuple[BMWBinarySensorEntityDescription, ...] = (
BMWBinarySensorEntityDescription(
key="lids",
name="Doors",
name="Lids",
device_class=BinarySensorDeviceClass.OPENING,
icon="mdi:car-door-lock",
# device class opening: On means open, Off means closed
Expand Down Expand Up @@ -165,7 +166,7 @@ class BMWBinarySensorEntityDescription(
),
BMWBinarySensorEntityDescription(
key="check_control_messages",
name="Control messages",
name="Check control messages",
device_class=BinarySensorDeviceClass.PROBLEM,
icon="mdi:car-tire-alert",
# device class problem: On means problem detected, Off means no problem
Expand Down Expand Up @@ -224,8 +225,6 @@ def __init__(
super().__init__(coordinator, vehicle)
self.entity_description = description
self._unit_system = unit_system

self._attr_name = f"{vehicle.name} {description.key}"
self._attr_unique_id = f"{vehicle.vin}-{description.key}"

@callback
Expand Down
20 changes: 10 additions & 10 deletions custom_components/bmw_connected_drive/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,31 @@ class BMWButtonEntityDescription(ButtonEntityDescription):
BMWButtonEntityDescription(
key="light_flash",
icon="mdi:car-light-alert",
name="Flash Lights",
name="Flash lights",
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_light_flash(),
),
BMWButtonEntityDescription(
key="sound_horn",
icon="mdi:bullhorn",
name="Sound Horn",
name="Sound horn",
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_horn(),
),
BMWButtonEntityDescription(
key="activate_air_conditioning",
icon="mdi:hvac",
name="Activate Air Conditioning",
name="Activate air conditioning",
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning(),
),
BMWButtonEntityDescription(
key="deactivate_air_conditioning",
icon="mdi:hvac-off",
name="Deactivate Air Conditioning",
name="Deactivate air conditioning",
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_air_conditioning_stop(),
),
BMWButtonEntityDescription(
key="find_vehicle",
icon="mdi:crosshairs-question",
name="Find Vehicle",
name="Find vehicle",
remote_function=lambda vehicle: vehicle.remote_services.trigger_remote_vehicle_finder(),
),
BMWButtonEntityDescription(
Expand Down Expand Up @@ -112,8 +112,6 @@ def __init__(
"""Initialize BMW vehicle sensor."""
super().__init__(coordinator, vehicle)
self.entity_description = description

self._attr_name = f"{vehicle.name} {description.name}"
self._attr_unique_id = f"{vehicle.vin}-{description.key}"

async def async_press(self) -> None:
Expand All @@ -122,9 +120,11 @@ async def async_press(self) -> None:
await self.entity_description.remote_function(self.vehicle)
elif self.entity_description.account_function:
_LOGGER.warning(
"The 'Refresh from cloud' button is deprecated. Use the 'homeassistant.update_entity' "
"service with any BMW entity for a full reload. See https://www.home-assistant.io/"
"integrations/bmw_connected_drive/#update-the-state--refresh-from-api for details"
"The 'Refresh from cloud' button is deprecated. Use the"
" 'homeassistant.update_entity' service with any BMW entity for a full"
" reload. See"
" https://www.home-assistant.io/integrations/bmw_connected_drive/#update-the-state--refresh-from-api"
" for details"
)
await self.entity_description.account_function(self.coordinator)

Expand Down
7 changes: 1 addition & 6 deletions custom_components/bmw_connected_drive/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,9 @@ def async_get_options_flow(
return BMWOptionsFlow(config_entry)


class BMWOptionsFlow(config_entries.OptionsFlow):
class BMWOptionsFlow(config_entries.OptionsFlowWithConfigEntry):
"""Handle a option flow for MyBMW."""

def __init__(self, config_entry: config_entries.ConfigEntry) -> None:
"""Initialize MyBMW option flow."""
self.config_entry = config_entry
self.options = dict(config_entry.options)

async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
Expand Down
15 changes: 5 additions & 10 deletions custom_components/bmw_connected_drive/const.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
"""Const file for the MyBMW integration."""
from homeassistant.const import (
LENGTH_KILOMETERS,
LENGTH_MILES,
VOLUME_GALLONS,
VOLUME_LITERS,
)
from homeassistant.const import UnitOfLength, UnitOfVolume

DOMAIN = "bmw_connected_drive"
ATTRIBUTION = "Data provided by MyBMW"
Expand All @@ -20,8 +15,8 @@
DATA_HASS_CONFIG = "hass_config"

UNIT_MAP = {
"KILOMETERS": LENGTH_KILOMETERS,
"MILES": LENGTH_MILES,
"LITERS": VOLUME_LITERS,
"GALLONS": VOLUME_GALLONS,
"KILOMETERS": UnitOfLength.KILOMETERS,
"MILES": UnitOfLength.MILES,
"LITERS": UnitOfVolume.LITERS,
"GALLONS": UnitOfVolume.GALLONS,
}
2 changes: 1 addition & 1 deletion custom_components/bmw_connected_drive/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
_LOGGER = logging.getLogger(__name__)


class BMWDataUpdateCoordinator(DataUpdateCoordinator):
class BMWDataUpdateCoordinator(DataUpdateCoordinator[None]):
"""Class to manage fetching BMW data."""

account: MyBMWAccount
Expand Down
20 changes: 12 additions & 8 deletions custom_components/bmw_connected_drive/device_tracker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
from __future__ import annotations

import logging
from typing import Literal
from typing import Any

from bimmer_connected.vehicle import MyBMWVehicle

from homeassistant.components.device_tracker import SOURCE_TYPE_GPS
from homeassistant.components.device_tracker.config_entry import TrackerEntity
from homeassistant.components.device_tracker import SourceType, TrackerEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
Expand All @@ -32,7 +31,10 @@ async def async_setup_entry(
entities.append(BMWDeviceTracker(coordinator, vehicle))
if not vehicle.is_vehicle_tracking_enabled:
_LOGGER.info(
"Tracking is (currently) disabled for vehicle %s (%s), defaulting to unknown",
(
"Tracking is (currently) disabled for vehicle %s (%s), defaulting"
" to unknown"
),
vehicle.name,
vehicle.vin,
)
Expand All @@ -54,10 +56,10 @@ def __init__(
super().__init__(coordinator, vehicle)

self._attr_unique_id = vehicle.vin
self._attr_name = vehicle.name
self._attr_name = None

@property
def extra_state_attributes(self) -> dict:
def extra_state_attributes(self) -> dict[str, Any]:
"""Return entity specific state attributes."""
return {**self._attrs, ATTR_DIRECTION: self.vehicle.vehicle_location.heading}

Expand All @@ -67,6 +69,7 @@ def latitude(self) -> float | None:
return (
self.vehicle.vehicle_location.location[0]
if self.vehicle.is_vehicle_tracking_enabled
and self.vehicle.vehicle_location.location
else None
)

Expand All @@ -76,10 +79,11 @@ def longitude(self) -> float | None:
return (
self.vehicle.vehicle_location.location[1]
if self.vehicle.is_vehicle_tracking_enabled
and self.vehicle.vehicle_location.location
else None
)

@property
def source_type(self) -> Literal["gps"]:
def source_type(self) -> SourceType:
"""Return the source type, eg gps or router, of the device."""
return SOURCE_TYPE_GPS
return SourceType.GPS
50 changes: 16 additions & 34 deletions custom_components/bmw_connected_drive/diagnostics.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"""Diagnostics support for the BMW Connected Drive integration."""
from __future__ import annotations

from dataclasses import asdict
import json
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import TYPE_CHECKING, Any

from bimmer_connected.utils import MyBMWJSONEncoder
Expand All @@ -12,7 +11,6 @@
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.device_registry import DeviceEntry

from .const import CONF_REFRESH_TOKEN, DOMAIN
Expand All @@ -31,7 +29,6 @@
"heading",
"vin",
"licensePlate",
"name",
"city",
"street",
"streetNumber",
Expand All @@ -42,31 +39,7 @@
]


async def async_get_fingerprints(
coordinator: "BMWDataUpdateCoordinator",
) -> dict[str, Any]:
"""Return pre-redacted fingerprints (i.e. the raw API responses)."""

fingerprints = {}

try:
# Use the library's fingerprint function to store all relevant
# HTTP requests into a temporary directory and load from there
with TemporaryDirectory() as tempdir:
tempdir_path = Path(tempdir)
coordinator.account.config.log_response_path = tempdir_path
await coordinator.account.get_vehicles()
for logfile in tempdir_path.iterdir():
with open(logfile, "rb") as fp:
fingerprints[logfile.name] = json.load(fp)
finally:
# Make sure that log_response_path is always set to None afterwards
coordinator.account.config.log_response_path = None

return fingerprints


def vehicle_to_dict(vehicle: MyBMWVehicle) -> dict:
def vehicle_to_dict(vehicle: MyBMWVehicle | None) -> dict:
"""Convert a MyBMWVehicle to a dictionary using MyBMWJSONEncoder."""
retval: dict = json.loads(json.dumps(vehicle, cls=MyBMWJSONEncoder))
return retval
Expand All @@ -78,17 +51,23 @@ async def async_get_config_entry_diagnostics(
"""Return diagnostics for a config entry."""
coordinator: BMWDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]

coordinator.account.config.log_responses = True
await coordinator.account.get_vehicles(force_init=True)

diagnostics_data = {
"info": async_redact_data(config_entry.data, TO_REDACT_INFO),
"data": [
async_redact_data(vehicle_to_dict(vehicle), TO_REDACT_DATA)
for vehicle in coordinator.account.vehicles
],
"fingerprint": async_redact_data(
await async_get_fingerprints(coordinator), TO_REDACT_DATA
[asdict(r) for r in coordinator.account.get_stored_responses()],
TO_REDACT_DATA,
),
}

coordinator.account.config.log_responses = False

return diagnostics_data


Expand All @@ -98,19 +77,22 @@ async def async_get_device_diagnostics(
"""Return diagnostics for a device."""
coordinator: BMWDataUpdateCoordinator = hass.data[DOMAIN][config_entry.entry_id]

coordinator.account.config.log_responses = True
await coordinator.account.get_vehicles(force_init=True)

vin = next(iter(device.identifiers))[1]
vehicle = coordinator.account.get_vehicle(vin)

if not vehicle:
raise HomeAssistantError("Vehicle not found")

diagnostics_data = {
"info": async_redact_data(config_entry.data, TO_REDACT_INFO),
"data": async_redact_data(vehicle_to_dict(vehicle), TO_REDACT_DATA),
# Always have to get the full fingerprint as the VIN is redacted beforehand by the library
"fingerprint": async_redact_data(
await async_get_fingerprints(coordinator), TO_REDACT_DATA
[asdict(r) for r in coordinator.account.get_stored_responses()],
TO_REDACT_DATA,
),
}

coordinator.account.config.log_responses = False

return diagnostics_data
Loading

0 comments on commit 385baa8

Please sign in to comment.