MQTT Home Emulation
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

tradfri.py 9.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. """
  5. tradfri devices
  6. ===============
  7. **Author:**
  8. * Dirk Alders <sudo-dirk@mount-mockery.de>
  9. **Description:**
  10. Emulation of tradfri devices
  11. Communication (MQTT)
  12. tradfri_light {
  13. | "state": ["ON" / "OFF" / "TOGGLE"]
  14. | "linkquality": [0...255] lqi
  15. | "brightness": [0...254]
  16. | "color_mode": ["color_temp"]
  17. | "color_temp": ["coolest", "cool", "neutral", "warm", "warmest", 250...454]
  18. | "color_temp_startup": ["coolest", "cool", "neutral", "warm", "warmest", "previous", 250...454]
  19. | "update": []
  20. | }
  21. +- get {
  22. | "state": ""
  23. | }
  24. +- set {
  25. "state": ["ON" / "OFF"]
  26. "brightness": [0...256]
  27. "color_temp": [250...454]
  28. "transition": [0...] seconds
  29. "brightness_move": [-X...0...X] X/s
  30. "brightness_step": [-X...0...X]
  31. "color_temp_move": [-X...0...X] X/s
  32. "color_temp_step": [-X...0...X]
  33. }
  34. """
  35. from devices.base import base
  36. import json
  37. import task
  38. import time
  39. class sw(base):
  40. """A tradfri device with switching functionality
  41. Args:
  42. mqtt_client (mqtt.mqtt_client): A MQTT Client instance
  43. topic (str): the base topic for this device
  44. """
  45. KEY_OUTPUT_0 = "state"
  46. PROPERTIES = [
  47. KEY_OUTPUT_0,
  48. ]
  49. def __init__(self, mqtt_client, topic):
  50. super().__init__(mqtt_client, topic)
  51. self[self.KEY_OUTPUT_0] = "off"
  52. #
  53. self.mqtt_client.add_callback(self.topic + '/set', self.__rx_set__)
  54. self.mqtt_client.add_callback(self.topic + '/get', self.__rx_get__)
  55. #
  56. cmd_base = self.topic.replace('/', '.') + '.'
  57. self.user_cmds = {
  58. cmd_base + 'toggle': self.__ui_toggle_output_0__,
  59. }
  60. def set_state(self, value):
  61. self.__set__(self.KEY_OUTPUT_0, "on" if value else "off")
  62. self.send_device_status()
  63. def __rx_set__(self, client, userdata, message):
  64. data = json.loads(message.payload)
  65. self.logger.info("Received set data: %s", repr(data))
  66. for key in data:
  67. self.__set__(key, data[key])
  68. self.send_device_status()
  69. if self.KEY_OUTPUT_0 in data and data.get(self.KEY_OUTPUT_0, 'OFF').lower() == "on":
  70. self.power_on(self.KEY_OUTPUT_0)
  71. def __rx_get__(self, client, userdata, message):
  72. self.send_device_status()
  73. def power_on_action(self):
  74. self[self.KEY_OUTPUT_0] = "on"
  75. self.send_device_status()
  76. def send_device_status(self):
  77. data = json.dumps(self)
  78. self.logger.info("Sending status: %s", repr(data))
  79. self.mqtt_client.send(self.topic, data)
  80. def __ui_toggle_output_0__(self, *args):
  81. self.__set__(self.KEY_OUTPUT_0, 'off' if self.get(self.KEY_OUTPUT_0).lower() == 'on' else 'on')
  82. self.send_device_status()
  83. class sw_br(sw):
  84. """A tradfri device with switching and brightness functionality
  85. Args:
  86. mqtt_client (mqtt.mqtt_client): A MQTT Client instance
  87. topic (str): the base topic for this device
  88. """
  89. PROPERTIES = sw.PROPERTIES + [
  90. "brightness",
  91. "brightness_move",
  92. ]
  93. def __init__(self, mqtt_client, topic):
  94. super().__init__(mqtt_client, topic)
  95. self.task = task.periodic(0.1, self.__task__)
  96. self.task.run()
  97. #
  98. self["brightness"] = 64
  99. def __task__(self, rt):
  100. db = self.get('brightness_move', 0)
  101. if db != 0:
  102. b = self["brightness"]
  103. b += (db / 10)
  104. if b < 0:
  105. b = 0
  106. elif b > 254:
  107. b = 254
  108. if b != self["brightness"]:
  109. self["brightness"] = b
  110. self.send_device_status()
  111. class sw_br_ct(sw_br):
  112. """A tradfri device with switching, brightness and colortemp functionality
  113. Args:
  114. mqtt_client (mqtt.mqtt_client): A MQTT Client instance
  115. topic (str): the base topic for this device
  116. """
  117. PROPERTIES = sw_br.PROPERTIES + [
  118. "color_temp",
  119. ]
  120. def __init__(self, mqtt_client, topic):
  121. super().__init__(mqtt_client, topic)
  122. self["color_temp"] = 413
  123. class button(base):
  124. """ Communication (MQTT)
  125. tradfri_button {
  126. "action": [
  127. "arrow_left_click",
  128. "arrow_left_hold",
  129. "arrow_left_release",
  130. "arrow_right_click",
  131. "arrow_right_hold",
  132. "arrow_right_release",
  133. "brightness_down_click",
  134. "brightness_down_hold",
  135. "brightness_down_release",
  136. "brightness_up_click",
  137. "brightness_up_hold",
  138. "brightness_up_release",
  139. "toggle"
  140. ]
  141. "action_duration": [0...] s
  142. "battery": [0...100] %
  143. "linkquality": [0...255] lqi
  144. "update": []
  145. }
  146. """
  147. ACTION_TOGGLE = "toggle"
  148. ACTION_BRIGHTNESS_UP = "brightness_up_click"
  149. ACTION_BRIGHTNESS_DOWN = "brightness_down_click"
  150. ACTION_RIGHT = "arrow_right_click"
  151. ACTION_LEFT = "arrow_left_click"
  152. ACTION_BRIGHTNESS_UP_LONG = "brightness_up_hold"
  153. ACTION_BRIGHTNESS_UP_RELEASE = "brightness_up_release"
  154. ACTION_BRIGHTNESS_DOWN_LONG = "brightness_down_hold"
  155. ACTION_BRIGHTNESS_DOWN_RELEASE = "brightness_down_release"
  156. ACTION_RIGHT_LONG = "arrow_right_hold"
  157. ACTION_RIGHT_RELEASE = "arrow_right_release"
  158. ACTION_LEFT_LONG = "arrow_left_hold"
  159. ACTION_LEFT_RELEASE = "arrow_left_release"
  160. ACTION_RELEASE = {
  161. ACTION_BRIGHTNESS_DOWN_LONG: ACTION_BRIGHTNESS_DOWN_RELEASE,
  162. ACTION_BRIGHTNESS_UP_LONG: ACTION_BRIGHTNESS_UP_RELEASE,
  163. ACTION_LEFT_LONG: ACTION_LEFT_RELEASE,
  164. ACTION_RIGHT_LONG: ACTION_RIGHT_RELEASE
  165. }
  166. #
  167. KEY_LINKQUALITY = "linkquality"
  168. KEY_BATTERY = "battery"
  169. KEY_ACTION = "action"
  170. KEY_ACTION_DURATION = "action_duration"
  171. def __init__(self, mqtt_client, topic, **kwargs):
  172. super().__init__(mqtt_client, topic, **kwargs)
  173. self.__device_in_use__ = False
  174. #
  175. cmd_base = self.topic.replace('/', '.') + '.'
  176. self.user_cmds = {
  177. cmd_base + 'toggle': self.__ui_button_toggle__,
  178. cmd_base + 'left': self.__ui_button_left__,
  179. cmd_base + 'left_long': self.__ui_button_left_long__,
  180. cmd_base + 'right': self.__ui_button_right__,
  181. cmd_base + 'right_long': self.__ui_button_right_long__,
  182. cmd_base + 'up': self.__ui_button_up__,
  183. cmd_base + 'up_long': self.__ui_button_up_long__,
  184. cmd_base + 'down': self.__ui_button_down__,
  185. cmd_base + 'down_long': self.__ui_button_down_long__,
  186. }
  187. def send_device_status(self, data):
  188. self.logger.info("Sending status: %s", repr(data))
  189. self.mqtt_client.send(self.topic, json.dumps(data))
  190. def __ui_button_toggle__(self, *args):
  191. self.send_device_status({self.KEY_ACTION: self.ACTION_TOGGLE})
  192. def __ui_button_left__(self, *args):
  193. self.send_device_status({self.KEY_ACTION: self.ACTION_LEFT})
  194. def __ui_button_left_long__(self, *args):
  195. self.__ui_button_long_press__(self.ACTION_LEFT_LONG, *args)
  196. def __ui_button_right__(self, *args):
  197. self.send_device_status({self.KEY_ACTION: self.ACTION_RIGHT})
  198. def __ui_button_right_long__(self, *args):
  199. self.__ui_button_long_press__(self.ACTION_RIGHT_LONG, *args)
  200. def __ui_button_up__(self, *args):
  201. self.send_device_status({self.KEY_ACTION: self.ACTION_BRIGHTNESS_UP})
  202. def __ui_button_up_long__(self, *args):
  203. self.__ui_button_long_press__(self.ACTION_BRIGHTNESS_UP_LONG, *args)
  204. def __ui_button_down__(self, *args):
  205. self.send_device_status({self.KEY_ACTION: self.ACTION_BRIGHTNESS_DOWN})
  206. def __ui_button_down_long__(self, *args):
  207. self.__ui_button_long_press__(self.ACTION_BRIGHTNESS_DOWN_LONG, *args)
  208. def __ui_button_long_press__(self, action, *args):
  209. try:
  210. dt = float(args[0])
  211. except (IndexError, ValueError):
  212. print("You need to give a numeric argument to define the period.")
  213. else:
  214. if not self.__device_in_use__:
  215. self.__device_in_use__ = True
  216. self.send_device_status({self.KEY_ACTION: action})
  217. time.sleep(dt)
  218. self.send_device_status({self.KEY_ACTION: self.ACTION_RELEASE[action]})
  219. self.__device_in_use__ = False
  220. def __ui_button_long_release__(self):
  221. self.__device_in_use__ = False