api.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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. from copy import deepcopy
  6. import logging
  7. from typing import Any
  8. import requests
  9. _LOGGER = logging.getLogger(__name__)
  10. MOCK_DATA = [
  11. {
  12. "device_id": 1,
  13. "device_type": "SOCKET",
  14. "device_name": "Lounge Socket 1",
  15. "device_uid": "0123-4567-8910-1112",
  16. "software_version": "2.13",
  17. "state": "ON",
  18. "voltage": 239,
  19. "current": 1.2,
  20. "energy_delivered": 3247,
  21. "last_reboot": "2024-01-01T10:04:23Z",
  22. },
  23. {
  24. "device_id": 2,
  25. "device_type": "SOCKET",
  26. "device_name": "Lounge Socket 2",
  27. "device_uid": "0123-4567-8910-3723",
  28. "software_version": "2.13",
  29. "state": "ON",
  30. "voltage": 237,
  31. "current": 0.1,
  32. "energy_delivered": 634,
  33. "last_reboot": "2024-03-12T17:33:01Z",
  34. },
  35. {
  36. "device_id": 3,
  37. "device_type": "ON_OFF_LIGHT",
  38. "device_name": "Lounge Light",
  39. "device_uid": "0123-4567-8910-4621",
  40. "software_version": "1.30",
  41. "state": "ON",
  42. "voltage": 237,
  43. "current": 0.2,
  44. "off_timer": "00:00",
  45. "last_reboot": "2023-11-11T09:03:01Z",
  46. },
  47. {
  48. "device_id": 4,
  49. "device_type": "DIMMABLE_LIGHT",
  50. "device_name": "Kitchen Light",
  51. "device_uid": "0123-4967-8940-4691",
  52. "software_version": "1.35",
  53. "state": "ON",
  54. "brightness": 85,
  55. "voltage": 237,
  56. "current": 1.275,
  57. "off_timer": "00:00",
  58. "last_reboot": "2023-11-11T09:03:01Z",
  59. },
  60. {
  61. "device_id": 5,
  62. "device_type": "TEMP_SENSOR",
  63. "device_name": "Kitchen Temp Sensor",
  64. "device_uid": "0123-4567-8910-9254",
  65. "software_version": "3.00",
  66. "temperature": 18.3,
  67. "last_reboot": "2024-05-02T19:46:00Z",
  68. },
  69. {
  70. "device_id": 6,
  71. "device_type": "TEMP_SENSOR",
  72. "device_name": "Lounge Temp Sensor",
  73. "device_uid": "0123-4567-8910-9255",
  74. "software_version": "1.30",
  75. "temperature": 19.2,
  76. "last_reboot": "2024-03-12T17:33:01Z",
  77. },
  78. {
  79. "device_id": 7,
  80. "device_type": "CONTACT_SENSOR",
  81. "device_name": "Kitchen Door Sensor",
  82. "device_uid": "0123-4567-8911-6295",
  83. "software_version": "1.41",
  84. "state": "OPEN",
  85. },
  86. {
  87. "device_id": 8,
  88. "device_type": "CONTACT_SENSOR",
  89. "device_name": "Lounge Door Sensor",
  90. "device_uid": "0123-4567-8911-1753",
  91. "software_version": "1.41",
  92. "state": "CLOSED",
  93. },
  94. {
  95. "device_id": 9,
  96. "device_type": "FAN",
  97. "device_name": "Lounge Fan",
  98. "device_uid": "0123-4599-1541-1793",
  99. "software_version": "2.11",
  100. "state": "ON",
  101. "oscillating": "OFF",
  102. "speed": 2,
  103. },
  104. ]
  105. class API:
  106. """Class for example API."""
  107. def __init__(self, host: str, user: str, pwd: str, mock: bool = False) -> None:
  108. """Initialise."""
  109. self.host = host
  110. self.user = user
  111. self.pwd = pwd
  112. # For getting and setting the mock data
  113. self.mock = mock
  114. self.mock_data = deepcopy(MOCK_DATA)
  115. # Mock auth error if user != test and pwd != 1234
  116. if mock and (self.user != "test" or self.pwd != "1234"):
  117. raise APIAuthError("Invalid credentials!")
  118. def get_data(self) -> list[dict[str, Any]]:
  119. """Get api data."""
  120. if self.mock:
  121. return self.get_mock_data()
  122. try:
  123. r = requests.get(f"http://{self.host}/api", timeout=10)
  124. return r.json()
  125. except requests.exceptions.ConnectTimeout as err:
  126. raise APIConnectionError("Timeout connecting to api") from err
  127. def set_data(self, device_id: int, parameter: str, value: Any) -> bool:
  128. """Set api data."""
  129. if self.mock:
  130. return self.set_mock_data(device_id, parameter, value)
  131. try:
  132. data = {parameter, value}
  133. r = requests.post(
  134. f"http://{self.host}/api/{device_id}", json=data, timeout=10
  135. )
  136. except requests.exceptions.ConnectTimeout as err:
  137. raise APIConnectionError("Timeout connecting to api") from err
  138. else:
  139. return r.status_code == 200
  140. # ----------------------------------------------------------------------------
  141. # The below methods are used to mimic a real api for the example that changes
  142. # its values based on commands from the switches and lights and obvioulsy will
  143. # not be needed wiht your real api.
  144. # ----------------------------------------------------------------------------
  145. def get_mock_data(self) -> dict[str, Any]:
  146. """Get mock api data."""
  147. return self.mock_data
  148. def set_mock_data(self, device_id: int, parameter: str, value: Any) -> bool:
  149. """Update mock data."""
  150. try:
  151. device = [
  152. devices
  153. for devices in self.mock_data
  154. if devices.get("device_id") == device_id
  155. ][0]
  156. except IndexError:
  157. # Device id does not exist
  158. return False
  159. other_devices = [
  160. devices
  161. for devices in self.mock_data
  162. if devices.get("device_id") != device_id
  163. ]
  164. # Modify device parameter
  165. if device.get(parameter):
  166. device[parameter] = value
  167. else:
  168. # Parameter does not exist on device
  169. return False
  170. # For sockets and lights, modify current values when off/on to mimic
  171. # real api and show changing sensors from your actions.
  172. if device["device_type"] in ["SOCKET", "ON_OFF_LIGHT", "DIMMABLE_LIGHT"]:
  173. if value == "OFF":
  174. device["current"] = 0
  175. else:
  176. original_device = [
  177. devices
  178. for devices in MOCK_DATA
  179. if devices.get("device_id") == device_id
  180. ][0]
  181. device["current"] = original_device.get("current")
  182. # For dimmable lights if brightness is set to > 0, set to on
  183. if device["device_type"] == "DIMMABLE_LIGHT":
  184. if parameter == "brightness":
  185. if value > 0:
  186. device["state"] = "ON"
  187. device["current"] = value * 0.015
  188. else:
  189. device["state"] = "OFF"
  190. if parameter == "state":
  191. if value == "ON":
  192. device["brightness"] = 100
  193. else:
  194. device["brightness"] = 0
  195. _LOGGER.debug("Device Updated: %s", device)
  196. # Update mock data
  197. self.mock_data = other_devices
  198. self.mock_data.append(device)
  199. return True
  200. class APIAuthError(Exception):
  201. """Exception class for auth error."""
  202. class APIConnectionError(Exception):
  203. """Exception class for connection error."""