api.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. """ZYXEL API Client"""
  2. import asyncio
  3. import base64
  4. import aiohttp
  5. from .const import (API_TIMEOUT,
  6. API_SCHEMA,
  7. API_BASE_PATH,
  8. API_LOGIN_PATH,
  9. LOGIN_PAYLOAD,
  10. KEY_RESULT,
  11. VAL_SUCCES,
  12. KEY_OBJECT)
  13. class RouterApiClientError(Exception):
  14. """Exception to indicate a general API error."""
  15. class RouterApiClientCommunicationError(RouterApiClientError):
  16. """Exception to indicate a communication error."""
  17. class RouterApiClientLoginError(RouterApiClientError):
  18. """Exception to indicate an api key error."""
  19. class RouterApiClientResponseError(RouterApiClientError):
  20. """Exception to indicate a response error."""
  21. class RouterApiClient:
  22. """Router API wrapper for ZYXEL routers"""
  23. def __init__(
  24. self,
  25. ip: str,
  26. user: str,
  27. password: str,
  28. session: aiohttp.ClientSession,
  29. ) -> None:
  30. """ZYXEL API Client."""
  31. self.ip = ip
  32. self.user = user
  33. self.password = password
  34. self._session = session
  35. async def async_login(self) -> bool:
  36. """Login and obtain the session cookie"""
  37. payload = LOGIN_PAYLOAD.copy()
  38. payload['Input_Account'] = self.user
  39. payload['Input_Passwd'] = base64.b64encode(
  40. self.password.encode('utf-8')).decode('utf-8')
  41. response = await self._session.post(
  42. f'{API_SCHEMA}://{self.ip}{API_LOGIN_PATH}',
  43. json=payload)
  44. if response.ok:
  45. try:
  46. data = response.json()
  47. if 'result' in data:
  48. if data['result'] == 'ZCFG_SUCCESS':
  49. return True
  50. else:
  51. raise RouterApiClientLoginError('Login failed')
  52. else:
  53. raise RouterApiClientResponseError('Key "result" not set in response')
  54. except Exception as json_exception:
  55. raise RouterApiClientResponseError(f'Unable to decode login response') \
  56. from json_exception
  57. raise RouterApiClientCommunicationError(
  58. f'Error connecting to router. Status: {response.status}')
  59. async def async_query_api(self,
  60. oid: str) -> dict:
  61. """Query an authenticated API endpoint"""
  62. try:
  63. async with asyncio.timeout(API_TIMEOUT):
  64. response = await self._session.get(
  65. f'{API_SCHEMA}://{self.endpoint}{API_BASE_PATH}',
  66. params={'oid': oid})
  67. if response.ok:
  68. try:
  69. data: dict = await response.json()
  70. if data.get(KEY_RESULT, None) == VAL_SUCCES:
  71. return data.get(KEY_OBJECT, [{}])[0]
  72. else:
  73. raise RouterApiClientResponseError(f'Response returned error')
  74. except Exception as json_exception:
  75. raise RouterApiClientResponseError(f'Unable to decode JSON') \
  76. from json_exception
  77. else:
  78. raise RouterApiClientCommunicationError(
  79. f'Error retrieving API. Status: {response.status}')
  80. except Exception as exception:
  81. raise RouterApiClientCommunicationError('Unable to connect to router API') \
  82. from exception