1818from time import time
1919from typing import Any
2020
21- from aiohttp import ClientSession
21+ from aiohttp import ClientError , ClientSession
2222
2323from .encryption import encrypt_command_payload
2424from ..bestway .model import (
3434DEFAULT_API_BASE = "https://smarthub-eu.bestwaycorp.com" # EU endpoint
3535APP_ID = "AhFLL54HnChhrxcl9ZUJL6QNfolTIB"
3636APP_SECRET = "4ECvVs13enL5AiYSmscNjvlaisklQDz7vWPCCWXcEFjhWfTmLT"
37- TIMEOUT = 10
37+ TIMEOUT = 20
3838
3939# Regional API endpoints (from ServiceConfig.java)
4040API_ENDPOINTS = {
@@ -53,6 +53,10 @@ class AwsIotAuthException(AwsIotException):
5353 """Authentication error."""
5454
5555
56+ class AwsIotConnectionError (AwsIotException ):
57+ """Transient connection error."""
58+
59+
5660class AwsIotApi :
5761 """AWS IoT API client matching Gizwits BestwayApi interface.
5862
@@ -215,7 +219,8 @@ async def authenticate(
215219 Authentication token
216220
217221 Raises:
218- AwsIotAuthException: If authentication fails
222+ AwsIotAuthException: If authentication is rejected
223+ AwsIotConnectionError: If the authentication service cannot be reached
219224 """
220225 import random
221226 import string
@@ -271,20 +276,32 @@ async def authenticate(
271276 _LOGGER .debug ("Sign in headers: %s" , "sign" in headers )
272277 _LOGGER .debug ("All header keys: %s" , list (headers .keys ()))
273278
274- async with asyncio .timeout (TIMEOUT ):
275- async with session .post (
276- url , headers = headers , json = payload , ssl = False
277- ) as resp :
278- data = await resp .json ()
279- _LOGGER .debug ("Auth response: %s" , data )
280- _LOGGER .debug ("Response status: %s" , resp .status )
281- token = data .get ("data" , {}).get ("token" )
282-
283- if not token :
284- _LOGGER .error ("No token in response. Full response: %s" , data )
285- raise AwsIotAuthException ("No token in authentication response" )
286-
287- return str (token )
279+ try :
280+ async with asyncio .timeout (TIMEOUT ):
281+ async with session .post (
282+ url , headers = headers , json = payload , ssl = False
283+ ) as resp :
284+ data = await resp .json ()
285+ _LOGGER .debug ("Auth response: %s" , data )
286+ _LOGGER .debug ("Response status: %s" , resp .status )
287+
288+ if resp .status in (401 , 403 ):
289+ raise AwsIotAuthException ("Authentication rejected" )
290+
291+ token = data .get ("data" , {}).get ("token" )
292+ if not token :
293+ _LOGGER .error ("No token in response. Full response: %s" , data )
294+ raise AwsIotAuthException ("No token in authentication response" )
295+
296+ return str (token )
297+ except (TimeoutError , ClientError ) as err :
298+ raise AwsIotConnectionError (
299+ "Unable to reach the authentication service"
300+ ) from err
301+
302+ def update_token (self , token : str ) -> None :
303+ """Replace the token used by subsequent API requests."""
304+ self ._token = token
288305
289306 @staticmethod
290307 async def bind_qr_code (
0 commit comments