api.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. """API Placeholder.
  2. You should create your api seperately and have it hosted on PYPI. This is included here for the sole purpose
  3. of making this example code executable.
  4. """
  5. import logging
  6. import base64
  7. import aiohttp
  8. import asyncio
  9. from .const import (API_SCHEMA,
  10. API_LOGIN_PATH,
  11. API_BASE_PATH,
  12. API_TIMEOUT,
  13. LOGIN_PAYLOAD,
  14. KEY_RESULT,
  15. KEY_OBJECT,
  16. VAL_SUCCES)
  17. _LOGGER = logging.getLogger(__name__)
  18. class RouterAPI:
  19. """Class for example API."""
  20. def __init__(self,
  21. host: str,
  22. user: str,
  23. pwd: str,
  24. session: aiohttp) -> None:
  25. """Initialise."""
  26. self.host = host
  27. self.user = user
  28. self.pwd = pwd
  29. self.session: aiohttp.ClientSession = session
  30. async def async_login(self) -> bool:
  31. """Login and obtain the session cookie"""
  32. payload = LOGIN_PAYLOAD.copy()
  33. payload['Input_Account'] = self.user
  34. payload['Input_Passwd'] = base64.b64encode(
  35. self.pwd.encode('utf-8')).decode('utf-8')
  36. try:
  37. response = await self.session.post(
  38. f'{API_SCHEMA}://{self.host}{API_LOGIN_PATH}',
  39. json=payload)
  40. except Exception as e:
  41. _LOGGER.error(f'Could not connect to router. {e}')
  42. raise RouterAPIConnectionError(
  43. f'Error connecting to router. {e}')
  44. if response.status == 401:
  45. raise RouterAPIAuthError('Username or password incorrect.')
  46. if response.ok:
  47. try:
  48. data = await response.json()
  49. if 'result' in data:
  50. if data['result'] == VAL_SUCCES:
  51. return True
  52. else:
  53. raise RouterAPIAuthError('Login failed')
  54. else:
  55. raise RouterAPIInvalidResponse('Key "result" not set in response')
  56. except Exception as json_exception:
  57. raise RouterAPIInvalidResponse(f'Unable to decode login response') \
  58. from json_exception
  59. else:
  60. raise RouterAPIInvalidResponse(f'Unknown status {response.status}')
  61. async def async_query_api(self,
  62. oid: str) -> dict:
  63. """Query an authenticated API endpoint"""
  64. async with asyncio.timeout(API_TIMEOUT):
  65. try:
  66. response = await self.session.get(
  67. f'{API_SCHEMA}://{self.host}{API_BASE_PATH}',
  68. params={'oid': oid})
  69. except Exception as exception:
  70. raise RouterAPIConnectionError('Unable to connect to router API') \
  71. from exception
  72. if response.status == 401:
  73. raise RouterAPIAuthError('Unauthenticated request. Did the username or password change?')
  74. if response.ok:
  75. try:
  76. data: dict = await response.json()
  77. if data.get(KEY_RESULT, None) == VAL_SUCCES:
  78. return data.get(KEY_OBJECT, [{}])[0]
  79. else:
  80. raise RouterAPIInvalidResponse(f'Response returned error')
  81. except Exception as json_exception:
  82. raise RouterAPIInvalidResponse(f'Unable to decode JSON') \
  83. from json_exception
  84. else:
  85. raise RouterAPIConnectionError(
  86. f'Error retrieving API. Status: {response.status}')
  87. @property
  88. def controller_name(self) -> str:
  89. """Return the name of the controller."""
  90. return self.host.replace(".", "_")
  91. class RouterAPIAuthError(Exception):
  92. """Exception class for auth error."""
  93. class RouterAPIConnectionError(Exception):
  94. """Exception class for connection error."""
  95. class RouterAPIInvalidResponse(Exception):
  96. """Exception class for invalid API response."""