Smarthome Functionen
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.

base.py 5.8KB

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