Smarthome Functionen
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

videv.py 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. """
  5. Virtual Device(s)
  6. Targets:
  7. * MQTT-Interface to control joined devices as one virtual device.
  8. * Primary signal routing
  9. * No functionality should be implemented here
  10. """
  11. # TODO: Extend virtual devices
  12. # * Digital-Audio-Sources (Spotify, MPD, Currently playing) oder direkt von my_apps?!
  13. # *
  14. from base import mqtt_base
  15. import devices
  16. import json
  17. BASETOPIC = "videv"
  18. try:
  19. from config import APP_NAME as ROOT_LOGGER_NAME
  20. except ImportError:
  21. ROOT_LOGGER_NAME = 'root'
  22. class base(mqtt_base):
  23. EXEC_RX_FUNC_ALWAYS = []
  24. def __init__(self, mqtt_client, topic, *args, default_values=None):
  25. super().__init__(mqtt_client, topic, default_values=default_values)
  26. self.__device_list__ = {}
  27. for videv_key, device in [reduced[:2] for reduced in args]:
  28. self.__device_list__[videv_key] = device
  29. # send initial state
  30. for key in self.keys():
  31. self.__tx__(key, self[key])
  32. # add receive topics
  33. mqtt_client.add_callback(self.topic + "/#", self.__rx__)
  34. def __tx__(self, key, data):
  35. if type(data) not in (str, ):
  36. data = json.dumps(data)
  37. if key in self.keys():
  38. self.mqtt_client.send(self.topic + '/' + key, data)
  39. else:
  40. self.logger.warning("Ignoring send request for key %s (not available for this class)", key)
  41. def __rx__(self, client, userdata, message):
  42. key = message.topic.split('/')[-1]
  43. if key in self.keys():
  44. try:
  45. data = json.loads(message.payload)
  46. except json.decoder.JSONDecodeError:
  47. data = message.payload
  48. if data != self[key] or key in self.EXEC_RX_FUNC_ALWAYS:
  49. self.__rx_functionality__(key, data)
  50. self.set(key, data)
  51. else:
  52. self.logger.info("Ignoring rx message with topic %s", message.topic)
  53. def __rx_functionality__(self, key, data):
  54. raise NotImplemented("Method __rx_functionality__ needs to be implemented in child class")
  55. def __device_data__(self, device, key, data):
  56. raise NotImplemented("Method __device_data__ needs to be implemented in child class")
  57. class base_routing(base):
  58. def __init__(self, mqtt_client, topic, *args, default_values=None):
  59. super().__init__(mqtt_client, topic, *args, default_values=default_values)
  60. #
  61. self.__device_key__ = {}
  62. index = 0
  63. for videv_key, device, device_key in args:
  64. if self.__device_list__[videv_key] != device:
  65. raise ReferenceError("Parent class generated a deviating device list")
  66. self.__device_key__[videv_key] = device_key
  67. index += 1
  68. # add callbacks
  69. for key in self.__device_list__:
  70. if self.__device_list__[key].__class__.__name__ == "group":
  71. self.__device_list__[key][0].add_callback(self.__device_key__[key], None, self.__device_data__, True)
  72. else:
  73. self.__device_list__[key].add_callback(self.__device_key__[key], None, self.__device_data__, True)
  74. def __rx_functionality__(self, key, data):
  75. try:
  76. self.__device_list__[key].set(self.__device_key__[key], data)
  77. except KeyError:
  78. self.logger.warning("RX passthrough not possible for key %s", key)
  79. def __device_data__(self, device, key, data):
  80. l1 = []
  81. for k, v in self.__device_list__.items():
  82. if v.__class__.__name__ == "group":
  83. if id(device) in [id(d) for d in v]:
  84. l1.append(k)
  85. else:
  86. if id(v) == id(device):
  87. l1.append(k)
  88. l2 = [k for k, v in self.__device_key__.items() if v == key]
  89. try:
  90. videv_key = [k for k in l1 if k in l2][0]
  91. except IndexError:
  92. self.logger.warning("videv_key not available for %s::%s", device.__class__.__name__, device.topic)
  93. else:
  94. self.set(videv_key, data)
  95. self.__tx__(videv_key, data)
  96. class videv_switching(base_routing):
  97. KEY_STATE = 'state'
  98. #
  99. DEFAULT_VALUES = {
  100. KEY_STATE: False,
  101. }
  102. def __init__(self, mqtt_client, topic, sw_device, sw_key):
  103. super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key))
  104. class videv_switching_timer(base_routing):
  105. KEY_STATE = 'state'
  106. KEY_TIMER = 'timer'
  107. #
  108. DEFAULT_VALUES = {
  109. KEY_STATE: False,
  110. KEY_TIMER: 0
  111. }
  112. def __init__(self, mqtt_client, topic, sw_device, sw_key, tm_device, tm_key):
  113. super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key), (self.KEY_TIMER, tm_device, tm_key))
  114. class videv_switching_motion(base_routing):
  115. KEY_STATE = 'state'
  116. KEY_TIMER = 'timer'
  117. KEY_MOTION_SENSOR = 'motion_%d'
  118. #
  119. DEFAULT_VALUES = {
  120. KEY_STATE: False,
  121. KEY_TIMER: 0
  122. }
  123. def __init__(self, mqtt_client, topic, sw_device, sw_key, motion_function):
  124. dv = {self.KEY_STATE: False, self.KEY_TIMER: 0}
  125. for i in range(0, len(motion_function.args)):
  126. dv[motion_function.KEY_MOTION_SENSOR % i] = False
  127. super().__init__(
  128. mqtt_client, topic,
  129. (self.KEY_STATE, sw_device, sw_key),
  130. (self.KEY_TIMER, motion_function, motion_function.KEY_TIMER),
  131. *[[self.KEY_MOTION_SENSOR % i, motion_function, motion_function.KEY_MOTION_SENSOR % i] for i in range(0, len(motion_function.args))],
  132. default_values=dv
  133. )
  134. class videv_switch_brightness(base_routing):
  135. KEY_STATE = 'state'
  136. KEY_BRIGHTNESS = 'brightness'
  137. #
  138. DEFAULT_VALUES = {
  139. KEY_STATE: False,
  140. KEY_BRIGHTNESS: 0
  141. }
  142. def __init__(self, mqtt_client, topic, sw_device, sw_key, br_device, br_key):
  143. #
  144. super().__init__(mqtt_client, topic, (self.KEY_STATE, sw_device, sw_key), (self.KEY_BRIGHTNESS, br_device, br_key))
  145. class videv_switch_brightness_color_temp(base_routing):
  146. KEY_STATE = 'state'
  147. KEY_BRIGHTNESS = 'brightness'
  148. KEY_COLOR_TEMP = 'color_temp'
  149. #
  150. DEFAULT_VALUES = {
  151. KEY_STATE: False,
  152. KEY_BRIGHTNESS: 0,
  153. KEY_COLOR_TEMP: 0,
  154. }
  155. def __init__(self, mqtt_client, topic, sw_device, sw_key, br_device, br_key, ct_device, ct_key):
  156. #
  157. super().__init__(
  158. mqtt_client, topic,
  159. (self.KEY_STATE, sw_device, sw_key),
  160. (self.KEY_BRIGHTNESS, br_device, br_key),
  161. (self.KEY_COLOR_TEMP, ct_device, ct_key)
  162. )
  163. class videv_heating(base_routing):
  164. KEY_TEMPERATURE = 'temperature'
  165. KEY_USER_TEMPERATURE_SETPOINT = 'user_temperature_setpoint'
  166. KEY_VALVE_TEMPERATURE_SETPOINT = 'valve_temperature_setpoint'
  167. KEY_AWAY_MODE = 'away_mode'
  168. KEY_SUMMER_MODE = 'summer_mode'
  169. KEY_START_BOOST = 'start_boost'
  170. KEY_SET_DEFAULT_TEMPERATURE = 'set_default_temperature'
  171. KEY_BOOST_TIMER = 'boost_timer'
  172. #
  173. EXEC_RX_FUNC_ALWAYS = [KEY_START_BOOST, KEY_SET_DEFAULT_TEMPERATURE, KEY_USER_TEMPERATURE_SETPOINT]
  174. def __init__(self, mqtt_client, topic, heating_function):
  175. #
  176. super().__init__(
  177. mqtt_client, topic,
  178. (self.KEY_TEMPERATURE, heating_function, heating_function.KEY_TEMPERATURE_CURRENT),
  179. (self.KEY_USER_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_USER_TEMPERATURE_SETPOINT),
  180. (self.KEY_VALVE_TEMPERATURE_SETPOINT, heating_function, heating_function.KEY_TEMPERATURE_SETPOINT),
  181. (self.KEY_AWAY_MODE, heating_function, heating_function.KEY_AWAY_MODE),
  182. (self.KEY_SUMMER_MODE, heating_function, heating_function.KEY_SUMMER_MODE),
  183. (self.KEY_START_BOOST, heating_function, heating_function.KEY_START_BOOST),
  184. (self.KEY_SET_DEFAULT_TEMPERATURE, heating_function, heating_function.KEY_SET_DEFAULT_TEMPERATURE),
  185. (self.KEY_BOOST_TIMER, heating_function, heating_function.KEY_BOOST_TIMER),
  186. default_values={
  187. self.KEY_TEMPERATURE: heating_function[heating_function.KEY_TEMPERATURE_CURRENT],
  188. self.KEY_VALVE_TEMPERATURE_SETPOINT: heating_function[heating_function.KEY_TEMPERATURE_SETPOINT],
  189. self.KEY_USER_TEMPERATURE_SETPOINT: heating_function[heating_function.KEY_USER_TEMPERATURE_SETPOINT],
  190. self.KEY_AWAY_MODE: heating_function[heating_function.KEY_AWAY_MODE],
  191. self.KEY_SUMMER_MODE: heating_function[heating_function.KEY_SUMMER_MODE],
  192. self.KEY_BOOST_TIMER: heating_function[heating_function.KEY_BOOST_TIMER],
  193. self.KEY_START_BOOST: True,
  194. self.KEY_SET_DEFAULT_TEMPERATURE: True,
  195. }
  196. )
  197. class videv_multistate(base):
  198. def __init__(self, mqtt_client, topic, key_for_device, device, num_states, default_values=None):
  199. dv = dict.fromkeys(["state_%d" % i for i in range(0, num_states)])
  200. for key in dv:
  201. dv[key] = False
  202. super().__init__(mqtt_client, topic, (key_for_device, device), default_values=dv)
  203. #
  204. device.add_callback(key_for_device, None, self.__index_rx__, True)
  205. def __index_rx__(self, device, key, data):
  206. for index, key in enumerate(self):
  207. self.set(key, index == data)
  208. self.__tx__(key, self[key])
  209. def __rx_functionality__(self, key, data):
  210. pass # read only device
  211. class videv_audio_player(base_routing):
  212. KEY_ACTIVE_PLAYER = 'player_%d'
  213. KEY_TITLE = 'title'
  214. NO_TITLE = '---'
  215. def __init__(self, mqtt_client, topic, *args):
  216. dv = dict.fromkeys([self.KEY_ACTIVE_PLAYER % i for i in range(0, len(args))])
  217. for key in dv:
  218. dv[key] = False
  219. dv[self.KEY_TITLE] = self.NO_TITLE
  220. super().__init__(
  221. mqtt_client, topic,
  222. *[[self.KEY_ACTIVE_PLAYER % i, device, devices.audio_status.KEY_STATE] for i, device in enumerate(args)],
  223. default_values=dv
  224. )
  225. for audio_device in args:
  226. audio_device.add_callback(audio_device.KEY_TITLE, None, self.__title_rx__, True)
  227. def __title_rx__(self, device, key, data):
  228. self.set(self.KEY_TITLE, data or self.NO_TITLE)
  229. self.__tx__(self.KEY_TITLE, self[self.KEY_TITLE])