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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. from base import mqtt_base
  5. from base import videv_base
  6. import json
  7. import time
  8. def is_json(data):
  9. try:
  10. json.loads(data)
  11. except json.decoder.JSONDecodeError:
  12. return False
  13. else:
  14. return True
  15. class base(mqtt_base):
  16. TX_TOPIC = "set"
  17. TX_VALUE = 0
  18. TX_DICT = 1
  19. TX_TYPE = -1
  20. TX_FILTER_DATA_KEYS = []
  21. #
  22. RX_KEYS = []
  23. RX_IGNORE_TOPICS = []
  24. RX_IGNORE_KEYS = []
  25. RX_FILTER_DATA_KEYS = []
  26. #
  27. CFG_DATA = {}
  28. def __init__(self, mqtt_client, topic):
  29. super().__init__(mqtt_client, topic, default_values=dict.fromkeys(self.RX_KEYS))
  30. # data storage
  31. self.__cfg_by_mid__ = None
  32. # initialisations
  33. mqtt_client.add_callback(topic=self.topic, callback=self.receive_callback)
  34. mqtt_client.add_callback(topic=self.topic+"/#", callback=self.receive_callback)
  35. #
  36. self.add_callback(None, None, self.__state_logging__, on_change_only=True)
  37. def __cfg_callback__(self, key, value, mid):
  38. if self.CFG_DATA.get(key) != value and self.__cfg_by_mid__ != mid and mid is not None:
  39. self.__cfg_by_mid__ = mid
  40. self.logger.warning("Differing configuration identified: Sending default configuration to defice: %s", repr(self.CFG_DATA))
  41. if self.TX_TYPE == self.TX_DICT:
  42. self.mqtt_client.send(self.topic + '/' + self.TX_TOPIC, json.dumps(self.CFG_DATA))
  43. else:
  44. for key in self.CFG_DATA:
  45. self.send_command(key, self.CFG_DATA.get(key))
  46. def set(self, key, data, mid=None, block_callback=[]):
  47. if key in self.CFG_DATA:
  48. self.__cfg_callback__(key, data, mid)
  49. if key in self.RX_IGNORE_KEYS:
  50. pass # ignore these keys
  51. elif key in self.RX_KEYS:
  52. return super().set(key, data, block_callback)
  53. else:
  54. self.logger.warning("Unexpected key %s", key)
  55. def receive_callback(self, client, userdata, message):
  56. if message.topic != self.topic + '/' + videv_base.KEY_INFO:
  57. content_key = message.topic[len(self.topic) + 1:]
  58. if content_key not in self.RX_IGNORE_TOPICS and (not message.topic.endswith(self.TX_TOPIC) or len(self.TX_TOPIC) == 0):
  59. self.logger.debug("Unpacking content_key \"%s\" from message.", content_key)
  60. if is_json(message.payload):
  61. data = json.loads(message.payload)
  62. if type(data) is dict:
  63. for key in data:
  64. self.set(key, self.__device_to_instance_filter__(key, data[key]), message.mid)
  65. else:
  66. self.set(content_key, self.__device_to_instance_filter__(content_key, data), message.mid)
  67. # String
  68. else:
  69. self.set(content_key, self.__device_to_instance_filter__(content_key, message.payload.decode('utf-8')), message.mid)
  70. else:
  71. self.logger.debug("Ignoring topic %s", content_key)
  72. def __device_to_instance_filter__(self, key, data):
  73. if key in self.RX_FILTER_DATA_KEYS:
  74. if data in [1, 'on', 'ON']:
  75. return True
  76. elif data in [0, 'off', 'OFF']:
  77. return False
  78. return data
  79. def __instance_to_device_filter__(self, key, data):
  80. if key in self.TX_FILTER_DATA_KEYS:
  81. if data is True:
  82. return "on"
  83. elif data is False:
  84. return "off"
  85. return data
  86. def send_command(self, key, data):
  87. data = self.__instance_to_device_filter__(key, data)
  88. if self.TX_TOPIC is not None:
  89. if self.TX_TYPE < 0:
  90. self.logger.error("Unknown tx type. Set TX_TYPE of class to a known value")
  91. else:
  92. self.logger.debug("Sending data for %s - %s", key, str(data))
  93. if self.TX_TYPE == self.TX_DICT:
  94. try:
  95. self.mqtt_client.send('/'.join([self.topic, self.TX_TOPIC]), json.dumps({key: data}))
  96. except TypeError:
  97. print(self.topic)
  98. print(key.__dict__)
  99. print(key)
  100. print(data)
  101. raise TypeError
  102. else:
  103. if type(data) not in [str, bytes]:
  104. data = json.dumps(data)
  105. self.mqtt_client.send('/'.join([self.topic, key, self.TX_TOPIC] if len(self.TX_TOPIC) > 0 else [self.topic, key]), data)
  106. else:
  107. self.logger.error("Unknown tx toptic. Set TX_TOPIC of class to a known value")
  108. class base_output(base):
  109. def __init__(self, mqtt_client, topic):
  110. super().__init__(mqtt_client, topic)
  111. self.__all_off_enabled__ = True
  112. def disable_all_off(self, state=True):
  113. self.__all_off_enabled__ = not state
  114. def all_off(self):
  115. if self.__all_off_enabled__:
  116. try:
  117. self.__all_off__()
  118. except (AttributeError, TypeError) as e:
  119. self.logger.warning("Method all_off was used, but __all_off__ method wasn't callable: %s", repr(e))