Skip to content

Commit

Permalink
Add bmw-unit-preferences (#449)
Browse files Browse the repository at this point in the history
* Force UTC in timestamps

* Refactor models

* Refactor MyBMWClientConfiguration, add bmw-units-preferences
  • Loading branch information
rikroe authored May 29, 2022
1 parent a5384f5 commit f1b6557
Show file tree
Hide file tree
Showing 16 changed files with 89 additions and 56 deletions.
31 changes: 20 additions & 11 deletions bimmer_connected/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
from bimmer_connected.api.client import MyBMWClient, MyBMWClientConfiguration
from bimmer_connected.api.regions import Regions
from bimmer_connected.const import VEHICLES_URL, CarBrands
from bimmer_connected.models import GPSPosition
from bimmer_connected.utils import deprecated
from bimmer_connected.vehicle import MyBMWVehicle
from bimmer_connected.vehicle.models import GPSPosition

VALID_UNTIL_OFFSET = datetime.timedelta(seconds=10)

Expand All @@ -34,29 +34,34 @@ class MyBMWAccount: # pylint: disable=too-many-instance-attributes
region: Regions
"""Region of the account. See `api.Regions`."""

mybmw_client_config: MyBMWClientConfiguration = None # type: ignore[assignment]
config: MyBMWClientConfiguration = None # type: ignore[assignment]
"""Optional. If provided, username/password/region are ignored."""

log_responses: InitVar[pathlib.Path] = None
"""Optional. If set, all responses from the server will be logged to this directory."""

observer_position: Optional[GPSPosition] = None
observer_position: InitVar[GPSPosition] = None
"""Optional. Required for getting a position on older cars."""

use_metric_units: InitVar[bool] = True
"""Optional. Use metric units (km, l) by default. Use imperial units (mi, gal) if False."""

vehicles: List[MyBMWVehicle] = field(default_factory=list, init=False)

def __post_init__(self, password, log_responses):
if self.mybmw_client_config is None:
self.mybmw_client_config = MyBMWClientConfiguration(
def __post_init__(self, password, log_responses, observer_position, use_metric_units):
if self.config is None:
self.config = MyBMWClientConfiguration(
MyBMWAuthentication(self.username, password, self.region),
log_response_path=log_responses,
observer_position=observer_position,
use_metric_units=use_metric_units,
)

async def get_vehicles(self) -> None:
"""Retrieve vehicle data from BMW servers."""
_LOGGER.debug("Getting vehicle list")

async with MyBMWClient(self.mybmw_client_config) as client:
async with MyBMWClient(self.config) as client:
vehicles_request_params = {
"apptimezone": self.utcdiff,
"appDateTime": int(datetime.datetime.now().timestamp() * 1000),
Expand All @@ -66,7 +71,7 @@ async def get_vehicles(self) -> None:
await client.get(
VEHICLES_URL,
params=vehicles_request_params,
headers=client.generate_default_header(self.region, brand),
headers=client.generate_default_header(brand),
)
for brand in CarBrands
]
Expand Down Expand Up @@ -94,11 +99,15 @@ def get_vehicle(self, vin: str) -> Optional[MyBMWVehicle]:

def set_observer_position(self, latitude: float, longitude: float) -> None:
"""Set the position of the observer for all vehicles."""
self.observer_position = GPSPosition(latitude=latitude, longitude=longitude)
self.config.observer_position = GPSPosition(latitude=latitude, longitude=longitude)

def set_refresh_token(self, refresh_token: str) -> None:
"""Overwrite the current value of the MyBMW refresh token."""
self.mybmw_client_config.authentication.refresh_token = refresh_token
self.config.authentication.refresh_token = refresh_token

def set_use_metric_units(self, use_metric_units: bool) -> None:
"""Change between using metric units (km, l) if True or imperial units (mi, gal) if False."""
self.config.use_metric_units = use_metric_units

@property
def timezone(self):
Expand All @@ -113,7 +122,7 @@ def utcdiff(self):
@property
def refresh_token(self) -> Optional[str]:
"""Returns the current refresh_token."""
return self.mybmw_client_config.authentication.refresh_token
return self.config.authentication.refresh_token


@deprecated("MyBMWAccount")
Expand Down
13 changes: 8 additions & 5 deletions bimmer_connected/api/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
from bimmer_connected.api.authentication import MyBMWAuthentication
from bimmer_connected.api.regions import get_server_url
from bimmer_connected.api.utils import get_correlation_id, log_to_to_file
from bimmer_connected.const import HTTPX_TIMEOUT, USER_AGENT, X_USER_AGENT, CarBrands, Regions
from bimmer_connected.const import HTTPX_TIMEOUT, USER_AGENT, X_USER_AGENT, CarBrands
from bimmer_connected.models import GPSPosition


@dataclass
Expand All @@ -19,6 +20,8 @@ class MyBMWClientConfiguration:

authentication: MyBMWAuthentication
log_response_path: Optional[pathlib.Path] = None
observer_position: Optional[GPSPosition] = None
use_metric_units: Optional[bool] = True


class MyBMWClient(httpx.AsyncClient):
Expand All @@ -35,7 +38,7 @@ def __init__(self, config: MyBMWClientConfiguration, *args, brand: CarBrands = N

# Set default values
kwargs["base_url"] = kwargs.get("base_url") or get_server_url(config.authentication.region)
kwargs["headers"] = kwargs.get("headers") or self.generate_default_header(config.authentication.region, brand)
kwargs["headers"] = kwargs.get("headers") or self.generate_default_header(brand)

# Register event hooks
kwargs["event_hooks"] = defaultdict(list, **kwargs.get("event_hooks", {}))
Expand Down Expand Up @@ -64,13 +67,13 @@ async def raise_for_status_event_handler(response: httpx.Response):

super().__init__(*args, **kwargs)

@staticmethod
def generate_default_header(region: Regions, brand: CarBrands = None) -> Dict[str, str]:
def generate_default_header(self, brand: CarBrands = None) -> Dict[str, str]:
"""Generate a header for HTTP requests to the server."""
return {
"accept": "application/json",
"accept-language": "en",
"user-agent": USER_AGENT,
"x-user-agent": X_USER_AGENT.format((brand or CarBrands.BMW), region.value),
"x-user-agent": X_USER_AGENT.format((brand or CarBrands.BMW), self.config.authentication.region.value),
**get_correlation_id(),
"bmw-units-preferences": "d=KM;v=L" if self.config.use_metric_units else "d=MI;v=G",
}
2 changes: 1 addition & 1 deletion bimmer_connected/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ async def fingerprint(args) -> None:
for vehicle in account.vehicles:
if vehicle.drive_train in HV_BATTERY_DRIVE_TRAINS:
print(f"Getting 'charging-sessions' for {vehicle.vin}")
async with MyBMWClient(account.mybmw_client_config, brand=vehicle.brand) as client:
async with MyBMWClient(account.config, brand=vehicle.brand) as client:
await client.post(
"/eadrax-chs/v1/charging-sessions",
params={"vin": vehicle.vin, "maxResults": 40, "include_date_picker": "true"},
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion bimmer_connected/vehicle/charging_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from dataclasses import dataclass
from typing import Any, Dict, List, Optional

from bimmer_connected.vehicle.models import StrEnum, VehicleDataBase
from bimmer_connected.models import StrEnum, VehicleDataBase

_LOGGER = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion bimmer_connected/vehicle/doors_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from dataclasses import dataclass, field
from typing import Any, Dict, List

from bimmer_connected.vehicle.models import StrEnum, VehicleDataBase
from bimmer_connected.models import StrEnum, VehicleDataBase

_LOGGER = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion bimmer_connected/vehicle/fuel_and_battery.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from dataclasses import dataclass
from typing import Any, Dict, Optional

from bimmer_connected.vehicle.models import StrEnum, ValueWithUnit, VehicleDataBase
from bimmer_connected.models import StrEnum, ValueWithUnit, VehicleDataBase

_LOGGER = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion bimmer_connected/vehicle/location.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

from bimmer_connected.const import Regions
from bimmer_connected.coord_convert import gcj2wgs
from bimmer_connected.models import GPSPosition, VehicleDataBase
from bimmer_connected.utils import parse_datetime
from bimmer_connected.vehicle.models import GPSPosition, VehicleDataBase

_LOGGER = logging.getLogger(__name__)

Expand Down
16 changes: 8 additions & 8 deletions bimmer_connected/vehicle/remote_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
REMOTE_SERVICE_URL,
VEHICLE_POI_URL,
)
from bimmer_connected.models import PointOfInterest, StrEnum
from bimmer_connected.utils import MyBMWJSONEncoder
from bimmer_connected.vehicle.models import PointOfInterest, StrEnum

if TYPE_CHECKING:
from bimmer_connected.vehicle import MyBMWVehicle
Expand Down Expand Up @@ -150,7 +150,7 @@ async def _start_remote_service(self, service_id: Services, params: Dict = None)
"""Start a generic remote service."""

url = REMOTE_SERVICE_URL.format(vin=self._vehicle.vin, service_type=service_id.value)
async with MyBMWClient(self._account.mybmw_client_config, brand=self._vehicle.brand) as client:
async with MyBMWClient(self._account.config, brand=self._vehicle.brand) as client:
response = await client.post(url, params=params)
return response.json().get("eventId")

Expand All @@ -177,7 +177,7 @@ async def _get_remote_service_status(self, event_id: str = None) -> RemoteServic
"""The execution status of the last remote service that was triggered."""
_LOGGER.debug("getting remote service status for '%s'", event_id)
url = REMOTE_SERVICE_STATUS_URL.format(vin=self._vehicle.vin, event_id=event_id)
async with MyBMWClient(self._account.mybmw_client_config, brand=self._vehicle.brand) as client:
async with MyBMWClient(self._account.config, brand=self._vehicle.brand) as client:
response = await client.post(url)
return RemoteServiceStatus(response.json())

Expand All @@ -199,7 +199,7 @@ async def trigger_send_poi(self, poi: Union[PointOfInterest, Dict]) -> RemoteSer
if isinstance(poi, Dict):
poi = PointOfInterest(**poi)

async with MyBMWClient(self._account.mybmw_client_config, brand=self._vehicle.brand) as client:
async with MyBMWClient(self._account.config, brand=self._vehicle.brand) as client:
await client.post(
VEHICLE_POI_URL,
headers={"content-type": "application/json"},
Expand Down Expand Up @@ -229,19 +229,19 @@ async def trigger_remote_vehicle_finder(self) -> RemoteServiceStatus:

async def _get_event_position(self, event_id) -> Dict:
url = REMOTE_SERVICE_POSITION_URL.format(event_id=event_id)
if not self._account.observer_position:
if not self._account.config.observer_position:
return {
"errorDetails": {
"title": "Unknown position",
"description": "Set observer position to retrieve vehicle coordinates!",
}
}
async with MyBMWClient(self._account.mybmw_client_config, brand=self._vehicle.brand) as client:
async with MyBMWClient(self._account.config, brand=self._vehicle.brand) as client:
response = await client.post(
url,
headers={
"latitude": str(self._account.observer_position.latitude),
"longitude": str(self._account.observer_position.longitude),
"latitude": str(self._account.config.observer_position.latitude),
"longitude": str(self._account.config.observer_position.longitude),
},
)
return response.json()
2 changes: 1 addition & 1 deletion bimmer_connected/vehicle/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional

from bimmer_connected.models import StrEnum, ValueWithUnit, VehicleDataBase
from bimmer_connected.utils import parse_datetime
from bimmer_connected.vehicle.models import StrEnum, ValueWithUnit, VehicleDataBase

_LOGGER = logging.getLogger(__name__)

Expand Down
6 changes: 3 additions & 3 deletions bimmer_connected/vehicle/vehicle.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@

from bimmer_connected.api.client import MyBMWClient
from bimmer_connected.const import SERVICE_PROPERTIES, SERVICE_STATUS, VEHICLE_IMAGE_URL, CarBrands
from bimmer_connected.models import StrEnum, ValueWithUnit
from bimmer_connected.utils import deprecated, parse_datetime
from bimmer_connected.vehicle.charging_profile import ChargingProfile
from bimmer_connected.vehicle.doors_windows import DoorsAndWindows
from bimmer_connected.vehicle.fuel_and_battery import FuelAndBattery
from bimmer_connected.vehicle.location import VehicleLocation
from bimmer_connected.vehicle.models import StrEnum, ValueWithUnit
from bimmer_connected.vehicle.remote_services import RemoteServices
from bimmer_connected.vehicle.reports import CheckControlMessageReport, ConditionBasedServiceReport
from bimmer_connected.vehicle.vehicle_status import VehicleStatus

if TYPE_CHECKING:
from bimmer_connected.account import MyBMWAccount
from bimmer_connected.vehicle.models import VehicleDataBase
from bimmer_connected.models import VehicleDataBase


_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -293,7 +293,7 @@ async def get_vehicle_image(self, direction: VehicleViewDirection) -> bytes:
view=direction.value,
)
# the accept field of the header needs to be updated as we want a png not the usual JSON
async with MyBMWClient(self.account.mybmw_client_config, brand=self.brand) as client:
async with MyBMWClient(self.account.config, brand=self.brand) as client:
response = await client.get(url, headers={"accept": "image/png"})
return response.content

Expand Down
2 changes: 1 addition & 1 deletion bimmer_connected/vehicle/vehicle_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import logging
from typing import TYPE_CHECKING, Dict, List, Optional

from bimmer_connected.models import GPSPosition, ValueWithUnit
from bimmer_connected.utils import deprecated
from bimmer_connected.vehicle.models import GPSPosition, ValueWithUnit

if TYPE_CHECKING:
from bimmer_connected.vehicle import MyBMWVehicle
Expand Down
8 changes: 8 additions & 0 deletions docs/source/module/models.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.. _models_module:

:mod:`bimmer_connected.models`
======================================

.. automodule:: bimmer_connected.models
:members:
:undoc-members:
8 changes: 0 additions & 8 deletions docs/source/module/vehicle.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,6 @@ The :mod:`bimmer_connected.vehicle` module contains all data & parsers for a veh
:undoc-members:


:mod:`bimmer_connected.vehicle.models`
--------------------------------------------------------

.. automodule:: bimmer_connected.vehicle.models
:members:
:undoc-members:


:mod:`bimmer_connected.vehicle.reports`
--------------------------------------------------------

Expand Down
Loading

0 comments on commit f1b6557

Please sign in to comment.