Skip to content

Commit

Permalink
Added new error codes
Browse files Browse the repository at this point in the history
  • Loading branch information
albertogeniola committed Oct 26, 2023
1 parent 46cda79 commit 0bf351f
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 7 deletions.
13 changes: 11 additions & 2 deletions meross_iot/http_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from meross_iot.model.http.device import HttpDeviceInfo
from meross_iot.model.http.error_codes import ErrorCodes
from meross_iot.model.http.exception import TooManyTokensException, TokenExpiredException, AuthenticatedPostException, \
HttpApiError, BadLoginException, BadDomainException
HttpApiError, BadLoginException, BadDomainException, MissingMFA, WrongMFA
from meross_iot.model.http.subdevice import HttpSubdeviceInfo
from meross_iot.utilities.misc import current_version
from meross_iot.utilities.stats import HttpStatsCounter
Expand Down Expand Up @@ -97,6 +97,7 @@ async def async_from_user_password(cls,
app_version: str = _MODULE_VERSION,
log_identifier: str = _DEFAULT_LOG_IDENTIFIER,
auto_retry_on_bad_domain: bool=True,
mfa_code: string = None,
*args, **kwargs) -> MerossHttpClient:
"""
Builds a MerossHttpClient using username/password combination.
Expand All @@ -110,6 +111,7 @@ async def async_from_user_password(cls,
:param app_version: App Version header parameter to use
:param log_identifier: Log identifier to use
:param auto_retry_on_bad_domain: when set, it enables auto-retry when BadDomain exception occurs.
:param mfa_code: multi-factor authentication code (optional)
:return: an instance of `MerossHttpClient`
"""
Expand All @@ -122,7 +124,8 @@ async def async_from_user_password(cls,
app_type=app_type,
app_version=app_version,
log_identifier=log_identifier,
auto_retry_on_bad_domain=auto_retry_on_bad_domain)
auto_retry_on_bad_domain=auto_retry_on_bad_domain,
mfa_code=mfa_code)
# Call log
await cls._async_log(creds=creds,
api_base_url=api_base_url,
Expand Down Expand Up @@ -258,6 +261,12 @@ async def async_login(cls,
else:
_LOGGER.exception(f"Login failed against {api_base_url}")
raise e
except HttpApiError as e:
if e.error_code == ErrorCodes.MFA_CODE_REQUIRED:
raise MissingMFA() from e
elif e.error_code == ErrorCodes.WRONG_MFA_CODE:
raise WrongMFA() from e
raise e

_LOGGER.info(f"Login successful against {api_base_url}")

Expand Down
6 changes: 6 additions & 0 deletions meross_iot/model/http/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ def error_code(self):
class BadLoginException(Exception):
pass

class MissingMFA(BadLoginException):
pass

class WrongMFA(BadLoginException):
pass

class BadDomainException(Exception):
def __init__(self, msg: str, api_domain: str, mqtt_domain:str):
super().__init__(msg)
Expand Down
5 changes: 3 additions & 2 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
from meross_iot.http_api import MerossHttpClient
from meross_iot.model.credentials import MerossCloudCreds

_TEST_API_BASE_URL = os.environ.get('MEROSS_API_URL', "https://iot.meross.com")
_TEST_API_BASE_URL = os.environ.get('MEROSS_API_URL', "https://iotx-us.meross.com")
_TEST_EMAIL = os.environ.get('MEROSS_EMAIL')
_TEST_EMAIL_MFA = os.environ.get('MEROSS_EMAIL_MFA')
_TEST_PASSWORD = os.environ.get('MEROSS_PASSWORD')
_TEST_CREDS = os.getenv("__MEROSS_CREDS")

Expand All @@ -24,7 +25,7 @@ async def async_get_client() -> Tuple[MerossHttpClient, bool]:
if _TEST_API_BASE_URL is not None:
api_base_url = _TEST_API_BASE_URL
else:
api_base_url = "https://iotx-eu.meross.com"
api_base_url = "https://iotx-us.meross.com"

if _TEST_CREDS is not None:
_LOGGER.info("Found cached credentials. Using them.")
Expand Down
21 changes: 18 additions & 3 deletions tests/test_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

from meross_iot.http_api import MerossHttpClient
from meross_iot.model.http.error_codes import ErrorCodes
from meross_iot.model.http.exception import BadLoginException, BadDomainException, HttpApiError
from tests import async_get_client, _TEST_EMAIL, _TEST_PASSWORD, _TEST_API_BASE_URL
from meross_iot.model.http.exception import BadLoginException, BadDomainException, HttpApiError, MissingMFA, WrongMFA
from tests import async_get_client, _TEST_EMAIL, _TEST_PASSWORD, _TEST_API_BASE_URL, _TEST_EMAIL_MFA

if os.name == 'nt':
import asyncio
Expand Down Expand Up @@ -74,6 +74,21 @@ async def test_bad_email(self):
self.assertEqual(e.error_code, ErrorCodes.CODE_WRONG_EMAIL)
raise e

@unittest_run_loop
async def test_missing_mfa(self):
with self.assertRaises(MissingMFA):
return await MerossHttpClient.async_from_user_password(api_base_url=_TEST_API_BASE_URL,
email=_TEST_EMAIL_MFA,
password=_TEST_PASSWORD)

@unittest_run_loop
async def test_wrong_mfa(self):
with self.assertRaises(WrongMFA):
return await MerossHttpClient.async_from_user_password(api_base_url=_TEST_API_BASE_URL,
email=_TEST_EMAIL_MFA,
password=_TEST_PASSWORD,
mfa_code="invalid")

@unittest_run_loop
async def test_device_listing(self):
devices = await self.meross_client.async_list_devices()
Expand All @@ -84,7 +99,7 @@ async def test_device_listing(self):
@unittest_run_loop
async def test_bad_domain(self):
with self.assertRaises(BadDomainException):
return await MerossHttpClient.async_from_user_password(api_base_url=_TEST_API_BASE_URL, email=_TEST_EMAIL, password=_TEST_PASSWORD, auto_retry_on_bad_domain=False)
return await MerossHttpClient.async_from_user_password(api_base_url="iot.meross.com", email=_TEST_EMAIL, password=_TEST_PASSWORD, auto_retry_on_bad_domain=False)


@unittest_run_loop
Expand Down

0 comments on commit 0bf351f

Please sign in to comment.