123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- #!/usr/bin/env python
- # -*- coding: UTF-8 -*-
- #
-
- import config
- from rpi_envsens.dht import dht_22
- from rpi_envsens.bmp import bmp_180
- import helpers
- import garage_protocol
- import gui
- import logging
- import numbers
- import os
- import report
- import socket_protocol
- import task
- import tcp_socket
- import time
- import wetation_protocol
- import wx
-
- try:
- from config import APP_NAME as ROOT_LOGGER_NAME
- except ImportError:
- ROOT_LOGGER_NAME = 'root'
-
-
- class WetationFrameProt(gui.Wetation):
- PROT_ID_GARAGE = 0
- PROT_ID_IN = 1
- PROT_ID_OUT = 2
- ALL_PROT_IDS = [PROT_ID_GARAGE, PROT_ID_IN, PROT_ID_OUT, ]
-
- PROT_NAMES = {
- PROT_ID_GARAGE: u'garage',
- PROT_ID_IN: u'wet-in',
- PROT_ID_OUT: u'wet-out',
- }
-
- PROT_IPS = {
- PROT_ID_GARAGE: config.server_garage_ip,
- PROT_ID_IN: config.server_in_ip,
- PROT_ID_OUT: config.server_out_ip,
- }
-
- PROT_PORTS = {
- PROT_ID_GARAGE: config.server_garage_port,
- PROT_ID_IN: config.server_in_port,
- PROT_ID_OUT: config.server_out_port,
- }
-
- PROT_SECRETS = {
- PROT_ID_GARAGE: config.server_garage_secret,
- PROT_ID_IN: config.server_in_secret,
- PROT_ID_OUT: config.server_out_secret,
- }
-
- PROT_CLASSES = {
- PROT_ID_GARAGE: garage_protocol.my_base_protocol_tcp,
- PROT_ID_IN: wetation_protocol.my_base_protocol_tcp,
- PROT_ID_OUT: wetation_protocol.my_base_protocol_tcp,
- }
-
- REQUEST_MSGS_SLOW = {
- PROT_ID_IN: [
- {
- 'service_id': socket_protocol.SID_READ_REQUEST,
- 'data_id': wetation_protocol.ENVDATA_STATISTIC_DHT,
- },
- {
- 'service_id': socket_protocol.SID_READ_REQUEST,
- 'data_id': wetation_protocol.ENVDATA_STATISTIC_BMP,
- },
- ],
- PROT_ID_OUT: [
- {
- 'service_id': socket_protocol.SID_READ_REQUEST,
- 'data_id': wetation_protocol.ENVDATA_STATISTIC_DHT,
- },
- {
- 'service_id': socket_protocol.SID_READ_REQUEST,
- 'data_id': wetation_protocol.ENVDATA_STATISTIC_BMP,
- },
- ],
- }
-
- REQUEST_MSGS_FAST = {
- PROT_ID_GARAGE: [
- {
- 'service_id': socket_protocol.SID_READ_REQUEST,
- 'data_id': garage_protocol.GATE_POSITION,
- },
- ],
- }
-
-
- def __init__(self, *args, **kwds):
- self.__min_temp_in__ = None
- self.__min_temp_out__ = None
- self.__max_temp_in__ = None
- self.__max_temp_out__ = None
- self.__prot__ = {}
- gui.Wetation.__init__(self, *args, **kwds)
- self.in_temperature_min.Bind(wx.EVT_LEFT_DOWN, self.reset_in_temp_minmax_evt)
- self.in_temperature_max.Bind(wx.EVT_LEFT_DOWN, self.reset_in_temp_minmax_evt)
- self.out_temperature_min.Bind(wx.EVT_LEFT_DOWN, self.reset_out_temp_minmax_evt)
- self.out_temperature_max.Bind(wx.EVT_LEFT_DOWN, self.reset_out_temp_minmax_evt)
- self.ShowFullScreen(config.FULL_SCREEN)
-
- self.__init_communication__()
- time.sleep(3.5) # Wait for established connections before starting the tasks
-
- self.__task_1s__ = task.periodic(1, self.__task_1s_callback__)
- self.__task_10s__ = task.periodic(10, self.__task_10s_callback__)
- self.__task_60s__ = task.periodic(60, self.__task_60s_callback__)
-
- def __init_communication__(self):
- #
- # Start TCP-Clients
- for prot_id in self.ALL_PROT_IDS:
- cn = self.PROT_NAMES[prot_id]
- logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
- logger.info('Initiating communication channel')
- c_tcp = tcp_socket.tcp_client_stp(self.PROT_IPS[prot_id], self.PROT_PORTS[prot_id], rx_log_lvl=logging.DEBUG)
- self.__prot__[prot_id] = self.PROT_CLASSES[prot_id](c_tcp, secret=self.PROT_SECRETS[prot_id], auto_auth=True, channel_name=cn)
- #
- self.__prot__[prot_id].register_callback(None, None, self.__prot_resp_callbacks__, prot_id)
-
- def __initiate_data_request__(self, rt, request_msgs):
- for prot_id in request_msgs:
- cn = self.PROT_NAMES[prot_id]
- logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
- for request_msg in request_msgs.get(prot_id, []):
- service_id = request_msg['service_id']
- data_id = request_msg['data_id']
- if self.__prot__[prot_id].connection_established():
- logger.debug('Sending data request for service_id %d and data_id %d', service_id, data_id)
- self.__prot__[prot_id].send(service_id, data_id, None)
- else:
- wx.CallAfter(self.__no_data__, prot_id, service_id + 1, data_id)
-
- def __task_1s_callback__(self, rt):
- # request data from fast prot ids
- self.__initiate_data_request__(rt, self.REQUEST_MSGS_FAST)
- # set date and time
- wx.CallAfter(self.update_time)
-
- def __task_10s_callback__(self, rt):
- # request data from slow prot ids
- self.__initiate_data_request__(rt, self.REQUEST_MSGS_SLOW)
-
- def __task_60s_callback__(self, rt):
- # reconnect prots if needed
- for prot_id in self.ALL_PROT_IDS:
- cn = self.PROT_NAMES[prot_id]
- logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
- if not self.__prot__[prot_id].connected():
- logger.warning("Trying to reconnect prot_id %d", prot_id)
- self.__prot__[prot_id].reconnect()
-
- def __validate_response_data__(self, prot_id, service_id, data_id, data):
- rv = False
- cn = self.PROT_NAMES[prot_id]
- logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
- if prot_id == self.PROT_ID_GARAGE:
- if service_id == socket_protocol.SID_READ_RESPONSE and data_id == garage_protocol.GATE_POSITION:
- rv = isinstance(data, numbers.Number)
- elif service_id == socket_protocol.SID_EXECUTE_RESPONSE and data_id == garage_protocol.OPEN_CLOSE_GATE:
- if data is True:
- rv = True
- elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
- if service_id == socket_protocol.SID_READ_RESPONSE and data_id == wetation_protocol.ENVDATA_STATISTIC_BMP:
- rv = True
- for main_key in [bmp_180.KEY_PRESSURE, bmp_180.KEY_TEMPERATURE, bmp_180.KEY_TIME]:
- if main_key not in data:
- rv = False
- break
- else:
- for sub_key in ['mean', 'min_val', 'max_val', 'quantifier']:
- if not isinstance(data[main_key].get(sub_key), numbers.Number):
- rv = False
- break
- elif service_id == socket_protocol.SID_READ_RESPONSE and data_id == wetation_protocol.ENVDATA_STATISTIC_DHT:
- rv = True
- for main_key in [dht_22.KEY_HUMIDITY, dht_22.KEY_TEMPERATURE, dht_22.KEY_TIME]:
- if main_key not in data:
- rv = False
- break
- else:
- for sub_key in ['mean', 'min_val', 'max_val', 'quantifier']:
- if not isinstance(data[main_key].get(sub_key), numbers.Number):
- rv = False
- break
- if rv is False:
- 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))
- return rv
-
- def __prot_resp_callbacks__(self, msg, prot_id):
- service_id = msg.get_service_id()
- data_id = msg.get_data_id()
- data = msg.get_data()
- if self.__validate_response_data__(prot_id, service_id, data_id, data):
- wx.CallAfter(self.__data__, prot_id, service_id, data_id, data)
- return socket_protocol.STATUS_OKAY, None
- else:
- wx.CallAfter(self.__no_data__, prot_id, service_id, data_id)
- return socket_protocol.STATUS_SERVICE_OR_DATA_UNKNOWN, None
-
- def __no_data__(self, prot_id, service_id, data_id):
- cn = self.PROT_NAMES[prot_id]
- logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
- if prot_id == self.PROT_ID_GARAGE:
- if service_id == socket_protocol.SID_READ_RESPONSE and data_id == garage_protocol.GATE_POSITION:
- logger.warning('Resetting GUI elements for %s', cn)
- self.heading_garage.Show(False)
- self.gate_oc.Show(False)
- self.gate_open.Show(False)
- self.gate_position.Show(False)
- self.gate_close.Show(False)
- self.Layout()
- return
- elif service_id == socket_protocol.SID_EXECUTE_RESPONSE and data_id == garage_protocol.OPEN_CLOSE_GATE:
- return
- elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
- if service_id == socket_protocol.SID_READ_RESPONSE and data_id == wetation_protocol.ENVDATA_STATISTIC_BMP:
- logger.warning('Resetting GUI elements for %s', cn)
- txt_pressure = '- hPa'
- if prot_id == self.PROT_ID_IN:
- self.in_pressure.SetLabel(txt_pressure)
- else:
- self.out_pressure.SetLabel(txt_pressure)
- self.Layout()
- return
- elif service_id == socket_protocol.SID_READ_RESPONSE and data_id == wetation_protocol.ENVDATA_STATISTIC_DHT:
- logger.warning('Resetting GUI elements for %s', cn)
- txt_temperature = '-.- °C'
- txt_humidity = '-.- %'
- if prot_id == self.PROT_ID_IN:
- self.in_temperature.SetLabel(txt_temperature)
- self.in_humidity.SetLabel(txt_humidity)
- else:
- self.out_temperature.SetLabel(txt_temperature)
- self.out_humidity.SetLabel(txt_humidity)
- self.Layout()
- return
- logger.warning("Unknown response with no valid data for prot_id %d, service_id=%d, data_id=%d", prot_id, service_id, data_id)
-
- def __data__(self, prot_id, service_id, data_id, data):
- cn = self.PROT_NAMES[prot_id]
- logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
- if prot_id == self.PROT_ID_GARAGE:
- if service_id == socket_protocol.SID_READ_RESPONSE and data_id == garage_protocol.GATE_POSITION:
- logger.debug('Setting %s position to %3d', cn, int(data * 100))
- self.heading_garage.Show(True)
- self.gate_oc.Show(True)
- self.gate_open.Show(True)
- self.gate_position.Show(True)
- self.gate_close.Show(True)
- self.gate_position.SetValue(int(data * 100))
- self.Layout()
- return
- elif service_id == socket_protocol.SID_EXECUTE_RESPONSE and data_id == garage_protocol.OPEN_CLOSE_GATE:
- return
- elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
- if service_id == socket_protocol.SID_READ_RESPONSE and data_id == wetation_protocol.ENVDATA_STATISTIC_BMP:
- data = helpers.continues_statistic_multivalue(**data)
- logger.debug('Setting %s pressure to %4d hPa', cn, data[bmp_180.KEY_PRESSURE].mean)
- #
- # Current environmental data
- if prot_id == self.PROT_ID_IN:
- wx.CallAfter(self.update_current_bmp_env_data, data, self.in_pressure)
- else:
- wx.CallAfter(self.update_current_bmp_env_data, data, self.out_pressure)
- return
- elif service_id == socket_protocol.SID_READ_RESPONSE and data_id == wetation_protocol.ENVDATA_STATISTIC_DHT:
- data = helpers.continues_statistic_multivalue(**data)
- #
- # Current environmental data
- temp = data[dht_22.KEY_TEMPERATURE].mean
- logger.debug('Setting %s temperature to %4.1f °C', cn, temp)
- logger.debug('Setting %s humidity to %3.1f %%', cn, data[dht_22.KEY_HUMIDITY].mean)
- if prot_id == self.PROT_ID_IN:
- if self.__max_temp_in__ is None or temp > self.__max_temp_in__:
- self.__max_temp_in__ = temp
- if self.__min_temp_in__ is None or temp < self.__min_temp_in__:
- self.__min_temp_in__ = temp
- wx.CallAfter(self.update_current_dht_env_data, data, self.in_temperature, self.in_humidity, self.in_temperature_min, self.in_temperature_max, self.__min_temp_in__, self.__max_temp_in__)
- else:
- if self.__max_temp_out__ is None or temp > self.__max_temp_out__:
- self.__max_temp_out__ = temp
- if self.__min_temp_out__ is None or temp < self.__min_temp_out__:
- self.__min_temp_out__ = temp
- wx.CallAfter(self.update_current_dht_env_data, data, self.out_temperature, self.out_humidity, self.out_temperature_min, self.out_temperature_max, self.__min_temp_out__, self.__max_temp_out__)
- return
- logger.warning("Unknown response with valid data for prot_id %d, service_id=%d, data_id=%d", prot_id, service_id, data_id)
-
- def update_current_bmp_env_data(self, env_data, pressure):
- pressure.SetLabel('%.0f hPa' % env_data[bmp_180.KEY_PRESSURE].mean)
- self.Layout()
-
- def update_current_dht_env_data(self, env_data, temperature, humidity, temperature_min, temperature_max, value_min, value_max):
- if isinstance(value_min, numbers.Number) and isinstance(value_max, numbers.Number):
- temperature_min.SetLabel('%.1f' % value_min)
- temperature_max.SetLabel('%.1f' % value_max)
- temperature.SetLabel('%.1f °C' % env_data[dht_22.KEY_TEMPERATURE].mean)
- humidity.SetLabel('%.1f %%' % env_data[dht_22.KEY_HUMIDITY].mean)
- self.Layout()
-
- def update_time(self):
- self.time.SetLabel(time.strftime("%H:%M"))
- self.date.SetLabel(time.strftime("%d.%m.%Y"))
- self.Layout()
-
- def gate_oc_evt(self, event):
- cn = self.PROT_NAMES[self.PROT_ID_GARAGE]
- logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
- r = wx.MessageDialog(
- self,
- "Soll das Garagentor betätigt werden?",
- "Garage",
- wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING
- ).ShowModal()
- if r == wx.ID_YES:
- logger.debug("Gate open/close request")
- self.__prot__[self.PROT_ID_GARAGE].send(socket_protocol.SID_EXECUTE_REQUEST, garage_protocol.OPEN_CLOSE_GATE, None)
- event.Skip()
-
- def reset_in_temp_minmax_evt(self, event):
- self.__min_temp_in__ = None
- self.__max_temp_in__ = None
- self.in_temperature_min.SetLabel("-.- °C")
- self.in_temperature_max.SetLabel("-.- °C")
- self.Layout()
- event.Skip()
-
- def reset_out_temp_minmax_evt(self, event):
- self.__min_temp_out__ = None
- self.__max_temp_out__ = None
- self.out_temperature_min.SetLabel("-.- °C")
- self.out_temperature_max.SetLabel("-.- °C")
- self.Layout()
- event.Skip()
-
- def run(self):
- self.__task_1s__.run()
- self.__task_10s__.run()
- self.__task_60s__.run()
-
- def close(self):
- self.__task_1s__.stop()
- self.__task_1s__.join()
- self.__task_10s__.stop()
- self.__task_10s__.join()
- self.__task_60s__.stop()
- self.__task_60s__.join()
-
- def __del__(self):
- self.close()
-
-
- class MyApp(wx.App):
- def OnInit(self):
- self.frame = WetationFrameProt(None, wx.ID_ANY, "")
- self.SetTopWindow(self.frame)
- self.frame.Show()
- return True
-
-
-
-
- if __name__ == "__main__":
- report.appLoggingConfigure(os.path.dirname(__file__), config.LOGTARGET, ((config.APP_NAME, config.LOGLVL), ), fmt=config.formatter, host=config.LOGHOST, port=config.LOGPORT)
- #
- app = MyApp(0)
- app.frame.run()
- app.MainLoop()
- app.frame.close()
|