From 3518c36158d224c0248ccf32701e0113d6821eb8 Mon Sep 17 00:00:00 2001 From: cdnninja Date: Sat, 10 Jan 2026 21:04:15 -0700 Subject: [PATCH 01/11] feat: USA OTP --- hyundai_kia_connect_api/ApiImpl.py | 7 +++++-- hyundai_kia_connect_api/KiaUvoApiUSA.py | 7 ++++--- hyundai_kia_connect_api/VehicleManager.py | 11 ++++++----- hyundai_kia_connect_api/const.py | 4 ++++ 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/hyundai_kia_connect_api/ApiImpl.py b/hyundai_kia_connect_api/ApiImpl.py index 8618768f..b0006062 100644 --- a/hyundai_kia_connect_api/ApiImpl.py +++ b/hyundai_kia_connect_api/ApiImpl.py @@ -26,6 +26,7 @@ GEO_LOCATION_PROVIDERS, OPENSTREETMAP, GOOGLE, + OTP_NOTIFY_TYPE ) _LOGGER = logging.getLogger(__name__) @@ -56,6 +57,7 @@ class WindowRequestOptions: @dataclass class OTPRequest: request_id: str | None + otp_key: str | None has_email: bool | None has_sms: bool | None email: str | None @@ -100,11 +102,12 @@ def login( """Login into cloud endpoints and return Token or OTP Details if OTP is triggered""" pass - def send_otp(self, otp_request: OTPRequest, otp_destination: str, otp_via: str) -> None: + def send_otp(self, otp_request: OTPRequest, notify_type: OTP_NOTIFY_TYPE) -> None: """Sends OTP to the user via selected destination and via""" pass - def verify_otp(self, otp_request: OTPRequest, otp_code: str) -> Token: + def verify_otp_and_complete_login(self, username: str, password: str, + pin: str | None = None, otp_request: OTPRequest = None, otp_code: str = None) -> Token: """Confirms OTP code sent to the user""" pass diff --git a/hyundai_kia_connect_api/KiaUvoApiUSA.py b/hyundai_kia_connect_api/KiaUvoApiUSA.py index 2febdf30..7632d902 100644 --- a/hyundai_kia_connect_api/KiaUvoApiUSA.py +++ b/hyundai_kia_connect_api/KiaUvoApiUSA.py @@ -15,7 +15,7 @@ from requests.adapters import HTTPAdapter from urllib3.util.ssl_ import create_urllib3_context -from .ApiImpl import ApiImpl, ClimateRequestOptions +from .ApiImpl import ApiImpl, ClimateRequestOptions, OTPRequest from .Token import Token from .Vehicle import Vehicle from .const import ( @@ -25,6 +25,7 @@ ORDER_STATUS, TEMPERATURE_UNITS, VEHICLE_LOCK_ACTION, + OTP_NOTIFY_TYPE ) from .utils import get_child_value, parse_datetime from .exceptions import AuthenticationError @@ -312,9 +313,9 @@ def start_login( f"{DOMAIN} - No session id returned in start_login. Response: {response.text}" ) - def send_otp(self, otp_key: str, notify_type: str, xid: str) -> dict: + def send_otp(self, otp_request: OTPRequest, notify_type: OTP_NOTIFY_TYPE, xid: str) -> dict: """Public helper to send OTP to the selected destination.""" - return self._send_otp(otp_key, notify_type, xid) + return self._send_otp(otp_request.otp_key, notify_type, otp_request.request_id) def verify_otp_and_complete_login( self, diff --git a/hyundai_kia_connect_api/VehicleManager.py b/hyundai_kia_connect_api/VehicleManager.py index 92620115..15f7a929 100644 --- a/hyundai_kia_connect_api/VehicleManager.py +++ b/hyundai_kia_connect_api/VehicleManager.py @@ -32,6 +32,7 @@ REGIONS, VALET_MODE_ACTION, VEHICLE_LOCK_ACTION, + OTP_NOTIFY_TYPE ) from .exceptions import APIError, AuthenticationOTPRequired from .HyundaiBlueLinkApiBR import HyundaiBlueLinkApiBR @@ -106,11 +107,11 @@ def login(self) -> bool | OTPRequest: self.otp_request = result return result - def send_otp(self, otp_destination: str, otp_via: str) -> None: - self.api.send_otp(self.otp_request, otp_destination, otp_via) + def send_otp(self, notify_type: OTP_NOTIFY_TYPE) -> None: + self.api.send_otp(self.otp_request, notify_type) - def verify_otp(self, otp_code: str) -> None: - self.token = self.api.verify_otp(self.otp_request, otp_code) + def verify_otp_and_complete_login(self, otp_code: str) -> None: + self.token = self.api.verify_otp_and_complete_login(self.username, self.password, self.pin, self.otp_request, otp_code) self.initialize_vehicles() def initialize_vehicles(self): @@ -207,7 +208,7 @@ def check_and_refresh_token(self) -> bool: if isinstance(result, Token): self.token: Token = result self.initialize_vehicles() - if isinstance(result, OTPOptions): + if isinstance(result, OTPRequest): raise AuthenticationOTPRequired("OTP required to refresh token") self.vehicles = self.api.refresh_vehicles(self.token, self.vehicles) return True diff --git a/hyundai_kia_connect_api/const.py b/hyundai_kia_connect_api/const.py index 2ff2b72a..4a7171ae 100644 --- a/hyundai_kia_connect_api/const.py +++ b/hyundai_kia_connect_api/const.py @@ -116,3 +116,7 @@ class WINDOW_STATE(IntEnum): class VALET_MODE_ACTION(Enum): ACTIVATE = "activate" DEACTIVATE = "deactivate" + +class OTP_NOTIFY_TYPE(Enum): + EMAIL = "EMAIL" + PHONE = "PHONE" \ No newline at end of file From 72c27682f6ddff7ac81b8676f50f3c690621b7a5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 11 Jan 2026 04:04:37 +0000 Subject: [PATCH 02/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- README.rst | 2 +- hyundai_kia_connect_api/ApiImpl.py | 12 +++++++++--- hyundai_kia_connect_api/KiaUvoApiUSA.py | 6 ++++-- hyundai_kia_connect_api/VehicleManager.py | 8 +++++--- hyundai_kia_connect_api/const.py | 3 ++- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index 74f140be..949038fa 100644 --- a/README.rst +++ b/README.rst @@ -49,7 +49,7 @@ Once this is done you can now make the following calls against the vehicle manag login(self) -#OTP Details are Alpha - not used yet. +#OTP Details are Alpha - not used yet. #Sent OTP send_otp(self, method) diff --git a/hyundai_kia_connect_api/ApiImpl.py b/hyundai_kia_connect_api/ApiImpl.py index b0006062..8fbca162 100644 --- a/hyundai_kia_connect_api/ApiImpl.py +++ b/hyundai_kia_connect_api/ApiImpl.py @@ -26,7 +26,7 @@ GEO_LOCATION_PROVIDERS, OPENSTREETMAP, GOOGLE, - OTP_NOTIFY_TYPE + OTP_NOTIFY_TYPE, ) _LOGGER = logging.getLogger(__name__) @@ -106,8 +106,14 @@ def send_otp(self, otp_request: OTPRequest, notify_type: OTP_NOTIFY_TYPE) -> Non """Sends OTP to the user via selected destination and via""" pass - def verify_otp_and_complete_login(self, username: str, password: str, - pin: str | None = None, otp_request: OTPRequest = None, otp_code: str = None) -> Token: + def verify_otp_and_complete_login( + self, + username: str, + password: str, + pin: str | None = None, + otp_request: OTPRequest = None, + otp_code: str = None, + ) -> Token: """Confirms OTP code sent to the user""" pass diff --git a/hyundai_kia_connect_api/KiaUvoApiUSA.py b/hyundai_kia_connect_api/KiaUvoApiUSA.py index 7632d902..c1db7c18 100644 --- a/hyundai_kia_connect_api/KiaUvoApiUSA.py +++ b/hyundai_kia_connect_api/KiaUvoApiUSA.py @@ -25,7 +25,7 @@ ORDER_STATUS, TEMPERATURE_UNITS, VEHICLE_LOCK_ACTION, - OTP_NOTIFY_TYPE + OTP_NOTIFY_TYPE, ) from .utils import get_child_value, parse_datetime from .exceptions import AuthenticationError @@ -313,7 +313,9 @@ def start_login( f"{DOMAIN} - No session id returned in start_login. Response: {response.text}" ) - def send_otp(self, otp_request: OTPRequest, notify_type: OTP_NOTIFY_TYPE, xid: str) -> dict: + def send_otp( + self, otp_request: OTPRequest, notify_type: OTP_NOTIFY_TYPE, xid: str + ) -> dict: """Public helper to send OTP to the selected destination.""" return self._send_otp(otp_request.otp_key, notify_type, otp_request.request_id) diff --git a/hyundai_kia_connect_api/VehicleManager.py b/hyundai_kia_connect_api/VehicleManager.py index 15f7a929..7dcad8d4 100644 --- a/hyundai_kia_connect_api/VehicleManager.py +++ b/hyundai_kia_connect_api/VehicleManager.py @@ -32,7 +32,7 @@ REGIONS, VALET_MODE_ACTION, VEHICLE_LOCK_ACTION, - OTP_NOTIFY_TYPE + OTP_NOTIFY_TYPE, ) from .exceptions import APIError, AuthenticationOTPRequired from .HyundaiBlueLinkApiBR import HyundaiBlueLinkApiBR @@ -109,9 +109,11 @@ def login(self) -> bool | OTPRequest: def send_otp(self, notify_type: OTP_NOTIFY_TYPE) -> None: self.api.send_otp(self.otp_request, notify_type) - + def verify_otp_and_complete_login(self, otp_code: str) -> None: - self.token = self.api.verify_otp_and_complete_login(self.username, self.password, self.pin, self.otp_request, otp_code) + self.token = self.api.verify_otp_and_complete_login( + self.username, self.password, self.pin, self.otp_request, otp_code + ) self.initialize_vehicles() def initialize_vehicles(self): diff --git a/hyundai_kia_connect_api/const.py b/hyundai_kia_connect_api/const.py index 4a7171ae..aa767345 100644 --- a/hyundai_kia_connect_api/const.py +++ b/hyundai_kia_connect_api/const.py @@ -117,6 +117,7 @@ class VALET_MODE_ACTION(Enum): ACTIVATE = "activate" DEACTIVATE = "deactivate" + class OTP_NOTIFY_TYPE(Enum): EMAIL = "EMAIL" - PHONE = "PHONE" \ No newline at end of file + PHONE = "PHONE" From 671010e94cc8c51ea1bae1ee6d15bdf6a4a01786 Mon Sep 17 00:00:00 2001 From: cdnninja Date: Sat, 10 Jan 2026 21:20:39 -0700 Subject: [PATCH 03/11] OTPMethods --- hyundai_kia_connect_api/ApiImpl.py | 4 +- hyundai_kia_connect_api/KiaUvoApiUSA.py | 158 ++-------------------- hyundai_kia_connect_api/VehicleManager.py | 2 +- 3 files changed, 15 insertions(+), 149 deletions(-) diff --git a/hyundai_kia_connect_api/ApiImpl.py b/hyundai_kia_connect_api/ApiImpl.py index 8fbca162..cceb9742 100644 --- a/hyundai_kia_connect_api/ApiImpl.py +++ b/hyundai_kia_connect_api/ApiImpl.py @@ -110,9 +110,9 @@ def verify_otp_and_complete_login( self, username: str, password: str, + otp_code: str, + otp_request: OTPRequest, pin: str | None = None, - otp_request: OTPRequest = None, - otp_code: str = None, ) -> Token: """Confirms OTP code sent to the user""" pass diff --git a/hyundai_kia_connect_api/KiaUvoApiUSA.py b/hyundai_kia_connect_api/KiaUvoApiUSA.py index c1db7c18..58994115 100644 --- a/hyundai_kia_connect_api/KiaUvoApiUSA.py +++ b/hyundai_kia_connect_api/KiaUvoApiUSA.py @@ -241,80 +241,8 @@ def _complete_login_with_otp( ) return final_sid - def start_login( - self, - username: str, - password: str, - token: Token | None = None, - ) -> tuple[Token | None, dict | None]: - """Start login and return either a Token or an OTP context. - - Parameters - ---------- - username : str - User email address - password : str - User password - token : Token | None - Existing token with stored rmtoken for reuse - - Returns - ------- - tuple[Token | None, dict | None] - (Token, None) if login succeeded without OTP, otherwise (None, ctx) - where ctx contains 'otpKey', 'xid', 'email', 'phone', 'hasEmail', 'hasPhone'. - """ - url = self.API_URL + "prof/authUser" - data = { - "deviceKey": self.device_id, - "deviceType": 2, - "userCredential": {"userId": username, "password": password}, - } - headers = self.api_headers() - if token and getattr(token, "device_id", None): - self.device_id = token.device_id - - if token and token.refresh_token: - _LOGGER.debug(f"{DOMAIN} - Attempting start_login with stored rmtoken") - headers["rmtoken"] = token.refresh_token - response = self.session.post(url, json=data, headers=headers) - _LOGGER.debug(f"{DOMAIN} - Start Sign In Response {response.text}") - response_json = response.json() - session_id = response.headers.get("sid") - if session_id: - _LOGGER.debug(f"got session id {session_id}") - valid_until = dt.datetime.now(dt.timezone.utc) + LOGIN_TOKEN_LIFETIME - existing_rmtoken = token.refresh_token if token else None - return ( - Token( - username=username, - password=password, - access_token=session_id, - refresh_token=existing_rmtoken, - valid_until=valid_until, - device_id=self.device_id, - ), - None, - ) - if "payload" in response_json and "otpKey" in response_json["payload"]: - payload = response_json["payload"] - xid = response.headers.get("xid", "") - ctx = { - "otpKey": payload["otpKey"], - "xid": xid, - "email": payload.get("email"), - "phone": payload.get("phone"), - "hasEmail": bool(payload.get("hasEmail")), - "hasPhone": bool(payload.get("hasPhone")), - "rmTokenExpired": bool(payload.get("rmTokenExpired")), - } - return None, ctx - raise Exception( - f"{DOMAIN} - No session id returned in start_login. Response: {response.text}" - ) - def send_otp( - self, otp_request: OTPRequest, notify_type: OTP_NOTIFY_TYPE, xid: str + self, otp_request: OTPRequest, notify_type: OTP_NOTIFY_TYPE ) -> dict: """Public helper to send OTP to the selected destination.""" return self._send_otp(otp_request.otp_key, notify_type, otp_request.request_id) @@ -323,12 +251,12 @@ def verify_otp_and_complete_login( self, username: str, password: str, - otp_key: str, - xid: str, otp_code: str, + otp_request: OTPRequest, + pin: str | None, ) -> Token: """Verify OTP and complete the login producing a Token.""" - sid, rmtoken = self._verify_otp(otp_key, otp_code, xid) + sid, rmtoken = self._verify_otp(otp_request.otp_key, otp_code, otp_request.request_id) final_sid = self._complete_login_with_otp(username, password, sid, rmtoken) _LOGGER.debug(f"got final session id {final_sid}") _LOGGER.info(f"{DOMAIN} - Storing rmtoken for future logins") @@ -340,6 +268,7 @@ def verify_otp_and_complete_login( refresh_token=rmtoken, valid_until=valid_until, device_id=self.device_id, + pin=pin ) def login( @@ -407,76 +336,13 @@ def login( payload = response_json["payload"] if payload.get("rmTokenExpired"): _LOGGER.info(f"{DOMAIN} - Stored rmtoken has expired, need new OTP") - otp_key = payload["otpKey"] - xid = response.headers.get("xid", "") - _LOGGER.info(f"{DOMAIN} - OTP required for login") - _LOGGER.info(f"{DOMAIN} - Email: {payload.get('email', 'N/A')}") - _LOGGER.info(f"{DOMAIN} - Phone: {payload.get('phone', 'N/A')}") - notify_type = "EMAIL" - handler = otp_handler or getattr(self, "_otp_handler", None) - if handler: - try: - ctx_choice = { - "stage": "choose_destination", - "hasEmail": bool(payload.get("hasEmail")), - "hasPhone": bool(payload.get("hasPhone")), - "email": payload.get("email"), - "phone": payload.get("phone"), - } - res = handler(ctx_choice) or {} - nt = str(res.get("notify_type", notify_type)).upper() - if nt in ("EMAIL", "PHONE"): - notify_type = nt - except Exception: - _LOGGER.debug( - f"{DOMAIN} - otp_handler choose_destination failed; using default" - ) - else: - if payload.get("hasEmail") and payload.get("hasPhone"): - print("\nOTP Authentication Required") - print(f"Email: {payload.get('email', 'N/A')}") - print(f"Phone: {payload.get('phone', 'N/A')}") - choice = ( - input("Send OTP to (E)mail or (P)hone? [E/P]: ").strip().upper() - ) - if choice == "P": - notify_type = "PHONE" - elif payload.get("hasPhone"): - notify_type = "PHONE" - self._send_otp(otp_key, notify_type, xid) - if not handler: - print(f"\nOTP sent to {notify_type.lower()}") - otp_code = None - if handler: - try: - ctx_code = { - "stage": "input_code", - "notify_type": notify_type, - "otpKey": otp_key, - "xid": xid, - } - res2 = handler(ctx_code) or {} - otp_code = str(res2.get("otp_code", "")).strip() - except Exception: - _LOGGER.debug(f"{DOMAIN} - otp_handler input_code failed") - if not otp_code: - if handler is None: - otp_code = input("Enter OTP code: ").strip() - else: - raise AuthenticationError(f"{DOMAIN} - OTP code required") - sid, rmtoken = self._verify_otp(otp_key, otp_code, xid) - final_sid = self._complete_login_with_otp(username, password, sid, rmtoken) - _LOGGER.debug(f"got final session id {final_sid}") - _LOGGER.info(f"{DOMAIN} - Storing rmtoken for future logins") - valid_until = dt.datetime.now(dt.timezone.utc) + LOGIN_TOKEN_LIFETIME - return Token( - username=username, - password=password, - access_token=final_sid, - refresh_token=rmtoken, - valid_until=valid_until, - device_id=self.device_id, - pin=pin, + return OTPRequest( + otp_key=payload["otpKey"], + request_id=response.headers.get("xid", ""), + email=payload.get("email"), + phone=payload.get("phone"), + has_email=bool(payload.get("hasEmail")), + has_phone=bool(payload.get("hasPhone")), ) raise Exception( f"{DOMAIN} - No session id returned in login. Response: {response.text} headers {response.headers} cookies {response.cookies}" diff --git a/hyundai_kia_connect_api/VehicleManager.py b/hyundai_kia_connect_api/VehicleManager.py index 7dcad8d4..793dba7d 100644 --- a/hyundai_kia_connect_api/VehicleManager.py +++ b/hyundai_kia_connect_api/VehicleManager.py @@ -112,7 +112,7 @@ def send_otp(self, notify_type: OTP_NOTIFY_TYPE) -> None: def verify_otp_and_complete_login(self, otp_code: str) -> None: self.token = self.api.verify_otp_and_complete_login( - self.username, self.password, self.pin, self.otp_request, otp_code + username=self.username, password=self.password, otp_code=otp_code, otp_request=self.otp_request, pin=self.pin, ) self.initialize_vehicles() From 999e90c9d140e185ec8c6731143a655225b4d31c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 11 Jan 2026 04:20:51 +0000 Subject: [PATCH 04/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- hyundai_kia_connect_api/KiaUvoApiUSA.py | 13 ++++++------- hyundai_kia_connect_api/VehicleManager.py | 6 +++++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/hyundai_kia_connect_api/KiaUvoApiUSA.py b/hyundai_kia_connect_api/KiaUvoApiUSA.py index 58994115..8b8e2085 100644 --- a/hyundai_kia_connect_api/KiaUvoApiUSA.py +++ b/hyundai_kia_connect_api/KiaUvoApiUSA.py @@ -28,7 +28,6 @@ OTP_NOTIFY_TYPE, ) from .utils import get_child_value, parse_datetime -from .exceptions import AuthenticationError _LOGGER = logging.getLogger(__name__) @@ -241,9 +240,7 @@ def _complete_login_with_otp( ) return final_sid - def send_otp( - self, otp_request: OTPRequest, notify_type: OTP_NOTIFY_TYPE - ) -> dict: + def send_otp(self, otp_request: OTPRequest, notify_type: OTP_NOTIFY_TYPE) -> dict: """Public helper to send OTP to the selected destination.""" return self._send_otp(otp_request.otp_key, notify_type, otp_request.request_id) @@ -256,7 +253,9 @@ def verify_otp_and_complete_login( pin: str | None, ) -> Token: """Verify OTP and complete the login producing a Token.""" - sid, rmtoken = self._verify_otp(otp_request.otp_key, otp_code, otp_request.request_id) + sid, rmtoken = self._verify_otp( + otp_request.otp_key, otp_code, otp_request.request_id + ) final_sid = self._complete_login_with_otp(username, password, sid, rmtoken) _LOGGER.debug(f"got final session id {final_sid}") _LOGGER.info(f"{DOMAIN} - Storing rmtoken for future logins") @@ -268,7 +267,7 @@ def verify_otp_and_complete_login( refresh_token=rmtoken, valid_until=valid_until, device_id=self.device_id, - pin=pin + pin=pin, ) def login( @@ -342,7 +341,7 @@ def login( email=payload.get("email"), phone=payload.get("phone"), has_email=bool(payload.get("hasEmail")), - has_phone=bool(payload.get("hasPhone")), + has_phone=bool(payload.get("hasPhone")), ) raise Exception( f"{DOMAIN} - No session id returned in login. Response: {response.text} headers {response.headers} cookies {response.cookies}" diff --git a/hyundai_kia_connect_api/VehicleManager.py b/hyundai_kia_connect_api/VehicleManager.py index 793dba7d..19928696 100644 --- a/hyundai_kia_connect_api/VehicleManager.py +++ b/hyundai_kia_connect_api/VehicleManager.py @@ -112,7 +112,11 @@ def send_otp(self, notify_type: OTP_NOTIFY_TYPE) -> None: def verify_otp_and_complete_login(self, otp_code: str) -> None: self.token = self.api.verify_otp_and_complete_login( - username=self.username, password=self.password, otp_code=otp_code, otp_request=self.otp_request, pin=self.pin, + username=self.username, + password=self.password, + otp_code=otp_code, + otp_request=self.otp_request, + pin=self.pin, ) self.initialize_vehicles() From f9eb3041373033f2383dd1bcb4d476da84e31d01 Mon Sep 17 00:00:00 2001 From: cdnninja Date: Sun, 11 Jan 2026 14:35:41 -0700 Subject: [PATCH 05/11] Switch to SMS --- hyundai_kia_connect_api/KiaUvoApiUSA.py | 2 +- hyundai_kia_connect_api/const.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hyundai_kia_connect_api/KiaUvoApiUSA.py b/hyundai_kia_connect_api/KiaUvoApiUSA.py index 8b8e2085..66ae07a4 100644 --- a/hyundai_kia_connect_api/KiaUvoApiUSA.py +++ b/hyundai_kia_connect_api/KiaUvoApiUSA.py @@ -290,7 +290,7 @@ def login( Existing token with stored rmtoken for reuse otp_handler : Callable[[dict], dict], optional Non-interactive OTP handler. Called twice: - - stage='choose_destination' -> return {'notify_type': 'EMAIL'|'PHONE'} + - stage='choose_destination' -> return {'notify_type': 'EMAIL'|'SMS'} - stage='input_code' -> return {'otp_code': ''} pin : str, optional diff --git a/hyundai_kia_connect_api/const.py b/hyundai_kia_connect_api/const.py index aa767345..535364c3 100644 --- a/hyundai_kia_connect_api/const.py +++ b/hyundai_kia_connect_api/const.py @@ -120,4 +120,4 @@ class VALET_MODE_ACTION(Enum): class OTP_NOTIFY_TYPE(Enum): EMAIL = "EMAIL" - PHONE = "PHONE" + SMS = "SMS" From 8291f7bc1dff085a2a7c5d308223377223b52d64 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 11 Jan 2026 21:38:03 +0000 Subject: [PATCH 06/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 86cb47f6..7b2d2998 100644 --- a/README.rst +++ b/README.rst @@ -50,7 +50,7 @@ Once this is done you can now make the following calls against the vehicle manag login(self) - #OTP Details are Alpha - not used yet. + #OTP Details are Alpha - not used yet. #Sent OTP send_otp(self, method) From 4b430fc8327dbcb1d97f516bf886be24d20db999 Mon Sep 17 00:00:00 2001 From: cdnninja Date: Sun, 11 Jan 2026 14:59:37 -0700 Subject: [PATCH 07/11] correct sms --- hyundai_kia_connect_api/KiaUvoApiUSA.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyundai_kia_connect_api/KiaUvoApiUSA.py b/hyundai_kia_connect_api/KiaUvoApiUSA.py index 66ae07a4..57938370 100644 --- a/hyundai_kia_connect_api/KiaUvoApiUSA.py +++ b/hyundai_kia_connect_api/KiaUvoApiUSA.py @@ -341,7 +341,7 @@ def login( email=payload.get("email"), phone=payload.get("phone"), has_email=bool(payload.get("hasEmail")), - has_phone=bool(payload.get("hasPhone")), + has_sms=bool(payload.get("hasPhone")), ) raise Exception( f"{DOMAIN} - No session id returned in login. Response: {response.text} headers {response.headers} cookies {response.cookies}" From dcefdcf034446592b09c344aa449f34bee5d4bd7 Mon Sep 17 00:00:00 2001 From: cdnninja Date: Sun, 11 Jan 2026 20:07:10 -0700 Subject: [PATCH 08/11] Refresh Token --- hyundai_kia_connect_api/KiaUvoApiUSA.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hyundai_kia_connect_api/KiaUvoApiUSA.py b/hyundai_kia_connect_api/KiaUvoApiUSA.py index 57938370..7908cfd5 100644 --- a/hyundai_kia_connect_api/KiaUvoApiUSA.py +++ b/hyundai_kia_connect_api/KiaUvoApiUSA.py @@ -275,7 +275,6 @@ def login( username: str, password: str, token: Token = None, - otp_handler: ty.Callable[[dict], dict] | None = None, pin: str | None = None, ) -> Token: """Login into cloud endpoints and return Token @@ -308,19 +307,17 @@ def login( } if token and getattr(token, "device_id", None): self.device_id = token.device_id - if otp_handler is not None: - self._otp_handler = otp_handler headers = self.api_headers() if token and token.refresh_token: data["deviceKey"] = self.device_id - _LOGGER.debug(f"{DOMAIN} - Attempting login with stored rmtoken") + _LOGGER.debug(f"{DOMAIN} - Attempting login with stored Refresh Token") headers["rmtoken"] = token.refresh_token response = self.session.post(url, json=data, headers=headers) _LOGGER.debug(f"{DOMAIN} - Sign In Response {response.text}") response_json = response.json() session_id = response.headers.get("sid") if session_id: - _LOGGER.debug(f"got session id {session_id}") + _LOGGER.debug(f"Got session id {session_id}") valid_until = dt.datetime.now(dt.timezone.utc) + LOGIN_TOKEN_LIFETIME existing_rmtoken = token.refresh_token if token else None return Token( @@ -346,6 +343,10 @@ def login( raise Exception( f"{DOMAIN} - No session id returned in login. Response: {response.text} headers {response.headers} cookies {response.cookies}" ) + + def refresh_access_token(self, token: Token) -> Token | OTPRequest: + """Refresh the token using the refresh token""" + self.login(token.username, token.password, token) def get_vehicles(self, token: Token) -> list[Vehicle]: """Return all Vehicle instances for a given Token""" From 843348291947ac3a4e82b405eb825dcae1cd2ce9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 03:07:28 +0000 Subject: [PATCH 09/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- hyundai_kia_connect_api/KiaUvoApiUSA.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyundai_kia_connect_api/KiaUvoApiUSA.py b/hyundai_kia_connect_api/KiaUvoApiUSA.py index 7908cfd5..e5635778 100644 --- a/hyundai_kia_connect_api/KiaUvoApiUSA.py +++ b/hyundai_kia_connect_api/KiaUvoApiUSA.py @@ -343,7 +343,7 @@ def login( raise Exception( f"{DOMAIN} - No session id returned in login. Response: {response.text} headers {response.headers} cookies {response.cookies}" ) - + def refresh_access_token(self, token: Token) -> Token | OTPRequest: """Refresh the token using the refresh token""" self.login(token.username, token.password, token) From 57ffc35f331eb365fe03d5dbbd2891c5a3a00d22 Mon Sep 17 00:00:00 2001 From: cdnninja Date: Sun, 11 Jan 2026 20:15:40 -0700 Subject: [PATCH 10/11] sms --- hyundai_kia_connect_api/KiaUvoApiUSA.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyundai_kia_connect_api/KiaUvoApiUSA.py b/hyundai_kia_connect_api/KiaUvoApiUSA.py index 7908cfd5..b3849299 100644 --- a/hyundai_kia_connect_api/KiaUvoApiUSA.py +++ b/hyundai_kia_connect_api/KiaUvoApiUSA.py @@ -336,7 +336,7 @@ def login( otp_key=payload["otpKey"], request_id=response.headers.get("xid", ""), email=payload.get("email"), - phone=payload.get("phone"), + sms=payload.get("phone"), has_email=bool(payload.get("hasEmail")), has_sms=bool(payload.get("hasPhone")), ) From 4cb18c55bb98a5ed3283367bb0af1383e7d2c74d Mon Sep 17 00:00:00 2001 From: cdnninja Date: Sun, 11 Jan 2026 20:46:14 -0700 Subject: [PATCH 11/11] fix return --- hyundai_kia_connect_api/KiaUvoApiUSA.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hyundai_kia_connect_api/KiaUvoApiUSA.py b/hyundai_kia_connect_api/KiaUvoApiUSA.py index 283b1e8d..da9594fd 100644 --- a/hyundai_kia_connect_api/KiaUvoApiUSA.py +++ b/hyundai_kia_connect_api/KiaUvoApiUSA.py @@ -346,7 +346,7 @@ def login( def refresh_access_token(self, token: Token) -> Token | OTPRequest: """Refresh the token using the refresh token""" - self.login(token.username, token.password, token) + return self.login(token.username, token.password, token) def get_vehicles(self, token: Token) -> list[Vehicle]: """Return all Vehicle instances for a given Token"""