Skip to content

Commit

Permalink
Updates for MyBMW 4.7.2 (#638)
Browse files Browse the repository at this point in the history
* Updates to MyBMW 4.5.0

* Bump to MyBMW 4.7.2

* Fix Python<=3.9
  • Loading branch information
rikroe authored Jul 27, 2024
1 parent 2e3eb11 commit 30246ac
Show file tree
Hide file tree
Showing 13 changed files with 453 additions and 79 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.5.1
rev: v0.5.5
hooks:
- id: ruff
args:
Expand All @@ -24,7 +24,7 @@ repos:
exclude_types: [csv, json]
exclude: ^test/responses/
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.1
rev: v1.11.0
hooks:
- id: mypy
name: mypy
Expand Down
25 changes: 13 additions & 12 deletions bimmer_connected/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,19 @@ class Regions(str, Enum):
}

APP_VERSIONS = {
Regions.NORTH_AMERICA: "3.11.1(29513)",
Regions.REST_OF_WORLD: "3.11.1(29513)",
Regions.CHINA: "3.11.1(29513)",
Regions.NORTH_AMERICA: "4.7.2(35379)",
Regions.REST_OF_WORLD: "4.7.2(35379)",
Regions.CHINA: "4.7.2(35379)",
}

HTTPX_TIMEOUT = 30.0

USER_AGENTS = {
Regions.NORTH_AMERICA: "Dart/3.0 (dart:io)",
Regions.REST_OF_WORLD: "Dart/3.0 (dart:io)",
Regions.CHINA: "Dart/3.0 (dart:io)",
Regions.NORTH_AMERICA: "Dart/3.3 (dart:io)",
Regions.REST_OF_WORLD: "Dart/3.3 (dart:io)",
Regions.CHINA: "Dart/3.3 (dart:io)",
}
X_USER_AGENT = "android(TQ2A.230405.003.B2);{brand};{app_version};{region}"
X_USER_AGENT = "android(AP2A.240605.024);{brand};{app_version};{region}"


AUTH_CHINA_PUBLIC_KEY_URL = "/eadrax-coas/v1/cop/publickey"
Expand All @@ -66,10 +66,11 @@ class Regions(str, Enum):
VEHICLE_PROFILE_URL = "/eadrax-vcs/v5/vehicle-data/profile"
VEHICLE_STATE_URL = "/eadrax-vcs/v4/vehicles/state"

REMOTE_SERVICE_BASE_URL = "/eadrax-vrccs/v3/presentation/remote-commands"
REMOTE_SERVICE_URL = REMOTE_SERVICE_BASE_URL + "/{vin}/{service_type}"
REMOTE_SERVICE_STATUS_URL = REMOTE_SERVICE_BASE_URL + "/eventStatus?eventId={event_id}"
REMOTE_SERVICE_POSITION_URL = REMOTE_SERVICE_BASE_URL + "/eventPosition?eventId={event_id}"
REMOTE_SERVICE_V3_BASE_URL = "/eadrax-vrccs/v3/presentation/remote-commands"
REMOTE_SERVICE_V4_BASE_URL = "/eadrax-vrccs/v4/presentation/remote-commands"
REMOTE_SERVICE_URL = REMOTE_SERVICE_V4_BASE_URL + "/{service_type}"
REMOTE_SERVICE_STATUS_URL = REMOTE_SERVICE_V3_BASE_URL + "/eventStatus?eventId={event_id}"
REMOTE_SERVICE_POSITION_URL = REMOTE_SERVICE_V4_BASE_URL + "/eventPosition?eventId={event_id}"

VEHICLE_CHARGING_DETAILS_URL = "/eadrax-crccs/v2/vehicles"
VEHICLE_CHARGING_BASE_URL = "/eadrax-crccs/v1/vehicles/{vin}"
Expand All @@ -78,7 +79,7 @@ class Regions(str, Enum):
VEHICLE_CHARGING_START_STOP_URL = VEHICLE_CHARGING_BASE_URL + "/{service_type}"

VEHICLE_IMAGE_URL = "/eadrax-ics/v5/presentation/vehicles/images"
VEHICLE_POI_URL = "/eadrax-dcs/v1/send-to-car/send-to-car"
VEHICLE_POI_URL = "/eadrax-dcs/v2/user/{gcid}/send-to-car"

VEHICLE_CHARGING_STATISTICS_URL = "/eadrax-chs/v2/charging-statistics"
VEHICLE_CHARGING_SESSIONS_URL = "/eadrax-chs/v2/charging-sessions"
Expand Down
29 changes: 18 additions & 11 deletions bimmer_connected/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class PointOfInterestAddress:
postalCode: Optional[str] = None
city: Optional[str] = None
country: Optional[str] = None
formatted: Optional[str] = None

# The following attributes are not by us but available in the API
banchi: Optional[str] = None
Expand All @@ -107,6 +108,7 @@ class PointOfInterestAddress:
region: Optional[str] = None
regionCode: Optional[str] = None
settlement: Optional[str] = None
subblock: Optional[str] = None


@dataclass
Expand All @@ -115,33 +117,38 @@ class PointOfInterest:

lat: InitVar[float]
lon: InitVar[float]
name: Optional[str] = DEFAULT_POI_NAME
name: InitVar[str] = DEFAULT_POI_NAME
street: InitVar[str] = None
postal_code: InitVar[str] = None
city: InitVar[str] = None
country: InitVar[str] = None

coordinates: GPSPosition = field(init=False)
locationAddress: Optional[PointOfInterestAddress] = field(init=False)
position: Dict[str, float] = field(init=False)
address: Optional[PointOfInterestAddress] = field(init=False)
# The following attributes are not by us but required in the API
formattedAddress: Optional[str] = None
entryPoints: List = field(init=False, default_factory=list)
entrances: Optional[List] = field(init=False)
placeType: Optional[str] = "ADDRESS"
category: Dict[str, Optional[str]] = field(init=False)
title: str = "Sent with ♥ by bimmer_connected"

# The following attributes are not by us but available in the API
address: Optional[str] = None
baseCategoryId: Optional[str] = None
phoneNumber: Optional[str] = None
provider: Optional[str] = None
providerId: Optional[str] = None
providerPoiId: str = ""
sourceType: Optional[str] = None
type: Optional[str] = None
vehicleCategoryId: Optional[str] = None

def __post_init__(self, lat, lon, street, postal_code, city, country):
self.coordinates = GPSPosition(lat, lon)
def __post_init__(self, lat, lon, name, street, postal_code, city, country):
position = GPSPosition(lat, lon)
self.position = {
"lat": position.latitude,
"lon": position.longitude,
}

self.locationAddress = PointOfInterestAddress(str(street), str(postal_code), str(city), str(country))
self.address = PointOfInterestAddress(str(street), str(postal_code), str(city), str(country))
self.category = {"losCategory": "Address", "mguVehicleCategoryId": None, "name": "Address"}
self.title = name

if not self.formattedAddress:
self.formattedAddress = ", ".join([str(i) for i in [street, postal_code, city] if i]) or "Coordinates only"
Expand Down
23 changes: 14 additions & 9 deletions bimmer_connected/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def add_vehicle_routes(self) -> None:
def add_remote_service_routes(self) -> None:
"""Add routes for remote services."""

self.post(path__regex=r"/eadrax-vrccs/v3/presentation/remote-commands/(?P<vin>.+)/(?P<service>.+)$").mock(
self.post(path__regex=r"/eadrax-vrccs/v4/presentation/remote-commands/(?!event.*)(?P<service>.+)$").mock(
side_effect=self.service_trigger_sideeffect
)
self.post(path__regex=r"/eadrax-crccs/v1/vehicles/(?P<vin>.+)/(?P<service>(start|stop)-charging)$").mock(
Expand All @@ -138,8 +138,8 @@ def add_remote_service_routes(self) -> None:
side_effect=self.service_status_sideeffect
)

self.post("/eadrax-dcs/v1/send-to-car/send-to-car").mock(side_effect=self.poi_sideeffect)
self.post("/eadrax-vrccs/v3/presentation/remote-commands/eventPosition", params={"eventId": mock.ANY}).respond(
self.post(path__regex=r"/eadrax-dcs/v2/user/(?P<gcid>.+)/send-to-car$").mock(side_effect=self.poi_sideeffect)
self.post("/eadrax-vrccs/v4/presentation/remote-commands/eventPosition", params={"eventId": mock.ANY}).respond(
200,
json=load_response(REMOTE_SERVICE_RESPONSE_EVENTPOSITION),
)
Expand Down Expand Up @@ -224,10 +224,12 @@ def vehicle_charging_settings_sideeffect(self, request: httpx.Request) -> httpx.
# # # # # # # # # # # # # # # # # # # # # # # #

def service_trigger_sideeffect(
self, request: httpx.Request, vin: str, service: Optional[str] = None
self, request: httpx.Request, vin: Optional[str] = None, service: Optional[str] = None
) -> httpx.Response:
"""Return specific eventId for each remote function."""

vin = vin or request.headers["bmw-vin"]

if service in ["door-lock", "door-unlock"]:
new_state = "LOCKED" if service == "door-lock" else "UNLOCKED"
self.states[vin]["state"]["doorsState"]["combinedSecurityState"] = new_state
Expand Down Expand Up @@ -316,15 +318,18 @@ def service_status_sideeffect(request: httpx.Request) -> httpx.Response:
return httpx.Response(200, json=load_response(response_data))

@staticmethod
def poi_sideeffect(request: httpx.Request) -> httpx.Response:
def poi_sideeffect(request: httpx.Request, gcid: str) -> httpx.Response:
"""Check if payload is a valid POI."""

assert gcid == "DUMMY"

data = json.loads(request.content)
tests = all(
[
len(data["vin"]) == 17,
isinstance(data["location"]["coordinates"]["latitude"], float),
isinstance(data["location"]["coordinates"]["longitude"], float),
len(data["location"]["name"]) > 0,
len(data["vehicleInformation"]["vin"]) == 17,
isinstance(data["places"][0]["position"]["lat"], float),
isinstance(data["places"][0]["position"]["lon"], float),
len(data["places"][0]["title"]) > 0,
]
)
if not tests:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"vehicleSoftwareUpgradeRequired": false
},
"horn": true,
"isBatteryPreconditioningSupported": false,
"isBmwChargingSupported": false,
"isCarSharingSupported": false,
"isChargeNowForBusinessSupported": false,
Expand All @@ -38,16 +39,57 @@
"isRemoteEngineStartSupported": false,
"isRemoteHistoryDeletionSupported": false,
"isRemoteHistorySupported": true,
"isRemoteParkingEes25Active": false,
"isRemoteParkingSupported": false,
"isRemoteServicesActivationRequired": false,
"isRemoteServicesBookingRequired": false,
"isScanAndChargeSupported": false,
"isSustainabilityAccumulatedViewEnabled": false,
"isSustainabilitySupported": false,
"isThirdPartyAppStoreSupported": false,
"isWifiHotspotServiceSupported": false,
"lights": true,
"locationBasedCommerceFeatures": {
"fueling": false,
"parking": false,
"reservations": false
},
"lock": true,
"remoteChargingCommands": {},
"remoteServices": {
"doorLock": {
"id": "doorLock",
"state": "ACTIVATED"
},
"doorUnlock": {
"id": "doorUnlock",
"state": "ACTIVATED"
},
"hornBlow": {
"id": "hornBlow",
"state": "ACTIVATED"
},
"inCarCamera": {
"id": "inCarCamera",
"state": "NOT_AVAILABLE"
},
"inCarCameraDwa": {
"id": "inCarCameraDwa",
"state": "NOT_AVAILABLE"
},
"lightFlash": {
"id": "lightFlash",
"state": "ACTIVATED"
},
"remote360": {
"id": "remote360",
"state": "NOT_AVAILABLE"
},
"surroundViewRecorder": {
"id": "surroundViewRecorder",
"state": "NOT_AVAILABLE"
}
},
"sendPoi": true,
"specialThemeSupport": [],
"unlock": true,
Expand Down Expand Up @@ -102,6 +144,18 @@
"lastFetched": "2024-01-20T10:18:57.283Z",
"lastUpdatedAt": "0001-01-01T00:00:00Z",
"requiredServices": [],
"securityOverviewMode": null
"securityOverviewMode": null,
"vehicleSoftwareVersion": {
"iStep": {
"iStep": 502,
"month": 11,
"seriesCluster": "F020",
"year": 13
},
"puStep": {
"month": 11,
"year": 13
}
}
}
}
Loading

0 comments on commit 30246ac

Please sign in to comment.