From 820475f3a2d421ecaa1826c66bc2c7ab8df6cd49 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Thu, 2 May 2024 12:00:12 -0600 Subject: [PATCH 01/31] Update audi_account.py --- custom_components/audiconnect/audi_account.py | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/custom_components/audiconnect/audi_account.py b/custom_components/audiconnect/audi_account.py index 14b6c9a7..9c7d6bdc 100644 --- a/custom_components/audiconnect/audi_account.py +++ b/custom_components/audiconnect/audi_account.py @@ -227,16 +227,29 @@ async def execute_vehicle_action(self, service): await self.connection.set_vehicle_window_heating(vin, False) async def start_climate_control(self, service): - _LOGGER.info("Initiating Start Climate Control Service...") + _LOGGER.debug("Initiating Start Climate Control Service...") vin = service.data.get(CONF_VIN).lower() + redacted_vin = "*" * (len(vin) - 4) + vin[-4:] # Optional Parameters temp_f = service.data.get(CONF_CLIMATE_TEMP_F, None) temp_c = service.data.get(CONF_CLIMATE_TEMP_C, None) - glass_heating = service.data.get(CONF_CLIMATE_GLASS, False) - seat_fl = service.data.get(CONF_CLIMATE_SEAT_FL, False) - seat_fr = service.data.get(CONF_CLIMATE_SEAT_FR, False) - seat_rl = service.data.get(CONF_CLIMATE_SEAT_RL, False) - seat_rr = service.data.get(CONF_CLIMATE_SEAT_RR, False) + glass_heating = service.data.get(CONF_CLIMATE_GLASS, None) + seat_fl = service.data.get(CONF_CLIMATE_SEAT_FL, None) + seat_fr = service.data.get(CONF_CLIMATE_SEAT_FR, None) + seat_rl = service.data.get(CONF_CLIMATE_SEAT_RL, None) + seat_rr = service.data.get(CONF_CLIMATE_SEAT_RR, None) + + _LOGGER.debug( + "Sending command to start climate control for vehicle %s with settings - Temp(F): %s, Temp(C): %s, Glass Heating: %s, Seat FL: %s, Seat FR: %s, Seat RL: %s, Seat RR: %s", + redacted_vin, + temp_f, + temp_c, + glass_heating, + seat_fl, + seat_fr, + seat_rl, + seat_rr, + ) await self.connection.start_climate_control( vin, From a4b3d6bde87b2a7252096e0f99303b5d366078fb Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Thu, 2 May 2024 12:01:03 -0600 Subject: [PATCH 02/31] Update audi_connect_account.py --- custom_components/audiconnect/audi_connect_account.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/custom_components/audiconnect/audi_connect_account.py b/custom_components/audiconnect/audi_connect_account.py index 3c57122b..40944247 100644 --- a/custom_components/audiconnect/audi_connect_account.py +++ b/custom_components/audiconnect/audi_connect_account.py @@ -293,6 +293,7 @@ async def start_climate_control( seat_rl: bool, seat_rr: bool, ): + redacted_vin = "*" * (len(vin) - 4) + vin[-4:] if not self._loggedin: await self.login() @@ -300,10 +301,6 @@ async def start_climate_control( return False try: - _LOGGER.debug( - f"Sending command to start climate control for vehicle {vin} with settings - Temp(F): {temp_f}, Temp(C): {temp_c}, Glass Heating: {glass_heating}, Seat FL: {seat_fl}, Seat FR: {seat_fr}, Seat RL: {seat_rl}, Seat RR: {seat_rr}" - ) - await self._audi_service.start_climate_control( vin, temp_f, @@ -315,7 +312,7 @@ async def start_climate_control( seat_rr, ) - _LOGGER.debug(f"Successfully started climate control of vehicle {vin}") + _LOGGER.debug("Successfully started climate control of vehicle with VIN: %s", redacted_vin) await self.notify(vin, ACTION_CLIMATISATION) @@ -323,7 +320,9 @@ async def start_climate_control( except Exception as exception: _LOGGER.error( - f"Unable to start climate control of vehicle {vin}. Error: {exception}", + "Unable to start climate control of vehicle with VIN: %s. Error: %s", + redacted_vin, + exception, exc_info=True, ) return False From 430841df861519b024a24403fa7c2e971a872613 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Thu, 2 May 2024 12:01:34 -0600 Subject: [PATCH 03/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 3fd9d7fe..fc4c3be6 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -591,6 +591,10 @@ async def start_climate_control( seat_rl: bool, seat_rr: bool, ): + seat_fl = False if seat_fl is None else seat_fl + seat_fr = False if seat_fr is None else seat_fr + seat_rl = False if seat_rl is None else seat_rl + seat_rr = False if seat_rr is None else seat_rr target_temperature = None if temp_f is not None: target_temperature = int(((temp_f - 32) * (5 / 9)) * 10 + 2731) From a20b480c4d5c349283b4506a36bebbbc1d77aa38 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 May 2024 18:04:16 +0000 Subject: [PATCH 04/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- custom_components/audiconnect/audi_connect_account.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/custom_components/audiconnect/audi_connect_account.py b/custom_components/audiconnect/audi_connect_account.py index 40944247..2d0f66ab 100644 --- a/custom_components/audiconnect/audi_connect_account.py +++ b/custom_components/audiconnect/audi_connect_account.py @@ -312,7 +312,10 @@ async def start_climate_control( seat_rr, ) - _LOGGER.debug("Successfully started climate control of vehicle with VIN: %s", redacted_vin) + _LOGGER.debug( + "Successfully started climate control of vehicle with VIN: %s", + redacted_vin, + ) await self.notify(vin, ACTION_CLIMATISATION) From 80afb4872d15d88f380cd5d3acc6616b22c9754b Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Wed, 29 May 2024 09:15:12 -0600 Subject: [PATCH 05/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index fc4c3be6..1a941550 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -591,17 +591,21 @@ async def start_climate_control( seat_rl: bool, seat_rr: bool, ): - seat_fl = False if seat_fl is None else seat_fl - seat_fr = False if seat_fr is None else seat_fr - seat_rl = False if seat_rl is None else seat_rl - seat_rr = False if seat_rr is None else seat_rr + # Handle None values for glass and seat heating + glass_heating = glass_heating if glass_heating is not None else False + seat_fl = seat_fl if seat_fl is not None else False + seat_fr = seat_fr if seat_fr is not None else False + seat_rl = seat_rl if seat_rl is not None else False + seat_rr = seat_rr if seat_rr is not None else False + + # Temperature Conversion target_temperature = None if temp_f is not None: target_temperature = int(((temp_f - 32) * (5 / 9)) * 10 + 2731) elif temp_c is not None: target_temperature = int(temp_c * 10 + 2731) - # Default Temp + # Default Temperature if None is provided target_temperature = target_temperature or 2941 # Construct Zone Settings From d7bca607ad8c2fbc14b0868b8387b85a93bdbe4e Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 07:08:42 -0600 Subject: [PATCH 06/31] Update const.py --- custom_components/audiconnect/const.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/custom_components/audiconnect/const.py b/custom_components/audiconnect/const.py index 08485784..554626de 100644 --- a/custom_components/audiconnect/const.py +++ b/custom_components/audiconnect/const.py @@ -89,3 +89,12 @@ 3: REGION_USA, 4: REGION_CHINA, } + +HDR_XAPP_VERSION = "4.26.0" +HDR_USER_AGENT = "Android/4.26.0 (Build 800240850.root project 'onetouch-android'.ext.buildTime) Android/13" +URL_HOST_ACTION = "https://emea.bff.cariad.digital/vehicle/v1/vehicles" +URL_HOST_ACTION_US = "https://mal-3a.prd.eu.dp.vwg-connect.com/api/bs" +URL_HOST_INFO = "https://emea.bff.cariad.digital/vehicle/v1/vehicles" +URL_HOST_INFO_US = "https://na.bff.cariad.digital/vehicle/v1/vehicles" +URL_INFO_VEHICLE = "https://app-api.live-my.audi.com/vgql/v1/graphql" +URL_INFO_VEHICLE_US = "https://app-api.my.aoa.audi.com/vgql/v1/graphql" From 37b5ca8b29851038f72b69026bd452929f6da3dc Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 07:09:11 -0600 Subject: [PATCH 07/31] Update audi_api.py --- custom_components/audiconnect/audi_api.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/custom_components/audiconnect/audi_api.py b/custom_components/audiconnect/audi_api.py index f6a346e1..76682fd0 100644 --- a/custom_components/audiconnect/audi_api.py +++ b/custom_components/audiconnect/audi_api.py @@ -7,6 +7,7 @@ from asyncio import TimeoutError, CancelledError from aiohttp import ClientResponseError from aiohttp.hdrs import METH_GET, METH_POST, METH_PUT +from .const import HDR_XAPP_VERSION, HDR_USER_AGENT from typing import Dict @@ -16,8 +17,6 @@ class AudiAPI: - HDR_XAPP_VERSION = "4.23.1" - HDR_USER_AGENT = "Android/4.23.1 (Build 800240120.root project 'onetouch-android'.ext.buildTime) Android/11" def __init__(self, session, proxy=None): self.__token = None @@ -38,7 +37,7 @@ async def request( self, method, url, - data, + data = None, headers: Dict[str, str] = None, raw_reply: bool = False, raw_contents: bool = False, @@ -129,9 +128,9 @@ def __get_headers(self): data = { "Accept": "application/json", "Accept-Charset": "utf-8", - "X-App-Version": self.HDR_XAPP_VERSION, + "X-App-Version": HDR_XAPP_VERSION, "X-App-Name": "myAudi", - "User-Agent": self.HDR_USER_AGENT, + "User-Agent": HDR_USER_AGENT, } if self.__token is not None: data["Authorization"] = "Bearer " + self.__token.get("access_token") From 88f7f1dc5192f298ef84885af60eedc97ab3cb1d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:09:18 +0000 Subject: [PATCH 08/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- custom_components/audiconnect/audi_api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/custom_components/audiconnect/audi_api.py b/custom_components/audiconnect/audi_api.py index 76682fd0..3d405078 100644 --- a/custom_components/audiconnect/audi_api.py +++ b/custom_components/audiconnect/audi_api.py @@ -17,7 +17,6 @@ class AudiAPI: - def __init__(self, session, proxy=None): self.__token = None self.__xclientid = None @@ -37,7 +36,7 @@ async def request( self, method, url, - data = None, + data=None, headers: Dict[str, str] = None, raw_reply: bool = False, raw_contents: bool = False, From bf3c062ccc7c54fbf17629085a888ada9dac2007 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 07:10:12 -0600 Subject: [PATCH 09/31] Update audi_account.py --- custom_components/audiconnect/audi_account.py | 65 ++++++++++++------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/custom_components/audiconnect/audi_account.py b/custom_components/audiconnect/audi_account.py index b6b71573..9c7d6bdc 100644 --- a/custom_components/audiconnect/audi_account.py +++ b/custom_components/audiconnect/audi_account.py @@ -1,35 +1,40 @@ -import asyncio import logging - import voluptuous as vol +import asyncio -from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform -from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.helpers.dispatcher import ( + async_dispatcher_send, +) +from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.util.dt import utcnow +from homeassistant.const import ( + CONF_PASSWORD, + CONF_USERNAME, +) +from .dashboard import Dashboard from .audi_connect_account import AudiConnectAccount, AudiConnectObserver from .audi_models import VehicleData + from .const import ( - COMPONENTS, + DOMAIN, + CONF_VIN, CONF_ACTION, + CONF_CLIMATE_TEMP_F, + CONF_CLIMATE_TEMP_C, CONF_CLIMATE_GLASS, CONF_CLIMATE_SEAT_FL, CONF_CLIMATE_SEAT_FR, CONF_CLIMATE_SEAT_RL, CONF_CLIMATE_SEAT_RR, - CONF_CLIMATE_TEMP_C, - CONF_CLIMATE_TEMP_F, CONF_REGION, CONF_SPIN, - CONF_VIN, - DOMAIN, SIGNAL_STATE_UPDATED, TRACKER_UPDATE, + COMPONENTS, UPDATE_SLEEP, ) -from .dashboard import Dashboard REFRESH_VEHICLE_DATA_FAILED_EVENT = "refresh_failed" REFRESH_VEHICLE_DATA_COMPLETED_EVENT = "refresh_completed" @@ -60,14 +65,6 @@ } ) -PLATFORMS: list[str] = [ - Platform.BINARY_SENSOR, - Platform.SENSOR, - Platform.DEVICE_TRACKER, - Platform.LOCK, - Platform.SWITCH, -] - SERVICE_REFRESH_CLOUD_DATA = "refresh_cloud_data" _LOGGER = logging.getLogger(__name__) @@ -122,7 +119,7 @@ def is_enabled(self, attr): # """Return true if the user has enabled the resource.""" # return attr in config[DOMAIN].get(CONF_RESOURCES, [attr]) - async def discover_vehicles(self, vehicles): + def discover_vehicles(self, vehicles): if len(vehicles) > 0: for vehicle in vehicles: vin = vehicle.vin.lower() @@ -152,8 +149,30 @@ async def discover_vehicles(self, vehicles): if instrument._component == "lock": cfg_vehicle.locks.add(instrument) - await self.hass.config_entries.async_forward_entry_setups( - self.config_entry, PLATFORMS + self.hass.async_create_task( + self.hass.config_entries.async_forward_entry_setup( + self.config_entry, "sensor" + ) + ) + self.hass.async_create_task( + self.hass.config_entries.async_forward_entry_setup( + self.config_entry, "binary_sensor" + ) + ) + self.hass.async_create_task( + self.hass.config_entries.async_forward_entry_setup( + self.config_entry, "switch" + ) + ) + self.hass.async_create_task( + self.hass.config_entries.async_forward_entry_setup( + self.config_entry, "device_tracker" + ) + ) + self.hass.async_create_task( + self.hass.config_entries.async_forward_entry_setup( + self.config_entry, "lock" + ) ) async def update(self, now): @@ -169,7 +188,7 @@ async def update(self, now): ] if new_vehicles: _LOGGER.debug("Retrieved %d vehicle(s)", len(new_vehicles)) - await self.discover_vehicles(new_vehicles) + self.discover_vehicles(new_vehicles) async_dispatcher_send(self.hass, SIGNAL_STATE_UPDATED) From 75bf35a1284042e8fb98462722ab5689a346ebf2 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 07:16:23 -0600 Subject: [PATCH 10/31] Update audi_account.py --- custom_components/audiconnect/audi_account.py | 66 +++++++------------ 1 file changed, 23 insertions(+), 43 deletions(-) diff --git a/custom_components/audiconnect/audi_account.py b/custom_components/audiconnect/audi_account.py index 9c7d6bdc..3ddedffa 100644 --- a/custom_components/audiconnect/audi_account.py +++ b/custom_components/audiconnect/audi_account.py @@ -1,40 +1,35 @@ +import asyncio import logging + import voluptuous as vol -import asyncio -import homeassistant.helpers.config_validation as cv -from homeassistant.helpers.dispatcher import ( - async_dispatcher_send, -) +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform from homeassistant.helpers.aiohttp_client import async_get_clientsession +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.util.dt import utcnow -from homeassistant.const import ( - CONF_PASSWORD, - CONF_USERNAME, -) -from .dashboard import Dashboard from .audi_connect_account import AudiConnectAccount, AudiConnectObserver from .audi_models import VehicleData - from .const import ( - DOMAIN, - CONF_VIN, + COMPONENTS, CONF_ACTION, - CONF_CLIMATE_TEMP_F, - CONF_CLIMATE_TEMP_C, CONF_CLIMATE_GLASS, CONF_CLIMATE_SEAT_FL, CONF_CLIMATE_SEAT_FR, CONF_CLIMATE_SEAT_RL, CONF_CLIMATE_SEAT_RR, + CONF_CLIMATE_TEMP_C, + CONF_CLIMATE_TEMP_F, CONF_REGION, CONF_SPIN, + CONF_VIN, + DOMAIN, SIGNAL_STATE_UPDATED, TRACKER_UPDATE, - COMPONENTS, UPDATE_SLEEP, ) +from .dashboard import Dashboard REFRESH_VEHICLE_DATA_FAILED_EVENT = "refresh_failed" REFRESH_VEHICLE_DATA_COMPLETED_EVENT = "refresh_completed" @@ -65,6 +60,14 @@ } ) +PLATFORMS: list[str] = [ + Platform.BINARY_SENSOR, + Platform.SENSOR, + Platform.DEVICE_TRACKER, + Platform.LOCK, + Platform.SWITCH, +] + SERVICE_REFRESH_CLOUD_DATA = "refresh_cloud_data" _LOGGER = logging.getLogger(__name__) @@ -119,7 +122,7 @@ def is_enabled(self, attr): # """Return true if the user has enabled the resource.""" # return attr in config[DOMAIN].get(CONF_RESOURCES, [attr]) - def discover_vehicles(self, vehicles): + async def discover_vehicles(self, vehicles): if len(vehicles) > 0: for vehicle in vehicles: vin = vehicle.vin.lower() @@ -149,31 +152,8 @@ def discover_vehicles(self, vehicles): if instrument._component == "lock": cfg_vehicle.locks.add(instrument) - self.hass.async_create_task( - self.hass.config_entries.async_forward_entry_setup( - self.config_entry, "sensor" - ) - ) - self.hass.async_create_task( - self.hass.config_entries.async_forward_entry_setup( - self.config_entry, "binary_sensor" - ) - ) - self.hass.async_create_task( - self.hass.config_entries.async_forward_entry_setup( - self.config_entry, "switch" - ) - ) - self.hass.async_create_task( - self.hass.config_entries.async_forward_entry_setup( - self.config_entry, "device_tracker" - ) - ) - self.hass.async_create_task( - self.hass.config_entries.async_forward_entry_setup( - self.config_entry, "lock" - ) - ) + await self.hass.config_entries.async_forward_entry_setups( + self.config_entry, PLATFORMS async def update(self, now): """Update status from the cloud.""" @@ -188,7 +168,7 @@ async def update(self, now): ] if new_vehicles: _LOGGER.debug("Retrieved %d vehicle(s)", len(new_vehicles)) - self.discover_vehicles(new_vehicles) + await self.discover_vehicles(new_vehicles) async_dispatcher_send(self.hass, SIGNAL_STATE_UPDATED) From 10ad8ceed7934561330eeb3762ea52cd34a072fb Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 07:17:40 -0600 Subject: [PATCH 11/31] Update audi_account.py --- custom_components/audiconnect/audi_account.py | 1 + 1 file changed, 1 insertion(+) diff --git a/custom_components/audiconnect/audi_account.py b/custom_components/audiconnect/audi_account.py index 3ddedffa..b6b71573 100644 --- a/custom_components/audiconnect/audi_account.py +++ b/custom_components/audiconnect/audi_account.py @@ -154,6 +154,7 @@ async def discover_vehicles(self, vehicles): await self.hass.config_entries.async_forward_entry_setups( self.config_entry, PLATFORMS + ) async def update(self, now): """Update status from the cloud.""" From 9f3a6f7c378be3fc0ac00f30ad27961b1bf053d3 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 07:23:32 -0600 Subject: [PATCH 12/31] Update audi_services.py --- .../audiconnect/audi_services.py | 338 ++++++++++++------ 1 file changed, 225 insertions(+), 113 deletions(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 7baac642..f48b096d 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -13,6 +13,15 @@ VehiclesResponse, ) from .audi_api import AudiAPI +from .const import ( + HDR_XAPP_VERSION, + HDR_USER_AGENT, + URL_INFO_VEHICLE, + URL_INFO_VEHICLE_US, + URL_HOST_ACTION, + URL_HOST_ACTION_US, + REGION_USA + ) from .util import to_byte_array, get_attr from hashlib import sha256, sha512 @@ -140,10 +149,7 @@ async def refresh_vehicle_data(self, vin: str): async def request_current_vehicle_data(self, vin: str): self._api.use_token(self.vwToken) data = await self._api.post( - "{homeRegion}/fs-car/bs/vsr/v1/{type}/{country}/vehicles/{vin}/requests".format( - homeRegion=await self._get_home_region(vin.upper()), - type=self._type, - country=self._country, + "https://na.bff.cariad.digital/vehicle/v1/vehicles/{vin}/pendingrequests".format( vin=vin.upper(), ) ) @@ -260,12 +266,12 @@ async def get_vehicle_information(self): "Accept": "application/json", "Accept-Charset": "utf-8", "X-App-Name": "myAudi", - "X-App-Version": AudiAPI.HDR_XAPP_VERSION, + "X-App-Version": HDR_XAPP_VERSION, "Accept-Language": "{l}-{c}".format( l=self._language, c=self._country.upper() ), "X-User-Country": self._country.upper(), - "User-Agent": AudiAPI.HDR_USER_AGENT, + "User-Agent": HDR_USER_AGENT, "Authorization": "Bearer " + self.audiToken["access_token"], "Content-Type": "application/json; charset=utf-8", } @@ -274,9 +280,9 @@ async def get_vehicle_information(self): } req_rsp, rep_rsptxt = await self._api.request( "POST", - "https://app-api.my.aoa.audi.com/vgql/v1/graphql" + URL_INFO_VEHICLE_US if self._country.upper() == "US" - else "https://app-api.live-my.audi.com/vgql/v1/graphql", # Starting in 2023, US users need to point at the aoa (Audi of America) URL. + else URL_INFO_VEHICLE, # Starting in 2023, US users need to point at the aoa (Audi of America) URL. json.dumps(req_data), headers=headers, allow_redirects=False, @@ -309,9 +315,9 @@ async def get_tripdata(self, vin: str, kind: str): "Accept": "application/json", "Accept-Charset": "utf-8", "X-App-Name": "myAudi", - "X-App-Version": AudiAPI.HDR_XAPP_VERSION, + "X-App-Version": HDR_XAPP_VERSION, "X-Client-ID": self.xclientId, - "User-Agent": AudiAPI.HDR_USER_AGENT, + "User-Agent": HDR_USER_AGENT, "Authorization": "Bearer " + self.vwToken["access_token"], } td_reqdata = { @@ -455,9 +461,9 @@ async def _get_security_token(self, vin: str, action: str): def _get_vehicle_action_header(self, content_type: str, security_token: str): headers = { - "User-Agent": "okhttp/3.7.0", - "Host": "msg.volkswagen.de", - "X-App-Version": "3.14.0", + "User-Agent": "Android/4.26.0 (Build 800240850.root project 'onetouch-android'.ext.buildTime) Android/13", + "Host": "mal-3a.prd.eu.dp.vwg-connect.com", + "X-App-Version": "4.26.0", "X-App-Name": "myAudi", "Authorization": "Bearer " + self.vwToken.get("access_token"), "Accept-charset": "UTF-8", @@ -474,39 +480,71 @@ async def set_vehicle_lock(self, vin: str, lock: bool): security_token = await self._get_security_token( vin, "rlu_v1/operations/" + ("LOCK" if lock else "UNLOCK") ) - data = '{action}'.format( - action="lock" if lock else "unlock" - ) - headers = self._get_vehicle_action_header( - "application/vnd.vwg.mbb.RemoteLockUnlock_v1_0_0+xml", security_token - ) - res = await self._api.request( - "POST", - "{homeRegion}/fs-car/bs/rlu/v1/{type}/{country}/vehicles/{vin}/actions".format( + + if self._country.upper() == REGION_USA: + data = None + headers = self._get_vehicle_action_header( + "application/json;charset=utf-8", security_token + ) + res = await self._api.request( + "POST", + "{host}/rlu/v1/vehicles/{vin}/{action}".format( + host=URL_HOST_ACTION_US, + vin=vin.upper(), + action="lock" if lock else "unlock" + ), + headers=headers, + data=data, + ) + + checkUrl = "{host}/rlu/v1/vehicles/{vin}/requests/{requestId}/status".format( + host=URL_HOST_ACTION_US, + vin=vin.upper(), + requestId=res["rluActionResponse"]["requestId"], + ) + + await self.check_request_succeeded( + checkUrl, + "lock vehicle" if lock else "unlock vehicle", + REQUEST_SUCCESSFUL, + REQUEST_FAILED, + "requestStatusResponse.status", + ) + + else: + data = '{action}'.format( + action="lock" if lock else "unlock" + ) + headers = self._get_vehicle_action_header( + "application/vnd.vwg.mbb.RemoteLockUnlock_v1_0_0+xml", security_token + ) + res = await self._api.request( + "POST", + "{homeRegion}/fs-car/bs/rlu/v1/{type}/{country}/vehicles/{vin}/actions".format( + homeRegion=await self._get_home_region(vin.upper()), + type=self._type, + country=self._country, + vin=vin.upper(), + ), + headers=headers, + data=data, + ) + + checkUrl = "{homeRegion}/fs-car/bs/rlu/v1/{type}/{country}/vehicles/{vin}/requests/{requestId}/status".format( homeRegion=await self._get_home_region(vin.upper()), type=self._type, country=self._country, vin=vin.upper(), - ), - headers=headers, - data=data, - ) - - checkUrl = "{homeRegion}/fs-car/bs/rlu/v1/{type}/{country}/vehicles/{vin}/requests/{requestId}/status".format( - homeRegion=await self._get_home_region(vin.upper()), - type=self._type, - country=self._country, - vin=vin.upper(), - requestId=res["rluActionResponse"]["requestId"], - ) + requestId=res["rluActionResponse"]["requestId"], + ) - await self.check_request_succeeded( - checkUrl, - "lock vehicle" if lock else "unlock vehicle", - REQUEST_SUCCESSFUL, - REQUEST_FAILED, - "requestStatusResponse.status", - ) + await self.check_request_succeeded( + checkUrl, + "lock vehicle" if lock else "unlock vehicle", + REQUEST_SUCCESSFUL, + REQUEST_FAILED, + "requestStatusResponse.status", + ) async def set_battery_charger(self, vin: str, start: bool, timer: bool): if start and timer: @@ -546,39 +584,70 @@ async def set_battery_charger(self, vin: str, start: bool, timer: bool): ) async def set_climatisation(self, vin: str, start: bool): - if start: - data = '{"action":{"type": "startClimatisation","settings": {"targetTemperature": 2940,"climatisationWithoutHVpower": true,"heaterSource": "electric","climaterElementSettings": {"isClimatisationAtUnlock": false, "isMirrorHeatingEnabled": true,}}}}' + if self._country.upper() == REGION_USA: + if start: + data = '{"action":{"type": "startClimatisation","settings": {"targetTemperature": 2940,"climatisationWithoutHVpower": true,"heaterSource": "electric","climaterElementSettings": {"isClimatisationAtUnlock": false, "isMirrorHeatingEnabled": true,}}}}' + else: + data = '{"action":{"type": "stopClimatisation"}}' + + headers = self._get_vehicle_action_header("application/json", None) + res = await self._api.request( + "POST", + "{host}/climatisation/v1/vehicles/{vin}/climater/actions".format( + host=URL_HOST_ACTION_US, + vin=vin.upper(), + ), + headers=headers, + data=data, + ) + + checkUrl = "{host}/climatisation/v1/vehicles/{vin}/climater/actions/{actionid}".format( + host=URL_HOST_ACTION_US, + vin=vin.upper(), + actionid=res["action"]["actionId"], + ) + + await self.check_request_succeeded( + checkUrl, + "start climatisation" if start else "stop climatisation", + SUCCEEDED, + FAILED, + "action.actionState", + ) else: - data = '{"action":{"type": "stopClimatisation"}}' + if start: + data = '{"action":{"type": "startClimatisation","settings": {"targetTemperature": 2940,"climatisationWithoutHVpower": true,"heaterSource": "electric","climaterElementSettings": {"isClimatisationAtUnlock": false, "isMirrorHeatingEnabled": true,}}}}' + else: + data = '{"action":{"type": "stopClimatisation"}}' - headers = self._get_vehicle_action_header("application/json", None) - res = await self._api.request( - "POST", - "{homeRegion}/fs-car/bs/climatisation/v1/{type}/{country}/vehicles/{vin}/climater/actions".format( + headers = self._get_vehicle_action_header("application/json", None) + res = await self._api.request( + "POST", + "{homeRegion}/fs-car/bs/climatisation/v1/{type}/{country}/vehicles/{vin}/climater/actions".format( + homeRegion=await self._get_home_region(vin.upper()), + type=self._type, + country=self._country, + vin=vin.upper(), + ), + headers=headers, + data=data, + ) + + checkUrl = "{homeRegion}/fs-car/bs/climatisation/v1/{type}/{country}/vehicles/{vin}/climater/actions/{actionid}".format( homeRegion=await self._get_home_region(vin.upper()), type=self._type, country=self._country, vin=vin.upper(), - ), - headers=headers, - data=data, - ) - - checkUrl = "{homeRegion}/fs-car/bs/climatisation/v1/{type}/{country}/vehicles/{vin}/climater/actions/{actionid}".format( - homeRegion=await self._get_home_region(vin.upper()), - type=self._type, - country=self._country, - vin=vin.upper(), - actionid=res["action"]["actionId"], - ) + actionid=res["action"]["actionId"], + ) - await self.check_request_succeeded( - checkUrl, - "start climatisation" if start else "stop climatisation", - SUCCEEDED, - FAILED, - "action.actionState", - ) + await self.check_request_succeeded( + checkUrl, + "start climatisation" if start else "stop climatisation", + SUCCEEDED, + FAILED, + "action.actionState", + ) async def start_climate_control( self, @@ -592,23 +661,28 @@ async def start_climate_control( seat_rr: bool, ): # Handle None values for glass and seat heating - glass_heating = glass_heating if glass_heating is not None else False - seat_fl = seat_fl if seat_fl is not None else False - seat_fr = seat_fr if seat_fr is not None else False - seat_rl = seat_rl if seat_rl is not None else False - seat_rr = seat_rr if seat_rr is not None else False + glass_heating = glass_heating if glass_heating else False + seat_fl = seat_fl if seat_fl else False + seat_fr = seat_fr if seat_fr else False + seat_rl = seat_rl if seat_rl else False + seat_rr = seat_rr if seat_rr else False # Temperature Conversion target_temperature = None if temp_f is not None: target_temperature = int(((temp_f - 32) * (5 / 9)) * 10 + 2731) + target_temperature_raw = temp_f + target_temperature_unit = "fahrenheit" elif temp_c is not None: target_temperature = int(temp_c * 10 + 2731) + target_temperature_raw = temp_c + target_temperature_unit = "celcius" # Default Temperature if None is provided target_temperature = target_temperature or 2941 - # Construct Zone Settings + # API 1 + # Construct Zone Settings for API 1 zone_settings = [ {"value": {"isEnabled": seat_fl, "position": "frontLeft"}}, {"value": {"isEnabled": seat_fr, "position": "frontRight"}}, @@ -616,7 +690,7 @@ async def start_climate_control( {"value": {"isEnabled": seat_rr, "position": "rearRight"}}, ] - data = { + data_1 = { "action": { "type": "startClimatisation", "settings": { @@ -632,36 +706,74 @@ async def start_climate_control( } } - data = json.dumps(data) + data_1 = json.dumps(data_1) + + # API 2 + data_2 = { + "targetTemperature": target_temperature_raw, + "targetTemperatureUnit": target_temperature_unit, + "climatisationWithoutExternalPower": True, + "climatizationAtUnlock": False, + "windowHeatingEnabled": glass_heating, + "zoneFrontLeftEnabled": seat_fl, + "zoneFrontRightEnabled": seat_fr, + "zoneRearLeftEnabled": seat_rl, + "zoneRearRightEnabled": seat_rr, + } + + data_2 = json.dumps(data_2) headers = self._get_vehicle_action_header("application/json", None) - res = await self._api.request( - "POST", - "{homeRegion}/fs-car/bs/climatisation/v1/{type}/{country}/vehicles/{vin}/climater/actions".format( - homeRegion=await self._get_home_region(vin.upper()), - type=self._type, - country=self._country, + if self._country.upper() == REGION_USA: + res = await self._api.request( + "POST", + "{host}/climatisation/v1/vehicles/{vin}/climater/actions".format( + host=URL_HOST_ACTION_US, + vin=vin.upper(), + ), + headers=headers, + data=data_1, + ) + + checkUrl = "{host}/climatisation/v1/vehicles/{vin}/climater/actions/{actionid}".format( + host=URL_HOST_ACTION_US, vin=vin.upper(), - ), - headers=headers, - data=data, - ) + actionid=res["action"]["actionId"], + ) - checkUrl = "{homeRegion}/fs-car/bs/climatisation/v1/{type}/{country}/vehicles/{vin}/climater/actions/{actionid}".format( - homeRegion=await self._get_home_region(vin.upper()), - type=self._type, - country=self._country, - vin=vin.upper(), - actionid=res["action"]["actionId"], - ) + await self.check_request_succeeded( + checkUrl, + "Start Climate Control", + SUCCEEDED, + FAILED, + "action.actionState", + ) - await self.check_request_succeeded( - checkUrl, - "start climatisation", - SUCCEEDED, - FAILED, - "action.actionState", - ) + else: + res = await self._api.request( + "POST", + "{host}/{vin}/climatisation/start".format( + host=URL_HOST_ACTION, + vin=vin.upper(), + ), + headers=headers, + data=data_2, + ) + + checkUrl = "{host}/{vin}/pendingrequests".format( + host=URL_HOST_ACTION, + vin=vin.upper(), + ) + + actionID = res["data"]["requestID"] + + await self.check_request_succeeded( + checkUrl, + "Start Climate Control", + SUCCEEDED, + FAILED, + "action.actionState", + ) async def set_window_heating(self, vin: str, start: bool): data = '{action}'.format( @@ -748,7 +860,7 @@ async def check_request_succeeded( raise Exception("Cannot {action}, operation timed out".format(action=action)) - # TR/2022-12-20: New secret for X_QMAuth + # TR/2022-12-20: New secrect for X_QMAuth def _calculate_X_QMAuth(self): # Calculate X-QMAuth value gmtime_100sec = int( @@ -817,7 +929,7 @@ async def refresh_token_if_necessary(self, elapsed_sec: int) -> bool: headers = { "Accept": "application/json", "Accept-Charset": "utf-8", - "User-Agent": AudiAPI.HDR_USER_AGENT, + "User-Agent": HDR_USER_AGENT, "Content-Type": "application/x-www-form-urlencoded", "X-Client-ID": self.xclientId, } @@ -851,7 +963,7 @@ async def refresh_token_if_necessary(self, elapsed_sec: int) -> bool: "Accept": "application/json", "Accept-Charset": "utf-8", "X-QMAuth": self._calculate_X_QMAuth(), - "User-Agent": AudiAPI.HDR_USER_AGENT, + "User-Agent": HDR_USER_AGENT, "Content-Type": "application/x-www-form-urlencoded", } # IDK token request data @@ -879,9 +991,9 @@ async def refresh_token_if_necessary(self, elapsed_sec: int) -> bool: headers = { "Accept": "application/json", "Accept-Charset": "utf-8", - "X-App-Version": AudiAPI.HDR_XAPP_VERSION, + "X-App-Version": HDR_XAPP_VERSION, "X-App-Name": "myAudi", - "User-Agent": AudiAPI.HDR_USER_AGENT, + "User-Agent": HDR_USER_AGENT, "Content-Type": "application/json; charset=utf-8", } asz_req_data = { @@ -997,9 +1109,9 @@ async def login_request(self, user: str, password: str): headers = { "Accept": "application/json", "Accept-Charset": "utf-8", - "X-App-Version": AudiAPI.HDR_XAPP_VERSION, + "X-App-Version": HDR_XAPP_VERSION, "X-App-Name": "myAudi", - "User-Agent": AudiAPI.HDR_USER_AGENT, + "User-Agent": HDR_USER_AGENT, } idk_data = { "response_type": "code", @@ -1102,7 +1214,7 @@ async def login_request(self, user: str, password: str): "Accept": "application/json", "Accept-Charset": "utf-8", "X-QMAuth": self._calculate_X_QMAuth(), - "User-Agent": AudiAPI.HDR_USER_AGENT, + "User-Agent": HDR_USER_AGENT, "Content-Type": "application/x-www-form-urlencoded", } # IDK token request data @@ -1132,9 +1244,9 @@ async def login_request(self, user: str, password: str): headers = { "Accept": "application/json", "Accept-Charset": "utf-8", - "X-App-Version": AudiAPI.HDR_XAPP_VERSION, + "X-App-Version": HDR_XAPP_VERSION, "X-App-Name": "myAudi", - "User-Agent": AudiAPI.HDR_USER_AGENT, + "User-Agent": HDR_USER_AGENT, "Content-Type": "application/json; charset=utf-8", } asz_req_data = { @@ -1158,7 +1270,7 @@ async def login_request(self, user: str, password: str): headers = { "Accept": "application/json", "Accept-Charset": "utf-8", - "User-Agent": AudiAPI.HDR_USER_AGENT, + "User-Agent": HDR_USER_AGENT, "Content-Type": "application/json; charset=utf-8", } mbboauth_reg_data = { @@ -1166,7 +1278,7 @@ async def login_request(self, user: str, password: str): "platform": "google", "client_brand": "Audi", "appName": "myAudi", - "appVersion": AudiAPI.HDR_XAPP_VERSION, + "appVersion": HDR_XAPP_VERSION, "appId": "de.myaudi.mobile.assistant", } mbboauth_client_reg_rsp, mbboauth_client_reg_rsptxt = await self._api.request( @@ -1185,7 +1297,7 @@ async def login_request(self, user: str, password: str): headers = { "Accept": "application/json", "Accept-Charset": "utf-8", - "User-Agent": AudiAPI.HDR_USER_AGENT, + "User-Agent": HDR_USER_AGENT, "Content-Type": "application/x-www-form-urlencoded", "X-Client-ID": self.xclientId, } @@ -1213,7 +1325,7 @@ async def login_request(self, user: str, password: str): headers = { "Accept": "application/json", "Accept-Charset": "utf-8", - "User-Agent": AudiAPI.HDR_USER_AGENT, + "User-Agent": HDR_USER_AGENT, "Content-Type": "application/x-www-form-urlencoded", "X-Client-ID": self.xclientId, } From 6ed572e9701e8c48082d4dfb23e3f3f785a6d692 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 13:23:42 +0000 Subject: [PATCH 13/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../audiconnect/audi_services.py | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index f48b096d..05071a91 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -20,8 +20,8 @@ URL_INFO_VEHICLE_US, URL_HOST_ACTION, URL_HOST_ACTION_US, - REGION_USA - ) + REGION_USA, +) from .util import to_byte_array, get_attr from hashlib import sha256, sha512 @@ -491,16 +491,18 @@ async def set_vehicle_lock(self, vin: str, lock: bool): "{host}/rlu/v1/vehicles/{vin}/{action}".format( host=URL_HOST_ACTION_US, vin=vin.upper(), - action="lock" if lock else "unlock" + action="lock" if lock else "unlock", ), headers=headers, data=data, ) - checkUrl = "{host}/rlu/v1/vehicles/{vin}/requests/{requestId}/status".format( - host=URL_HOST_ACTION_US, - vin=vin.upper(), - requestId=res["rluActionResponse"]["requestId"], + checkUrl = ( + "{host}/rlu/v1/vehicles/{vin}/requests/{requestId}/status".format( + host=URL_HOST_ACTION_US, + vin=vin.upper(), + requestId=res["rluActionResponse"]["requestId"], + ) ) await self.check_request_succeeded( @@ -710,16 +712,16 @@ async def start_climate_control( # API 2 data_2 = { - "targetTemperature": target_temperature_raw, - "targetTemperatureUnit": target_temperature_unit, - "climatisationWithoutExternalPower": True, - "climatizationAtUnlock": False, - "windowHeatingEnabled": glass_heating, - "zoneFrontLeftEnabled": seat_fl, - "zoneFrontRightEnabled": seat_fr, - "zoneRearLeftEnabled": seat_rl, - "zoneRearRightEnabled": seat_rr, - } + "targetTemperature": target_temperature_raw, + "targetTemperatureUnit": target_temperature_unit, + "climatisationWithoutExternalPower": True, + "climatizationAtUnlock": False, + "windowHeatingEnabled": glass_heating, + "zoneFrontLeftEnabled": seat_fl, + "zoneFrontRightEnabled": seat_fr, + "zoneRearLeftEnabled": seat_rl, + "zoneRearRightEnabled": seat_rr, + } data_2 = json.dumps(data_2) From 377ed0751ca63a6b3866f8bfe43879c03aec71f9 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 07:25:49 -0600 Subject: [PATCH 14/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 05071a91..34442186 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -149,7 +149,10 @@ async def refresh_vehicle_data(self, vin: str): async def request_current_vehicle_data(self, vin: str): self._api.use_token(self.vwToken) data = await self._api.post( - "https://na.bff.cariad.digital/vehicle/v1/vehicles/{vin}/pendingrequests".format( + "{homeRegion}/fs-car/bs/vsr/v1/{type}/{country}/vehicles/{vin}/requests".format( + homeRegion=await self._get_home_region(vin.upper()), + type=self._type, + country=self._country, vin=vin.upper(), ) ) From b14ebf139ddb98d7e1571bdc842c17419023f229 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 07:31:34 -0600 Subject: [PATCH 15/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 34442186..2fff707d 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -464,7 +464,7 @@ async def _get_security_token(self, vin: str, action: str): def _get_vehicle_action_header(self, content_type: str, security_token: str): headers = { - "User-Agent": "Android/4.26.0 (Build 800240850.root project 'onetouch-android'.ext.buildTime) Android/13", + "User-Agent": HDR_USER_AGENT, "Host": "mal-3a.prd.eu.dp.vwg-connect.com", "X-App-Version": "4.26.0", "X-App-Name": "myAudi", @@ -865,7 +865,7 @@ async def check_request_succeeded( raise Exception("Cannot {action}, operation timed out".format(action=action)) - # TR/2022-12-20: New secrect for X_QMAuth + # TR/2022-12-20: New secret for X_QMAuth def _calculate_X_QMAuth(self): # Calculate X-QMAuth value gmtime_100sec = int( From 639b24dbf1671e24f1ec42d225a2bb3ca948e416 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 08:04:26 -0600 Subject: [PATCH 16/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 2fff707d..1ba86f61 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -465,7 +465,7 @@ async def _get_security_token(self, vin: str, action: str): def _get_vehicle_action_header(self, content_type: str, security_token: str): headers = { "User-Agent": HDR_USER_AGENT, - "Host": "mal-3a.prd.eu.dp.vwg-connect.com", + "Host": "mal-3a.prd.eu.dp.vwg-connect.com" if self._country.upper() == REGION_USA else "msg.volkswagen.de", "X-App-Version": "4.26.0", "X-App-Name": "myAudi", "Authorization": "Bearer " + self.vwToken.get("access_token"), From 675f2a15e1663fd1e880a8d1ced7443d721fdb4a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 14:04:52 +0000 Subject: [PATCH 17/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- custom_components/audiconnect/audi_services.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 1ba86f61..61ffcac9 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -465,7 +465,9 @@ async def _get_security_token(self, vin: str, action: str): def _get_vehicle_action_header(self, content_type: str, security_token: str): headers = { "User-Agent": HDR_USER_AGENT, - "Host": "mal-3a.prd.eu.dp.vwg-connect.com" if self._country.upper() == REGION_USA else "msg.volkswagen.de", + "Host": "mal-3a.prd.eu.dp.vwg-connect.com" + if self._country.upper() == REGION_USA + else "msg.volkswagen.de", "X-App-Version": "4.26.0", "X-App-Name": "myAudi", "Authorization": "Bearer " + self.vwToken.get("access_token"), From 17ddf26455eb14f0eca9a5db4b5100cc1c128db7 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 08:24:44 -0600 Subject: [PATCH 18/31] Update util.py --- custom_components/audiconnect/util.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/custom_components/audiconnect/util.py b/custom_components/audiconnect/util.py index bbdbc231..39271a3f 100644 --- a/custom_components/audiconnect/util.py +++ b/custom_components/audiconnect/util.py @@ -55,3 +55,9 @@ def parse_datetime(time_value): except ValueError: continue return None + +def get_status_by_id(data, target_id): + for item in data['data']: + if item['id'] == target_id: + return item['status'] + return None From dc3e9477c48026edb652670828bcb3bad63ebf92 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 14:25:49 +0000 Subject: [PATCH 19/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- custom_components/audiconnect/util.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/custom_components/audiconnect/util.py b/custom_components/audiconnect/util.py index 39271a3f..63318796 100644 --- a/custom_components/audiconnect/util.py +++ b/custom_components/audiconnect/util.py @@ -56,8 +56,9 @@ def parse_datetime(time_value): continue return None + def get_status_by_id(data, target_id): - for item in data['data']: - if item['id'] == target_id: - return item['status'] + for item in data["data"]: + if item["id"] == target_id: + return item["status"] return None From f32ed2ed161384ad4d63ad73e52ead2f147b3a47 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 08:26:22 -0600 Subject: [PATCH 20/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 61ffcac9..e7a9a6f4 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -22,7 +22,7 @@ URL_HOST_ACTION_US, REGION_USA, ) -from .util import to_byte_array, get_attr +from .util import to_byte_array, get_attr, get_status_by_id from hashlib import sha256, sha512 import hmac @@ -779,7 +779,7 @@ async def start_climate_control( "Start Climate Control", SUCCEEDED, FAILED, - "action.actionState", + res["data"]["requestID"], ) async def set_window_heating(self, vin: str, start: bool): @@ -853,7 +853,11 @@ async def check_request_succeeded( self._api.use_token(self.vwToken) res = await self._api.get(url) - status = get_attr(res, path) + if "pendingrequests" in url: + target_id = path + status = get_status_by_id(res, target_id) + else: + status = get_attr(res, path) if status is None or (failedCode is not None and status == failedCode): raise Exception( From 4f9cbba406a8ab9f4fa6340762c687fb0e8cc1c8 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 08:31:00 -0600 Subject: [PATCH 21/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index e7a9a6f4..25e8c45d 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -772,8 +772,6 @@ async def start_climate_control( vin=vin.upper(), ) - actionID = res["data"]["requestID"] - await self.check_request_succeeded( checkUrl, "Start Climate Control", From a8b73c5a9a58921f1ba62f1c64c063fedd534638 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 08:37:00 -0600 Subject: [PATCH 22/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 25e8c45d..393d6540 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -683,7 +683,7 @@ async def start_climate_control( elif temp_c is not None: target_temperature = int(temp_c * 10 + 2731) target_temperature_raw = temp_c - target_temperature_unit = "celcius" + target_temperature_unit = "celsius" # Default Temperature if None is provided target_temperature = target_temperature or 2941 From 93b157a39ead939fd68443f5ea8aa3efe514b0fc Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 08:44:03 -0600 Subject: [PATCH 23/31] Update audi_services.py --- .../audiconnect/audi_services.py | 77 +++++++++---------- 1 file changed, 37 insertions(+), 40 deletions(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 393d6540..f249c8ca 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -688,47 +688,44 @@ async def start_climate_control( # Default Temperature if None is provided target_temperature = target_temperature or 2941 - # API 1 - # Construct Zone Settings for API 1 - zone_settings = [ - {"value": {"isEnabled": seat_fl, "position": "frontLeft"}}, - {"value": {"isEnabled": seat_fr, "position": "frontRight"}}, - {"value": {"isEnabled": seat_rl, "position": "rearLeft"}}, - {"value": {"isEnabled": seat_rr, "position": "rearRight"}}, - ] - - data_1 = { - "action": { - "type": "startClimatisation", - "settings": { - "targetTemperature": target_temperature, - "climatisationWithoutHVpower": True, - "heaterSource": "electric", - "climaterElementSettings": { - "isClimatisationAtUnlock": False, - "isMirrorHeatingEnabled": glass_heating, - "zoneSettings": {"zoneSetting": zone_settings}, + if API_LEVEL == 1: + zone_settings = [ + {"value": {"isEnabled": seat_fl, "position": "frontLeft"}}, + {"value": {"isEnabled": seat_fr, "position": "frontRight"}}, + {"value": {"isEnabled": seat_rl, "position": "rearLeft"}}, + {"value": {"isEnabled": seat_rr, "position": "rearRight"}}, + ] + + data = { + "action": { + "type": "startClimatisation", + "settings": { + "targetTemperature": target_temperature, + "climatisationWithoutHVpower": True, + "heaterSource": "electric", + "climaterElementSettings": { + "isClimatisationAtUnlock": False, + "isMirrorHeatingEnabled": glass_heating, + "zoneSettings": {"zoneSetting": zone_settings}, + }, }, - }, + } } - } - data_1 = json.dumps(data_1) - - # API 2 - data_2 = { - "targetTemperature": target_temperature_raw, - "targetTemperatureUnit": target_temperature_unit, - "climatisationWithoutExternalPower": True, - "climatizationAtUnlock": False, - "windowHeatingEnabled": glass_heating, - "zoneFrontLeftEnabled": seat_fl, - "zoneFrontRightEnabled": seat_fr, - "zoneRearLeftEnabled": seat_rl, - "zoneRearRightEnabled": seat_rr, - } - - data_2 = json.dumps(data_2) + elif API_LEVEL = 2: + data = { + "targetTemperature": target_temperature_raw, + "targetTemperatureUnit": target_temperature_unit, + "climatisationWithoutExternalPower": True, + "climatizationAtUnlock": False, + "windowHeatingEnabled": glass_heating, + "zoneFrontLeftEnabled": seat_fl, + "zoneFrontRightEnabled": seat_fr, + "zoneRearLeftEnabled": seat_rl, + "zoneRearRightEnabled": seat_rr, + } + + data = json.dumps(data) headers = self._get_vehicle_action_header("application/json", None) if self._country.upper() == REGION_USA: @@ -739,7 +736,7 @@ async def start_climate_control( vin=vin.upper(), ), headers=headers, - data=data_1, + data=data, ) checkUrl = "{host}/climatisation/v1/vehicles/{vin}/climater/actions/{actionid}".format( @@ -764,7 +761,7 @@ async def start_climate_control( vin=vin.upper(), ), headers=headers, - data=data_2, + data=data, ) checkUrl = "{host}/{vin}/pendingrequests".format( From d1f3843cac58531611438f8bb951caf6e35a600b Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 08:45:32 -0600 Subject: [PATCH 24/31] Update const.py --- custom_components/audiconnect/const.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/custom_components/audiconnect/const.py b/custom_components/audiconnect/const.py index 554626de..f4800725 100644 --- a/custom_components/audiconnect/const.py +++ b/custom_components/audiconnect/const.py @@ -16,6 +16,8 @@ MIN_UPDATE_INTERVAL = 15 DEFAULT_UPDATE_INTERVAL = 15 UPDATE_SLEEP = 5 +API_LEVEL = 1 +# API Options are 1 or 2. 1 being the old API (2023 or older) and 2 being the new API (2024 or newer) CONF_SPIN = "spin" CONF_REGION = "region" From abb85f7a68676cde9ea71196cfc649a570fcebeb Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 08:46:10 -0600 Subject: [PATCH 25/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 1 + 1 file changed, 1 insertion(+) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index f249c8ca..3e51b6eb 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -21,6 +21,7 @@ URL_HOST_ACTION, URL_HOST_ACTION_US, REGION_USA, + API_LEVEL, ) from .util import to_byte_array, get_attr, get_status_by_id From 133be86c6bda5a7958798c4b0c748c5a90736b56 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 08:47:10 -0600 Subject: [PATCH 26/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 3e51b6eb..295fa55f 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -713,7 +713,7 @@ async def start_climate_control( } } - elif API_LEVEL = 2: + elif API_LEVEL == 2: data = { "targetTemperature": target_temperature_raw, "targetTemperatureUnit": target_temperature_unit, From b26d5841785d31682b221a44df4ca89299da9422 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 14:47:17 +0000 Subject: [PATCH 27/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../audiconnect/audi_services.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 295fa55f..3b72d7a0 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -715,16 +715,16 @@ async def start_climate_control( elif API_LEVEL == 2: data = { - "targetTemperature": target_temperature_raw, - "targetTemperatureUnit": target_temperature_unit, - "climatisationWithoutExternalPower": True, - "climatizationAtUnlock": False, - "windowHeatingEnabled": glass_heating, - "zoneFrontLeftEnabled": seat_fl, - "zoneFrontRightEnabled": seat_fr, - "zoneRearLeftEnabled": seat_rl, - "zoneRearRightEnabled": seat_rr, - } + "targetTemperature": target_temperature_raw, + "targetTemperatureUnit": target_temperature_unit, + "climatisationWithoutExternalPower": True, + "climatizationAtUnlock": False, + "windowHeatingEnabled": glass_heating, + "zoneFrontLeftEnabled": seat_fl, + "zoneFrontRightEnabled": seat_fr, + "zoneRearLeftEnabled": seat_rl, + "zoneRearRightEnabled": seat_rr, + } data = json.dumps(data) From 904dfae033bf3144ebb10bb2614e4ce4399bc52c Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 09:14:16 -0600 Subject: [PATCH 28/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 3b72d7a0..5898f20e 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -20,6 +20,8 @@ URL_INFO_VEHICLE_US, URL_HOST_ACTION, URL_HOST_ACTION_US, + URL_HOST_INFO, + URL_HOST_INFO_US, REGION_USA, API_LEVEL, ) @@ -150,10 +152,8 @@ async def refresh_vehicle_data(self, vin: str): async def request_current_vehicle_data(self, vin: str): self._api.use_token(self.vwToken) data = await self._api.post( - "{homeRegion}/fs-car/bs/vsr/v1/{type}/{country}/vehicles/{vin}/requests".format( - homeRegion=await self._get_home_region(vin.upper()), - type=self._type, - country=self._country, + "{host}/{vin}/vehiclewakeup".format( + host=URL_HOST_INFO_US if self._country.upper() == REGION_USA else URL_HOST_INFO, vin=vin.upper(), ) ) From f37b80a92604312b61cde4ab251f1ece70b132c1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 15:14:27 +0000 Subject: [PATCH 29/31] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- custom_components/audiconnect/audi_services.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index 5898f20e..ff396f10 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -153,7 +153,9 @@ async def request_current_vehicle_data(self, vin: str): self._api.use_token(self.vwToken) data = await self._api.post( "{host}/{vin}/vehiclewakeup".format( - host=URL_HOST_INFO_US if self._country.upper() == REGION_USA else URL_HOST_INFO, + host=URL_HOST_INFO_US + if self._country.upper() == REGION_USA + else URL_HOST_INFO, vin=vin.upper(), ) ) From 355f0a9f351b147433bc42b044d9569aa67d9517 Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 09:16:26 -0600 Subject: [PATCH 30/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index ff396f10..e6f2cd4a 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -287,7 +287,7 @@ async def get_vehicle_information(self): req_rsp, rep_rsptxt = await self._api.request( "POST", URL_INFO_VEHICLE_US - if self._country.upper() == "US" + if self._country.upper() == REGION_USA else URL_INFO_VEHICLE, # Starting in 2023, US users need to point at the aoa (Audi of America) URL. json.dumps(req_data), headers=headers, From 6c0202c0b40ad954d2830771b71806de92c70f7c Mon Sep 17 00:00:00 2001 From: coreywillwhat <104224685+coreywillwhat@users.noreply.github.com> Date: Fri, 26 Jul 2024 12:13:58 -0600 Subject: [PATCH 31/31] Update audi_services.py --- custom_components/audiconnect/audi_services.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/custom_components/audiconnect/audi_services.py b/custom_components/audiconnect/audi_services.py index e6f2cd4a..9f988b17 100644 --- a/custom_components/audiconnect/audi_services.py +++ b/custom_components/audiconnect/audi_services.py @@ -412,8 +412,8 @@ async def _get_home_region_setter(self, vin: str): async def _get_security_token(self, vin: str, action: str): # Challenge headers = { - "User-Agent": "okhttp/3.7.0", - "X-App-Version": "3.14.0", + "User-Agent": HDR_USER_AGENT, + "X-App-Version": HDR_XAPP_VERSION, "X-App-Name": "myAudi", "Accept": "application/json", "Authorization": "Bearer " + self.vwToken.get("access_token"), @@ -447,9 +447,9 @@ async def _get_security_token(self, vin: str, action: str): } headers = { - "User-Agent": "okhttp/3.7.0", + "User-Agent": HDR_USER_AGENT, "Content-Type": "application/json", - "X-App-Version": "3.14.0", + "X-App-Version": HDR_XAPP_VERSION, "X-App-Name": "myAudi", "Accept": "application/json", "Authorization": "Bearer " + self.vwToken.get("access_token"), @@ -471,7 +471,7 @@ def _get_vehicle_action_header(self, content_type: str, security_token: str): "Host": "mal-3a.prd.eu.dp.vwg-connect.com" if self._country.upper() == REGION_USA else "msg.volkswagen.de", - "X-App-Version": "4.26.0", + "X-App-Version": HDR_XAPP_VERSION, "X-App-Name": "myAudi", "Authorization": "Bearer " + self.vwToken.get("access_token"), "Accept-charset": "UTF-8",