From 1afb30344afac9fce1e85db30036fbc8d92e1dd7 Mon Sep 17 00:00:00 2001 From: rikroe <42204099+rikroe@users.noreply.github.com> Date: Wed, 11 Jan 2023 01:09:45 +0100 Subject: [PATCH] Add diagnostics to bmw_connected_drive (#74871) * Add diagnostics to bmw_connected_drive * Add tests for diagnostics * Move get_fingerprints to library, bump bimmer_connected to 0.10.4 * Update bimmer_connected to 0.11.0 * Fix pytest * Mock actual diagnostics HTTP calls * Update tests for bimmer_connected 0.12.0 * Don't raise errors if vehicle is not found Co-authored-by: rikroe --- .../bmw_connected_drive/diagnostics.py | 98 +++ .../bmw_connected_drive/__init__.py | 77 +- .../bmw_connected_drive/conftest.py | 11 +- .../diagnostics/diagnostics_config_entry.json | 657 ++++++++++++++++++ .../diagnostics/diagnostics_device.json | 655 +++++++++++++++++ .../bmw_connected_drive/test_diagnostics.py | 104 +++ 6 files changed, 1559 insertions(+), 43 deletions(-) create mode 100644 homeassistant/components/bmw_connected_drive/diagnostics.py create mode 100644 tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_config_entry.json create mode 100644 tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_device.json create mode 100644 tests/components/bmw_connected_drive/test_diagnostics.py diff --git a/homeassistant/components/bmw_connected_drive/diagnostics.py b/homeassistant/components/bmw_connected_drive/diagnostics.py new file mode 100644 index 00000000000..c69d06d818f --- /dev/null +++ b/homeassistant/components/bmw_connected_drive/diagnostics.py @@ -0,0 +1,98 @@ +"""Diagnostics support for the BMW Connected Drive integration.""" +from __future__ import annotations + +from dataclasses import asdict +import json +from typing import TYPE_CHECKING, Any + +from bimmer_connected.utils import MyBMWJSONEncoder + +from homeassistant.components.diagnostics.util import async_redact_data +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME +from homeassistant.core import HomeAssistant +from homeassistant.helpers.device_registry import DeviceEntry + +from .const import CONF_REFRESH_TOKEN, DOMAIN + +if TYPE_CHECKING: + from bimmer_connected.vehicle import MyBMWVehicle + + from .coordinator import BMWDataUpdateCoordinator + +TO_REDACT_INFO = [CONF_USERNAME, CONF_PASSWORD, CONF_REFRESH_TOKEN] +TO_REDACT_DATA = [ + "lat", + "latitude", + "lon", + "longitude", + "heading", + "vin", + "licensePlate", + "city", + "street", + "streetNumber", + "postalCode", + "phone", + "formatted", + "subtitle", +] + + +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 + + +async def async_get_config_entry_diagnostics( + hass: HomeAssistant, config_entry: ConfigEntry +) -> dict[str, Any]: + """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( + [asdict(r) for r in coordinator.account.get_stored_responses()], + TO_REDACT_DATA, + ), + } + + coordinator.account.config.log_responses = False + + return diagnostics_data + + +async def async_get_device_diagnostics( + hass: HomeAssistant, config_entry: ConfigEntry, device: DeviceEntry +) -> dict[str, Any]: + """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) + + 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( + [asdict(r) for r in coordinator.account.get_stored_responses()], + TO_REDACT_DATA, + ), + } + + coordinator.account.config.log_responses = False + + return diagnostics_data diff --git a/tests/components/bmw_connected_drive/__init__.py b/tests/components/bmw_connected_drive/__init__.py index 81b3bb9fff3..ed31b4308b8 100644 --- a/tests/components/bmw_connected_drive/__init__.py +++ b/tests/components/bmw_connected_drive/__init__.py @@ -3,7 +3,10 @@ import json from pathlib import Path -from bimmer_connected.account import MyBMWAccount +from bimmer_connected.api.authentication import MyBMWAuthentication +from bimmer_connected.const import VEHICLE_STATE_URL, VEHICLES_URL +import httpx +import respx from homeassistant import config_entries from homeassistant.components.bmw_connected_drive.const import ( @@ -13,7 +16,6 @@ from homeassistant.components.bmw_connected_drive.const import ( ) from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME from homeassistant.core import HomeAssistant -from homeassistant.util.dt import utcnow from tests.common import MockConfigEntry, get_fixture_path, load_fixture @@ -39,49 +41,46 @@ FIXTURE_CONFIG_ENTRY = { "unique_id": f"{FIXTURE_USER_INPUT[CONF_REGION]}-{FIXTURE_USER_INPUT[CONF_REGION]}", } +FIXTURE_PATH = Path(get_fixture_path("", integration=BMW_DOMAIN)) -async def mock_vehicles_from_fixture(account: MyBMWAccount) -> None: - """Load MyBMWVehicle from fixtures and add them to the account.""" - fixture_path = Path(get_fixture_path("", integration=BMW_DOMAIN)) +def vehicles_sideeffect(request: httpx.Request) -> httpx.Response: + """Return /vehicles response based on x-user-agent.""" + x_user_agent = request.headers.get("x-user-agent", "").split(";") + brand = x_user_agent[1] + vehicles = [] + for vehicle_file in FIXTURE_PATH.rglob(f"vehicles_v2_{brand}_*.json"): + vehicles.extend(json.loads(load_fixture(vehicle_file, integration=BMW_DOMAIN))) + return httpx.Response(200, json=vehicles) - fixture_vehicles_bmw = list(fixture_path.rglob("vehicles_v2_bmw_*.json")) - fixture_vehicles_mini = list(fixture_path.rglob("vehicles_v2_mini_*.json")) - # Load vehicle base lists as provided by vehicles/v2 API - vehicles = { - "bmw": [ - vehicle - for bmw_file in fixture_vehicles_bmw - for vehicle in json.loads(load_fixture(bmw_file, integration=BMW_DOMAIN)) - ], - "mini": [ - vehicle - for mini_file in fixture_vehicles_mini - for vehicle in json.loads(load_fixture(mini_file, integration=BMW_DOMAIN)) - ], - } - fetched_at = utcnow() - - # Create a vehicle with base + specific state as provided by state/VIN API - for vehicle_base in [vehicle for brand in vehicles.values() for vehicle in brand]: - vehicle_state_path = ( - Path("vehicles") - / vehicle_base["attributes"]["bodyType"] - / f"state_{vehicle_base['vin']}_0.json" - ) - vehicle_state = json.loads( - load_fixture( - vehicle_state_path, - integration=BMW_DOMAIN, - ) +def vehicle_state_sideeffect(request: httpx.Request) -> httpx.Response: + """Return /vehicles/state response.""" + state_file = next(FIXTURE_PATH.rglob(f"state_{request.headers['bmw-vin']}_*.json")) + try: + return httpx.Response( + 200, json=json.loads(load_fixture(state_file, integration=BMW_DOMAIN)) ) + except KeyError: + return httpx.Response(404) - account.add_vehicle( - vehicle_base, - vehicle_state, - fetched_at, - ) + +def mock_vehicles() -> respx.Router: + """Return mocked adapter for vehicles.""" + router = respx.mock(assert_all_called=False) + + # Get vehicle list + router.get(VEHICLES_URL).mock(side_effect=vehicles_sideeffect) + + # Get vehicle state + router.get(VEHICLE_STATE_URL).mock(side_effect=vehicle_state_sideeffect) + + return router + + +async def mock_login(auth: MyBMWAuthentication) -> None: + """Mock a successful login.""" + auth.access_token = "SOME_ACCESS_TOKEN" async def setup_mocked_integration(hass: HomeAssistant) -> MockConfigEntry: diff --git a/tests/components/bmw_connected_drive/conftest.py b/tests/components/bmw_connected_drive/conftest.py index bf9d32ed9fa..887df4da603 100644 --- a/tests/components/bmw_connected_drive/conftest.py +++ b/tests/components/bmw_connected_drive/conftest.py @@ -1,12 +1,15 @@ """Fixtures for BMW tests.""" -from bimmer_connected.account import MyBMWAccount +from bimmer_connected.api.authentication import MyBMWAuthentication import pytest -from . import mock_vehicles_from_fixture +from . import mock_login, mock_vehicles @pytest.fixture async def bmw_fixture(monkeypatch): - """Patch the vehicle fixtures into a MyBMWAccount.""" - monkeypatch.setattr(MyBMWAccount, "get_vehicles", mock_vehicles_from_fixture) + """Patch the MyBMW Login and mock HTTP calls.""" + monkeypatch.setattr(MyBMWAuthentication, "login", mock_login) + + with mock_vehicles(): + yield mock_vehicles diff --git a/tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_config_entry.json b/tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_config_entry.json new file mode 100644 index 00000000000..9c56e0595b6 --- /dev/null +++ b/tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_config_entry.json @@ -0,0 +1,657 @@ +{ + "info": { + "username": "**REDACTED**", + "password": "**REDACTED**", + "region": "rest_of_world", + "refresh_token": "**REDACTED**" + }, + "data": [ + { + "data": { + "appVehicleType": "CONNECTED", + "attributes": { + "a4aType": "USB_ONLY", + "bodyType": "I01", + "brand": "BMW_I", + "color": 4284110934, + "countryOfOrigin": "CZ", + "driveTrain": "ELECTRIC_WITH_RANGE_EXTENDER", + "driverGuideInfo": { + "androidAppScheme": "com.bmwgroup.driversguide.row", + "androidStoreUrl": "https://play.google.com/store/apps/details?id=com.bmwgroup.driversguide.row", + "iosAppScheme": "bmwdriversguide:///open", + "iosStoreUrl": "https://apps.apple.com/de/app/id714042749?mt=8" + }, + "headUnitType": "NBT", + "hmiVersion": "ID4", + "lastFetched": "2022-07-10T09:25:53.104Z", + "model": "i3 (+ REX)", + "softwareVersionCurrent": { + "iStep": 510, + "puStep": { "month": 11, "year": 21 }, + "seriesCluster": "I001" + }, + "softwareVersionExFactory": { + "iStep": 502, + "puStep": { "month": 3, "year": 15 }, + "seriesCluster": "I001" + }, + "year": 2015 + }, + "mappingInfo": { + "isAssociated": false, + "isLmmEnabled": false, + "isPrimaryUser": true, + "mappingStatus": "CONFIRMED" + }, + "vin": "**REDACTED**", + "is_metric": true, + "fetched_at": "2022-07-10T11:00:00+00:00", + "capabilities": { + "climateFunction": "AIR_CONDITIONING", + "climateNow": true, + "climateTimerTrigger": "DEPARTURE_TIMER", + "horn": true, + "isBmwChargingSupported": true, + "isCarSharingSupported": false, + "isChargeNowForBusinessSupported": false, + "isChargingHistorySupported": true, + "isChargingHospitalityEnabled": false, + "isChargingLoudnessEnabled": false, + "isChargingPlanSupported": true, + "isChargingPowerLimitEnabled": false, + "isChargingSettingsEnabled": false, + "isChargingTargetSocEnabled": false, + "isClimateTimerSupported": true, + "isCustomerEsimSupported": false, + "isDCSContractManagementSupported": true, + "isDataPrivacyEnabled": false, + "isEasyChargeEnabled": false, + "isEvGoChargingSupported": false, + "isMiniChargingSupported": false, + "isNonLscFeatureEnabled": false, + "isRemoteEngineStartSupported": false, + "isRemoteHistoryDeletionSupported": false, + "isRemoteHistorySupported": true, + "isRemoteParkingSupported": false, + "isRemoteServicesActivationRequired": false, + "isRemoteServicesBookingRequired": false, + "isScanAndChargeSupported": false, + "isSustainabilitySupported": false, + "isWifiHotspotServiceSupported": false, + "lastStateCallState": "ACTIVATED", + "lights": true, + "lock": true, + "remoteChargingCommands": {}, + "sendPoi": true, + "specialThemeSupport": [], + "unlock": true, + "vehicleFinder": false, + "vehicleStateSource": "LAST_STATE_CALL" + }, + "state": { + "chargingProfile": { + "chargingControlType": "WEEKLY_PLANNER", + "chargingMode": "DELAYED_CHARGING", + "chargingPreference": "CHARGING_WINDOW", + "chargingSettings": { + "hospitality": "NO_ACTION", + "idcc": "NO_ACTION", + "targetSoc": 100 + }, + "climatisationOn": false, + "departureTimes": [ + { + "action": "DEACTIVATE", + "id": 1, + "timeStamp": { "hour": 7, "minute": 35 }, + "timerWeekDays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY" + ] + }, + { + "action": "DEACTIVATE", + "id": 2, + "timeStamp": { "hour": 18, "minute": 0 }, + "timerWeekDays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ] + }, + { + "action": "DEACTIVATE", + "id": 3, + "timeStamp": { "hour": 7, "minute": 0 }, + "timerWeekDays": [] + }, + { "action": "DEACTIVATE", "id": 4, "timerWeekDays": [] } + ], + "reductionOfChargeCurrent": { + "end": { "hour": 1, "minute": 30 }, + "start": { "hour": 18, "minute": 1 } + } + }, + "checkControlMessages": [], + "climateTimers": [ + { + "departureTime": { "hour": 6, "minute": 40 }, + "isWeeklyTimer": true, + "timerAction": "ACTIVATE", + "timerWeekDays": ["THURSDAY", "SUNDAY"] + }, + { + "departureTime": { "hour": 12, "minute": 50 }, + "isWeeklyTimer": false, + "timerAction": "ACTIVATE", + "timerWeekDays": ["MONDAY"] + }, + { + "departureTime": { "hour": 18, "minute": 59 }, + "isWeeklyTimer": true, + "timerAction": "DEACTIVATE", + "timerWeekDays": ["WEDNESDAY"] + } + ], + "combustionFuelLevel": { + "range": 105, + "remainingFuelLiters": 6, + "remainingFuelPercent": 65 + }, + "currentMileage": 137009, + "doorsState": { + "combinedSecurityState": "UNLOCKED", + "combinedState": "CLOSED", + "hood": "CLOSED", + "leftFront": "CLOSED", + "leftRear": "CLOSED", + "rightFront": "CLOSED", + "rightRear": "CLOSED", + "trunk": "CLOSED" + }, + "driverPreferences": { "lscPrivacyMode": "OFF" }, + "electricChargingState": { + "chargingConnectionType": "CONDUCTIVE", + "chargingLevelPercent": 82, + "chargingStatus": "WAITING_FOR_CHARGING", + "chargingTarget": 100, + "isChargerConnected": true, + "range": 174 + }, + "isLeftSteering": true, + "isLscSupported": true, + "lastFetched": "2022-06-22T14:24:23.982Z", + "lastUpdatedAt": "2022-06-22T13:58:52Z", + "range": 174, + "requiredServices": [ + { + "dateTime": "2022-10-01T00:00:00.000Z", + "description": "Next service due by the specified date.", + "status": "OK", + "type": "BRAKE_FLUID" + }, + { + "dateTime": "2023-05-01T00:00:00.000Z", + "description": "Next vehicle check due after the specified distance or date.", + "status": "OK", + "type": "VEHICLE_CHECK" + }, + { + "dateTime": "2023-05-01T00:00:00.000Z", + "description": "Next state inspection due by the specified date.", + "status": "OK", + "type": "VEHICLE_TUV" + } + ], + "roofState": { "roofState": "CLOSED", "roofStateType": "SUN_ROOF" }, + "windowsState": { + "combinedState": "CLOSED", + "leftFront": "CLOSED", + "rightFront": "CLOSED" + } + } + }, + "fuel_and_battery": { + "remaining_range_fuel": [105, "km"], + "remaining_range_electric": [174, "km"], + "remaining_range_total": [279, "km"], + "remaining_fuel": [6, "L"], + "remaining_fuel_percent": 65, + "remaining_battery_percent": 82, + "charging_status": "WAITING_FOR_CHARGING", + "charging_start_time_no_tz": "2022-07-10T18:01:00", + "charging_end_time": null, + "is_charger_connected": true, + "account_timezone": { + "_std_offset": "0:00:00", + "_dst_offset": "0:00:00", + "_dst_saved": "0:00:00", + "_hasdst": false, + "_tznames": ["UTC", "UTC"] + }, + "charging_start_time": "2022-07-10T18:01:00+00:00" + }, + "vehicle_location": { + "location": null, + "heading": null, + "vehicle_update_timestamp": "2022-07-10T09:25:53+00:00", + "account_region": "row", + "remote_service_position": null + }, + "doors_and_windows": { + "door_lock_state": "UNLOCKED", + "lids": [ + { "name": "hood", "state": "CLOSED", "is_closed": true }, + { "name": "leftFront", "state": "CLOSED", "is_closed": true }, + { "name": "leftRear", "state": "CLOSED", "is_closed": true }, + { "name": "rightFront", "state": "CLOSED", "is_closed": true }, + { "name": "rightRear", "state": "CLOSED", "is_closed": true }, + { "name": "trunk", "state": "CLOSED", "is_closed": true }, + { "name": "sunRoof", "state": "CLOSED", "is_closed": true } + ], + "windows": [ + { "name": "leftFront", "state": "CLOSED", "is_closed": true }, + { "name": "rightFront", "state": "CLOSED", "is_closed": true } + ], + "all_lids_closed": true, + "all_windows_closed": true, + "open_lids": [], + "open_windows": [] + }, + "condition_based_services": { + "messages": [ + { + "service_type": "BRAKE_FLUID", + "state": "OK", + "due_date": "2022-10-01T00:00:00+00:00", + "due_distance": [null, null] + }, + { + "service_type": "VEHICLE_CHECK", + "state": "OK", + "due_date": "2023-05-01T00:00:00+00:00", + "due_distance": [null, null] + }, + { + "service_type": "VEHICLE_TUV", + "state": "OK", + "due_date": "2023-05-01T00:00:00+00:00", + "due_distance": [null, null] + } + ], + "is_service_required": false + }, + "check_control_messages": { + "messages": [], + "has_check_control_messages": false + }, + "charging_profile": { + "is_pre_entry_climatization_enabled": false, + "timer_type": "WEEKLY_PLANNER", + "departure_times": [ + { + "_timer_dict": { + "action": "DEACTIVATE", + "id": 1, + "timeStamp": { "hour": 7, "minute": 35 }, + "timerWeekDays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY" + ] + }, + "action": "DEACTIVATE", + "start_time": "07:35:00", + "timer_id": 1, + "weekdays": ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"] + }, + { + "_timer_dict": { + "action": "DEACTIVATE", + "id": 2, + "timeStamp": { "hour": 18, "minute": 0 }, + "timerWeekDays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ] + }, + "action": "DEACTIVATE", + "start_time": "18:00:00", + "timer_id": 2, + "weekdays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ] + }, + { + "_timer_dict": { + "action": "DEACTIVATE", + "id": 3, + "timeStamp": { "hour": 7, "minute": 0 }, + "timerWeekDays": [] + }, + "action": "DEACTIVATE", + "start_time": "07:00:00", + "timer_id": 3, + "weekdays": [] + }, + { + "_timer_dict": { + "action": "DEACTIVATE", + "id": 4, + "timerWeekDays": [] + }, + "action": "DEACTIVATE", + "start_time": null, + "timer_id": 4, + "weekdays": [] + } + ], + "preferred_charging_window": { + "_window_dict": { + "end": { "hour": 1, "minute": 30 }, + "start": { "hour": 18, "minute": 1 } + }, + "end_time": "01:30:00", + "start_time": "18:01:00" + }, + "charging_preferences": "CHARGING_WINDOW", + "charging_mode": "DELAYED_CHARGING" + }, + "available_attributes": [ + "gps_position", + "vin", + "remaining_range_total", + "mileage", + "charging_time_remaining", + "charging_end_time", + "charging_time_label", + "charging_status", + "connection_status", + "remaining_battery_percent", + "remaining_range_electric", + "last_charging_end_result", + "remaining_fuel", + "remaining_range_fuel", + "remaining_fuel_percent", + "condition_based_services", + "check_control_messages", + "door_lock_state", + "timestamp", + "lids", + "windows" + ], + "brand": "bmw", + "drive_train": "ELECTRIC_WITH_RANGE_EXTENDER", + "drive_train_attributes": [ + "remaining_range_total", + "mileage", + "charging_time_remaining", + "charging_end_time", + "charging_time_label", + "charging_status", + "connection_status", + "remaining_battery_percent", + "remaining_range_electric", + "last_charging_end_result", + "remaining_fuel", + "remaining_range_fuel", + "remaining_fuel_percent" + ], + "has_combustion_drivetrain": true, + "has_electric_drivetrain": true, + "is_charging_plan_supported": true, + "is_lsc_enabled": true, + "is_vehicle_active": false, + "is_vehicle_tracking_enabled": false, + "lsc_type": "ACTIVATED", + "mileage": [137009, "km"], + "name": "i3 (+ REX)", + "timestamp": "2022-07-10T09:25:53+00:00", + "vin": "**REDACTED**" + } + ], + "fingerprint": [ + { + "filename": "bmw-vehicles.json", + "content": [ + { + "appVehicleType": "CONNECTED", + "attributes": { + "a4aType": "USB_ONLY", + "bodyType": "I01", + "brand": "BMW_I", + "color": 4284110934, + "countryOfOrigin": "CZ", + "driveTrain": "ELECTRIC_WITH_RANGE_EXTENDER", + "driverGuideInfo": { + "androidAppScheme": "com.bmwgroup.driversguide.row", + "androidStoreUrl": "https://play.google.com/store/apps/details?id=com.bmwgroup.driversguide.row", + "iosAppScheme": "bmwdriversguide:///open", + "iosStoreUrl": "https://apps.apple.com/de/app/id714042749?mt=8" + }, + "headUnitType": "NBT", + "hmiVersion": "ID4", + "lastFetched": "2022-07-10T09:25:53.104Z", + "model": "i3 (+ REX)", + "softwareVersionCurrent": { + "iStep": 510, + "puStep": { "month": 11, "year": 21 }, + "seriesCluster": "I001" + }, + "softwareVersionExFactory": { + "iStep": 502, + "puStep": { "month": 3, "year": 15 }, + "seriesCluster": "I001" + }, + "year": 2015 + }, + "mappingInfo": { + "isAssociated": false, + "isLmmEnabled": false, + "isPrimaryUser": true, + "mappingStatus": "CONFIRMED" + }, + "vin": "**REDACTED**" + } + ] + }, + { "filename": "mini-vehicles.json", "content": [] }, + { + "filename": "bmw-vehicles_state_WBY0FINGERPRINT01.json", + "content": { + "capabilities": { + "climateFunction": "AIR_CONDITIONING", + "climateNow": true, + "climateTimerTrigger": "DEPARTURE_TIMER", + "horn": true, + "isBmwChargingSupported": true, + "isCarSharingSupported": false, + "isChargeNowForBusinessSupported": false, + "isChargingHistorySupported": true, + "isChargingHospitalityEnabled": false, + "isChargingLoudnessEnabled": false, + "isChargingPlanSupported": true, + "isChargingPowerLimitEnabled": false, + "isChargingSettingsEnabled": false, + "isChargingTargetSocEnabled": false, + "isClimateTimerSupported": true, + "isCustomerEsimSupported": false, + "isDCSContractManagementSupported": true, + "isDataPrivacyEnabled": false, + "isEasyChargeEnabled": false, + "isEvGoChargingSupported": false, + "isMiniChargingSupported": false, + "isNonLscFeatureEnabled": false, + "isRemoteEngineStartSupported": false, + "isRemoteHistoryDeletionSupported": false, + "isRemoteHistorySupported": true, + "isRemoteParkingSupported": false, + "isRemoteServicesActivationRequired": false, + "isRemoteServicesBookingRequired": false, + "isScanAndChargeSupported": false, + "isSustainabilitySupported": false, + "isWifiHotspotServiceSupported": false, + "lastStateCallState": "ACTIVATED", + "lights": true, + "lock": true, + "remoteChargingCommands": {}, + "sendPoi": true, + "specialThemeSupport": [], + "unlock": true, + "vehicleFinder": false, + "vehicleStateSource": "LAST_STATE_CALL" + }, + "state": { + "chargingProfile": { + "chargingControlType": "WEEKLY_PLANNER", + "chargingMode": "DELAYED_CHARGING", + "chargingPreference": "CHARGING_WINDOW", + "chargingSettings": { + "hospitality": "NO_ACTION", + "idcc": "NO_ACTION", + "targetSoc": 100 + }, + "climatisationOn": false, + "departureTimes": [ + { + "action": "DEACTIVATE", + "id": 1, + "timeStamp": { "hour": 7, "minute": 35 }, + "timerWeekDays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY" + ] + }, + { + "action": "DEACTIVATE", + "id": 2, + "timeStamp": { "hour": 18, "minute": 0 }, + "timerWeekDays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ] + }, + { + "action": "DEACTIVATE", + "id": 3, + "timeStamp": { "hour": 7, "minute": 0 }, + "timerWeekDays": [] + }, + { "action": "DEACTIVATE", "id": 4, "timerWeekDays": [] } + ], + "reductionOfChargeCurrent": { + "end": { "hour": 1, "minute": 30 }, + "start": { "hour": 18, "minute": 1 } + } + }, + "checkControlMessages": [], + "climateTimers": [ + { + "departureTime": { "hour": 6, "minute": 40 }, + "isWeeklyTimer": true, + "timerAction": "ACTIVATE", + "timerWeekDays": ["THURSDAY", "SUNDAY"] + }, + { + "departureTime": { "hour": 12, "minute": 50 }, + "isWeeklyTimer": false, + "timerAction": "ACTIVATE", + "timerWeekDays": ["MONDAY"] + }, + { + "departureTime": { "hour": 18, "minute": 59 }, + "isWeeklyTimer": true, + "timerAction": "DEACTIVATE", + "timerWeekDays": ["WEDNESDAY"] + } + ], + "combustionFuelLevel": { + "range": 105, + "remainingFuelLiters": 6, + "remainingFuelPercent": 65 + }, + "currentMileage": 137009, + "doorsState": { + "combinedSecurityState": "UNLOCKED", + "combinedState": "CLOSED", + "hood": "CLOSED", + "leftFront": "CLOSED", + "leftRear": "CLOSED", + "rightFront": "CLOSED", + "rightRear": "CLOSED", + "trunk": "CLOSED" + }, + "driverPreferences": { "lscPrivacyMode": "OFF" }, + "electricChargingState": { + "chargingConnectionType": "CONDUCTIVE", + "chargingLevelPercent": 82, + "chargingStatus": "WAITING_FOR_CHARGING", + "chargingTarget": 100, + "isChargerConnected": true, + "range": 174 + }, + "isLeftSteering": true, + "isLscSupported": true, + "lastFetched": "2022-06-22T14:24:23.982Z", + "lastUpdatedAt": "2022-06-22T13:58:52Z", + "range": 174, + "requiredServices": [ + { + "dateTime": "2022-10-01T00:00:00.000Z", + "description": "Next service due by the specified date.", + "status": "OK", + "type": "BRAKE_FLUID" + }, + { + "dateTime": "2023-05-01T00:00:00.000Z", + "description": "Next vehicle check due after the specified distance or date.", + "status": "OK", + "type": "VEHICLE_CHECK" + }, + { + "dateTime": "2023-05-01T00:00:00.000Z", + "description": "Next state inspection due by the specified date.", + "status": "OK", + "type": "VEHICLE_TUV" + } + ], + "roofState": { "roofState": "CLOSED", "roofStateType": "SUN_ROOF" }, + "windowsState": { + "combinedState": "CLOSED", + "leftFront": "CLOSED", + "rightFront": "CLOSED" + } + } + } + } + ] +} diff --git a/tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_device.json b/tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_device.json new file mode 100644 index 00000000000..d76f2c80712 --- /dev/null +++ b/tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_device.json @@ -0,0 +1,655 @@ +{ + "info": { + "username": "**REDACTED**", + "password": "**REDACTED**", + "region": "rest_of_world", + "refresh_token": "**REDACTED**" + }, + "data": { + "data": { + "appVehicleType": "CONNECTED", + "attributes": { + "a4aType": "USB_ONLY", + "bodyType": "I01", + "brand": "BMW_I", + "color": 4284110934, + "countryOfOrigin": "CZ", + "driveTrain": "ELECTRIC_WITH_RANGE_EXTENDER", + "driverGuideInfo": { + "androidAppScheme": "com.bmwgroup.driversguide.row", + "androidStoreUrl": "https://play.google.com/store/apps/details?id=com.bmwgroup.driversguide.row", + "iosAppScheme": "bmwdriversguide:///open", + "iosStoreUrl": "https://apps.apple.com/de/app/id714042749?mt=8" + }, + "headUnitType": "NBT", + "hmiVersion": "ID4", + "lastFetched": "2022-07-10T09:25:53.104Z", + "model": "i3 (+ REX)", + "softwareVersionCurrent": { + "iStep": 510, + "puStep": { "month": 11, "year": 21 }, + "seriesCluster": "I001" + }, + "softwareVersionExFactory": { + "iStep": 502, + "puStep": { "month": 3, "year": 15 }, + "seriesCluster": "I001" + }, + "year": 2015 + }, + "mappingInfo": { + "isAssociated": false, + "isLmmEnabled": false, + "isPrimaryUser": true, + "mappingStatus": "CONFIRMED" + }, + "vin": "**REDACTED**", + "is_metric": true, + "fetched_at": "2022-07-10T11:00:00+00:00", + "capabilities": { + "climateFunction": "AIR_CONDITIONING", + "climateNow": true, + "climateTimerTrigger": "DEPARTURE_TIMER", + "horn": true, + "isBmwChargingSupported": true, + "isCarSharingSupported": false, + "isChargeNowForBusinessSupported": false, + "isChargingHistorySupported": true, + "isChargingHospitalityEnabled": false, + "isChargingLoudnessEnabled": false, + "isChargingPlanSupported": true, + "isChargingPowerLimitEnabled": false, + "isChargingSettingsEnabled": false, + "isChargingTargetSocEnabled": false, + "isClimateTimerSupported": true, + "isCustomerEsimSupported": false, + "isDCSContractManagementSupported": true, + "isDataPrivacyEnabled": false, + "isEasyChargeEnabled": false, + "isEvGoChargingSupported": false, + "isMiniChargingSupported": false, + "isNonLscFeatureEnabled": false, + "isRemoteEngineStartSupported": false, + "isRemoteHistoryDeletionSupported": false, + "isRemoteHistorySupported": true, + "isRemoteParkingSupported": false, + "isRemoteServicesActivationRequired": false, + "isRemoteServicesBookingRequired": false, + "isScanAndChargeSupported": false, + "isSustainabilitySupported": false, + "isWifiHotspotServiceSupported": false, + "lastStateCallState": "ACTIVATED", + "lights": true, + "lock": true, + "remoteChargingCommands": {}, + "sendPoi": true, + "specialThemeSupport": [], + "unlock": true, + "vehicleFinder": false, + "vehicleStateSource": "LAST_STATE_CALL" + }, + "state": { + "chargingProfile": { + "chargingControlType": "WEEKLY_PLANNER", + "chargingMode": "DELAYED_CHARGING", + "chargingPreference": "CHARGING_WINDOW", + "chargingSettings": { + "hospitality": "NO_ACTION", + "idcc": "NO_ACTION", + "targetSoc": 100 + }, + "climatisationOn": false, + "departureTimes": [ + { + "action": "DEACTIVATE", + "id": 1, + "timeStamp": { "hour": 7, "minute": 35 }, + "timerWeekDays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY" + ] + }, + { + "action": "DEACTIVATE", + "id": 2, + "timeStamp": { "hour": 18, "minute": 0 }, + "timerWeekDays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ] + }, + { + "action": "DEACTIVATE", + "id": 3, + "timeStamp": { "hour": 7, "minute": 0 }, + "timerWeekDays": [] + }, + { "action": "DEACTIVATE", "id": 4, "timerWeekDays": [] } + ], + "reductionOfChargeCurrent": { + "end": { "hour": 1, "minute": 30 }, + "start": { "hour": 18, "minute": 1 } + } + }, + "checkControlMessages": [], + "climateTimers": [ + { + "departureTime": { "hour": 6, "minute": 40 }, + "isWeeklyTimer": true, + "timerAction": "ACTIVATE", + "timerWeekDays": ["THURSDAY", "SUNDAY"] + }, + { + "departureTime": { "hour": 12, "minute": 50 }, + "isWeeklyTimer": false, + "timerAction": "ACTIVATE", + "timerWeekDays": ["MONDAY"] + }, + { + "departureTime": { "hour": 18, "minute": 59 }, + "isWeeklyTimer": true, + "timerAction": "DEACTIVATE", + "timerWeekDays": ["WEDNESDAY"] + } + ], + "combustionFuelLevel": { + "range": 105, + "remainingFuelLiters": 6, + "remainingFuelPercent": 65 + }, + "currentMileage": 137009, + "doorsState": { + "combinedSecurityState": "UNLOCKED", + "combinedState": "CLOSED", + "hood": "CLOSED", + "leftFront": "CLOSED", + "leftRear": "CLOSED", + "rightFront": "CLOSED", + "rightRear": "CLOSED", + "trunk": "CLOSED" + }, + "driverPreferences": { "lscPrivacyMode": "OFF" }, + "electricChargingState": { + "chargingConnectionType": "CONDUCTIVE", + "chargingLevelPercent": 82, + "chargingStatus": "WAITING_FOR_CHARGING", + "chargingTarget": 100, + "isChargerConnected": true, + "range": 174 + }, + "isLeftSteering": true, + "isLscSupported": true, + "lastFetched": "2022-06-22T14:24:23.982Z", + "lastUpdatedAt": "2022-06-22T13:58:52Z", + "range": 174, + "requiredServices": [ + { + "dateTime": "2022-10-01T00:00:00.000Z", + "description": "Next service due by the specified date.", + "status": "OK", + "type": "BRAKE_FLUID" + }, + { + "dateTime": "2023-05-01T00:00:00.000Z", + "description": "Next vehicle check due after the specified distance or date.", + "status": "OK", + "type": "VEHICLE_CHECK" + }, + { + "dateTime": "2023-05-01T00:00:00.000Z", + "description": "Next state inspection due by the specified date.", + "status": "OK", + "type": "VEHICLE_TUV" + } + ], + "roofState": { "roofState": "CLOSED", "roofStateType": "SUN_ROOF" }, + "windowsState": { + "combinedState": "CLOSED", + "leftFront": "CLOSED", + "rightFront": "CLOSED" + } + } + }, + "fuel_and_battery": { + "remaining_range_fuel": [105, "km"], + "remaining_range_electric": [174, "km"], + "remaining_range_total": [279, "km"], + "remaining_fuel": [6, "L"], + "remaining_fuel_percent": 65, + "remaining_battery_percent": 82, + "charging_status": "WAITING_FOR_CHARGING", + "charging_start_time_no_tz": "2022-07-10T18:01:00", + "charging_end_time": null, + "is_charger_connected": true, + "account_timezone": { + "_std_offset": "0:00:00", + "_dst_offset": "0:00:00", + "_dst_saved": "0:00:00", + "_hasdst": false, + "_tznames": ["UTC", "UTC"] + }, + "charging_start_time": "2022-07-10T18:01:00+00:00" + }, + "vehicle_location": { + "location": null, + "heading": null, + "vehicle_update_timestamp": "2022-07-10T09:25:53+00:00", + "account_region": "row", + "remote_service_position": null + }, + "doors_and_windows": { + "door_lock_state": "UNLOCKED", + "lids": [ + { "name": "hood", "state": "CLOSED", "is_closed": true }, + { "name": "leftFront", "state": "CLOSED", "is_closed": true }, + { "name": "leftRear", "state": "CLOSED", "is_closed": true }, + { "name": "rightFront", "state": "CLOSED", "is_closed": true }, + { "name": "rightRear", "state": "CLOSED", "is_closed": true }, + { "name": "trunk", "state": "CLOSED", "is_closed": true }, + { "name": "sunRoof", "state": "CLOSED", "is_closed": true } + ], + "windows": [ + { "name": "leftFront", "state": "CLOSED", "is_closed": true }, + { "name": "rightFront", "state": "CLOSED", "is_closed": true } + ], + "all_lids_closed": true, + "all_windows_closed": true, + "open_lids": [], + "open_windows": [] + }, + "condition_based_services": { + "messages": [ + { + "service_type": "BRAKE_FLUID", + "state": "OK", + "due_date": "2022-10-01T00:00:00+00:00", + "due_distance": [null, null] + }, + { + "service_type": "VEHICLE_CHECK", + "state": "OK", + "due_date": "2023-05-01T00:00:00+00:00", + "due_distance": [null, null] + }, + { + "service_type": "VEHICLE_TUV", + "state": "OK", + "due_date": "2023-05-01T00:00:00+00:00", + "due_distance": [null, null] + } + ], + "is_service_required": false + }, + "check_control_messages": { + "messages": [], + "has_check_control_messages": false + }, + "charging_profile": { + "is_pre_entry_climatization_enabled": false, + "timer_type": "WEEKLY_PLANNER", + "departure_times": [ + { + "_timer_dict": { + "action": "DEACTIVATE", + "id": 1, + "timeStamp": { "hour": 7, "minute": 35 }, + "timerWeekDays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY" + ] + }, + "action": "DEACTIVATE", + "start_time": "07:35:00", + "timer_id": 1, + "weekdays": ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"] + }, + { + "_timer_dict": { + "action": "DEACTIVATE", + "id": 2, + "timeStamp": { "hour": 18, "minute": 0 }, + "timerWeekDays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ] + }, + "action": "DEACTIVATE", + "start_time": "18:00:00", + "timer_id": 2, + "weekdays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ] + }, + { + "_timer_dict": { + "action": "DEACTIVATE", + "id": 3, + "timeStamp": { "hour": 7, "minute": 0 }, + "timerWeekDays": [] + }, + "action": "DEACTIVATE", + "start_time": "07:00:00", + "timer_id": 3, + "weekdays": [] + }, + { + "_timer_dict": { + "action": "DEACTIVATE", + "id": 4, + "timerWeekDays": [] + }, + "action": "DEACTIVATE", + "start_time": null, + "timer_id": 4, + "weekdays": [] + } + ], + "preferred_charging_window": { + "_window_dict": { + "end": { "hour": 1, "minute": 30 }, + "start": { "hour": 18, "minute": 1 } + }, + "end_time": "01:30:00", + "start_time": "18:01:00" + }, + "charging_preferences": "CHARGING_WINDOW", + "charging_mode": "DELAYED_CHARGING" + }, + "available_attributes": [ + "gps_position", + "vin", + "remaining_range_total", + "mileage", + "charging_time_remaining", + "charging_end_time", + "charging_time_label", + "charging_status", + "connection_status", + "remaining_battery_percent", + "remaining_range_electric", + "last_charging_end_result", + "remaining_fuel", + "remaining_range_fuel", + "remaining_fuel_percent", + "condition_based_services", + "check_control_messages", + "door_lock_state", + "timestamp", + "lids", + "windows" + ], + "brand": "bmw", + "drive_train": "ELECTRIC_WITH_RANGE_EXTENDER", + "drive_train_attributes": [ + "remaining_range_total", + "mileage", + "charging_time_remaining", + "charging_end_time", + "charging_time_label", + "charging_status", + "connection_status", + "remaining_battery_percent", + "remaining_range_electric", + "last_charging_end_result", + "remaining_fuel", + "remaining_range_fuel", + "remaining_fuel_percent" + ], + "has_combustion_drivetrain": true, + "has_electric_drivetrain": true, + "is_charging_plan_supported": true, + "is_lsc_enabled": true, + "is_vehicle_active": false, + "is_vehicle_tracking_enabled": false, + "lsc_type": "ACTIVATED", + "mileage": [137009, "km"], + "name": "i3 (+ REX)", + "timestamp": "2022-07-10T09:25:53+00:00", + "vin": "**REDACTED**" + }, + "fingerprint": [ + { + "filename": "bmw-vehicles.json", + "content": [ + { + "appVehicleType": "CONNECTED", + "attributes": { + "a4aType": "USB_ONLY", + "bodyType": "I01", + "brand": "BMW_I", + "color": 4284110934, + "countryOfOrigin": "CZ", + "driveTrain": "ELECTRIC_WITH_RANGE_EXTENDER", + "driverGuideInfo": { + "androidAppScheme": "com.bmwgroup.driversguide.row", + "androidStoreUrl": "https://play.google.com/store/apps/details?id=com.bmwgroup.driversguide.row", + "iosAppScheme": "bmwdriversguide:///open", + "iosStoreUrl": "https://apps.apple.com/de/app/id714042749?mt=8" + }, + "headUnitType": "NBT", + "hmiVersion": "ID4", + "lastFetched": "2022-07-10T09:25:53.104Z", + "model": "i3 (+ REX)", + "softwareVersionCurrent": { + "iStep": 510, + "puStep": { "month": 11, "year": 21 }, + "seriesCluster": "I001" + }, + "softwareVersionExFactory": { + "iStep": 502, + "puStep": { "month": 3, "year": 15 }, + "seriesCluster": "I001" + }, + "year": 2015 + }, + "mappingInfo": { + "isAssociated": false, + "isLmmEnabled": false, + "isPrimaryUser": true, + "mappingStatus": "CONFIRMED" + }, + "vin": "**REDACTED**" + } + ] + }, + { "filename": "mini-vehicles.json", "content": [] }, + { + "filename": "bmw-vehicles_state_WBY0FINGERPRINT01.json", + "content": { + "capabilities": { + "climateFunction": "AIR_CONDITIONING", + "climateNow": true, + "climateTimerTrigger": "DEPARTURE_TIMER", + "horn": true, + "isBmwChargingSupported": true, + "isCarSharingSupported": false, + "isChargeNowForBusinessSupported": false, + "isChargingHistorySupported": true, + "isChargingHospitalityEnabled": false, + "isChargingLoudnessEnabled": false, + "isChargingPlanSupported": true, + "isChargingPowerLimitEnabled": false, + "isChargingSettingsEnabled": false, + "isChargingTargetSocEnabled": false, + "isClimateTimerSupported": true, + "isCustomerEsimSupported": false, + "isDCSContractManagementSupported": true, + "isDataPrivacyEnabled": false, + "isEasyChargeEnabled": false, + "isEvGoChargingSupported": false, + "isMiniChargingSupported": false, + "isNonLscFeatureEnabled": false, + "isRemoteEngineStartSupported": false, + "isRemoteHistoryDeletionSupported": false, + "isRemoteHistorySupported": true, + "isRemoteParkingSupported": false, + "isRemoteServicesActivationRequired": false, + "isRemoteServicesBookingRequired": false, + "isScanAndChargeSupported": false, + "isSustainabilitySupported": false, + "isWifiHotspotServiceSupported": false, + "lastStateCallState": "ACTIVATED", + "lights": true, + "lock": true, + "remoteChargingCommands": {}, + "sendPoi": true, + "specialThemeSupport": [], + "unlock": true, + "vehicleFinder": false, + "vehicleStateSource": "LAST_STATE_CALL" + }, + "state": { + "chargingProfile": { + "chargingControlType": "WEEKLY_PLANNER", + "chargingMode": "DELAYED_CHARGING", + "chargingPreference": "CHARGING_WINDOW", + "chargingSettings": { + "hospitality": "NO_ACTION", + "idcc": "NO_ACTION", + "targetSoc": 100 + }, + "climatisationOn": false, + "departureTimes": [ + { + "action": "DEACTIVATE", + "id": 1, + "timeStamp": { "hour": 7, "minute": 35 }, + "timerWeekDays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY" + ] + }, + { + "action": "DEACTIVATE", + "id": 2, + "timeStamp": { "hour": 18, "minute": 0 }, + "timerWeekDays": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ] + }, + { + "action": "DEACTIVATE", + "id": 3, + "timeStamp": { "hour": 7, "minute": 0 }, + "timerWeekDays": [] + }, + { "action": "DEACTIVATE", "id": 4, "timerWeekDays": [] } + ], + "reductionOfChargeCurrent": { + "end": { "hour": 1, "minute": 30 }, + "start": { "hour": 18, "minute": 1 } + } + }, + "checkControlMessages": [], + "climateTimers": [ + { + "departureTime": { "hour": 6, "minute": 40 }, + "isWeeklyTimer": true, + "timerAction": "ACTIVATE", + "timerWeekDays": ["THURSDAY", "SUNDAY"] + }, + { + "departureTime": { "hour": 12, "minute": 50 }, + "isWeeklyTimer": false, + "timerAction": "ACTIVATE", + "timerWeekDays": ["MONDAY"] + }, + { + "departureTime": { "hour": 18, "minute": 59 }, + "isWeeklyTimer": true, + "timerAction": "DEACTIVATE", + "timerWeekDays": ["WEDNESDAY"] + } + ], + "combustionFuelLevel": { + "range": 105, + "remainingFuelLiters": 6, + "remainingFuelPercent": 65 + }, + "currentMileage": 137009, + "doorsState": { + "combinedSecurityState": "UNLOCKED", + "combinedState": "CLOSED", + "hood": "CLOSED", + "leftFront": "CLOSED", + "leftRear": "CLOSED", + "rightFront": "CLOSED", + "rightRear": "CLOSED", + "trunk": "CLOSED" + }, + "driverPreferences": { "lscPrivacyMode": "OFF" }, + "electricChargingState": { + "chargingConnectionType": "CONDUCTIVE", + "chargingLevelPercent": 82, + "chargingStatus": "WAITING_FOR_CHARGING", + "chargingTarget": 100, + "isChargerConnected": true, + "range": 174 + }, + "isLeftSteering": true, + "isLscSupported": true, + "lastFetched": "2022-06-22T14:24:23.982Z", + "lastUpdatedAt": "2022-06-22T13:58:52Z", + "range": 174, + "requiredServices": [ + { + "dateTime": "2022-10-01T00:00:00.000Z", + "description": "Next service due by the specified date.", + "status": "OK", + "type": "BRAKE_FLUID" + }, + { + "dateTime": "2023-05-01T00:00:00.000Z", + "description": "Next vehicle check due after the specified distance or date.", + "status": "OK", + "type": "VEHICLE_CHECK" + }, + { + "dateTime": "2023-05-01T00:00:00.000Z", + "description": "Next state inspection due by the specified date.", + "status": "OK", + "type": "VEHICLE_TUV" + } + ], + "roofState": { "roofState": "CLOSED", "roofStateType": "SUN_ROOF" }, + "windowsState": { + "combinedState": "CLOSED", + "leftFront": "CLOSED", + "rightFront": "CLOSED" + } + } + } + } + ] +} diff --git a/tests/components/bmw_connected_drive/test_diagnostics.py b/tests/components/bmw_connected_drive/test_diagnostics.py new file mode 100644 index 00000000000..816154416a8 --- /dev/null +++ b/tests/components/bmw_connected_drive/test_diagnostics.py @@ -0,0 +1,104 @@ +"""Test BMW diagnostics.""" + +import datetime +import json +import os +import time + +from freezegun import freeze_time + +from homeassistant.components.bmw_connected_drive.const import DOMAIN +from homeassistant.core import HomeAssistant +from homeassistant.helpers import device_registry as dr + +from . import setup_mocked_integration + +from tests.common import load_fixture +from tests.components.diagnostics import ( + get_diagnostics_for_config_entry, + get_diagnostics_for_device, +) + + +@freeze_time(datetime.datetime(2022, 7, 10, 11)) +async def test_config_entry_diagnostics(hass: HomeAssistant, hass_client, bmw_fixture): + """Test config entry diagnostics.""" + + # Make sure that local timezone for test is UTC + os.environ["TZ"] = "UTC" + time.tzset() + + mock_config_entry = await setup_mocked_integration(hass) + + diagnostics = await get_diagnostics_for_config_entry( + hass, hass_client, mock_config_entry + ) + + diagnostics_fixture = json.loads( + load_fixture("diagnostics/diagnostics_config_entry.json", DOMAIN) + ) + + assert diagnostics == diagnostics_fixture + + +@freeze_time(datetime.datetime(2022, 7, 10, 11)) +async def test_device_diagnostics(hass: HomeAssistant, hass_client, bmw_fixture): + """Test device diagnostics.""" + + # Make sure that local timezone for test is UTC + os.environ["TZ"] = "UTC" + time.tzset() + + mock_config_entry = await setup_mocked_integration(hass) + + device_registry = dr.async_get(hass) + reg_device = device_registry.async_get_device( + identifiers={(DOMAIN, "WBY00000000REXI01")}, + ) + assert reg_device is not None + + diagnostics = await get_diagnostics_for_device( + hass, hass_client, mock_config_entry, reg_device + ) + + diagnostics_fixture = json.loads( + load_fixture("diagnostics/diagnostics_device.json", DOMAIN) + ) + + assert diagnostics == diagnostics_fixture + + +@freeze_time(datetime.datetime(2022, 7, 10, 11)) +async def test_device_diagnostics_vehicle_not_found( + hass: HomeAssistant, hass_client, bmw_fixture +): + """Test device diagnostics when the vehicle cannot be found.""" + + # Make sure that local timezone for test is UTC + os.environ["TZ"] = "UTC" + time.tzset() + + mock_config_entry = await setup_mocked_integration(hass) + + device_registry = dr.async_get(hass) + reg_device = device_registry.async_get_device( + identifiers={(DOMAIN, "WBY00000000REXI01")}, + ) + assert reg_device is not None + + # Change vehicle identifier so that vehicle will not be found + device_registry.async_update_device( + reg_device.id, new_identifiers={(DOMAIN, "WBY00000000REXI99")} + ) + + diagnostics = await get_diagnostics_for_device( + hass, hass_client, mock_config_entry, reg_device + ) + + diagnostics_fixture = json.loads( + load_fixture("diagnostics/diagnostics_device.json", DOMAIN) + ) + # Mock empty data if car is not found in account anymore + diagnostics_fixture["data"] = None + + assert diagnostics == diagnostics_fixture