Repository: bimmerconnected/bimmer_connected
Pull request #638 (2024-07-27 09:39:36)

Updates for MyBMW 4.7.2
## Proposed change
<!--
  Describe the big picture of your changes here to communicate to the
  maintainers why we should accept this pull request. If it fixes a bug
  or resolves a feature request, be sure to link to that issue in the
  additional information section.
-->
Update to MyBMW 4.7.2.

Most changes are regarding remote services - new URLs and a completely refactored send-to-car.
Vehicle state contains some new information, but the old data is still available for now.

## Type of change
- [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] New feature (which adds functionality to this library)
- [ ] Breaking change (fix/feature causing existing functionality to break)
- [ ] Code quality improvements to existing code or addition of tests

## Additional information
<!--
  Details are important, and help maintainers processing your PR.
  Please be sure to fill out additional details, if applicable.
-->

- This PR fixes or closes issue: fixes #
- This PR is related to issue: 

## Checklist
<!--
  Put an `x` in the boxes that apply. You can also fill these out after
  creating the PR. If you're unsure about any of them, don't hesitate to ask.
  We're here to help! This is simply a reminder of what we are going to look
  for before merging your code.
-->

- [x] The code change is tested and works locally.
- [x] Tests have been added to verify that the new code works.



Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 21c9409..7da8f1c 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -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:
@@ -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
diff --git a/bimmer_connected/const.py b/bimmer_connected/const.py
index cfc2c5d..a007f66 100644
--- a/bimmer_connected/const.py
+++ b/bimmer_connected/const.py
@@ -39,19 +39,19 @@ OCP_APIM_KEYS = {
 }
 
 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"
@@ -66,10 +66,11 @@ VEHICLES_URL = "/eadrax-vcs/v5/vehicle-list"
 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}"
@@ -78,7 +79,7 @@ VEHICLE_CHARGING_PROFILE_SET_URL = VEHICLE_CHARGING_BASE_URL + "/charging-profil
 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"
diff --git a/bimmer_connected/models.py b/bimmer_connected/models.py
index cd01ff3..a45d33b 100644
--- a/bimmer_connected/models.py
+++ b/bimmer_connected/models.py
@@ -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
@@ -107,6 +108,7 @@ class PointOfInterestAddress:
     region: Optional[str] = None
     regionCode: Optional[str] = None
     settlement: Optional[str] = None
+    subblock: Optional[str] = None
 
 
 @dataclass
@@ -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"
diff --git a/bimmer_connected/vehicle/remote_services.py b/bimmer_connected/vehicle/remote_services.py
index a41462d..426c3f8 100644
--- a/bimmer_connected/vehicle/remote_services.py
+++ b/bimmer_connected/vehicle/remote_services.py
@@ -33,7 +33,7 @@ TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f"
 _LOGGER = logging.getLogger(__name__)
 
 #: time in seconds between polling updates on the status of a remote service
-_POLLING_CYCLE = 3
+_POLLING_CYCLE = 3.5
 
 #: maximum number of seconds to wait for the server to return a positive answer
 _POLLING_TIMEOUT = 240
@@ -110,15 +110,20 @@ class RemoteServices:
 
         # Check if service requires a specific url and add all required parameters
         url = SERVICE_URLS.get(service_id, REMOTE_SERVICE_URL)
-        url = url.format(vin=self._vehicle.vin, service_type=service_id.value)
+
+        remote_service_headers = {"content-type": "application/json"}
+        if "{vin}" not in url:
+            remote_service_headers["bmw-vin"] = self._vehicle.vin
+
+        url = url.format(vin=self._vehicle.vin, service_type=service_id.value, gcid=self._account.gcid)
 
         # Trigger service and get event id
         async with MyBMWClient(self._account.config, brand=self._vehicle.brand) as client:
             response = await client.post(
                 url,
-                headers={"content-type": "application/json"} if data else None,
+                headers=remote_service_headers,
                 params=params,
-                content=json.dumps(data, cls=MyBMWJSONEncoder) if data else None,
+                content=json.dumps(data or {}, cls=MyBMWJSONEncoder),
             )
             event_id = response.json().get("eventId") if response.content else None
 
@@ -299,8 +304,10 @@ class RemoteServices:
         return await self.trigger_remote_service(
             Services.SEND_POI,
             data={
-                "location": poi,
-                "vin": self._vehicle.vin,
+                "places": [poi],
+                "vehicleInformation": {
+                    "vin": self._vehicle.vin,
+                },
             },
         )
 
diff --git a/bimmer_connected/vehicle/vehicle.py b/bimmer_connected/vehicle/vehicle.py
index 62841c3..f6a8cff 100644
--- a/bimmer_connected/vehicle/vehicle.py
+++ b/bimmer_connected/vehicle/vehicle.py
@@ -41,7 +41,7 @@ class VehicleViewDirection(StrEnum):
     This is used to get a rendered image of the vehicle.
     """
 
-    FRONTSIDE = "AngleSideViewForty"
+    FRONTSIDE = "VehicleStatus"
     FRONT = "FrontView"
     # REARSIDE = 'REARSIDE'
     # REAR = 'REAR'


Test patch:
diff --git a/bimmer_connected/tests/common.py b/bimmer_connected/tests/common.py
index 0ea1c8b..d4e32a6 100644
--- a/bimmer_connected/tests/common.py
+++ b/bimmer_connected/tests/common.py
@@ -122,7 +122,7 @@ class MyBMWMockRouter(respx.MockRouter):
     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(
@@ -138,8 +138,8 @@ class MyBMWMockRouter(respx.MockRouter):
             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),
         )
@@ -224,10 +224,12 @@ class MyBMWMockRouter(respx.MockRouter):
     # # # # # # # # # # # # # # # # # # # # # # # #
 
     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
@@ -316,15 +318,18 @@ class MyBMWMockRouter(respx.MockRouter):
         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:
diff --git a/bimmer_connected/tests/responses/F31/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000000F31.json b/bimmer_connected/tests/responses/F31/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000000F31.json
index 15668a3..5671494 100644
--- a/bimmer_connected/tests/responses/F31/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000000F31.json
+++ b/bimmer_connected/tests/responses/F31/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000000F31.json
@@ -12,6 +12,7 @@
             "vehicleSoftwareUpgradeRequired": false
         },
         "horn": true,
+        "isBatteryPreconditioningSupported": false,
         "isBmwChargingSupported": false,
         "isCarSharingSupported": false,
         "isChargeNowForBusinessSupported": false,
@@ -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,
@@ -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
+            }
+        }
     }
 }
\ No newline at end of file
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 c34391d..88cd7c5 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
@@ -1,15 +1,19 @@
 {
     "capabilities": {
         "a4aType": "BLUETOOTH",
-        "checkSustainabilityDPP": false,
+        "alarmSystem": false,
         "climateFunction": "AIR_CONDITIONING",
         "climateNow": true,
         "digitalKey": {
             "bookedServicePackage": "SMACC_1_5",
+            "isDigitalKeyFirstSupported": false,
             "readerGraphics": "readerGraphics",
-            "state": "ACTIVATED"
+            "state": "ACTIVATED",
+            "vehicleSoftwareUpgradeRequired": false
         },
         "horn": true,
+        "inCarCamera": false,
+        "inCarCameraDwa": false,
         "isBmwChargingSupported": true,
         "isCarSharingSupported": false,
         "isChargeNowForBusinessSupported": true,
@@ -20,35 +24,78 @@
         "isChargingPowerLimitEnabled": false,
         "isChargingSettingsEnabled": false,
         "isChargingTargetSocEnabled": false,
+        "isClimateTimerSupported": false,
         "isClimateTimerWeeklyActive": false,
         "isCustomerEsimSupported": false,
         "isDCSContractManagementSupported": true,
         "isDataPrivacyEnabled": false,
         "isEasyChargeEnabled": false,
         "isEvGoChargingSupported": false,
+        "isLocationBasedChargingSettingsSupported": false,
         "isMiniChargingSupported": false,
         "isNonLscFeatureEnabled": false,
         "isPersonalPictureUploadSupported": false,
-        "isRemoteEngineStartSupported": false,
+        "isPlugAndChargeSupported": false,
+        "isRemoteEngineStartEnabled": false,
+        "isRemoteEngineStartSupported": true,
         "isRemoteHistoryDeletionSupported": false,
         "isRemoteHistorySupported": true,
+        "isRemoteParkingEes25Active": false,
         "isRemoteParkingSupported": false,
         "isRemoteServicesActivationRequired": false,
         "isRemoteServicesBookingRequired": false,
         "isScanAndChargeSupported": true,
         "isSustainabilityAccumulatedViewEnabled": false,
         "isSustainabilitySupported": false,
+        "isThirdPartyAppStoreSupported": false,
         "isWifiHotspotServiceSupported": true,
         "lastStateCallState": "ACTIVATED",
         "lights": true,
+        "locationBasedCommerceFeatures": {
+            "fueling": false,
+            "parking": false,
+            "reservations": false
+        },
         "lock": true,
         "remote360": true,
         "remoteChargingCommands": {},
+        "remoteServices": {
+            "doorLock": {
+                "id": "doorLock",
+                "state": "ACTIVATED"
+            },
+            "doorUnlock": {
+                "id": "doorUnlock",
+                "state": "ACTIVATED"
+            },
+            "hornBlow": {
+                "id": "hornBlow",
+                "state": "ACTIVATED"
+            },
+            "inCarCamera": {
+                "id": "inCarCamera"
+            },
+            "inCarCameraDwa": {
+                "id": "inCarCameraDwa"
+            },
+            "lightFlash": {
+                "id": "lightFlash",
+                "state": "ACTIVATED"
+            },
+            "remote360": {
+                "id": "remote360",
+                "state": "ACTIVATED"
+            },
+            "surroundViewRecorder": {
+                "id": "surroundViewRecorder"
+            }
+        },
         "remoteSoftwareUpgrade": true,
         "sendPoi": true,
         "specialThemeSupport": [],
-        "speechThirdPartyAlexa": false,
+        "speechThirdPartyAlexa": true,
         "speechThirdPartyAlexaSDK": false,
+        "surroundViewRecorder": false,
         "unlock": true,
         "vehicleFinder": true,
         "vehicleStateSource": "LAST_STATE_CALL"
@@ -218,6 +265,7 @@
                 "type": "VEHICLE_CHECK"
             }
         ],
+        "securityOverviewMode": "ARMED",
         "tireState": {
             "frontLeft": {
                 "status": {
@@ -244,6 +292,18 @@
                 }
             }
         },
+        "vehicleSoftwareVersion": {
+            "iStep": {
+                "iStep": 0,
+                "month": 0,
+                "seriesCluster": "",
+                "year": 0
+            },
+            "puStep": {
+                "month": 0,
+                "year": 0
+            }
+        },
         "windowsState": {
             "combinedState": "CLOSED",
             "leftFront": "CLOSED",
diff --git a/bimmer_connected/tests/responses/G20/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO03.json b/bimmer_connected/tests/responses/G20/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO03.json
index 52c72d1..5a0f61e 100644
--- a/bimmer_connected/tests/responses/G20/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO03.json
+++ b/bimmer_connected/tests/responses/G20/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO03.json
@@ -1,16 +1,20 @@
 {
     "capabilities": {
         "a4aType": "NOT_SUPPORTED",
-        "checkSustainabilityDPP": false,
+        "alarmSystem": false,
         "climateFunction": "VENTILATION",
         "climateNow": true,
         "climateTimerTrigger": "DEPARTURE_TIMER",
         "digitalKey": {
             "bookedServicePackage": "SMACC_1_5",
+            "isDigitalKeyFirstSupported": false,
             "readerGraphics": "readerGraphics",
-            "state": "ACTIVATED"
+            "state": "ACTIVATED",
+            "vehicleSoftwareUpgradeRequired": false
         },
         "horn": true,
+        "inCarCamera": false,
+        "inCarCameraDwa": false,
         "isBmwChargingSupported": false,
         "isCarSharingSupported": false,
         "isChargeNowForBusinessSupported": false,
@@ -28,29 +32,71 @@
         "isDataPrivacyEnabled": false,
         "isEasyChargeEnabled": false,
         "isEvGoChargingSupported": false,
+        "isLocationBasedChargingSettingsSupported": false,
         "isMiniChargingSupported": false,
         "isNonLscFeatureEnabled": false,
         "isPersonalPictureUploadSupported": false,
-        "isRemoteEngineStartSupported": false,
+        "isPlugAndChargeSupported": false,
+        "isRemoteEngineStartEnabled": false,
+        "isRemoteEngineStartSupported": true,
         "isRemoteHistoryDeletionSupported": false,
         "isRemoteHistorySupported": true,
+        "isRemoteParkingEes25Active": false,
         "isRemoteParkingSupported": false,
         "isRemoteServicesActivationRequired": false,
         "isRemoteServicesBookingRequired": false,
         "isScanAndChargeSupported": false,
         "isSustainabilityAccumulatedViewEnabled": false,
         "isSustainabilitySupported": false,
-        "isWifiHotspotServiceSupported": true,
+        "isThirdPartyAppStoreSupported": false,
+        "isWifiHotspotServiceSupported": false,
         "lastStateCallState": "ACTIVATED",
         "lights": true,
+        "locationBasedCommerceFeatures": {
+            "fueling": false,
+            "parking": false,
+            "reservations": false
+        },
         "lock": true,
         "remote360": true,
         "remoteChargingCommands": {},
+        "remoteServices": {
+            "doorLock": {
+                "id": "doorLock",
+                "state": "ACTIVATED"
+            },
+            "doorUnlock": {
+                "id": "doorUnlock",
+                "state": "ACTIVATED"
+            },
+            "hornBlow": {
+                "id": "hornBlow",
+                "state": "ACTIVATED"
+            },
+            "inCarCamera": {
+                "id": "inCarCamera"
+            },
+            "inCarCameraDwa": {
+                "id": "inCarCameraDwa"
+            },
+            "lightFlash": {
+                "id": "lightFlash",
+                "state": "ACTIVATED"
+            },
+            "remote360": {
+                "id": "remote360",
+                "state": "ACTIVATED"
+            },
+            "surroundViewRecorder": {
+                "id": "surroundViewRecorder"
+            }
+        },
         "remoteSoftwareUpgrade": true,
         "sendPoi": true,
         "specialThemeSupport": [],
-        "speechThirdPartyAlexa": false,
+        "speechThirdPartyAlexa": true,
         "speechThirdPartyAlexaSDK": false,
+        "surroundViewRecorder": false,
         "unlock": true,
         "vehicleFinder": true,
         "vehicleStateSource": "LAST_STATE_CALL"
@@ -180,6 +226,7 @@
                 "type": "TIRE_WEAR_FRONT"
             }
         ],
+        "securityOverviewMode": null,
         "tireState": {
             "frontLeft": {
                 "details": {
@@ -266,6 +313,18 @@
                 }
             }
         },
+        "vehicleSoftwareVersion": {
+            "iStep": {
+                "iStep": 0,
+                "month": 0,
+                "seriesCluster": "",
+                "year": 0
+            },
+            "puStep": {
+                "month": 0,
+                "year": 0
+            }
+        },
         "windowsState": {
             "combinedState": "CLOSED",
             "leftFront": "CLOSED",
diff --git a/bimmer_connected/tests/responses/G26/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO02.json b/bimmer_connected/tests/responses/G26/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO02.json
index b9a035c..43a345c 100644
--- a/bimmer_connected/tests/responses/G26/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO02.json
+++ b/bimmer_connected/tests/responses/G26/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO02.json
@@ -1,15 +1,19 @@
 {
     "capabilities": {
         "a4aType": "NOT_SUPPORTED",
-        "checkSustainabilityDPP": false,
+        "alarmSystem": false,
         "climateFunction": "AIR_CONDITIONING",
         "climateNow": true,
         "digitalKey": {
             "bookedServicePackage": "SMACC_1_5",
+            "isDigitalKeyFirstSupported": false,
             "readerGraphics": "readerGraphics",
-            "state": "ACTIVATED"
+            "state": "ACTIVATED",
+            "vehicleSoftwareUpgradeRequired": false
         },
         "horn": true,
+        "inCarCamera": false,
+        "inCarCameraDwa": false,
         "isBmwChargingSupported": true,
         "isCarSharingSupported": false,
         "isChargeNowForBusinessSupported": true,
@@ -20,35 +24,78 @@
         "isChargingPowerLimitEnabled": true,
         "isChargingSettingsEnabled": true,
         "isChargingTargetSocEnabled": true,
+        "isClimateTimerSupported": false,
         "isClimateTimerWeeklyActive": false,
-        "isCustomerEsimSupported": true,
+        "isCustomerEsimSupported": false,
         "isDCSContractManagementSupported": true,
         "isDataPrivacyEnabled": false,
         "isEasyChargeEnabled": true,
         "isEvGoChargingSupported": false,
+        "isLocationBasedChargingSettingsSupported": false,
         "isMiniChargingSupported": false,
         "isNonLscFeatureEnabled": false,
         "isPersonalPictureUploadSupported": false,
-        "isRemoteEngineStartSupported": false,
+        "isPlugAndChargeSupported": false,
+        "isRemoteEngineStartEnabled": false,
+        "isRemoteEngineStartSupported": true,
         "isRemoteHistoryDeletionSupported": false,
         "isRemoteHistorySupported": true,
+        "isRemoteParkingEes25Active": false,
         "isRemoteParkingSupported": false,
         "isRemoteServicesActivationRequired": false,
         "isRemoteServicesBookingRequired": false,
         "isScanAndChargeSupported": true,
         "isSustainabilityAccumulatedViewEnabled": false,
         "isSustainabilitySupported": false,
+        "isThirdPartyAppStoreSupported": false,
         "isWifiHotspotServiceSupported": false,
         "lastStateCallState": "ACTIVATED",
         "lights": true,
+        "locationBasedCommerceFeatures": {
+            "fueling": false,
+            "parking": false,
+            "reservations": false
+        },
         "lock": true,
         "remote360": true,
         "remoteChargingCommands": {},
+        "remoteServices": {
+            "doorLock": {
+                "id": "doorLock",
+                "state": "ACTIVATED"
+            },
+            "doorUnlock": {
+                "id": "doorUnlock",
+                "state": "ACTIVATED"
+            },
+            "hornBlow": {
+                "id": "hornBlow",
+                "state": "ACTIVATED"
+            },
+            "inCarCamera": {
+                "id": "inCarCamera"
+            },
+            "inCarCameraDwa": {
+                "id": "inCarCameraDwa"
+            },
+            "lightFlash": {
+                "id": "lightFlash",
+                "state": "ACTIVATED"
+            },
+            "remote360": {
+                "id": "remote360",
+                "state": "ACTIVATED"
+            },
+            "surroundViewRecorder": {
+                "id": "surroundViewRecorder"
+            }
+        },
         "remoteSoftwareUpgrade": true,
         "sendPoi": true,
         "specialThemeSupport": [],
-        "speechThirdPartyAlexa": false,
+        "speechThirdPartyAlexa": true,
         "speechThirdPartyAlexaSDK": false,
+        "surroundViewRecorder": false,
         "unlock": true,
         "vehicleFinder": true,
         "vehicleStateSource": "LAST_STATE_CALL"
@@ -212,6 +259,7 @@
                 "type": "TIRE_WEAR_FRONT"
             }
         ],
+        "securityOverviewMode": "NOT_ARMED",
         "tireState": {
             "frontLeft": {
                 "details": {
@@ -302,6 +350,18 @@
                 }
             }
         },
+        "vehicleSoftwareVersion": {
+            "iStep": {
+                "iStep": 0,
+                "month": 0,
+                "seriesCluster": "",
+                "year": 0
+            },
+            "puStep": {
+                "month": 0,
+                "year": 0
+            }
+        },
         "windowsState": {
             "combinedState": "CLOSED",
             "leftFront": "CLOSED",
diff --git a/bimmer_connected/tests/responses/G70/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO05.json b/bimmer_connected/tests/responses/G70/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO05.json
index 2f346b5..79982ab 100644
--- a/bimmer_connected/tests/responses/G70/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO05.json
+++ b/bimmer_connected/tests/responses/G70/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO05.json
@@ -1,16 +1,19 @@
 {
     "capabilities": {
         "a4aType": "BLUETOOTH",
-        "checkSustainabilityDPP": false,
+        "alarmSystem": true,
         "climateFunction": "AIR_CONDITIONING",
         "climateNow": true,
         "digitalKey": {
             "bookedServicePackage": "SMACC_2_UWB",
+            "isDigitalKeyFirstSupported": false,
             "readerGraphics": "readerGraphics",
-            "state": "ACTIVATED"
+            "state": "ACTIVATED",
+            "vehicleSoftwareUpgradeRequired": true
         },
         "horn": true,
         "inCarCamera": true,
+        "inCarCameraDwa": true,
         "isBmwChargingSupported": true,
         "isCarSharingSupported": false,
         "isChargeNowForBusinessSupported": true,
@@ -21,35 +24,80 @@
         "isChargingPowerLimitEnabled": true,
         "isChargingSettingsEnabled": true,
         "isChargingTargetSocEnabled": true,
+        "isClimateTimerSupported": false,
         "isClimateTimerWeeklyActive": false,
         "isCustomerEsimSupported": true,
         "isDCSContractManagementSupported": true,
         "isDataPrivacyEnabled": false,
         "isEasyChargeEnabled": true,
         "isEvGoChargingSupported": false,
+        "isLocationBasedChargingSettingsSupported": true,
         "isMiniChargingSupported": false,
         "isNonLscFeatureEnabled": false,
         "isPersonalPictureUploadSupported": false,
-        "isRemoteEngineStartSupported": false,
+        "isPlugAndChargeSupported": false,
+        "isRemoteEngineStartEnabled": false,
+        "isRemoteEngineStartSupported": true,
         "isRemoteHistoryDeletionSupported": false,
         "isRemoteHistorySupported": true,
+        "isRemoteParkingEes25Active": false,
         "isRemoteParkingSupported": false,
         "isRemoteServicesActivationRequired": false,
         "isRemoteServicesBookingRequired": false,
         "isScanAndChargeSupported": true,
         "isSustainabilityAccumulatedViewEnabled": false,
         "isSustainabilitySupported": false,
+        "isThirdPartyAppStoreSupported": false,
         "isWifiHotspotServiceSupported": false,
         "lastStateCallState": "ACTIVATED",
         "lights": true,
+        "locationBasedCommerceFeatures": {
+            "fueling": false,
+            "parking": false,
+            "reservations": false
+        },
         "lock": true,
         "remote360": true,
         "remoteChargingCommands": {},
+        "remoteServices": {
+            "doorLock": {
+                "id": "doorLock",
+                "state": "ACTIVATED"
+            },
+            "doorUnlock": {
+                "id": "doorUnlock",
+                "state": "ACTIVATED"
+            },
+            "hornBlow": {
+                "id": "hornBlow",
+                "state": "ACTIVATED"
+            },
+            "inCarCamera": {
+                "id": "inCarCamera",
+                "state": "ACTIVATED"
+            },
+            "inCarCameraDwa": {
+                "id": "inCarCameraDwa",
+                "state": "ACTIVATED"
+            },
+            "lightFlash": {
+                "id": "lightFlash",
+                "state": "ACTIVATED"
+            },
+            "remote360": {
+                "id": "remote360",
+                "state": "ACTIVATED"
+            },
+            "surroundViewRecorder": {
+                "id": "surroundViewRecorder",
+                "state": "ACTIVATED"
+            }
+        },
         "remoteSoftwareUpgrade": true,
         "sendPoi": true,
         "specialThemeSupport": [],
         "speechThirdPartyAlexa": false,
-        "speechThirdPartyAlexaSDK": false,
+        "speechThirdPartyAlexaSDK": true,
         "surroundViewRecorder": true,
         "unlock": true,
         "vehicleFinder": true,
@@ -219,6 +267,7 @@
             "roofState": "CLOSED",
             "roofStateType": "SUN_ROOF"
         },
+        "securityOverviewMode": "ARMED",
         "tireState": {
             "frontLeft": {
                 "details": {
@@ -309,6 +358,18 @@
                 }
             }
         },
+        "vehicleSoftwareVersion": {
+            "iStep": {
+                "iStep": 0,
+                "month": 0,
+                "seriesCluster": "",
+                "year": 0
+            },
+            "puStep": {
+                "month": 0,
+                "year": 0
+            }
+        },
         "windowsState": {
             "combinedState": "CLOSED",
             "leftFront": "CLOSED",
diff --git a/bimmer_connected/tests/responses/I20/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO01.json b/bimmer_connected/tests/responses/I20/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO01.json
index 417a31c..4219e90 100644
--- a/bimmer_connected/tests/responses/I20/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO01.json
+++ b/bimmer_connected/tests/responses/I20/bmw-eadrax-vcs_v4_vehicles_state_WBA00000000DEMO01.json
@@ -1,16 +1,19 @@
 {
     "capabilities": {
         "a4aType": "BLUETOOTH",
-        "checkSustainabilityDPP": false,
+        "alarmSystem": true,
         "climateFunction": "AIR_CONDITIONING",
         "climateNow": true,
         "digitalKey": {
             "bookedServicePackage": "SMACC_2_UWB",
+            "isDigitalKeyFirstSupported": false,
             "readerGraphics": "readerGraphics",
-            "state": "ACTIVATED"
+            "state": "ACTIVATED",
+            "vehicleSoftwareUpgradeRequired": true
         },
         "horn": true,
         "inCarCamera": true,
+        "inCarCameraDwa": true,
         "isBmwChargingSupported": true,
         "isCarSharingSupported": false,
         "isChargeNowForBusinessSupported": true,
@@ -21,27 +24,38 @@
         "isChargingPowerLimitEnabled": true,
         "isChargingSettingsEnabled": true,
         "isChargingTargetSocEnabled": true,
+        "isClimateTimerSupported": false,
         "isClimateTimerWeeklyActive": false,
         "isCustomerEsimSupported": true,
         "isDCSContractManagementSupported": true,
         "isDataPrivacyEnabled": false,
         "isEasyChargeEnabled": true,
         "isEvGoChargingSupported": false,
+        "isLocationBasedChargingSettingsSupported": false,
         "isMiniChargingSupported": false,
         "isNonLscFeatureEnabled": false,
         "isPersonalPictureUploadSupported": false,
-        "isRemoteEngineStartSupported": false,
+        "isPlugAndChargeSupported": false,
+        "isRemoteEngineStartEnabled": false,
+        "isRemoteEngineStartSupported": true,
         "isRemoteHistoryDeletionSupported": false,
         "isRemoteHistorySupported": true,
+        "isRemoteParkingEes25Active": false,
         "isRemoteParkingSupported": false,
         "isRemoteServicesActivationRequired": false,
         "isRemoteServicesBookingRequired": false,
         "isScanAndChargeSupported": true,
         "isSustainabilityAccumulatedViewEnabled": false,
         "isSustainabilitySupported": false,
+        "isThirdPartyAppStoreSupported": false,
         "isWifiHotspotServiceSupported": false,
         "lastStateCallState": "ACTIVATED",
         "lights": true,
+        "locationBasedCommerceFeatures": {
+            "fueling": false,
+            "parking": false,
+            "reservations": false
+        },
         "lock": true,
         "remote360": true,
         "remoteChargingCommands": {
@@ -56,10 +70,44 @@
                 "NOT_SUPPORTED"
             ]
         },
+        "remoteServices": {
+            "doorLock": {
+                "id": "doorLock",
+                "state": "ACTIVATED"
+            },
+            "doorUnlock": {
+                "id": "doorUnlock",
+                "state": "ACTIVATED"
+            },
+            "hornBlow": {
+                "id": "hornBlow",
+                "state": "ACTIVATED"
+            },
+            "inCarCamera": {
+                "id": "inCarCamera",
+                "state": "ACTIVATED"
+            },
+            "inCarCameraDwa": {
+                "id": "inCarCameraDwa",
+                "state": "ACTIVATED"
+            },
+            "lightFlash": {
+                "id": "lightFlash",
+                "state": "ACTIVATED"
+            },
+            "remote360": {
+                "id": "remote360",
+                "state": "ACTIVATED"
+            },
+            "surroundViewRecorder": {
+                "id": "surroundViewRecorder",
+                "state": "ACTIVATED"
+            }
+        },
         "remoteSoftwareUpgrade": true,
         "sendPoi": true,
         "specialThemeSupport": [],
-        "speechThirdPartyAlexa": false,
+        "speechThirdPartyAlexa": true,
         "speechThirdPartyAlexaSDK": false,
         "surroundViewRecorder": true,
         "unlock": true,
@@ -230,6 +278,7 @@
             "roofState": "CLOSED",
             "roofStateType": "SUN_ROOF"
         },
+        "securityOverviewMode": "ARMED",
         "tireState": {
             "frontLeft": {
                 "details": {
@@ -320,6 +369,18 @@
                 }
             }
         },
+        "vehicleSoftwareVersion": {
+            "iStep": {
+                "iStep": 0,
+                "month": 0,
+                "seriesCluster": "",
+                "year": 0
+            },
+            "puStep": {
+                "month": 0,
+                "year": 0
+            }
+        },
         "windowsState": {
             "combinedState": "CLOSED",
             "leftFront": "CLOSED",
diff --git a/bimmer_connected/tests/test_remote_services.py b/bimmer_connected/tests/test_remote_services.py
index fd489bc..79b0fa4 100644
--- a/bimmer_connected/tests/test_remote_services.py
+++ b/bimmer_connected/tests/test_remote_services.py
@@ -384,31 +384,30 @@ async def test_poi(bmw_fixture: respx.Router):
 def test_poi_parsing():
     """Test correct parsing of PointOfInterest."""
 
-    poi_data = PointOfInterest(**POI_DATA)
-
     # Check parsing of attributes required by API
-    assert poi_data.coordinates.latitude == POI_DATA["lat"]
-    assert poi_data.coordinates.longitude == POI_DATA["lon"]
-    assert poi_data.name == POI_DATA["name"]
+    poi_data = PointOfInterest(**POI_DATA)
+    assert poi_data.position["lat"] == POI_DATA["lat"]
+    assert poi_data.position["lon"] == POI_DATA["lon"]
+    assert poi_data.title == POI_DATA["name"]
     assert poi_data.formattedAddress == f"{POI_DATA['street']}, {POI_DATA['postal_code']}, {POI_DATA['city']}"
 
     # Check the default attributes
     poi_data = PointOfInterest(lat=POI_DATA["lat"], lon=POI_DATA["lon"])
-    assert poi_data.coordinates.latitude == POI_DATA["lat"]
-    assert poi_data.coordinates.longitude == POI_DATA["lon"]
-    assert poi_data.name == "Sent with ♥ by bimmer_connected"
+    assert poi_data.position["lat"] == POI_DATA["lat"]
+    assert poi_data.position["lon"] == POI_DATA["lon"]
+    assert poi_data.title == "Sent with ♥ by bimmer_connected"
     assert poi_data.formattedAddress == "Coordinates only"
 
     # Check the default attributes with formatted address
     poi_data = PointOfInterest(lat=POI_DATA["lat"], lon=POI_DATA["lon"], formattedAddress="Somewhere over rainbow")
-    assert poi_data.coordinates.latitude == POI_DATA["lat"]
-    assert poi_data.coordinates.longitude == POI_DATA["lon"]
-    assert poi_data.name == "Sent with ♥ by bimmer_connected"
+    assert poi_data.position["lat"] == POI_DATA["lat"]
+    assert poi_data.position["lon"] == POI_DATA["lon"]
+    assert poi_data.title == "Sent with ♥ by bimmer_connected"
     assert poi_data.formattedAddress == "Somewhere over rainbow"
 
     # Check parsing with numeric postal code
     poi_data = PointOfInterest(lat=POI_DATA["lat"], lon=POI_DATA["lon"], postal_code=1234)
-    assert poi_data.coordinates.latitude == POI_DATA["lat"]
-    assert poi_data.coordinates.longitude == POI_DATA["lon"]
-    assert poi_data.name == "Sent with ♥ by bimmer_connected"
-    assert poi_data.locationAddress.postalCode == "1234"
+    assert poi_data.position["lat"] == POI_DATA["lat"]
+    assert poi_data.position["lon"] == POI_DATA["lon"]
+    assert poi_data.title == "Sent with ♥ by bimmer_connected"
+    assert poi_data.address.postalCode == "1234"