#!/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]) 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()