Try default tapo credentials for klap and aes (#685)

* Try default tapo credentials for klap and aes

* Add tests
This commit is contained in:
Steven B
2024-01-23 14:44:32 +00:00
committed by GitHub
parent c8ac3a29c7
commit 718983c401
5 changed files with 134 additions and 42 deletions

View File

@@ -30,7 +30,7 @@ from .exceptions import (
from .httpclient import HttpClient
from .json import dumps as json_dumps
from .json import loads as json_loads
from .protocol import BaseTransport
from .protocol import DEFAULT_CREDENTIALS, BaseTransport, get_default_credentials
_LOGGER = logging.getLogger(__name__)
@@ -69,12 +69,12 @@ class AesTransport(BaseTransport):
) and not self._credentials_hash:
self._credentials = Credentials()
if self._credentials:
self._login_params = self._get_login_params()
self._login_params = self._get_login_params(self._credentials)
else:
self._login_params = json_loads(
base64.b64decode(self._credentials_hash.encode()).decode() # type: ignore[union-attr]
)
self._default_credentials: Optional[Credentials] = None
self._http_client: HttpClient = HttpClient(config)
self._handshake_done = False
@@ -98,26 +98,27 @@ class AesTransport(BaseTransport):
"""The hashed credentials used by the transport."""
return base64.b64encode(json_dumps(self._login_params).encode()).decode()
def _get_login_params(self):
def _get_login_params(self, credentials):
"""Get the login parameters based on the login_version."""
un, pw = self.hash_credentials(self._login_version == 2)
un, pw = self.hash_credentials(self._login_version == 2, credentials)
password_field_name = "password2" if self._login_version == 2 else "password"
return {password_field_name: pw, "username": un}
def hash_credentials(self, login_v2):
@staticmethod
def hash_credentials(login_v2, credentials):
"""Hash the credentials."""
if login_v2:
un = base64.b64encode(
_sha1(self._credentials.username.encode()).encode()
_sha1(credentials.username.encode()).encode()
).decode()
pw = base64.b64encode(
_sha1(self._credentials.password.encode()).encode()
_sha1(credentials.password.encode()).encode()
).decode()
else:
un = base64.b64encode(
_sha1(self._credentials.username.encode()).encode()
_sha1(credentials.username.encode()).encode()
).decode()
pw = base64.b64encode(self._credentials.password.encode()).decode()
pw = base64.b64encode(credentials.password.encode()).decode()
return un, pw
def _handle_response_error_code(self, resp_dict: dict, msg: str):
@@ -173,10 +174,28 @@ class AesTransport(BaseTransport):
async def perform_login(self):
"""Login to the device."""
try:
await self.try_login(self._login_params)
except AuthenticationException as ex:
if ex.error_code != SmartErrorCode.LOGIN_ERROR:
raise ex
if self._default_credentials is None:
self._default_credentials = get_default_credentials(
DEFAULT_CREDENTIALS["TAPO"]
)
await self.perform_handshake()
await self.try_login(self._get_login_params(self._default_credentials))
_LOGGER.debug(
"%s: logged in with default credentials",
self._host,
)
async def try_login(self, login_params):
"""Try to login with supplied login_params."""
self._login_token = None
login_request = {
"method": "login_device",
"params": self._login_params,
"params": login_params,
"request_time_milis": round(time.time() * 1000),
}
request = json_dumps(login_request)
@@ -260,7 +279,13 @@ class AesTransport(BaseTransport):
if not self._handshake_done or self._handshake_session_expired():
await self.perform_handshake()
if not self._login_token:
await self.perform_login()
try:
await self.perform_login()
# After a login failure handshake needs to
# be redone or a 9999 error is received.
except AuthenticationException as ex:
self._handshake_done = False
raise ex
return await self.send_secure_passthrough(request)