diff --git a/bimmer_connected/tests/responses/G01/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO04.json b/bimmer_connected/tests/responses/G01/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO04.json index 88cd7c53..3e7a9586 100644 --- a/bimmer_connected/tests/responses/G01/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO04.json +++ b/bimmer_connected/tests/responses/G01/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO04.json @@ -251,14 +251,14 @@ "type": "BRAKE_FLUID" }, { - "dateTime": "2024-12-01T00:00:00.000Z", + "dateTime": "2024-11-01T00:00:00.000Z", "description": "", "mileage": 50000, "status": "OK", "type": "VEHICLE_TUV" }, { - "dateTime": "2024-12-01T00:00:00.000Z", + "dateTime": "2024-11-01T00:00:00.000Z", "description": "", "mileage": 50000, "status": "OK", diff --git a/bimmer_connected/tests/test_vehicle_status.py b/bimmer_connected/tests/test_vehicle_status.py index 9a869a22..1ab3164a 100644 --- a/bimmer_connected/tests/test_vehicle_status.py +++ b/bimmer_connected/tests/test_vehicle_status.py @@ -210,10 +210,16 @@ async def test_plugged_in_waiting_for_charge_window(caplog, bmw_fixture: respx.R @pytest.mark.asyncio async def test_condition_based_services(caplog, bmw_fixture: respx.Router): """Test condition based service messages.""" - vehicle = (await prepare_account_with_vehicles()).get_vehicle(VIN_G26) + vehicle = (await prepare_account_with_vehicles()).get_vehicle(VIN_G01) + + assert vehicle.condition_based_services.next_service_by_distance.due_distance == (50000, "km") + assert vehicle.condition_based_services.next_service_by_distance.service_type == "BRAKE_FLUID" + + assert vehicle.condition_based_services.next_service_by_time.due_date == datetime.datetime(2024, 11, 1, tzinfo=UTC) + assert vehicle.condition_based_services.next_service_by_time.service_type == "VEHICLE_CHECK" cbs = vehicle.condition_based_services.messages - assert len(cbs) == 5 + assert len(cbs) == 4 assert cbs[0].state == ConditionBasedServiceStatus.OK expected_cbs0 = datetime.datetime(year=2024, month=12, day=1, tzinfo=UTC) assert expected_cbs0 == cbs[0].due_date @@ -225,7 +231,7 @@ async def test_condition_based_services(caplog, bmw_fixture: respx.Router): assert cbs[1].due_distance == (50000, "km") assert cbs[2].state == ConditionBasedServiceStatus.OK - expected_cbs2 = datetime.datetime(year=2024, month=12, day=1, tzinfo=UTC) + expected_cbs2 = datetime.datetime(year=2024, month=11, day=1, tzinfo=UTC) assert expected_cbs2 == cbs[2].due_date assert cbs[2].due_distance == (50000, "km") @@ -367,6 +373,7 @@ async def test_check_control_messages(caplog, bmw_fixture: respx.Router): """ vehicle = (await prepare_account_with_vehicles()).get_vehicle(VIN_G01) assert vehicle.check_control_messages.has_check_control_messages is True + assert vehicle.check_control_messages.urgent_check_control_messages == "ENGINE_OIL" ccms = vehicle.check_control_messages.messages assert len(ccms) == 2 @@ -377,6 +384,7 @@ async def test_check_control_messages(caplog, bmw_fixture: respx.Router): vehicle = (await prepare_account_with_vehicles()).get_vehicle(VIN_G20) assert vehicle.check_control_messages.has_check_control_messages is False + assert vehicle.check_control_messages.urgent_check_control_messages is None ccms = vehicle.check_control_messages.messages assert len(ccms) == 2 diff --git a/bimmer_connected/vehicle/reports.py b/bimmer_connected/vehicle/reports.py index 0598a546..2d81d93f 100644 --- a/bimmer_connected/vehicle/reports.py +++ b/bimmer_connected/vehicle/reports.py @@ -55,6 +55,12 @@ class ConditionBasedServiceReport(VehicleDataBase): is_service_required: bool = False """Indicate if a service is required.""" + next_service_by_distance: Optional[ConditionBasedService] = None + """Next service by distance.""" + + next_service_by_time: Optional[ConditionBasedService] = None + """Next service by due date.""" + @classmethod def _parse_vehicle_data(cls, vehicle_data: Dict) -> Optional[Dict]: """Parse doors and windows.""" @@ -64,6 +70,26 @@ def _parse_vehicle_data(cls, vehicle_data: Dict) -> Optional[Dict]: retval["messages"] = [ConditionBasedService.from_api_entry(**m) for m in messages] retval["is_service_required"] = any((m.state != ConditionBasedServiceStatus.OK) for m in retval["messages"]) + retval["next_service_by_distance"] = next( + iter( + sorted( + [m for m in retval["messages"] if m.due_distance.value is not None], + key=lambda x: f"{x.due_distance.value:010}-{x.service_type}", + ) + ), + None, + ) + + retval["next_service_by_time"] = next( + iter( + sorted( + [m for m in retval["messages"] if m.due_date is not None], + key=lambda x: f"{x.due_date!s}-{x.service_type}", + ) + ), + None, + ) + return retval @@ -108,6 +134,8 @@ class CheckControlMessageReport(VehicleDataBase): has_check_control_messages: bool = False """Indicate if check control messages are present.""" + urgent_check_control_messages: Optional[str] = None + @classmethod def _parse_vehicle_data(cls, vehicle_data: Dict) -> Optional[Dict]: """Parse doors and windows.""" @@ -116,6 +144,11 @@ def _parse_vehicle_data(cls, vehicle_data: Dict) -> Optional[Dict]: if ATTR_STATE in vehicle_data and (messages := vehicle_data[ATTR_STATE].get("checkControlMessages")): retval["messages"] = [CheckControlMessage.from_api_entry(**m) for m in messages if m["severity"] != "OK"] retval["has_check_control_messages"] = len([m for m in retval["messages"] if m.state != "LOW"]) > 0 + retval["urgent_check_control_messages"] = ( + ", ".join([m.description_short for m in retval["messages"] if m.state != "LOW"]) + if retval["has_check_control_messages"] + else None + ) return retval