diff --git a/homeassistant/components/bmw_connected_drive/manifest.json b/homeassistant/components/bmw_connected_drive/manifest.json index cafaced5223..f1768d5a0c7 100644 --- a/homeassistant/components/bmw_connected_drive/manifest.json +++ b/homeassistant/components/bmw_connected_drive/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive", "iot_class": "cloud_polling", "loggers": ["bimmer_connected"], - "requirements": ["bimmer_connected==0.12.1"] + "requirements": ["bimmer_connected==0.13.0"] } diff --git a/requirements_all.txt b/requirements_all.txt index 23ae790f360..a3ddeac5dbb 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -425,7 +425,7 @@ beautifulsoup4==4.11.1 bellows==0.34.10 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.12.1 +bimmer_connected==0.13.0 # homeassistant.components.bizkaibus bizkaibus==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index dfd65179546..5237078f5ee 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -358,7 +358,7 @@ beautifulsoup4==4.11.1 bellows==0.34.10 # homeassistant.components.bmw_connected_drive -bimmer_connected==0.12.1 +bimmer_connected==0.13.0 # homeassistant.components.bluetooth bleak-retry-connector==3.0.1 diff --git a/tests/components/bmw_connected_drive/__init__.py b/tests/components/bmw_connected_drive/__init__.py index ed31b4308b8..12957db5cac 100644 --- a/tests/components/bmw_connected_drive/__init__.py +++ b/tests/components/bmw_connected_drive/__init__.py @@ -1,10 +1,13 @@ """Tests for the for the BMW Connected Drive integration.""" -import json from pathlib import Path from bimmer_connected.api.authentication import MyBMWAuthentication -from bimmer_connected.const import VEHICLE_STATE_URL, VEHICLES_URL +from bimmer_connected.const import ( + VEHICLE_CHARGING_DETAILS_URL, + VEHICLE_STATE_URL, + VEHICLES_URL, +) import httpx import respx @@ -17,7 +20,12 @@ from homeassistant.components.bmw_connected_drive.const import ( from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME from homeassistant.core import HomeAssistant -from tests.common import MockConfigEntry, get_fixture_path, load_fixture +from tests.common import ( + MockConfigEntry, + get_fixture_path, + load_json_array_fixture, + load_json_object_fixture, +) FIXTURE_USER_INPUT = { CONF_USERNAME: "user@domain.com", @@ -42,6 +50,17 @@ FIXTURE_CONFIG_ENTRY = { } FIXTURE_PATH = Path(get_fixture_path("", integration=BMW_DOMAIN)) +FIXTURE_FILES = { + "vehicles": sorted(FIXTURE_PATH.rglob("*-eadrax-vcs_v4_vehicles.json")), + "states": { + p.stem.split("_")[-1]: p + for p in FIXTURE_PATH.rglob("*-eadrax-vcs_v4_vehicles_state_*.json") + }, + "charging": { + p.stem.split("_")[-1]: p + for p in FIXTURE_PATH.rglob("*-eadrax-crccs_v2_vehicles_*.json") + }, +} def vehicles_sideeffect(request: httpx.Request) -> httpx.Response: @@ -49,17 +68,31 @@ def vehicles_sideeffect(request: httpx.Request) -> httpx.Response: 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))) + for vehicle_file in FIXTURE_FILES["vehicles"]: + if vehicle_file.name.startswith(brand): + vehicles.extend( + load_json_array_fixture(vehicle_file, integration=BMW_DOMAIN) + ) return httpx.Response(200, json=vehicles) 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: + state_file = FIXTURE_FILES["states"][request.headers["bmw-vin"]] return httpx.Response( - 200, json=json.loads(load_fixture(state_file, integration=BMW_DOMAIN)) + 200, json=load_json_object_fixture(state_file, integration=BMW_DOMAIN) + ) + except KeyError: + return httpx.Response(404) + + +def vehicle_charging_sideeffect(request: httpx.Request) -> httpx.Response: + """Return /vehicles/state response.""" + try: + charging_file = FIXTURE_FILES["charging"][request.headers["bmw-vin"]] + return httpx.Response( + 200, json=load_json_object_fixture(charging_file, integration=BMW_DOMAIN) ) except KeyError: return httpx.Response(404) @@ -75,6 +108,10 @@ def mock_vehicles() -> respx.Router: # Get vehicle state router.get(VEHICLE_STATE_URL).mock(side_effect=vehicle_state_sideeffect) + # Get vehicle charging details + router.get(VEHICLE_CHARGING_DETAILS_URL).mock( + side_effect=vehicle_charging_sideeffect + ) return router 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 index 9c56e0595b6..12e85bb8523 100644 --- a/tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_config_entry.json +++ b/tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_config_entry.json @@ -45,6 +45,64 @@ "mappingStatus": "CONFIRMED" }, "vin": "**REDACTED**", + "charging_settings": { + "chargeAndClimateSettings": { + "chargeAndClimateTimer": { "showDepartureTimers": false } + }, + "chargeAndClimateTimerDetail": { + "chargingMode": { + "chargingPreference": "CHARGING_WINDOW", + "endTimeSlot": "0001-01-01T01:30:00", + "startTimeSlot": "0001-01-01T18:01:00", + "type": "TIME_SLOT" + }, + "departureTimer": { + "type": "WEEKLY_DEPARTURE_TIMER", + "weeklyTimers": [ + { + "daysOfTheWeek": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY" + ], + "id": 1, + "time": "0001-01-01T07:35:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ], + "id": 2, + "time": "0001-01-01T18:00:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [], + "id": 3, + "time": "0001-01-01T07:00:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [], + "id": 4, + "time": "0001-01-01T00:00:00", + "timerAction": "DEACTIVATE" + } + ] + }, + "isPreconditionForDepartureActive": false + }, + "servicePack": "TCB1" + }, "is_metric": true, "fetched_at": "2022-07-10T11:00:00+00:00", "capabilities": { @@ -230,6 +288,7 @@ "charging_start_time_no_tz": "2022-07-10T18:01:00", "charging_end_time": null, "is_charger_connected": true, + "charging_target": 100, "account_timezone": { "_std_offset": "0:00:00", "_dst_offset": "0:00:00", @@ -376,7 +435,10 @@ "start_time": "18:01:00" }, "charging_preferences": "CHARGING_WINDOW", - "charging_mode": "DELAYED_CHARGING" + "charging_mode": "DELAYED_CHARGING", + "ac_current_limit": null, + "ac_available_limits": null, + "charging_preferences_service_pack": "TCB1" }, "available_attributes": [ "gps_position", @@ -384,6 +446,7 @@ "remaining_range_total", "mileage", "charging_time_remaining", + "charging_start_time", "charging_end_time", "charging_time_label", "charging_status", @@ -391,6 +454,11 @@ "remaining_battery_percent", "remaining_range_electric", "last_charging_end_result", + "ac_current_limit", + "charging_target", + "charging_mode", + "charging_preferences", + "is_pre_entry_climatization_enabled", "remaining_fuel", "remaining_range_fuel", "remaining_fuel_percent", @@ -407,6 +475,7 @@ "remaining_range_total", "mileage", "charging_time_remaining", + "charging_start_time", "charging_end_time", "charging_time_label", "charging_status", @@ -414,6 +483,11 @@ "remaining_battery_percent", "remaining_range_electric", "last_charging_end_result", + "ac_current_limit", + "charging_target", + "charging_mode", + "charging_preferences", + "is_pre_entry_climatization_enabled", "remaining_fuel", "remaining_range_fuel", "remaining_fuel_percent" @@ -422,6 +496,17 @@ "has_electric_drivetrain": true, "is_charging_plan_supported": true, "is_lsc_enabled": true, + "is_remote_charge_start_enabled": false, + "is_remote_charge_stop_enabled": false, + "is_remote_climate_start_enabled": true, + "is_remote_climate_stop_enabled": false, + "is_remote_horn_enabled": true, + "is_remote_lights_enabled": true, + "is_remote_lock_enabled": true, + "is_remote_sendpoi_enabled": true, + "is_remote_set_ac_limit_enabled": false, + "is_remote_set_target_soc_enabled": false, + "is_remote_unlock_enabled": true, "is_vehicle_active": false, "is_vehicle_tracking_enabled": false, "lsc_type": "ACTIVATED", @@ -433,7 +518,7 @@ ], "fingerprint": [ { - "filename": "bmw-vehicles.json", + "filename": "bmw-eadrax-vcs_v4_vehicles.json", "content": [ { "appVehicleType": "CONNECTED", @@ -476,9 +561,9 @@ } ] }, - { "filename": "mini-vehicles.json", "content": [] }, + { "filename": "mini-eadrax-vcs_v4_vehicles.json", "content": [] }, { - "filename": "bmw-vehicles_state_WBY0FINGERPRINT01.json", + "filename": "bmw-eadrax-vcs_v4_vehicles_state_WBY0FINGERPRINT01.json", "content": { "capabilities": { "climateFunction": "AIR_CONDITIONING", @@ -652,6 +737,67 @@ } } } + }, + { + "filename": "bmw-eadrax-crccs_v2_vehicles_WBY0FINGERPRINT01.json", + "content": { + "chargeAndClimateSettings": { + "chargeAndClimateTimer": { "showDepartureTimers": false } + }, + "chargeAndClimateTimerDetail": { + "chargingMode": { + "chargingPreference": "CHARGING_WINDOW", + "endTimeSlot": "0001-01-01T01:30:00", + "startTimeSlot": "0001-01-01T18:01:00", + "type": "TIME_SLOT" + }, + "departureTimer": { + "type": "WEEKLY_DEPARTURE_TIMER", + "weeklyTimers": [ + { + "daysOfTheWeek": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY" + ], + "id": 1, + "time": "0001-01-01T07:35:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ], + "id": 2, + "time": "0001-01-01T18:00:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [], + "id": 3, + "time": "0001-01-01T07:00:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [], + "id": 4, + "time": "0001-01-01T00:00:00", + "timerAction": "DEACTIVATE" + } + ] + }, + "isPreconditionForDepartureActive": false + }, + "servicePack": "TCB1" + } } ] } diff --git a/tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_device.json b/tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_device.json index d76f2c80712..8e1fe5019c7 100644 --- a/tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_device.json +++ b/tests/components/bmw_connected_drive/fixtures/diagnostics/diagnostics_device.json @@ -44,6 +44,64 @@ "mappingStatus": "CONFIRMED" }, "vin": "**REDACTED**", + "charging_settings": { + "chargeAndClimateSettings": { + "chargeAndClimateTimer": { "showDepartureTimers": false } + }, + "chargeAndClimateTimerDetail": { + "chargingMode": { + "chargingPreference": "CHARGING_WINDOW", + "endTimeSlot": "0001-01-01T01:30:00", + "startTimeSlot": "0001-01-01T18:01:00", + "type": "TIME_SLOT" + }, + "departureTimer": { + "type": "WEEKLY_DEPARTURE_TIMER", + "weeklyTimers": [ + { + "daysOfTheWeek": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY" + ], + "id": 1, + "time": "0001-01-01T07:35:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ], + "id": 2, + "time": "0001-01-01T18:00:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [], + "id": 3, + "time": "0001-01-01T07:00:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [], + "id": 4, + "time": "0001-01-01T00:00:00", + "timerAction": "DEACTIVATE" + } + ] + }, + "isPreconditionForDepartureActive": false + }, + "servicePack": "TCB1" + }, "is_metric": true, "fetched_at": "2022-07-10T11:00:00+00:00", "capabilities": { @@ -229,6 +287,7 @@ "charging_start_time_no_tz": "2022-07-10T18:01:00", "charging_end_time": null, "is_charger_connected": true, + "charging_target": 100, "account_timezone": { "_std_offset": "0:00:00", "_dst_offset": "0:00:00", @@ -375,7 +434,10 @@ "start_time": "18:01:00" }, "charging_preferences": "CHARGING_WINDOW", - "charging_mode": "DELAYED_CHARGING" + "charging_mode": "DELAYED_CHARGING", + "ac_current_limit": null, + "ac_available_limits": null, + "charging_preferences_service_pack": "TCB1" }, "available_attributes": [ "gps_position", @@ -383,6 +445,7 @@ "remaining_range_total", "mileage", "charging_time_remaining", + "charging_start_time", "charging_end_time", "charging_time_label", "charging_status", @@ -390,6 +453,11 @@ "remaining_battery_percent", "remaining_range_electric", "last_charging_end_result", + "ac_current_limit", + "charging_target", + "charging_mode", + "charging_preferences", + "is_pre_entry_climatization_enabled", "remaining_fuel", "remaining_range_fuel", "remaining_fuel_percent", @@ -406,6 +474,7 @@ "remaining_range_total", "mileage", "charging_time_remaining", + "charging_start_time", "charging_end_time", "charging_time_label", "charging_status", @@ -413,6 +482,11 @@ "remaining_battery_percent", "remaining_range_electric", "last_charging_end_result", + "ac_current_limit", + "charging_target", + "charging_mode", + "charging_preferences", + "is_pre_entry_climatization_enabled", "remaining_fuel", "remaining_range_fuel", "remaining_fuel_percent" @@ -421,6 +495,17 @@ "has_electric_drivetrain": true, "is_charging_plan_supported": true, "is_lsc_enabled": true, + "is_remote_charge_start_enabled": false, + "is_remote_charge_stop_enabled": false, + "is_remote_climate_start_enabled": true, + "is_remote_climate_stop_enabled": false, + "is_remote_horn_enabled": true, + "is_remote_lights_enabled": true, + "is_remote_lock_enabled": true, + "is_remote_sendpoi_enabled": true, + "is_remote_set_ac_limit_enabled": false, + "is_remote_set_target_soc_enabled": false, + "is_remote_unlock_enabled": true, "is_vehicle_active": false, "is_vehicle_tracking_enabled": false, "lsc_type": "ACTIVATED", @@ -431,7 +516,7 @@ }, "fingerprint": [ { - "filename": "bmw-vehicles.json", + "filename": "bmw-eadrax-vcs_v4_vehicles.json", "content": [ { "appVehicleType": "CONNECTED", @@ -474,9 +559,9 @@ } ] }, - { "filename": "mini-vehicles.json", "content": [] }, + { "filename": "mini-eadrax-vcs_v4_vehicles.json", "content": [] }, { - "filename": "bmw-vehicles_state_WBY0FINGERPRINT01.json", + "filename": "bmw-eadrax-vcs_v4_vehicles_state_WBY0FINGERPRINT01.json", "content": { "capabilities": { "climateFunction": "AIR_CONDITIONING", @@ -650,6 +735,67 @@ } } } + }, + { + "filename": "bmw-eadrax-crccs_v2_vehicles_WBY0FINGERPRINT01.json", + "content": { + "chargeAndClimateSettings": { + "chargeAndClimateTimer": { "showDepartureTimers": false } + }, + "chargeAndClimateTimerDetail": { + "chargingMode": { + "chargingPreference": "CHARGING_WINDOW", + "endTimeSlot": "0001-01-01T01:30:00", + "startTimeSlot": "0001-01-01T18:01:00", + "type": "TIME_SLOT" + }, + "departureTimer": { + "type": "WEEKLY_DEPARTURE_TIMER", + "weeklyTimers": [ + { + "daysOfTheWeek": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY" + ], + "id": 1, + "time": "0001-01-01T07:35:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ], + "id": 2, + "time": "0001-01-01T18:00:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [], + "id": 3, + "time": "0001-01-01T07:00:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [], + "id": 4, + "time": "0001-01-01T00:00:00", + "timerAction": "DEACTIVATE" + } + ] + }, + "isPreconditionForDepartureActive": false + }, + "servicePack": "TCB1" + } } ] } diff --git a/tests/components/bmw_connected_drive/fixtures/vehicles/I01_REX/bmw-eadrax-crccs_v2_vehicles_WBY00000000REXI01.json b/tests/components/bmw_connected_drive/fixtures/vehicles/I01_REX/bmw-eadrax-crccs_v2_vehicles_WBY00000000REXI01.json new file mode 100644 index 00000000000..03bfc1cae04 --- /dev/null +++ b/tests/components/bmw_connected_drive/fixtures/vehicles/I01_REX/bmw-eadrax-crccs_v2_vehicles_WBY00000000REXI01.json @@ -0,0 +1,60 @@ +{ + "chargeAndClimateSettings": { + "chargeAndClimateTimer": { + "showDepartureTimers": false + } + }, + "chargeAndClimateTimerDetail": { + "chargingMode": { + "chargingPreference": "CHARGING_WINDOW", + "endTimeSlot": "0001-01-01T01:30:00", + "startTimeSlot": "0001-01-01T18:01:00", + "type": "TIME_SLOT" + }, + "departureTimer": { + "type": "WEEKLY_DEPARTURE_TIMER", + "weeklyTimers": [ + { + "daysOfTheWeek": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY" + ], + "id": 1, + "time": "0001-01-01T07:35:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ], + "id": 2, + "time": "0001-01-01T18:00:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [], + "id": 3, + "time": "0001-01-01T07:00:00", + "timerAction": "DEACTIVATE" + }, + { + "daysOfTheWeek": [], + "id": 4, + "time": "0001-01-01T00:00:00", + "timerAction": "DEACTIVATE" + } + ] + }, + "isPreconditionForDepartureActive": false + }, + "servicePack": "TCB1" +} diff --git a/tests/components/bmw_connected_drive/fixtures/vehicles/I01/vehicles_v2_bmw_0.json b/tests/components/bmw_connected_drive/fixtures/vehicles/I01_REX/bmw-eadrax-vcs_v4_vehicles.json similarity index 100% rename from tests/components/bmw_connected_drive/fixtures/vehicles/I01/vehicles_v2_bmw_0.json rename to tests/components/bmw_connected_drive/fixtures/vehicles/I01_REX/bmw-eadrax-vcs_v4_vehicles.json diff --git a/tests/components/bmw_connected_drive/fixtures/vehicles/I01/state_WBY00000000REXI01_0.json b/tests/components/bmw_connected_drive/fixtures/vehicles/I01_REX/bmw-eadrax-vcs_v4_vehicles_state_WBY00000000REXI01.json similarity index 100% rename from tests/components/bmw_connected_drive/fixtures/vehicles/I01/state_WBY00000000REXI01_0.json rename to tests/components/bmw_connected_drive/fixtures/vehicles/I01_REX/bmw-eadrax-vcs_v4_vehicles_state_WBY00000000REXI01.json