wether station gui client
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

smarthome.py 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. #!/usr/bin/env python
  2. # -*- coding: UTF-8 -*-
  3. #
  4. import config
  5. from rpi_envsens.dht import dht_22
  6. from rpi_envsens.bmp import bmp_180
  7. import garage_protocol
  8. import gui
  9. import logging
  10. import numbers
  11. import os
  12. import report
  13. import task
  14. import tcp_socket
  15. import time
  16. import wetation_protocol
  17. import wx
  18. logger = logging.getLogger('APP')
  19. class WetationFrameProt(gui.Wetation):
  20. PROT_ID_GARAGE = 0
  21. PROT_ID_IN = 1
  22. PROT_ID_OUT = 2
  23. ALL_PROT_IDS = [PROT_ID_GARAGE, PROT_ID_IN, PROT_ID_OUT, ]
  24. PROT_IPS = {
  25. PROT_ID_GARAGE: config.server_garage_ip,
  26. PROT_ID_IN: config.server_in_ip,
  27. PROT_ID_OUT: config.server_out_ip,
  28. }
  29. PROT_PORTS = {
  30. PROT_ID_GARAGE: config.server_garage_port,
  31. PROT_ID_IN: config.server_in_port,
  32. PROT_ID_OUT: config.server_out_port,
  33. }
  34. PROT_SECRETS = {
  35. PROT_ID_GARAGE: config.server_garage_secret,
  36. PROT_ID_IN: config.server_in_secret,
  37. PROT_ID_OUT: config.server_out_secret,
  38. }
  39. PROT_CLASSES = {
  40. PROT_ID_GARAGE: garage_protocol.my_base_protocol_tcp,
  41. PROT_ID_IN: wetation_protocol.my_base_protocol_tcp,
  42. PROT_ID_OUT: wetation_protocol.my_base_protocol_tcp,
  43. }
  44. REQUEST_MSGS_SLOW = {
  45. PROT_ID_IN: [
  46. {
  47. 'service_id': wetation_protocol.my_base_protocol_tcp.SID_READ_REQUEST,
  48. 'data_id': wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA,
  49. },
  50. ],
  51. PROT_ID_OUT: [
  52. {
  53. 'service_id': wetation_protocol.my_base_protocol_tcp.SID_READ_REQUEST,
  54. 'data_id': wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA,
  55. },
  56. ],
  57. }
  58. REQUEST_MSGS_FAST = {
  59. PROT_ID_GARAGE: [
  60. {
  61. 'service_id': garage_protocol.my_base_protocol_tcp.SID_READ_REQUEST,
  62. 'data_id': garage_protocol.my_base_protocol_tcp.GATE_POSITION,
  63. },
  64. ],
  65. }
  66. def __init__(self, *args, **kwds):
  67. self.__min_temp_in__ = None
  68. self.__min_temp_out__ = None
  69. self.__max_temp_in__ = None
  70. self.__max_temp_out__ = None
  71. self.__prot__ = {}
  72. gui.Wetation.__init__(self, *args, **kwds)
  73. self.in_temperature_min.Bind(wx.EVT_LEFT_DOWN, self.reset_in_temp_minmax_evt)
  74. self.in_temperature_max.Bind(wx.EVT_LEFT_DOWN, self.reset_in_temp_minmax_evt)
  75. self.out_temperature_min.Bind(wx.EVT_LEFT_DOWN, self.reset_out_temp_minmax_evt)
  76. self.out_temperature_max.Bind(wx.EVT_LEFT_DOWN, self.reset_out_temp_minmax_evt)
  77. self.ShowFullScreen(config.FULL_SCREEN)
  78. self.__init_communication__()
  79. self.__task_1s__ = task.periodic(1, self.__task_1s_callback__)
  80. self.__task_10s__ = task.periodic(10, self.__task_10s_callback__)
  81. self.__task_60s__ = task.periodic(60, self.__task_60s_callback__)
  82. def __init_communication__(self):
  83. #
  84. # Start TCP-Clients
  85. for prot_id in self.ALL_PROT_IDS:
  86. logger.debug('Initiating communication channel for prot_id %d', prot_id)
  87. c_tcp = tcp_socket.tcp_client_stp(self.PROT_IPS[prot_id], self.PROT_PORTS[prot_id], rx_log_lvl=logging.DEBUG)
  88. self.__prot__[prot_id] = self.PROT_CLASSES[prot_id](c_tcp, secret=self.PROT_SECRETS[prot_id], auto_auth=True)
  89. #
  90. self.__prot__[prot_id].register_callback(None, None, self.__prot_resp_callbacks__, prot_id)
  91. def __initiate_data_request__(self, rt, request_msgs):
  92. for prot_id in request_msgs:
  93. for request_msg in request_msgs.get(prot_id, []):
  94. service_id = request_msg['service_id']
  95. data_id = request_msg['data_id']
  96. if self.__prot__[prot_id].connection_established():
  97. logger.debug('Sending data request for prot_id %d, service_id %d and data_id %d', prot_id, service_id, data_id)
  98. self.__prot__[prot_id].send(service_id, data_id, None)
  99. else:
  100. wx.CallAfter(self.__no_data__, prot_id, service_id + 1, data_id)
  101. def __task_1s_callback__(self, rt):
  102. # request data from fast prot ids
  103. self.__initiate_data_request__(rt, self.REQUEST_MSGS_FAST)
  104. # set date and time
  105. wx.CallAfter(self.update_time)
  106. def __task_10s_callback__(self, rt):
  107. # request data from slow prot ids
  108. self.__initiate_data_request__(rt, self.REQUEST_MSGS_SLOW)
  109. def __task_60s_callback__(self, rt):
  110. # reconnect prots if needed
  111. for prot_id in self.ALL_PROT_IDS:
  112. if not self.__prot__[prot_id].connected():
  113. logger.debug("Trying to reconnect prot_id %d", prot_id)
  114. self.__prot__[prot_id].reconnect()
  115. def __validate_response_data__(self, prot_id, service_id, data_id, data):
  116. rv = False
  117. if prot_id == self.PROT_ID_GARAGE:
  118. if service_id == garage_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.GATE_POSITION:
  119. rv = isinstance(data, numbers.Number)
  120. elif service_id == garage_protocol.my_base_protocol_tcp.SID_EXECUTE_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.OPEN_CLOSE_GATE:
  121. if data is True:
  122. rv = True
  123. elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
  124. if service_id == wetation_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA:
  125. rv = isinstance(data.get(dht_22.KEY_TEMPERATURE), numbers.Number) and isinstance(data.get(dht_22.KEY_HUMIDITY), numbers.Number) and isinstance(data.get(bmp_180.KEY_PRESSURE), numbers.Number)
  126. if rv is False:
  127. logger.warning("Validation failed for message: prot_id=%s, service_id=%s, data_id=%s, data=%s", repr(prot_id), repr(service_id), repr(data_id), repr(data))
  128. return rv
  129. def __prot_resp_callbacks__(self, msg, prot_id):
  130. service_id = msg.get_service_id()
  131. data_id = msg.get_data_id()
  132. data = msg.get_data()
  133. if self.__validate_response_data__(prot_id, service_id, data_id, data):
  134. wx.CallAfter(self.__data__, prot_id, service_id, data_id, data)
  135. return wetation_protocol.my_base_protocol_tcp.STATUS_OKAY, None
  136. else:
  137. wx.CallAfter(self.__no_data__, prot_id, service_id, data_id)
  138. return wetation_protocol.my_base_protocol_tcp.STATUS_SERVICE_OR_DATA_UNKNOWN, None
  139. def __no_data__(self, prot_id, service_id, data_id):
  140. if prot_id == self.PROT_ID_GARAGE:
  141. if service_id == garage_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.GATE_POSITION:
  142. logger.debug('Resetting GUI for prot_id %d, service_id=%d, data_id=%d', prot_id, service_id, data_id)
  143. self.heading_garage.Show(False)
  144. self.gate_oc.Show(False)
  145. self.gate_open.Show(False)
  146. self.gate_position.Show(False)
  147. self.gate_close.Show(False)
  148. self.Layout()
  149. return
  150. elif service_id == garage_protocol.my_base_protocol_tcp.SID_EXECUTE_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.OPEN_CLOSE_GATE:
  151. return
  152. elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
  153. if service_id == wetation_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA:
  154. logger.debug('Resetting GUI for prot_id %d, service_id=%d, data_id=%d', prot_id, service_id, data_id)
  155. txt_temperature = '-.- °C'
  156. txt_humidity = '-.- %'
  157. txt_pressure = '- hPa'
  158. if prot_id == self.PROT_ID_IN:
  159. self.in_temperature.SetLabel(txt_temperature)
  160. self.in_humidity.SetLabel(txt_humidity)
  161. self.in_pressure.SetLabel(txt_pressure)
  162. else:
  163. self.out_temperature.SetLabel(txt_temperature)
  164. self.out_humidity.SetLabel(txt_humidity)
  165. self.out_pressure.SetLabel(txt_pressure)
  166. self.Layout()
  167. return
  168. logger.warning("Unknown response with no valid data for prot_id %d, service_id=%d, data_id=%d", prot_id, service_id, data_id)
  169. def __data__(self, prot_id, service_id, data_id, data):
  170. if prot_id == self.PROT_ID_GARAGE:
  171. if service_id == garage_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.GATE_POSITION:
  172. self.heading_garage.Show(True)
  173. self.gate_oc.Show(True)
  174. self.gate_open.Show(True)
  175. self.gate_position.Show(True)
  176. self.gate_close.Show(True)
  177. self.gate_position.SetValue(int(data * 100))
  178. self.Layout()
  179. return garage_protocol.my_base_protocol_tcp.STATUS_OKAY, None
  180. elif service_id == garage_protocol.my_base_protocol_tcp.SID_EXECUTE_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.OPEN_CLOSE_GATE:
  181. return garage_protocol.my_base_protocol_tcp.STATUS_OKAY, None
  182. elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
  183. if service_id == wetation_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA:
  184. temp = data[dht_22.KEY_TEMPERATURE]
  185. if prot_id == self.PROT_ID_IN:
  186. if self.__max_temp_in__ is None or temp > self.__max_temp_in__:
  187. self.__max_temp_in__ = temp
  188. if self.__min_temp_in__ is None or temp < self.__min_temp_in__:
  189. self.__min_temp_in__ = temp
  190. wx.CallAfter(self.update_current_env_data, data, self.in_temperature, self.in_humidity, self.in_pressure, self.in_temperature_min, self.in_temperature_max, self.__min_temp_in__, self.__max_temp_in__)
  191. else:
  192. if self.__max_temp_out__ is None or temp > self.__max_temp_out__:
  193. self.__max_temp_out__ = temp
  194. if self.__min_temp_out__ is None or temp < self.__min_temp_out__:
  195. self.__min_temp_out__ = temp
  196. wx.CallAfter(self.update_current_env_data, data, self.out_temperature, self.out_humidity, self.out_pressure, self.out_temperature_min, self.out_temperature_max, self.__min_temp_out__, self.__max_temp_out__)
  197. return wetation_protocol.my_base_protocol_tcp.STATUS_OKAY, None
  198. logger.warning("Unknown response with valid data for prot_id %d, service_id=%d, data_id=%d", prot_id, service_id, data_id)
  199. return wetation_protocol.my_base_protocol_tcp.STATUS_SERVICE_OR_DATA_UNKNOWN, None
  200. def update_current_env_data(self, env_data, temperature, humidity, pressure, temperature_min, temperature_max, value_min, value_max):
  201. if isinstance(value_min, numbers.Number) and isinstance(value_max, numbers.Number):
  202. temperature_min.SetLabel('%.1f' % value_min)
  203. temperature_max.SetLabel('%.1f' % value_max)
  204. temperature.SetLabel('%.1f °C' % env_data[dht_22.KEY_TEMPERATURE])
  205. humidity.SetLabel('%.1f %%' % env_data[dht_22.KEY_HUMIDITY])
  206. pressure.SetLabel('%.0f hPa' % env_data[bmp_180.KEY_PRESSURE])
  207. self.Layout()
  208. def update_time(self):
  209. self.time.SetLabel(time.strftime("%H:%M"))
  210. self.date.SetLabel(time.strftime("%d.%m.%Y"))
  211. self.Layout()
  212. def gate_oc_evt(self, event):
  213. logger.debug("Gate open/close request")
  214. self.__prot__[self.PROT_ID_GARAGE].send(garage_protocol.my_base_protocol_tcp.SID_EXECUTE_REQUEST, garage_protocol.my_base_protocol_tcp.OPEN_CLOSE_GATE, None)
  215. event.Skip()
  216. def reset_in_temp_minmax_evt(self, event):
  217. self.__min_temp_in__ = None
  218. self.__max_temp_in__ = None
  219. self.in_temperature_min.SetLabel("-.- °C")
  220. self.in_temperature_max.SetLabel("-.- °C")
  221. self.Layout()
  222. event.Skip()
  223. def reset_out_temp_minmax_evt(self, event):
  224. self.__min_temp_out__ = None
  225. self.__max_temp_out__ = None
  226. self.out_temperature_min.SetLabel("-.- °C")
  227. self.out_temperature_max.SetLabel("-.- °C")
  228. self.Layout()
  229. event.Skip()
  230. def run(self):
  231. self.__task_1s__.run()
  232. self.__task_10s__.run()
  233. self.__task_60s__.run()
  234. def close(self):
  235. self.__task_1s__.stop()
  236. self.__task_1s__.join()
  237. self.__task_10s__.stop()
  238. self.__task_10s__.join()
  239. self.__task_60s__.stop()
  240. self.__task_60s__.join()
  241. def __del__(self):
  242. self.close()
  243. class MyApp(wx.App):
  244. def OnInit(self):
  245. self.frame = WetationFrameProt(None, wx.ID_ANY, "")
  246. self.SetTopWindow(self.frame)
  247. self.frame.Show()
  248. return True
  249. if __name__ == "__main__":
  250. report.appLoggingConfigure(os.path.dirname(__file__), config.LOGTARGET, config.loggers)
  251. #
  252. app = MyApp(0)
  253. app.frame.run()
  254. app.MainLoop()
  255. app.frame.close()