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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import json
  2. import logging
  3. import task
  4. try:
  5. from config import APP_NAME as ROOT_LOGGER_NAME
  6. except ImportError:
  7. ROOT_LOGGER_NAME = 'root'
  8. class common_base(dict):
  9. DEFAULT_VALUES = {}
  10. def __init__(self, default_values=None):
  11. super().__init__(default_values or self.DEFAULT_VALUES)
  12. #
  13. self.__callback_list__ = []
  14. self.logger = logging.getLogger(ROOT_LOGGER_NAME).getChild("devices")
  15. def add_callback(self, key, data, callback, on_change_only=False, init_now=False):
  16. """
  17. key: key or None for all keys
  18. data: data or None for all data
  19. """
  20. cb_tup = (key, data, callback, on_change_only)
  21. if cb_tup not in self.__callback_list__:
  22. self.__callback_list__.append(cb_tup)
  23. if init_now and self.get(key) is not None:
  24. callback(self, key, self[key])
  25. def set(self, key, data, block_callback=[]):
  26. if key in self.keys():
  27. value_changed = self[key] != data
  28. self[key] = data
  29. for cb_key, cb_data, callback, on_change_only in self.__callback_list__:
  30. if cb_key is None or key == cb_key: # key fits to callback definition
  31. if cb_data is None or cb_data == self[key]: # data fits to callback definition
  32. if value_changed or not on_change_only: # change status fits to callback definition
  33. if not callback in block_callback: # block given callbacks
  34. callback(self, key, self[key])
  35. else:
  36. self.logger.warning("Unexpected key %s", key)
  37. class mqtt_base(common_base):
  38. def __init__(self, mqtt_client, topic, default_values=None):
  39. super().__init__(default_values)
  40. #
  41. self.mqtt_client = mqtt_client
  42. self.topic = topic
  43. for entry in self.topic.split('/'):
  44. self.logger = self.logger.getChild(entry)
  45. class videv_base(mqtt_base):
  46. KEY_INFO = '__info__'
  47. #
  48. SET_TOPIC = "set"
  49. def __init__(self, mqtt_client, topic, default_values=None):
  50. super().__init__(mqtt_client, topic, default_values=default_values)
  51. self.__display_dict__ = {}
  52. self.__control_dict__ = {}
  53. self.__periodic__ = task.periodic(300, self.send_all)
  54. self.__periodic__.run()
  55. def send_all(self, rt):
  56. try:
  57. for key in self:
  58. if self[key] is not None:
  59. self.__tx__(key, self[key])
  60. except RuntimeError:
  61. self.logger.warning("Runtimeerror while sending cyclic videv information. This may happen on startup.")
  62. def add_display(self, my_key, ext_device, ext_key, on_change_only=True):
  63. """
  64. listen to data changes of ext_device and update videv information
  65. """
  66. if my_key not in self.keys():
  67. self[my_key] = None
  68. if ext_device.__class__.__name__ == "group":
  69. # store information to identify callback from ext_device
  70. self.__display_dict__[(id(ext_device[0]), ext_key)] = my_key
  71. # register a callback to listen for data from external device
  72. ext_device[0].add_callback(ext_key, None, self.__rx_ext_device_data__, on_change_only, init_now=True)
  73. else:
  74. # store information to identify callback from ext_device
  75. self.__display_dict__[(id(ext_device), ext_key)] = my_key
  76. # register a callback to listen for data from external device
  77. ext_device.add_callback(ext_key, None, self.__rx_ext_device_data__, on_change_only, init_now=True)
  78. # send initial display data to videv interface
  79. data = ext_device.get(ext_key)
  80. if data is not None:
  81. self.__tx__(my_key, data)
  82. def __rx_ext_device_data__(self, ext_device, ext_key, data):
  83. my_key = self.__display_dict__[(id(ext_device), ext_key)]
  84. self.set(my_key, data)
  85. self.__tx__(my_key, data)
  86. def __tx__(self, key, data):
  87. if type(data) not in (str, ):
  88. data = json.dumps(data)
  89. self.mqtt_client.send('/'.join([self.topic, key]), data)
  90. def add_control(self, my_key, ext_device, ext_key, on_change_only=True):
  91. """
  92. listen to videv information and pass data to ext_device
  93. """
  94. if my_key not in self.keys():
  95. self[my_key] = None
  96. # store information to identify callback from videv
  97. self.__control_dict__[my_key] = (ext_device, ext_key, on_change_only)
  98. # add callback for videv changes
  99. self.mqtt_client.add_callback('/'.join([self.topic, my_key, self.SET_TOPIC]), self.__rx_videv_data__)
  100. def __rx_videv_data__(self, client, userdata, message):
  101. my_key = message.topic.split('/')[-2]
  102. try:
  103. data = json.loads(message.payload)
  104. except json.decoder.JSONDecodeError:
  105. data = message.payload
  106. ext_device, ext_key, on_change_only = self.__control_dict__[my_key]
  107. if my_key in self.keys():
  108. if data != self[my_key] or not on_change_only:
  109. ext_device.send_command(ext_key, data)
  110. else:
  111. self.logger.info("Ignoring rx message with topic %s", message.topic)
  112. def add_routing(self, my_key, ext_device, ext_key, on_change_only_disp=True, on_change_only_videv=True):
  113. """
  114. listen to data changes of ext_device and update videv information
  115. and
  116. listen to videv information and pass data to ext_device
  117. """
  118. # add display
  119. self.add_display(my_key, ext_device, ext_key, on_change_only_disp)
  120. self.add_control(my_key, ext_device, ext_key, on_change_only_videv)