Source code for bimmer_connected.vehicle.reports

"""Models the state of a vehicle."""

import datetime
import logging
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional

from bimmer_connected.const import ATTR_STATE
from bimmer_connected.models import StrEnum, ValueWithUnit, VehicleDataBase
from bimmer_connected.utils import parse_datetime

_LOGGER = logging.getLogger(__name__)


[docs]class ConditionBasedServiceStatus(StrEnum): """Status of the condition based services.""" OK = "OK" OVERDUE = "OVERDUE" PENDING = "PENDING" UNKNOWN = "UNKNOWN"
[docs]@dataclass class ConditionBasedService: """Entry in the list of condition based services.""" service_type: str state: ConditionBasedServiceStatus due_date: Optional[datetime.datetime] due_distance: ValueWithUnit
[docs] @classmethod def from_api_entry( cls, type: str, status: str, dateTime: Optional[str] = None, mileage: Optional[int] = None, is_metric: bool = True, **kwargs ): """Parse a condition based service entry from the API format to `ConditionBasedService`.""" due_distance = ValueWithUnit(mileage, "km" if is_metric else "mi") if mileage else ValueWithUnit(None, None) due_date = parse_datetime(dateTime) if dateTime else None return cls(type, ConditionBasedServiceStatus(status), due_date, due_distance)
[docs]@dataclass class ConditionBasedServiceReport(VehicleDataBase): """Parse and summarizes condition based services (e.g. next oil service).""" messages: List[ConditionBasedService] = field(default_factory=list) """List of the condition based services.""" is_service_required: bool = False """Indicate if a service is required.""" @classmethod def _parse_vehicle_data(cls, vehicle_data: Dict) -> Optional[Dict]: """Parse doors and windows.""" retval: Dict[str, Any] = {} if ATTR_STATE in vehicle_data and "requiredServices" in vehicle_data[ATTR_STATE]: messages = vehicle_data[ATTR_STATE]["requiredServices"] retval["messages"] = [ ConditionBasedService.from_api_entry(**m, is_metric=vehicle_data["is_metric"]) for m in messages ] retval["is_service_required"] = any((m.state != ConditionBasedServiceStatus.OK) for m in retval["messages"]) return retval
[docs]class CheckControlStatus(StrEnum): """Status of the condition based services.""" OK = "OK" LOW = "LOW" MEDIUM = "MEDIUM" HIGH = "HIGH" CRITICAL = "CRITICAL" UNKNOWN = "UNKNOWN"
[docs]@dataclass class CheckControlMessage: """Check control message sent from the server.""" description_short: str description_long: Optional[str] state: CheckControlStatus
[docs] @classmethod def from_api_entry(cls, type: str, severity: str, longDescription: Optional[str] = None, **kwargs): """Parse a check control entry from the API format to `CheckControlMessage`.""" return cls(type, longDescription, CheckControlStatus(severity))
[docs]@dataclass class CheckControlMessageReport(VehicleDataBase): """Parse and summarizes check control messages (e.g. low tire pressure).""" messages: List[CheckControlMessage] = field(default_factory=list) """List of check control messages.""" has_check_control_messages: bool = False """Indicate if check control messages are present.""" @classmethod def _parse_vehicle_data(cls, vehicle_data: Dict) -> Optional[Dict]: """Parse doors and windows.""" retval: Dict[str, Any] = {} if ATTR_STATE in vehicle_data and "checkControlMessages" in vehicle_data[ATTR_STATE]: messages = vehicle_data[ATTR_STATE]["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 return retval