Beispiel #1
0
class Valve():

    def __init__(self,valve_name,usbConnectHandler):
        #log.setLevel(logging.INFO)
        self.config_handler = ConfigManager()
        self.alarm_mgr_handler = AlertManager()

        matchObj = re.findall(r'\d', valve_name, 1)
        valve_id = int(matchObj[0])
        self.vlv_id = valve_id

        self.vlv_name = valve_name
        self.vlv_board_pin_relay = int(self.config_handler.getConfigParam(self.vlv_name,"OutBoardPin"))

        self.vlv_status = G_UNKNOWN
        self.vlv_prevstatus = G_UNKNOWN

        self.vlv_lightstatus = ""
        self.vlv_prevlightstatus = ""

        self.vlv_light_list = {}  #Dict of lights. key = color GREEN RED WHITE

        self.vlv_start_time = time.time()
        self.vlv_update_time=time.time()
        self.vlv_open_time = None
        self.vlv_manualopen_time = None
        self.vlv_manual_mode = False

        self.vlv_error_time = None
        self.vlv_last_alert_time = 0
        self.vlv_last_sev1_alert_time = 0
        self.vlv_last_cmd_sent_time = None
        self.vlv_last_cmd_trigger_time = None
        self.vlv_next_auto_cmd_allowed_time = time.time() + float(self.config_handler.getConfigParam("VALVE_MANAGER", "VALVE_MANAGER_LOOP_TIMEOUT"))
        self.vlv_next_manual_cmd_allowed_time = time.time() + float(self.config_handler.getConfigParam("VALVE_MANAGER", "VALVE_MANAGER_LOOP_TIMEOUT"))
        self.vlv_lock_time=None

        self.seconds_between_alerts=float(self.config_handler.getConfigParam("ALERT", "TimeBetweenAlerts"))
        self.vlv_alert_light_time = None
        self.auto_force_close_valve = False
        self.vlv_force_lock = False
        self.vlv_add_alert_time_by_type = {}  #Key is Alert type, data is time()

        self.valve_properties = None
        valveconfigfilename = self.config_handler.getConfigParam("INTERNAL", "VALVE_CONFIG_DEFINITION_FILE")
        self.loadValveConfig(valveconfigfilename)

        self.nbrfault=0

        self.vlv_statusEventList=[]

        self.usbConnectHandler=usbConnectHandler
        self.initBoardPinModeOutput(int(self.config_handler.getConfigParam(self.vlv_name,"OutBoardPin")))
        tmplog = "Valve Serial Device: %s pin %d" % (self.usbConnectHandler.connection.device, int(self.config_handler.getConfigParam(self.vlv_name,"OutBoardPin")))
        log.info(tmplog)

        #Force close on startup
        self.vlv_close_time = time.time()
        self.triggerValve("close")

    def loadValveConfig(self,valveconfigfilename):
        try:
            valveconfigfilename = self.config_handler.getConfigParam("INTERNAL", "VALVE_CONFIG_DEFINITION_FILE")
            valvesConfigJSON = {}
            #loadValveConfig(self.valveconfigfilename)

            f=open(valveconfigfilename)
            valvesConfigJSON=json.load(f)
            f.close()

            key_list = valvesConfigJSON.keys()

            for keysv in key_list: #Delete other keys, must be a better way !
                if keysv != self.vlv_name:
                    pass
                else:
                    self.valve_properties = valvesConfigJSON[keysv]
                    print(self.valve_properties)
            pass
        except IOError:
            log.error("Config file " + valveconfigfilename + " does not exist ! ")
            log.error("Exiting...")
            os._exit(-1)
        except Exception:
            traceback.print_exc()
            log.error("Exiting...")
            os._exit(-1)

    def isValveOpen(self,mything,myservice,myid):
        return self.vlv_status==G_OPEN

    def startLightFlash(self,color):
        key=self.vlv_name+"_"+color
        if (key in self.vlv_light_list):
            # log.info("Green startFlashLight started !!!")
            self.vlv_light_list[key].startFlashLight()

    def stopLightFlash(self, color):
        key = self.vlv_name + "_" + color
        if (key in self.vlv_light_list):
            # log.info("Green startFlashLight started !!!")
            self.vlv_light_list[key].stopFlashLight()

    def turnOnLight(self, color):
        key = self.vlv_name + "_" + color
        if (key in self.vlv_light_list):
            # log.info("Green startFlashLight started !!!")
            self.vlv_light_list[key].turnOnLight()

    def turnOffLight(self, color):
        key = self.vlv_name + "_" + color
        if (key in self.vlv_light_list):
            # log.info("Green startFlashLight started !!!")
            self.vlv_light_list[key].turnOffLight()

    def addAlert(self, id, device,extratxt=""):
        self.vlv_last_alert_time = time.time()
        status_text="request for Alert %s %s %s" %(id, device,extratxt)

        if (id in self.vlv_add_alert_time_by_type):
            lastalerttime = self.vlv_add_alert_time_by_type[id]
            if ( time.time() >(lastalerttime+self.seconds_between_alerts)):
                try:
                    del self.vlv_add_alert_time_by_type[id]
                except KeyError:
                    pass

                log.info("%s can now be sent again for %s!" %(id,device))
            else:
                log.debug("Skip %s" % status_text)
        else:
            self.vlv_add_alert_time_by_type[id]=time.time()
            status_text = self.alarm_mgr_handler.addAlert(id, device, extratxt)
            log.warning(status_text)

        return status_text

    def determineValveOpenClosedStatus(self):
        #log.debug("Valve Determine Status called !")
        valve_status_text=self.vlv_name+":"+self.vlv_status
        logstr=""
        do_print_status=False

        if (self.vlv_prevstatus != self.vlv_status):
            self.vlv_update_time = time.time()
            do_print_status = True

        self.vlv_prevstatus = self.vlv_status
        if (do_print_status == True):
            self.printStatus()
        return (valve_status_text)

    def updateSensor(self):

        #Nothing to check here ! Weather API @Todo
        try:
            status_text="OK"
            # self.tid,self.module,self.device,self.status,self.text)
        except Exception:
            self.auto_force_close_valve = True
            sensor_status_text = self.addAlert("HW101", self.vlv_name )

        resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]",self.vlv_name ,self.vlv_status,status_text)

        return resp

    def lock(self):
        tmptxt=""
        if self.vlv_force_lock==False:
            tmptxt="%s Valve Lock down requested" % (self.vlv_name)
            self.vlv_force_lock = True
            self.vlv_lock_time=time.time()
        else:
            self.vlv_force_lock = False
            tmptxt="%s Valve UnLock requested" % (self.vlv_name)
            # self.vlv_lock_time=None
        log.info(tmptxt)

        # resp = CommmandQResponse(time.time() * 1000000, "[DeviceManager] " + self.determineValveOpenClosedStatus())
        # self.tid,self.module,self.device,self.status,self.text)
        mod="[DeviceManager]"
        stat="[STATUS]"
        str0 = self.determineValveOpenClosedStatus().split(':')
        dev=str0[0]
        if str0.__len__() >1:
            stat=str0[1]
        resp = CommmandQResponse(time.time() * 1000000, mod, dev, stat,"")

        return (resp)

    def status(self):
        log.debug("Valve status called !")
        self.updateSensor()
        rsptxt=self.getCmdQResponseStatusStr()

        #resp = CommmandQResponse(time.time() * 1000000, "[DeviceManager] " + self.determineValveOpenClosedStatus())
        # self.tid,self.module,self.device,self.status,self.text)
        mod="[DeviceManager]"
        stat="[STATUS]"
        str0 = self.determineValveOpenClosedStatus().split(':')
        dev=str0[0]
        if str0.__len__() >1:
            stat=str0[1]
        resp = CommmandQResponse(time.time() * 1000000, mod, dev, stat,rsptxt)

        return (resp)

    def getCmdQResponseStatusStr(self):
        resp_json=None
        try:
            rspstr={}
            if self.vlv_open_time != None:
                rspstr[G_OPEN]=GarageUtil.getTimePrintOut(self,time.time()-self.vlv_open_time)

            else:
                rspstr[G_OPEN]=""
            if self.vlv_close_time != None and self.vlv_open_time != None:
                rspstr[G_CLOSED] = GarageUtil.getTimePrintOut(self,time.time()-self.vlv_close_time)
            else:
                rspstr[G_CLOSED]=""
            if self.vlv_error_time != None:
                rspstr[G_ERROR] = GarageUtil.getTimePrintOut(self,time.time()-self.vlv_close_time)
            else:
                rspstr[G_ERROR]="Check!"

            if self.vlv_lock_time !=None:
                rspstr[G_LOCK] = GarageUtil.getTimePrintOut(self,time.time()-self.vlv_lock_time)
            else:
                rspstr[G_LOCK] = ""

            resp_json = json.dumps(rspstr)

        except Exception:
            log.error("Bug handling of getCmdQResponseStatusStr JSON convert")
            traceback.print_exc()
            resp_json="Error!"

        return json.dumps(resp_json)

    def clear(self):
        # resp = CommmandQResponse(time.time()*1000000, "Valve alarm cleared" )
        resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]", "", "", "Valve alarm cleared")
        return (resp)

    def addLight(self, key,lightobj):
        self.vlv_light_list[key]=lightobj
        self.initBoardPinModeOutput(self.vlv_light_list[key].board_pin_id)
        self.turnOffLight(key)
        log.debug(str(lightobj))
        pass

    def initBoardPinModeOutput(self, pin):
        log.info("Init Board Pin %d Mode Output %s" % (pin, self.vlv_name))
        self.usbConnectHandler.pinMode(pin, self.usbConnectHandler.OUTPUT)
        self.s_update_time = time.time()


    def initBoardPinModeInput(self, pin):
        log.info("Init Board Pin %d Mode Input %s" % (pin, self.vlv_name))
        self.usbConnectHandler.pinMode(pin, self.usbConnectHandler.INPUT)
        self.s_update_time=time.time()

    def manualopen(self):
        try:
            status_text = "ManualOpen"
            self.alarm_mgr_handler.clearAlertDevice("VALVE_COMMAND", self.vlv_name,"called from manualopen()")
            self.addAlert("VO003", self.vlv_name, " Manually opened")
            self.vlv_manualopen_time=time.time()
            self.vlv_open_time = time.time()
            self.vlv_manual_mode = True
            #self.triggerValve("open")
            self.open()
            resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]", "", "", status_text)
            log.debug(status_text)
            return resp
        except Exception:
            traceback.print_exc()
            logstr = "open() Valve %s Status = %s Fatal Exception" % (self.vlv_name, self.vlv_status)
            log.error(logstr)
            os._exit(-1)

        # resp=CommmandQResponse(0, status_text)
        #resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]", "", "", status_text)
        #log.debug(status_text)
        #return resp

    def open(self):

        status_text="Open"
        self.alarm_mgr_handler.clearAlertDevice("VALVE_COMMAND", self.vlv_name, "from open()")
        try:
            self.vlv_open_time=time.time()
            if self.vlv_force_lock == False:
                if (self.vlv_status  == G_CLOSED ):
                    if time.time() > self.vlv_next_manual_cmd_allowed_time:
                        self.alarm_mgr_handler.clearAlertDevice("VALVE_OPEN", self.vlv_name,"from open() / "+self.printStatus(True))
                        self.triggerValve("open")
                        status_text=self.addAlert("VTO01", self.vlv_name)
                        self.vlv_next_manual_cmd_allowed_time = time.time() + float(self.config_handler.getConfigParam("VALVE_COMMON", "TimeBetweenButtonManualPressed"))
                        # self.startLightFlash('GREEN')
                    else:
                        # open denied. Too early to retry!
                        self.vlv_manual_mode = False
                        status_text = self.addAlert("VTO02", self.vlv_name)


                else:
                    # open denied. current status is " + self.vlv_status
                    status_text = self.addAlert("VTO03", self.vlv_name, self.vlv_status)

            else: #Lock!
                status_text = self.addAlert("VTO04", self.vlv_name, self.vlv_status)

            self.vlv_last_cmd_trigger_time=time.time()

        except Exception:
            traceback.print_exc()
            logstr = "open() Valve %s Status = %s Fatal Exception" % (self.vlv_name, self.vlv_status)
            log.error(logstr)
            os._exit(-1)
        # resp=CommmandQResponse(0, status_text)
        resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]", "", "", status_text)
        log.debug(status_text)
        return resp

    def close(self):
        logtxt = self.vlv_name +" "
        status_text = "Close"

        try:
            # if time.time() >= (self.vlv_close_time+ 2*float(self.config_handler.getConfigParam("NOTIFICATION_MANAGER", "NOTIFICATION_MANAGER_LOOP_TIMEOUT"))): #Dont clear alarm right away. give time to notification manager
            #     self.alarm_mgr_handler.clearAlertDevice("VALVE_OPEN", self.vlv_name)
            #     self.alarm_mgr_handler.clearAlertDevice("VALVE_COMMAND", self.vlv_name)
            #     self.vlv_last_sev1_alert_time = time.time()

            # if To avoid misleading logs. Valve will be closed regardless
            if self.vlv_manual_mode == True and self.auto_force_close_valve == False:
                logtxt = logtxt + "Close Manual "
                log.info(logtxt)
                if time.time() > self.vlv_next_manual_cmd_allowed_time:
                    # close. valve normal path!
                    status_text = self.addAlert("VTC01", self.vlv_name)
                    self.vlv_next_manual_cmd_allowed_time = time.time() + float(self.config_handler.getConfigParam("VALVE_COMMON", "TimeBetweenButtonManualPressed"))
                else:
                    # Closing to often. closing anyways. Could indicate a GUI bug !
                    #if self.vlv_status != G_CLOSED and time.time() > (self.vlv_start_time+30):
                    self.alarm_mgr_handler.clearAlertDevice("VALVE_COMMAND", self.vlv_name,"from close()")
                    status_text = self.addAlert("VTC02", self.vlv_name, "from close()")

                if self.vlv_status != G_OPEN:
                    #                         and time.time() > (self.vlv_start_time+30): #hard coded msg, dont wan to see on startup
                    # already closed !
                    # Avoid on startup
                    #self.alarm_mgr_handler.clearAlertDevice("VALVE_COMMAND", self.vlv_name)
                    status_text = self.addAlert("VTC03", self.vlv_name,self.vlv_status)
            else:
                logtxt = logtxt + "close() Auto "
                log.debug(logtxt)

            self.vlv_close_time = time.time()
            self.triggerValve("close")
            self.vlv_last_cmd_trigger_time=time.time()
            self.vlv_manual_mode = False #reset manual mode to false


        except Exception:
            traceback.print_exc()
            logstr = "close() Valve %s Status = %s Fatal Exception" % (self.vlv_name, self.vlv_status)
            log.error(logstr)
            os._exit(-1)
        # resp=CommmandQResponse(0, status_text)
        resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]", "", "", status_text)

        log.debug(status_text)

        return resp

    '''
    return True if OK, False if problem.
    OS exit if fatal !
    '''
    def triggerValve(self,cmd="close"):
        logtxt=self.vlv_name+" "+cmd+" triggerValve "
        #ValveManager Check Policy will not call this because status os LOCKOPEN and OPEN in this mode !

        valve_cmd = None
        if self.valve_properties["TimeProperties"]["reverse_hi_low"] == "False":
            valve_cmd = self.usbConnectHandler.LOW
        else:
            valve_cmd = self.usbConnectHandler.HIGH

        try:
            if (cmd == "open"):
                #self.vlv_open_time = time.time()
                if (self.vlv_force_lock):
                    logtxt = logtxt + "Trigger valve open refused because of Manual Override"
                else:
                    if self.valve_properties["TimeProperties"]["reverse_hi_low"] == "False":
                        valve_cmd = self.usbConnectHandler.HIGH
                    else:
                        valve_cmd = self.usbConnectHandler.LOW
                    self.vlv_status=G_OPEN
            elif cmd == "close":
                #self.vlv_close_time = time.time()
                self.vlv_manual_mode = False #Manual Mode disbaled by close
                if self.valve_properties["TimeProperties"]["reverse_hi_low"] == "False":
                    valve_cmd = self.usbConnectHandler.LOW
                else:
                    valve_cmd = self.usbConnectHandler.HIGH
                self.vlv_status = G_CLOSED
            else:
                self.vlv_status = G_ERROR
                logtxt = logtxt + "Error with triggerValve command"
                status_text = self.addAlert("SW101", self.vlv_name)

            self.usbConnectHandler.digitalWrite(self.vlv_board_pin_relay, valve_cmd)
            self.vlv_next_auto_cmd_allowed_time = time.time() + float(self.config_handler.getConfigParam("VALVE_COMMON", "TimeBeforeAutoRetryClose"))
            self.vlv_next_manual_cmd_allowed_time = time.time() + float(self.config_handler.getConfigParam("VALVE_COMMON", "TimeBetweenButtonManualPressed"))
            self.vlv_last_cmd_sent_time=time.time()
            log.debug(logtxt)

        except Exception:
            log.error("triggerValve Open or Close fatal error !")
            traceback.print_exc()
            os._exit(-1)

        return True



    def printStatus(self, silent=False):
        logstr = "%s:%s " % (self.vlv_name, self.vlv_status)


        if self.vlv_update_time != None:
            printut = datetime.datetime.fromtimestamp(self.vlv_update_time).strftime("%Y%m%d-%H%M%S")
        else:
            printut = "None"
        if self.vlv_open_time != None:
            printot = datetime.datetime.fromtimestamp(self.vlv_open_time).strftime("%Y%m%d-%H%M%S")
        else:
            printot = "None"
        if self.vlv_manualopen_time != None:
            printmot = datetime.datetime.fromtimestamp(self.vlv_manualopen_time).strftime("%Y%m%d-%H%M%S")
        else:
            printmot = "None"
        if self.vlv_close_time != None:
            printct = datetime.datetime.fromtimestamp(self.vlv_close_time).strftime("%Y%m%d-%H%M%S")
        else:
            printct = "None"
        if self.vlv_error_time != None:
            printerrt = datetime.datetime.fromtimestamp(self.vlv_error_time).strftime("%Y%m%d-%H%M%S")
        else:
            printerrt = "None"
        if self.vlv_last_alert_time != None:
            printlast = datetime.datetime.fromtimestamp(self.vlv_last_alert_time).strftime("%Y%m%d-%H%M%S")
        else:
            printlast = "None"
        try:
            logstr = logstr + "updte=" + printut + " opn=" + printot + " clse=" + printct + " mopn="+printmot+" err=" + printerrt + " Alert=" + printlast
            if silent == False:
                log.info(logstr)
        except Exception:
            log.error("Time Stamp print error ?!?  print to stdout ")
            print(logstr)

        return logstr

    def getAllLightStatus(self):
        all_light_status = ""
        for color in sorted({"GREEN", "RED", "WHITE"}):
            key_dev_color = self.vlv_name + "_" + color
            if key_dev_color in self.vlv_light_list:
                light_status = color[0]
                if self.vlv_light_list[key_dev_color].getLightStatus() == "ON":
                    all_light_status += light_status.lower()
                elif self.vlv_light_list[key_dev_color].getLightStatus() == "FLASH":
                    all_light_status += light_status.upper()
                else:
                    all_light_status += "-"
            else:
                log.debug("%s %s light defined yet" % (color,self.vlv_name))
        return all_light_status

    def test(self):
        self.initBoardPinModeOutput(self.vlv_board_pin_relay)
        logbuf = "Arduino Init Pin=%d" % self.vlv_board_pin_relay
        log.info(logbuf)
        for n in range(0, 1):
            self.usbConnectHandler.digitalWrite(self.vlv_board_pin_relay, self.usbConnectHandler.HIGH)
            log.info("ON")
            sleep(3)
            self.usbConnectHandler.digitalWrite(self.vlv_board_pin_relay, self.usbConnectHandler.LOW)
            log.info("OFF")
            sleep(2)
            n += 1
        resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]", "", "", "test!")

        return resp

    def get_serialdevicename(self):
        return self.usbConnectHandler.connection.device

    def get_vlv_name(self):
        return self.vlv_name
Beispiel #2
0
class GarageManager():

    def __init__(self):
        #log.setLevel(logging.INFO)
        log.info("GarageManager Starting")
        self.garage_manager_start_time=time.time()
        self.config_handler = ConfigManager()
        self.alarm_mgr_handler = AlertManager()
        self.GarageOpenTriggerWarningElapsedTime = float(self.config_handler.getConfigParam("GARAGE_COMMON","GarageOpenTriggerWarningElapsedTime"))
        self.GarageOpenTriggerCloseDoorElapsedTime = float(self.config_handler.getConfigParam("GARAGE_COMMON","GarageOpenTriggerCloseDoorElapsedTime"))
        self.LightGarageOpenTriggerCloseDoorPreWarningBeforeClose = float(self.config_handler.getConfigParam("GARAGE_COMMON","LightGarageOpenTriggerCloseDoorPreWarningBeforeClose"))
        self.cherryweb_server_last_run_time = time.time()
        self.gm_add_alert_time_by_type = {}  #Key is Alert type, data is time()
        self.seconds_between_alerts=float(self.config_handler.getConfigParam("ALERT", "TimeBetweenAlerts"))
        self.g_add_alert_time_by_type = {}  #Key is Alert type, data is time()
        self.error_message_count = 0




    def monitor(self):
        self.dev_manager_handler = DeviceManager()
        self.deviceList=self.dev_manager_handler.deviceList
        i=0

        while (True):

            if time.time() > (self.garage_manager_start_time+60):
                if cherrypy.engine.state == cherrypy.engine.states.STARTED:
                    log.debug("Cherrypy Web Server Thread Running")
                    self.cherryweb_server_last_run_time = time.time()
                else:
                    log.error("Cherrypy Web Server Thread Dead")
                    if (time.time() > (self.cherryweb_server_last_run_time + 120) ):
                        log.error("Cherrypy Web server thread not running, force exit of garage processes for crontab restart !")
                        os._exit(-1)
                    elif (time.time() > (self.cherryweb_server_last_run_time + 30) ):
                        # 15sec to allow for cherry pi web server to start
                        log.error("Cherrypy Web server thread not running, sending alert SW001 !")
                        # status_text = self.alarm_mgr_handler.addAlert("SW001", "RASPBERRY_PI")
                        status_text = self.addAlert("SW001", "RASPBERRY_PI","Cherrypy Web server thread not running")
                        log.error(status_text)
            else:
                log.debug("Cherrypy Web server thread monitoring off for 1 min after GarageManager thread startup")

            for key in self.deviceList:
                sensor_status_str = ""
                obj = self.deviceList[key]
                if isinstance(obj, GarageDoor):
                    obj.updateSensor()
                    obj.determineGarageDoorOpenClosedStatus()
                    self.checkGaragePolicy(obj)
                    if log.isEnabledFor(logging.DEBUG) or i % 100000 == 0:
                        tmplog = "%s Device: %s" % (obj.get_g_name(), obj.get_serialdevicename())
                        log.info(tmplog)
                # else:
                #     if self.error_message_count % 1000 == 0:
                #         log.info("No Garage configured!")
                #     self.error_message_count = self.error_message_count + 1

            self.alarm_mgr_handler.processAlerts()

            if log.isEnabledFor(logging.DEBUG) or i%10000==0:
                log.info("** garageManager heart beat %d **" % (i))
                self.dev_manager_handler.listDevices()
                self.alarm_mgr_handler.status()
            sleep(float(self.config_handler.getConfigParam("GARAGE_MANAGER","GARAGE_MANAGER_LOOP_TIMEOUT")))
            i=i+1
        pass

    #Warpper for add alert
    def addAlert(self, id, device,extratxt=""):
        self.g_last_alert_time = time.time()
        status_text="request for Alert %s %s %s" %(id, device,extratxt)


        if (id in self.gm_add_alert_time_by_type):
            lastalerttime = self.g_add_alert_time_by_type[id]
            if ( time.time() >(lastalerttime+self.seconds_between_alerts)):
                self.gm_add_alert_time_by_type.remove(id)
                log.info("%s can now be sent again for %s!" %(id,device))
            else:
                log.debug("Skip %s" % status_text)
        else:
            self.gm_add_alert_time_by_type[id]=time.time()
            status_text = self.alarm_mgr_handler.addAlert(id, device, extratxt)
            log.warning(status_text)

        return status_text


    def checkGaragePolicy(self,gd: GarageDoor ):
        try:

            # This is how the open time threasholds are defined.  refopentime is there to ensure opentime non null value.
            # ------- <opentimewarning>----<opentimeredcritical>--<opentimefinal>----<opentimelightingstop>
            refopentime = time.time()
            if gd.g_open_time != None:
                refopentime = gd.g_open_time
            opentimefinal = refopentime + float(self.GarageOpenTriggerCloseDoorElapsedTime)
            opentimeredcritical = refopentime + opentimefinal - self.LightGarageOpenTriggerCloseDoorPreWarningBeforeClose
            opentimewarning = refopentime + float(self.GarageOpenTriggerWarningElapsedTime)



            if gd.g_status == G_OPEN:  #Locked Status is LOCKOPEN ! Don't allow auto close on lock open.

                remain_time_before_next_command_allowed = gd.g_next_auto_cmd_allowed_time - time.time()


                #datetime.datetime.fromtimestamp(time.time()).strftime("%Y%m%d-%H%M%S")
                if remain_time_before_next_command_allowed > 0:
                    tmpstr="checkGaragePolicy %s open=%s Allowed Next_Manual_Cmd=%s Next_Auto_Cmd=%s --> Remain=%d sec"  % (gd.g_name, \
                                                                                                    # datetime.datetime.fromtimestamp(time.time()).strftime("%Y%m%d-%H%M%S"),\
                                                                                                    datetime.datetime.fromtimestamp(gd.g_open_time).strftime("%Y%m%d-%H%M%S"), \
                                                                                                    datetime.datetime.fromtimestamp(gd.g_next_manual_cmd_allowed_time).strftime("%Y%m%d-%H%M%S"), \
                                                                                                    datetime.datetime.fromtimestamp(gd.g_next_auto_cmd_allowed_time).strftime("%Y%m%d-%H%M%S"), \
                                                                                                    remain_time_before_next_command_allowed)
                    if (int(time.time())%60==0  ):
                        log.info(tmpstr )
                if (gd.g_open_time != None): #Is there an open time stamp ?
                    if time.time() > opentimefinal:
                        # " GARAGE OPEN TIME EXPIRED ALERT"
                        #status_text = gd.g_name + " " + self.alarm_magr_handler.alertTable["G0001"]["text"]
                        self.alarm_mgr_handler.clearAlertDevice("GARAGE_COMMAND", gd.g_name," from checkGaragePolicy() G_OPEN opentimefinal")
                        self.alarm_mgr_handler.clearAlertDevice("GARAGE_OPEN", gd.g_name, " from checkGaragePolicy() G_OPEN opentimefinal")
                        status_text = gd.addAlert("GO001", gd.g_name)
                        gd.g_last_alert_time = time.time()
                        log.debug(status_text)
                        #close door when timer expires!
                        if gd.g_next_auto_cmd_allowed_time != None and time.time() > gd.g_next_auto_cmd_allowed_time:
                            gd.g_next_auto_cmd_allowed_time = time.time() + float(self.config_handler.getConfigParam("GARAGE_COMMON", "TimeBeforeAutoRetryCloseDoor"))

                            tmpstr = "checkGaragePolicy triggerGarageDoor %s Next Auto Cmd Allowed Time=%s --> Remain=%d sec" % (gd.g_name, \
                                                                                                           datetime.datetime.fromtimestamp(gd.g_next_auto_cmd_allowed_time).strftime("%Y%m%d-%H%M%S"), \
                                                                                                           remain_time_before_next_command_allowed)
                            log.info(tmpstr)
                            if (gd.g_auto_force_ignore_garage_open_close_cmd == False and gd.g_manual_force_lock_garage_open_close_cmd==False):
                                gd.triggerGarageDoor() # return True is No Manual Overide
                            else:
                                tmpstr = "checkGaragePolicy %s Automatic triggerGarageDoor not allowed" % (gd.g_name)
                                log.info(tmpstr)

                    elif time.time() > opentimeredcritical :
                            # and time.time() < (gd.g_open_time + self.GarageOpenTriggerCloseDoorElapsedTime) :
                        #LightGarageOpenTriggerCloseDoorPreWarningBeforeClose
                        gd.startLightFlash('RED')
                        gd.stopLightFlash('GREEN')
                        gd.stopLightFlash('WHITE')
                        gd.turnOffLight('GREEN')
                        gd.turnOffLight('WHITE')
                    elif time.time() > opentimewarning:
                        # status_text = gd.g_name + " GARAGE OPEN TIME WARNING ALERT"
                        # self.alarm_magr_handler.addAlert(CommmandQResponse(0, status_text))
                        self.alarm_mgr_handler.clearAlertDevice("GARAGE_COMMAND", gd.g_name," from checkGaragePolicy() G_OPEN opentimewarning")
                        self.alarm_mgr_handler.clearAlertDevice("GARAGE_OPEN", gd.g_name, " from checkGaragePolicy() G_OPEN opentimewarning")
                        status_text = gd.addAlert("GO002", gd.g_name)
                        gd.g_last_alert_time = time.time()
                        log.debug(status_text)
                        gd.startLightFlash('GREEN')
                    else:
                        gd.turnOnLight('GREEN')
                        gd.turnOnLight('WHITE')
                        gd.stopLightFlash('RED')
                        gd.turnOffLight('RED')


            if (gd.g_status == G_LOCKOPEN):
                #Alert in this case every GarageLockOpenTriggerAlarmElapsedTime
                if (gd.g_lock_time!=None and time.time() > (gd.g_lock_time + float(self.config_handler.getConfigParam("GARAGE_COMMON","GarageLockOpenTriggerAlarmElapsedTime")))):
                    self.alarm_mgr_handler.clearAlertDevice("GARAGE_COMMAND", gd.g_name, "from checkGaragePolicy() G_LOCKOPEN")
                    self.alarm_mgr_handler.clearAlertDevice("GARAGE_OPEN", gd.g_name," from checkGaragePolicy() G_LOCKLOPEN")
                    status_text = gd.addAlert("GLO01", gd.g_name)
                    gd.startLightFlash('WHITE')
                    gd.startLightFlash('RED')
                    gd.startLightFlash('GREEN')
                    gd.g_last_alert_time = time.time()
                    log.debug(status_text)
                    gd.g_lock_time = time.time()
                else:
                    gd.stopLightFlash('WHITE')
                    gd.stopLightFlash('RED')
                    gd.stopLightFlash('GREEN')
                    gd.turnOnLight('WHITE')
                    gd.turnOnLight('GREEN')
                    gd.turnOnLight('RED')


            if (gd.g_status.find(G_CLOSED)>=0):
                if (gd.g_close_time != None): #Is there an open time stamp ?
                    # Manage specific case for light when GarageManager was restarted, door lock but door not opened !
                    close_white_light_delay=120
                    opentimelightingstop = gd.g_close_time + close_white_light_delay
                    #Change light status during the close_white_light_delay period and a little more!
                    if time.time() <= (gd.g_close_time + (close_white_light_delay+float(self.config_handler.getConfigParam("GARAGE_COMMON","GarageDoorAssumedClosedTime"))) ):
                        log.debug("%s Turn off all lights!" % gd.g_name)
                        gd.stopLightFlash('WHITE')
                        if time.time() > opentimelightingstop:
                            gd.turnOffLight('WHITE')
                        else:
                            gd.turnOnLight('WHITE')

                        gd.turnOffLight('GREEN')
                        gd.turnOffLight('RED')

                        gd.stopLightFlash('GREEN')
                        gd.stopLightFlash('RED')

        except Exception:
            traceback.print_exc()
            gd.g_auto_force_ignore_garage_open_close_cmd=True
            # status_text=gd.g_name + " CLOSE BY COMMAND DISABLED"
            # self.alarm_magr_handler.addAlert(CommmandQResponse(0, status_text))
            self.alarm_mgr_handler.clearAlertDevice("GARAGE_COMMAND", gd.g_name)
            self.alarm_mgr_handler.clearAlertDevice("GARAGE_OPEN", gd.g_name)
            status_text = gd.addAlert("GCD01", gd.g_name)
            gd.g_last_alert_time = time.time()
            log.debug(status_text)
            sleep(5)
            os._exit(-1)

    def addAlert(self, id, device,extratxt=""):
        self.g_last_alert_time = time.time()
        status_text="request for Alert %s %s %s" %(id, device,extratxt)

        if (id in self.g_add_alert_time_by_type):
            lastalerttime = self.g_add_alert_time_by_type[id]
            if ( time.time() >(lastalerttime+self.seconds_between_alerts)):
                try:
                    del self.g_add_alert_time_by_type[id]
                except KeyError:
                    pass

                log.info("%s can now be sent again for %s!" %(id,device))
            else:
                log.debug("Skip %s" % status_text)
        else:
            self.g_add_alert_time_by_type[id]=time.time()
            status_text = self.alarm_mgr_handler.addAlert(id, device, extratxt)
            log.warning(status_text)

        return status_text
Beispiel #3
0
class WeatherManager(metaclass=SingletonMeta):
    def __init__(self):
        #log.setLevel(logging.INFO)
        log.info("WeatherManager Starting")
        self.weather_manager_start_time = time.time()

        self.config_handler = ConfigManager()
        self.alarm_mgr_handler = AlertManager()
        self.cherryweb_server_last_run_time = time.time()

        #WEATHER_CHECK_ENABLE
        self.meteo_check = self.config_handler.getConfigParam(
            "WEATHER_MANAGER", "WEATHER_CHECK_ENABLE")
        self.meteo_url = self.config_handler.getConfigParam(
            "WEATHER_MANAGER", "WEATHER_URL")
        self.meteo_url_json = self.config_handler.getConfigParam(
            "WEATHER_MANAGER", "WEATHER_URL_JSON")
        self.use_json_resp = False

        self.isRainForecast = False

    def monitor(self):
        i = 0
        lastlogprint = time.time()
        while (True):
            if i % 10000 == 0 or time.time() > (lastlogprint + float(
                    self.config_handler.getConfigParam(
                        "VALVE_MANAGER",
                        "VALVE_DISPLAY_ALL_STATUS_INTERVAL"))):
                log.info("** WeatherManager heart beat %d **" % (i))
                lastlogprint = time.time()

            if time.time() > (self.weather_manager_start_time + 60):
                if cherrypy.engine.state == cherrypy.engine.states.STARTED:
                    if i % 1000 == 0:
                        log.debug("Cherrypy Web Server Thread Running")
                    self.cherryweb_server_last_run_time = time.time()
                else:
                    log.error("Cherrypy Web Server Thread Dead")
                    if (time.time() >
                        (self.cherryweb_server_last_run_time + 120)):
                        log.error(
                            "Cherrypy Web server thread not running, force exit of valve processes for crontab restart !"
                        )
                        os._exit(-1)
                    elif (time.time() >
                          (self.cherryweb_server_last_run_time + 30)):
                        # 15sec to allow for cherry pi web server to start
                        log.error(
                            "Cherrypy Web server thread not running, sending alert SW001 !"
                        )
                        # status_text = self.alarm_mgr_handler.addAlert("SW001", "RASPBERRY_PI")
                        status_text = self.alarm_mgr_handler.addAlert(
                            "SW001", "WEATHER",
                            "cherryweb_server_last_run_time")
                        log.error(status_text)
            else:
                if i % 5 == 0:
                    log.debug(
                        "Cherrypy Web server thread monitoring off for 1 min after ValveManager thread startup"
                    )

            try:
                if self.meteo_check == "True":
                    if self.use_json_resp == True:
                        asyncio.run(self.getJSONWeatherFromWTTR())
                    else:
                        asyncio.run(self.getWeatherFromWTTR())
                else:
                    log.info("Weather check disabled")
            except Exception:
                #self.addAlert("SW002", self.__class__.__name__ , "getWeatherFromWTTR Exeption")
                log.error("Get Weather Error!")
                traceback.print_exc()

            sleep(
                float(
                    self.config_handler.getConfigParam(
                        "WEATHER_MANAGER", "WEATHER_MANAGER_LOOP_TIMEOUT")))
            i = i + 1
        pass

    async def getJSONWeatherFromWTTR(self):
        found_rain = False
        async with aiohttp.ClientSession() as session:
            meteo_url = self.meteo_url_json
            try:
                async with session.get(meteo_url) as resp:
                    meteo_json = await resp.json()
                    logstr1 = "Status:" + str(
                        resp.status
                    ) + " Content-type:", resp.headers['content-type']
                    log.debug(logstr1)
                    if resp.status != 200:  # 200 is normal response
                        logstr = self.meteo_json + " Status:" + str(
                            resp.status
                        ) + " Content-type:", resp.headers['content-type']
                        log.error(logstr)
                        status_text = self.alarm_mgr_handler.addAlert(
                            "SW001", "WEATHER", logstr)
                        log.error(status_text)
                        #sleep(10)  #Make sure no crazy requests
                    else:
                        logstr2 = json.dumps(meteo_json)
                        log.debug(logstr2)
                        current_condition = meteo_json["current_condition"][0][
                            "weatherDesc"][0]['value'].upper()
                        localObsDateTime = meteo_json["current_condition"][0][
                            "localObsDateTime"]
                        if "RAIN" in current_condition and "POSSIBLE" not in current_condition:  #MIST also possible !
                            found_rain = True
                        logstr3 = current_condition + " on " + localObsDateTime
                        log.info(logstr3)
            except Exception:
                log.error("Get Weather Error, check url:" +
                          self.meteo_url_json)
                traceback.print_exc()

            if found_rain == True:
                self.isRainForecast = True
            else:
                self.isRainForecast = False

        logstr = "is Rain Forecasted? " + str(self.isRainForecast)
        log.info(logstr)

    async def getWeatherFromWTTR(self):
        found_rain = False
        async with aiohttp.ClientSession() as session:
            meteo_url = self.meteo_url
            async with session.get(meteo_url) as resp:
                html = await resp.text()
                logstr1 = "Status:" + str(
                    resp.status
                ) + " Content-type:", resp.headers['content-type']
                log.debug(logstr1)
                lines = html.split('\n')
                log.info("*Weather*")
                for line in lines:
                    # linesub = line[16:]
                    # linesub2 = re.sub(f'[^{re.escape(string.printable)}]', '', linesub)
                    # linesub3 = linesub2.rstrip().lstrip().upper()
                    linetmp = line.upper()
                    if "RAIN" in linetmp:  #MIST also possible !
                        found_rain = True
                    log.info(line)

        if found_rain == True:
            self.isRainForecast = True
        else:
            self.isRainForecast = False
        logstr = "is Rain Forecasted? " + str(self.isRainForecast)
        log.info(logstr)

    def isRainForecasted(self):
        return self.isRainForecast
Beispiel #4
0
class ValveManager():
    def __init__(self):
        #log.setLevel(logging.INFO)
        log.info("ValveManager Starting")
        self.valve_manager_start_time = time.time()
        self.config_handler = ConfigManager()
        self.alarm_mgr_handler = AlertManager()
        self.notif_mgr_handler = NotificationManager()
        self.email_sender = self.config_handler.getConfigParam(
            "EMAIL_ACCOUNT_INFORMATION", "USER")
        self.email_recipient = self.config_handler.getConfigParam(
            "EMAIL_ACCOUNT_INFORMATION", "RECIPIENTLISTINFO")

        self.weather_mgr_handler = WeatherManager()

        self.isRainForecast = False
        self.isRainForecastPrev = False

        self.ValveOpenTriggerCriticalElapsedTime = float(
            self.config_handler.getConfigParam(
                "VALVE_COMMON", "ValveOpenTriggerCriticalElapsedTime"))
        self.ValveOpenTriggerWarningElapsedTime = float(
            self.config_handler.getConfigParam(
                "VALVE_COMMON", "ValveOpenTriggerWarningElapsedTime"))
        self.ValveHardwareResponseTime = float(
            self.config_handler.getConfigParam("VALVE_COMMON",
                                               "ValveHardwareResponseTime"))

        self.cherryweb_server_last_run_time = time.time()
        self.gm_add_alert_time_by_type = {}  #Key is Alert type, data is time()
        self.seconds_between_alerts = float(
            self.config_handler.getConfigParam("ALERT", "TimeBetweenAlerts"))
        self.vlv_add_alert_time_by_type = {
        }  #Key is Alert type, data is time()
        self.error_message_count = 0
        self.valve_last_info_log = {}  #use as heart beat !
        self.last_schedule_process_time = {
        }  #Avoid closing the valve at every loop
        self.endtime_null = "19990101-00:00:00"
        self.weekdays_name = [
            'MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY',
            'SUNDAY'
        ]
        self.valve_run_end_time_dict = {}  #Track end time for valve
        self.last_alert_closed_sev3_checkvalvepolicy = {
        }  #Avoid closing the valve at every loop
        self.last_alert_open_sev3_checkvalvepolicy = {
        }  #Avoid closing the valve at every loop

        self.default_language = self.config_handler.getConfigParam(
            "NOTIFICATION_COMMON", "DEFAULT_LANGUAGE")

        self.valveconfigfilename = self.config_handler.getConfigParam(
            "INTERNAL", "VALVE_CONFIG_DEFINITION_FILE")
        self.valvesConfigJSON = {}
        self.valves_by_id = {}
        self.loadValveConfig(self.valveconfigfilename)

    def loadValveConfig(self, valveconfigfilename):
        try:
            f = open(self.valveconfigfilename)
            self.valvesConfigJSON = json.load(f)
            #self.valvesConfigJSON=json.dumps(tmpcfg, indent=2, sort_keys=True)
            f.close()
            self.processValveConfig()
        except IOError:
            log.error("Config file " + self.valveconfigfilename +
                      " does not exist ! ")
            log.error("Exiting...")
            os._exit(-1)
        except Exception:
            traceback.print_exc()
            log.error("Exiting...")
            os._exit(-1)

    def processValveConfig(self):
        try:
            valve_ordered_array = []
            for keysv in self.valvesConfigJSON:
                id = self.valvesConfigJSON[keysv]["TimeProperties"]["id"]
                self.valves_by_id[
                    id] = keysv  #hash to sort valve by id. No lamda working!

            for id in sorted(self.valves_by_id, key=int):
                keysv = self.valves_by_id[id]
                valve_ordered_array.append(keysv)
            valve_ordered_array_length = len(valve_ordered_array)

            for vlvidx in range(valve_ordered_array_length):
                keysv = valve_ordered_array[vlvidx]
                keysvprevious = None
                logtxt = keysv + " "
                cfg_start_time_field = self.valvesConfigJSON[keysv][
                    "TimeProperties"]["start_time"]
                cfg_duration_field = self.valvesConfigJSON[keysv][
                    "TimeProperties"]["duration"]
                cfg_calendar_field = self.valvesConfigJSON[keysv][
                    "TimeProperties"]["calendar"]
                previous_cfg_start_time_field = None
                previous_cfg_duration_field = None
                previous_cfg_calendar_field = None
                previous_cfg_duration_field_array = None
                previous_cfg_duration_field_array_len = 0
                previous_cfg_calendar_field_array = None
                previous_cfg_calendar_field_array_len = 0
                previous_flag = False

                if cfg_start_time_field == "PREVIOUS":
                    previous_flag = True
                    logtxt = " PREVIOUS keyword found."
                    log.debug(logtxt)

                    if vlvidx == 0:
                        log.error(keysv + " Cannot be set to previous with id")
                        log.error(keysv +
                                  "Config error for cfg_start_time in " +
                                  self.valveconfigfilename)
                        os._exit(-1)

                    keysvprevious = valve_ordered_array[vlvidx - 1]
                    previous_cfg_start_time_field = self.valvesConfigJSON[
                        keysvprevious]["TimeProperties"]["start_time"]
                    previous_cfg_duration_field = self.valvesConfigJSON[
                        keysvprevious]["TimeProperties"]["duration"]
                    previous_cfg_calendar_field = self.valvesConfigJSON[
                        keysvprevious]["TimeProperties"]["calendar"]
                    previous_cfg_duration_field_array = previous_cfg_duration_field.split(
                        ',')
                    previous_cfg_duration_field_array_len = len(
                        previous_cfg_duration_field_array)
                    previous_cfg_calendar_field_array = previous_cfg_calendar_field.split(
                        ',')
                    previous_cfg_calendar_field_array_len = len(
                        previous_cfg_calendar_field_array)
                    logtxt = str(
                        vlvidx
                    ) + ") PREVIOUS value for " + keysvprevious + " from " + keysv + " st=" + previous_cfg_start_time_field
                    log.debug(logtxt)

                if previous_flag == True:
                    cfg_start_time_field_array = previous_cfg_start_time_field.split(
                        ',')
                else:
                    cfg_start_time_field_array = cfg_start_time_field.split(
                        ',')
                cfg_start_time_field_array_len = len(
                    cfg_start_time_field_array)
                cfg_duration_field_array = cfg_duration_field.split(',')
                cfg_duration_field_array_len = len(cfg_duration_field_array)
                cfg_calendar_field_array = cfg_calendar_field.split(',')
                cfg_calendar_field_array_len = len(cfg_calendar_field_array)
                if (cfg_start_time_field_array_len !=
                        cfg_duration_field_array_len
                        or cfg_start_time_field_array_len !=
                        cfg_calendar_field_array_len):
                    # Different valve configs, only use 1st entry by default.
                    logtxt = logtxt + " - Different valve configs, start_time, calendar or duration array len unequal (" + str(cfg_start_time_field_array_len) \
                             + "/"+str(cfg_duration_field_array_len) + "/"+str(cfg_calendar_field_array_len) +") "
                    log.debug(logtxt)

                if (cfg_start_time_field_array_len <= 0
                        or cfg_duration_field_array_len <= 0):
                    logtxt = logtxt + "start_time & duration array len unequal (" + str(cfg_start_time_field_array_len) + "/"+str(cfg_duration_field_array_len) \
                             + "/"+str(cfg_calendar_field_array_len) +") "
                    raise Exception(logtxt)

                logtxt2 = keysv + " " + " previous_flag:" + str(previous_flag)
                logtxt2 = " cfg_start_time_field:" + cfg_start_time_field + " cfg_duration_field:" + str(
                    cfg_duration_field
                ) + " cfg_calendar_field:" + cfg_calendar_field
                for idx in range(cfg_start_time_field_array_len
                                 ):  #Look in json field separated by commas
                    cfg_start_time = cfg_start_time_field_array[idx]
                    if idx > 0:
                        if (cfg_duration_field_array_len - 1) < idx:
                            # cfg_duration = 0
                            cfg_duration_field = self.valvesConfigJSON[keysv][
                                "TimeProperties"]["duration"]
                            cfg_duration_field = cfg_duration_field + ",0"
                            cfg_duration_field_array = cfg_duration_field.split(
                                ',')
                            cfg_duration_field_array_len = len(
                                cfg_duration_field_array)
                            self.valvesConfigJSON[keysv]["TimeProperties"][
                                "duration"] = cfg_duration_field
                        # else:
                        #     cfg_duration = cfg_duration_field_array[idx]
                        if (cfg_calendar_field_array_len - 1) < idx:
                            # cfg_calendar = 0
                            cfg_calendar_field = self.valvesConfigJSON[keysv][
                                "TimeProperties"]["calendar"]
                            cfg_calendar_field = cfg_calendar_field + ",OFF"
                            cfg_calendar_field_array = cfg_calendar_field.split(
                                ',')
                            cfg_calendar_field_array_len = len(
                                cfg_calendar_field_array)
                            self.valvesConfigJSON[keysv]["TimeProperties"][
                                "calendar"] = cfg_calendar_field

                    # else:
                    #     cfg_duration = cfg_duration_field_array[idx]
                    if previous_flag == True:
                        # If previous start time is previous + duration
                        if previous_cfg_duration_field_array_len == cfg_start_time_field_array_len:
                            previous_cfg_duration = previous_cfg_duration_field_array[
                                idx]
                        else:
                            previous_cfg_duration = 0
                            log.error(
                                "previous_flag=True with previous_cfg_duration_array len > idx"
                            )
                        now = datetime.datetime.now()
                        tmpstartdatetime = now.strftime("%Y%m%d") + "-"
                        start_datetime_str = tmpstartdatetime + cfg_start_time + ":00"  # 0s to remove ambiguity
                        logtxt = logtxt + " start_datetime #" + str(
                            idx) + "=" + start_datetime_str
                        log.debug(logtxt)
                        start_datetime_init = datetime.datetime.strptime(
                            start_datetime_str, "%Y%m%d-%H:%M:%S")
                        start_datetime = start_datetime_init + datetime.timedelta(
                            minutes=int(previous_cfg_duration))
                        start_datetime_str = start_datetime.strftime("%H:%M")
                        if idx > 0:
                            start_datetime_str = self.valvesConfigJSON[keysv][
                                "TimeProperties"][
                                    "start_time"] + "," + start_datetime_str

                        self.valvesConfigJSON[keysv]["TimeProperties"][
                            "start_time"] = start_datetime_str

                    cfg_start_time_field = self.valvesConfigJSON[keysv][
                        "TimeProperties"]["start_time"]
                    cfg_duration_field = self.valvesConfigJSON[keysv][
                        "TimeProperties"]["duration"]
                    log.debug(
                        str(idx) + ">processValveConfig " + keysv + " st=" +
                        cfg_start_time_field + " dur=" + cfg_duration_field +
                        " id=" + id)

                self.valve_last_info_log[keysv] = time.time() + float(
                    self.config_handler.getConfigParam(
                        "VALVE_MANAGER", "VALVE_DISPLAY_OPEN_STATUS_INTERVAL"))
                self.last_schedule_process_time[keysv] = time.time(
                )  # Initial time for schedule
                self.last_alert_closed_sev3_checkvalvepolicy[
                    keysv] = time.time() - float(
                        self.config_handler.getConfigParam(
                            "INTERNAL", "LOG_SEVERITY3_REPEAT_INTERVAL")
                    )  # Initial time for schedule
                self.last_alert_open_sev3_checkvalvepolicy[keysv] = time.time(
                ) - float(
                    self.config_handler.getConfigParam(
                        "INTERNAL", "LOG_SEVERITY3_REPEAT_INTERVAL")
                )  # Initial time for schedule
            #os._exit(-1)
        except Exception:
            traceback.print_exc()
            log.error("Exiting...")
            os._exit(-1)

        emailsub = "Valve Config Summary"
        emailstr = "*** " + emailsub + " ***\n"
        log.info(emailstr)
        for vlvidx in range(valve_ordered_array_length):
            #Print summary
            keysv = valve_ordered_array[vlvidx]
            cfg_start_time_field = self.valvesConfigJSON[keysv][
                "TimeProperties"]["start_time"]
            cfg_duration_field = self.valvesConfigJSON[keysv][
                "TimeProperties"]["duration"]
            cfg_calendar_field = self.valvesConfigJSON[keysv][
                "TimeProperties"]["calendar"]
            logtxt = keysv + " start_time:" + cfg_start_time_field + "  duration:" + str(
                cfg_duration_field) + " calendar:" + cfg_calendar_field
            log.info(logtxt)
            emailstr = emailstr + logtxt + "\n"
        #os._exit(-1)
        try:
            self.notif_mgr_handler.send_email(self.email_sender,
                                              self.email_recipient, emailstr,
                                              emailsub)
        except Exception:
            traceback.print_exc()
            errtxt = self.email_sender + " " + self.email_recipient + " " + emailstr + " " + emailsub
            log.error(errtxt)

        #os._exit(-1)

    def monitor(self):
        self.dev_manager_handler = DeviceManager()
        self.deviceList = self.dev_manager_handler.deviceList
        i = 0
        lastlogprint = time.time()

        while (True):
            self.isRainForecast = self.weather_mgr_handler.isRainForecasted()
            logstr = "is Rain Forecasted Direct: " + str(self.isRainForecast)
            log.debug(logstr)

            if time.time() > (self.valve_manager_start_time + 60):
                if cherrypy.engine.state == cherrypy.engine.states.STARTED:
                    if i % 1000 == 0:
                        log.debug("Cherrypy Web Server Thread Running")
                    self.cherryweb_server_last_run_time = time.time()
                else:
                    log.error("Cherrypy Web Server Thread Dead")
                    if (time.time() >
                        (self.cherryweb_server_last_run_time + 120)):
                        log.error(
                            "Cherrypy Web server thread not running, force exit of valve processes for crontab restart !"
                        )
                        os._exit(-1)
                    elif (time.time() >
                          (self.cherryweb_server_last_run_time + 30)):
                        # 15sec to allow for cherry pi web server to start
                        log.error(
                            "Cherrypy Web server thread not running, sending alert SW001 !"
                        )
                        # status_text = self.alarm_mgr_handler.addAlert("SW001", "RASPBERRY_PI")
                        status_text = self.addAlert(
                            "SW001", "RASPBERRY_PI",
                            "Cherrypy Web server thread not running")
                        log.error(status_text)
            else:
                if i % 5 == 0:
                    log.debug(
                        "Cherrypy Web server thread monitoring off for 1 min after ValveManager thread startup"
                    )

            for key in self.deviceList:
                sensor_status_str = ""
                obj = self.deviceList[key]
                if isinstance(obj, Valve):
                    obj.updateSensor()
                    obj.determineValveOpenClosedStatus()
                    self.ScheduleValve(obj)
                    self.checkValvePolicy(obj)
                    if log.isEnabledFor(logging.DEBUG) and i % 10000 == 0:
                        tmplog = "%s Device: %s" % (obj.get_vlv_name(),
                                                    obj.get_serialdevicename())
                        log.info(tmplog)

            self.alarm_mgr_handler.processAlerts()

            if i % 10000 == 0 or time.time() > (lastlogprint + float(
                    self.config_handler.getConfigParam(
                        "VALVE_MANAGER",
                        "VALVE_DISPLAY_ALL_STATUS_INTERVAL"))):
                log.info("** valveManager heart beat %d **" % (i))
                lastlogprint = time.time()
                self.dev_manager_handler.listDevices()
                self.alarm_mgr_handler.status()

            if self.isRainForecastPrev != self.isRainForecast:
                #For a print on rain forecast change
                logstr = "is Rain Forecasted: " + str(self.isRainForecast)
                self.isRainForecastPrev = self.isRainForecast
                log.info(logstr)

            sleep(
                float(
                    self.config_handler.getConfigParam(
                        "VALVE_MANAGER", "VALVE_MANAGER_LOOP_TIMEOUT")))
            i = i + 1

        pass

    def getWeekdays(self, day):
        i = self.weekdays_name.index(day)  # get the index of the selected day
        return i

    #################################################
    # Is this a day to run
    #################################################
    def isDayRun(self, vname, cal, idx):
        caldx = 0  #Take calendar 0 i.e. 1st element of array as default.
        # calendar: OFF,EVEN,MONDAY,EVERYDAY
        calname = "OFF"
        isdayrun = False
        dt = datetime.datetime.today()
        wd = datetime.datetime.today().weekday()

        cal_array = cal.split(',')
        nbrcal = len(cal_array)

        if (idx <= (nbrcal - 1)):
            calname = cal_array[idx].upper()
        else:
            calname = cal_array[0].upper()

        if calname == None or calname == "OFF":
            log.debug(vname + " OFF")
            isdayrun = False
        elif calname == "EVERYDAY":
            isdayrun = True
        elif calname == "EVEN":
            if dt.day % 2 == 0:
                isdayrun = True
        elif calname == "ODD":
            if dt.day % 2 == 1:
                isdayrun = True
        elif calname in self.weekdays_name:
            day = self.getWeekdays(calname)
            if (day == wd):
                isdayrun = True
            tmplog = vname + " #" + str(
                idx) + " Weekday check " + calname + " day=" + str(
                    day) + " wd=" + str(wd) + " " + str(isdayrun)
            log.debug(tmplog)

        else:
            log.error("Unsupported Calendar Name:'" + calname + "' for " +
                      vname)
            isdayrun = False

        tmplog = vname + " #" + str(idx) + " isdayrun=" + str(
            isdayrun) + " cal=" + cal + " nbr calendars=" + str(nbrcal)
        log.debug(tmplog)
        return isdayrun

    #################################################
    # Schedule valvle method
    #################################################
    def ScheduleValve(self, vlv: Valve):
        logtxt = "ScheduleValve: " + vlv.vlv_name + " " + vlv.vlv_status + " "
        logtxt2 = logtxt

        now = datetime.datetime.now()
        motime = "None"

        try:
            if time.time() < (
                    self.last_schedule_process_time[vlv.vlv_name] + float(
                        self.config_handler.getConfigParam(
                            "VALVE_COMMON", "SCHEDULE_CHECK_INTERVAL_MIN"))):
                return logtxt

            #last_alert_closed_sev3_checkvalvepolicy updated in check policy
            if time.time() > (
                    self.last_alert_closed_sev3_checkvalvepolicy[vlv.vlv_name]
                    + float(
                        self.config_handler.getConfigParam(
                            "INTERNAL", "LOG_SEVERITY3_REPEAT_INTERVAL"))):
                log.debug(logtxt)
            self.last_schedule_process_time[vlv.vlv_name] = time.time()

            # debug messages & events handling start

            if vlv.vlv_manual_mode == True:
                if vlv.vlv_manualopen_time != None:
                    motime = datetime.datetime.fromtimestamp(
                        vlv.vlv_manualopen_time).strftime("%Y%m%d-%H%M%S")
                logtxt = logtxt + "Skip Scheduler. Manual Mode enabled "
                log.debug(logtxt)
                return logtxt

            logtxt2 = logtxt2 + "Manual Force Status:%s  -  Last Manual Open Time:%s" % (
                vlv.auto_force_close_valve, motime) + " - "
            if time.time() > (
                    self.last_alert_closed_sev3_checkvalvepolicy[vlv.vlv_name]
                    + float(
                        self.config_handler.getConfigParam(
                            "INTERNAL", "LOG_SEVERITY3_REPEAT_INTERVAL"))):
                log.debug(logtxt2)  # debug

            tmpstartdatetime = now.strftime("%Y%m%d") + "-"
            if vlv.auto_force_close_valve == True:
                #Skip scheduling
                logtxt = logtxt + " auto_force_close_valve enabled. Skip Scheduling."
                log.debug(logtxt)
                vlv.vlv_manual_mode = False
                self.vlv_close_time = time.time()
                vlv.triggerValve("close")
                vlv.close()
                return logtxt
        except Exception:
            vlv.addAlert("SW002", vlv.vlv_name, " Error force closing")
            raise Exception("Bloc Messages & events or Auto Force Close")
            return

        try:

            if vlv.vlv_name in self.valvesConfigJSON:
                cfg_start_time = self.valvesConfigJSON[
                    vlv.vlv_name]["TimeProperties"]["start_time"]
                cfg_duration = self.valvesConfigJSON[
                    vlv.vlv_name]["TimeProperties"]["duration"]
                cfg_calendar = self.valvesConfigJSON[
                    vlv.vlv_name]["TimeProperties"]["calendar"]
                cfg_current_weather_ignore = self.valvesConfigJSON[
                    vlv.vlv_name]["TimeProperties"]["current_weather_ignore"]
                valve_enable = False
                cfg_start_time_array = cfg_start_time.split(',')
                cfg_start_time_array_len = len(cfg_start_time_array)
                cfg_duration_array = cfg_duration.split(',')
                cfg_duration_array_len = len(cfg_duration_array)
                if (cfg_start_time_array_len != cfg_duration_array_len):
                    logtxt = logtxt + "start_time & duration array len unequal (" + str(
                        cfg_start_time_array_len) + "/" + str(
                            cfg_duration_array_len) + ")"
                    raise Exception(logtxt)

                logtxtvalvetimetrigger = ""
                for idx in range(cfg_start_time_array_len):
                    isdayrun = self.isDayRun(vlv.vlv_name, cfg_calendar, idx)
                    logtxt2 = vlv.vlv_name + " #" + str(
                        idx) + ">" + "start_time:" + cfg_start_time_array[
                            idx] + " dur:" + cfg_duration_array[idx]
                    start_datetime_str = tmpstartdatetime + cfg_start_time_array[
                        idx] + ":00"  #0s to remove ambiguity
                    # logtxt = logtxt + " start_datetime #"+str(idx)+"=" + start_datetime_str
                    start_datetime = datetime.datetime.strptime(
                        start_datetime_str, "%Y%m%d-%H:%M:%S")
                    end_datetime = start_datetime + datetime.timedelta(
                        minutes=int(cfg_duration_array[idx]))
                    start_datetime_str2 = start_datetime.strftime(
                        "%Y%m%d-%H:%M:%S")
                    end_datetime_str2 = end_datetime.strftime(
                        "%Y%m%d-%H:%M:%S")

                    isRainForecastOverride = self.isRainForecast
                    if self.isRainForecast == True and cfg_current_weather_ignore == "True":
                        isRainForecastOverride = False
                        logtxt2 = logtxt2 + " Override Rain forcast! "

                    if (isRainForecastOverride == False and isdayrun == True
                            and now >= start_datetime and now <= end_datetime):
                        logtxt2 = logtxt2 + " Turn VALVE_ON"
                        valve_enable = valve_enable | True
                        logtxtvalvetimetrigger = logtxtvalvetimetrigger + start_datetime.strftime(
                            "%d-%Hh%Mm") + " to " + end_datetime.strftime(
                                "%d-%Hh%Mm")
                    else:
                        logtxt2 = logtxt2 + " Turn VALVE_OFF"
                        valve_enable = valve_enable | False

                    if time.time() > (
                            self.last_alert_closed_sev3_checkvalvepolicy[
                                vlv.vlv_name] + float(
                                    self.config_handler.getConfigParam(
                                        "INTERNAL",
                                        "LOG_SEVERITY3_REPEAT_INTERVAL"))):
                        logtxt2 = logtxt2 + " [s=" + start_datetime_str2 + ", e=" + end_datetime_str2 + "] "
                        log.debug(logtxt2)

                    #end for idx

                if (valve_enable):
                    if vlv.vlv_status != G_OPEN:
                        if self.isRainForecast == True and cfg_current_weather_ignore == "True":
                            logtxtOver = vlv.vlv_name + " Override Rain forcast, " + G_OPEN + "!"
                            log.info(logtxtOver)
                        vlv.open()
                        logtxt = logtxt + "Open " + logtxtvalvetimetrigger
                    else:
                        logtxt = logtxt + "Already Open " + logtxtvalvetimetrigger
                else:
                    if vlv.vlv_status != G_CLOSED:
                        vlv.close()
                        logtxt = logtxt + "Closed"
                    else:
                        logtxt = logtxt + "Already Closed"

                if time.time() > (self.last_alert_closed_sev3_checkvalvepolicy[
                        vlv.vlv_name] + float(
                            self.config_handler.getConfigParam(
                                "INTERNAL", "LOG_SEVERITY3_REPEAT_INTERVAL"))):
                    log.debug(logtxt)

            else:
                logtxt = logtxt + " not defined in " + self.valveconfigfilename
                log.error(logtxt)
                raise Exception(logtxt)
        except Exception:
            traceback.print_exc()
            vlv.auto_force_close_valve = True
            vlv.vlv_manual_mode = False
            #self.alarm_mgr_handler.clearAlertDevice("VALVE_COMMAND", vlv.vlv_name)
            #self.alarm_mgr_handler.clearAlertDevice("VALVE_OPEN", vlv.vlv_name)
            status_text = vlv.addAlert("SW002", vlv.vlv_name, logtxt)
            vlv.vlv_last_alert_time = time.time()
            log.error(status_text)

            #Do all to close!
            self.vlv_close_time = time.time()
            vlv.triggerValve("close")
            vlv.close()

        # Display Status at fixed interval
        if vlv.vlv_status != G_CLOSED and time.time(
        ) > self.valve_last_info_log[vlv.vlv_name]:
            # limit number of logs !
            log.info(logtxt)
            self.valve_last_info_log[vlv.vlv_name] = time.time() + float(
                self.config_handler.getConfigParam(
                    "VALVE_MANAGER", "VALVE_DISPLAY_OPEN_STATUS_INTERVAL"))

    #Valve policy should take precedence over schedule in case something goes wrong
    def checkValvePolicy(self, vlv: Valve):
        logtxt = " checkValvePolicy: " + vlv.vlv_name + " "
        tmpstr = ""
        try:
            # This is how the open time threasholds are defined.  refopentime is there to ensure opentime non null value.
            # ------- <opentimewarning>----<opentimeredcritical>--<opentimefinal>----<opentimelightingstop>
            refopentime = time.time()
            if vlv.vlv_open_time != None:
                refopentime = vlv.vlv_open_time
            opentimehw = refopentime + float(self.ValveHardwareResponseTime)
            opentimecritical = refopentime + float(
                self.ValveOpenTriggerCriticalElapsedTime)
            opentimewarning = refopentime + float(
                self.ValveOpenTriggerWarningElapsedTime)

            event_active_time = refopentime + float(
                30)  #Alarm hardocoded 30 sec

            if vlv.vlv_status == G_OPEN:  #Locked Status is LOCKOPEN ! Don't allow auto close on lock open.
                if (vlv.vlv_open_time != None):  #Is there an open time stamp ?
                    if time.time() > opentimecritical:
                        logtxt = logtxt + " Open Time Exceeded "
                        log.debug(logtxt)
                        # self.alarm_mgr_handler.clearAlertDevice("VALVE_COMMAND", vlv.vlv_name)
                        # self.alarm_mgr_handler.clearAlertDevice("VALVE_OPEN", vlv.vlv_name)

                        if (time.time() > vlv.vlv_last_alert_time \
                            +  float(self.config_handler.getConfigParam("INTERNAL", "LOG_SEVERITY1_REPEAT_INTERVAL"))):
                            log.critical(logtxt)

                        self.alarm_mgr_handler.clearAlertID(
                            "VO001", vlv.vlv_name)
                        self.alarm_mgr_handler.clearAlertID(
                            "VO002", vlv.vlv_name)
                        self.alarm_mgr_handler.clearAlertID(
                            "VO003", vlv.vlv_name)
                        status_text = self.addAlert("VO001", vlv.vlv_name,
                                                    logtxt)
                        log.debug(status_text)
                        vlv.vlv_last_alert_time = time.time()

                        self.vlv_manual_mode = False
                        vlv.auto_force_close_valve = True
                        #disable scheduler for some time because of manual mode to false and auto close impacts

                        self.last_schedule_process_time[
                            vlv.vlv_name] = time.time() + 90
                        vlv.close()
                    elif time.time() > opentimewarning:
                        logtxt = logtxt + " Open Time Warning!"
                        log.debug(logtxt)
                        #self.alarm_mgr_handler.clearAlertDevice("VALVE_COMMAND", vlv.vlv_name)
                        #self.alarm_mgr_handler.clearAlertDevice("VALVE_OPEN", vlv.vlv_name)
                        if (time.time() > vlv.vlv_last_alert_time \
                            +  float(self.config_handler.getConfigParam("INTERNAL", "LOG_SEVERITY2_REPEAT_INTERVAL"))):
                            log.warning(logtxt)
                            self.last_alert_open_sev3_checkvalvepolicy[
                                vlv.vlv_name] = time.time()
                        status_text = self.addAlert("VO002", vlv.vlv_name)
                        vlv.vlv_last_alert_time = time.time()
                        log.debug(status_text)
                        #self.vlv_manual_mode = False
                    elif time.time() > opentimehw:
                        if time.time() > (
                                self.last_alert_open_sev3_checkvalvepolicy[
                                    vlv.vlv_name] + float(
                                        self.config_handler.getConfigParam(
                                            "INTERNAL",
                                            "LOG_SEVERITY3_REPEAT_INTERVAL"))):
                            logtxt = logtxt + " open HW time expired. nothing to do. OK! "
                            log.debug(logtxt)
                            self.last_alert_open_sev3_checkvalvepolicy[
                                vlv.vlv_name] = time.time()
                    else:
                        logtxt = logtxt + vlv.vlv_status + " " + tmpstr
                if time.time() > (self.last_alert_open_sev3_checkvalvepolicy[
                        vlv.vlv_name] + float(
                            self.config_handler.getConfigParam(
                                "INTERNAL", "LOG_SEVERITY3_REPEAT_INTERVAL"))):
                    log.debug(logtxt)
                    self.last_alert_open_sev3_checkvalvepolicy[
                        vlv.vlv_name] = time.time()
            if vlv.vlv_status == G_CLOSED:
                if time.time() >= (
                        vlv.vlv_close_time + 2 * float(
                            self.config_handler.getConfigParam(
                                "NOTIFICATION_MANAGER",
                                "NOTIFICATION_MANAGER_LOOP_TIMEOUT"))
                ):  # Dont clear alarm right away. give time to notification manager
                    self.alarm_mgr_handler.clearAlertDevice(
                        "VALVE_OPEN", vlv.vlv_name, logtxt + " G_CLOSED")
                    self.alarm_mgr_handler.clearAlertDevice(
                        "VALVE_COMMAND", vlv.vlv_name, logtxt + " G_CLOSED")
                    logtxt = logtxt + " G_CLOSED. clearAlertDevice/VALVE_COMMAND/VALVE_OPEN Alert."

                    if time.time() > (
                            self.last_alert_closed_sev3_checkvalvepolicy[
                                vlv.vlv_name] + float(
                                    self.config_handler.getConfigParam(
                                        "INTERNAL",
                                        "LOG_SEVERITY3_REPEAT_INTERVAL"))
                    ):  #reduce load, dont clear forever
                        log.debug(logtxt)
                        #vlv.vlv_last_alert_time = time.time()
                        self.last_alert_closed_sev3_checkvalvepolicy[
                            vlv.vlv_name] = time.time()
        except Exception:
            traceback.print_exc()
            vlv.auto_force_close_valve = True
            status_text = vlv.addAlert("SW101", vlv.vlv_name)
            vlv.vlv_last_alert_time = time.time()
            log.error(status_text)
            self.vlv_close_time = time.time()
            vlv.triggerValve("close")
            vlv.close()
            sleep(5)
            os._exit(-1)

    def addAlert(self, id, device, extratxt=""):
        self.vlv_last_alert_time = time.time()
        status_text = "request for Alert %s %s %s" % (id, device, extratxt)

        try:
            self.vlv_add_alert_time_by_type[id] = time.time()
            status_text = self.alarm_mgr_handler.addAlert(id, device, extratxt)
            log.warning(status_text)
        except Exception:
            traceback.print_exc()
            logtxt = "ValveManager AddAlert Exception:" + str(
                traceback.format_list(
                    traceback.extract_stack())) + "    sys.excInfo:" + str(
                        sys.exc_info())
            log.error(logtxt)

        return status_text
Beispiel #5
0
class GarageDoor():
    def __init__(self, garage_name, usbConnectHandler):
        #log.setLevel(logging.INFO)
        self.config_handler = ConfigManager()
        self.alarm_mgr_handler = AlertManager()

        matchObj = re.findall(r'\d', garage_name, 1)
        garage_id = int(matchObj[0])
        self.g_id = garage_id

        self.g_name = garage_name
        self.g_board_pin_relay = int(
            self.config_handler.getConfigParam(self.g_name, "GarageBoardPin"))

        self.g_status = G_UNKNOWN
        self.g_prevstatus = G_UNKNOWN

        self.g_lightstatus = ""
        self.g_prevlightstatus = ""

        self.g_sensor_props = {}
        self.g_light_list = {}  #Dict of lights. key = color GREEN RED WHITE
        self.g_update_time = time.time()
        self.g_open_time = None
        self.g_close_time = None
        self.g_error_time = None
        self.g_sensor_error_time = None
        self.g_last_alert_time = None
        self.g_last_cmd_sent_time = None
        self.g_last_cmd_trigger_time = None
        self.g_next_auto_cmd_allowed_time = time.time() + float(
            self.config_handler.getConfigParam("GARAGE_MANAGER",
                                               "GARAGE_MANAGER_LOOP_TIMEOUT"))
        self.g_next_manual_cmd_allowed_time = time.time() + float(
            self.config_handler.getConfigParam("GARAGE_MANAGER",
                                               "GARAGE_MANAGER_LOOP_TIMEOUT"))
        self.g_lock_time = None

        self.seconds_between_alerts = float(
            self.config_handler.getConfigParam("ALERT", "TimeBetweenAlerts"))
        self.g_alert_light_time = None
        self.g_auto_force_ignore_garage_open_close_cmd = False
        self.g_manual_force_lock_garage_open_close_cmd = False
        self.g_add_alert_time_by_type = {}  #Key is Alert type, data is time()

        self.nbrfault = 0

        self.g_statusEventList = []

        self.usbConnectHandler = usbConnectHandler
        self.initBoardPinModeOutput(
            int(
                self.config_handler.getConfigParam(self.g_name,
                                                   "GarageBoardPin")))
        tmplog = "Garage Serial Device: %s pin %d" % (
            self.usbConnectHandler.connection.device,
            int(
                self.config_handler.getConfigParam(self.g_name,
                                                   "GarageBoardPin")))
        log.info(tmplog)

    def isGarageOpen(self, mything, myservice, myid):
        return self.g_status == G_OPEN

    def startLightFlash(self, color):
        key = self.g_name + "_" + color
        if (key in self.g_light_list):
            # log.info("Green startFlashLight started !!!")
            self.g_light_list[key].startFlashLight()

    def stopLightFlash(self, color):
        key = self.g_name + "_" + color
        if (key in self.g_light_list):
            # log.info("Green startFlashLight started !!!")
            self.g_light_list[key].stopFlashLight()

    def turnOnLight(self, color):
        key = self.g_name + "_" + color
        if (key in self.g_light_list):
            # log.info("Green startFlashLight started !!!")
            self.g_light_list[key].turnOnLight()

    def turnOffLight(self, color):
        key = self.g_name + "_" + color
        if (key in self.g_light_list):
            # log.info("Green startFlashLight started !!!")
            self.g_light_list[key].turnOffLight()

    def addAlert(self, id, device, extratxt=""):
        self.g_last_alert_time = time.time()
        status_text = "request for Alert %s %s %s" % (id, device, extratxt)

        if (id in self.g_add_alert_time_by_type):
            lastalerttime = self.g_add_alert_time_by_type[id]
            if (time.time() > (lastalerttime + self.seconds_between_alerts)):
                try:
                    del self.g_add_alert_time_by_type[id]
                except KeyError:
                    pass

                log.info("%s can now be sent again for %s!" % (id, device))
            else:
                log.debug("Skip %s" % status_text)
        else:
            self.g_add_alert_time_by_type[id] = time.time()
            status_text = self.alarm_mgr_handler.addAlert(id, device, extratxt)
            log.warning(status_text)

        return status_text

    def updateSensor(self):
        sensor_status_text = ""
        try:
            for sensor in self.g_sensor_props:
                self.g_sensor_props[sensor].s_update_time = time.time()
                read_status = self.usbConnectHandler.digitalRead(
                    self.g_sensor_props[sensor].board_pin_id)
                self.g_sensor_props[sensor].status = S_SENSOR_STATUS_LIST[
                    read_status]  #0=open 1=closed
                sensor_status_text = sensor_status_text + "%s/%s/%s " % (
                    self.g_name, sensor, S_SENSOR_STATUS_LIST[read_status])
                log.debug("Sensor %s Status = %d" % (sensor, read_status))
            resp = self.determineGarageDoorOpenClosedStatus()
        except Exception:
            self.g_auto_force_ignore_garage_open_close_cmd = True
            sensor_status_text = self.addAlert("HW001",
                                               self.g_name + "_" + sensor)
            status_text = self.addAlert("GCD01", self.g_name)
            # self.tid,self.module,self.device,self.status,self.text)
            resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]",
                                     self.g_name + "_" + sensor, S_ERROR,
                                     status_text)
            # os._exit(6)
        return resp

    def determineGarageDoorOpenClosedStatus(self):
        log.debug("GarageDoor dertermine Door Open Closed Status called !")
        sensorkey0 = "[UNKNOWN]"
        sensor_status_text = self.g_name + ":" + G_UNKNOWN
        logstr = ""
        do_print_status = False
        ''' Check garage status. Garage status g_status value based on sensor value if all sensors report the same'''
        for i, sensor in enumerate(self.g_sensor_props):
            logstr = "%d Garage %d Sensor %s Status = %s" % (
                i, self.g_id, sensor, self.g_sensor_props[sensor].status)

            if i == 0:
                #keep track of 1st sensor, not necessarely in order to see all are the same
                sensorkey0 = sensor
            else:
                if (self.g_sensor_props[sensor].status ==
                        self.g_sensor_props[sensorkey0].status):
                    self.g_status = self.g_sensor_props[sensor].status
                    if (S_ERROR in self.g_statusEventList):
                        self.g_statusEventList.remove(S_ERROR)
                        # self.stopLightFlash('RED')
                        # self.stopLightFlash('WHITE')
                        # self.stopLightFlash('GREEN')
                        for sensorkey in self.g_sensor_props:
                            sensordevname = self.g_name + "_" + sensorkey
                            self.alarm_mgr_handler.clearAlertDevice(
                                "SENSOR", sensordevname,
                                "from determineGarageDoorOpenClosedStatus")
                        self.nbrfault = 0
                        self.g_sensor_error_time = None
                else:
                    self.g_sensor_props[sensor].status = S_ERROR
                    self.g_sensor_props[sensor].s_update_time = time.time()
                    logstr = " %d Garage %d Sensor %s Status = %s" % (
                        i, self.g_id, sensor, S_WARNING)

                    if (S_ERROR not in self.g_statusEventList):
                        self.g_statusEventList.append(S_ERROR)
                        self.g_sensor_error_time = time.time()
                        #log.warning(logstr)

                    self.nbrfault = self.nbrfault + 1
                    # if self.nbrfault>float(self.config_handler.getConfigParam("GARAGE_MANAGER", "SENSOR_DEFECT_ASSESSMENT_TIME")):
                    if self.g_sensor_error_time != None and \
                                time.time() > (self.g_sensor_error_time + (float(self.config_handler.getConfigParam("GARAGE_MANAGER", "SENSOR_DEFECT_ASSESSMENT_TIME")))):
                        # sensor_status_text = "Garage " + self.g_name + " Sensor " + S_ERROR
                        self.g_sensor_props[sensor].status = S_ERROR
                        self.g_status = G_ERROR
                        sensor_status_text = self.addAlert(
                            "GS001", self.g_name + "_" + sensor)
                        self.g_auto_force_ignore_garage_open_close_cmd = True
                        status_text = self.addAlert("GCD01", self.g_name)
                        self.startLightFlash('RED')
                        self.startLightFlash('GREEN')
                        self.startLightFlash('WHITE')

        #Overide Garage but keep Sensor status upto date
        if self.g_manual_force_lock_garage_open_close_cmd == True:
            #Strip LOCK in case already there
            self.g_status = self.g_status.replace(G_LOCK, "")
            self.g_status = G_LOCK + self.g_status
            logstr = "Garage %s Status = %s" % (self.g_id, self.g_status)

            if self.g_lock_time != None and time.time(
            ) > self.g_lock_time + float(
                    self.config_handler.getConfigParam(
                        "ALERT", "AlertDefaultClearInterval")):
                self.alarm_mgr_handler.clearAlertID("GTO04", self.g_name)

        log.debug(logstr)

        if (self.g_prevstatus != self.g_status):
            self.g_update_time = time.time()
            sensor_status_text = self.g_name + ":" + self.g_status
            log_status_text = self.g_name + " change from " + self.g_prevstatus + " to " + self.g_status
            log.info(log_status_text)

            # Check if previously locked but same status

            if ("LOCK" in self.g_prevstatus and "LOCK" in self.g_status):
                #or ("LOCK" in self.g_status and "LOCK" not in self.g_prevstatus):
                self.g_open_time = time.time()
                self.g_close_time = time.time()
                self.g_lock_time = time.time()
                log_status_text = self.g_name + " Reset open/close timers " + "PrevStatus:" + self.g_prevstatus + " Status:" + self.g_status
                log.info(log_status_text)

            self.g_prevstatus = self.g_status
            if self.g_status == G_OPEN:
                self.g_open_time = time.time()
            elif self.g_status == G_CLOSED or self.g_status == G_LOCKCLOSED:
                # self.g_auto_force_ignore_garage_open_close_cmd = False
                self.g_close_time = time.time()
                # Clear all alarms when all sensors are OK since garage is closed.
                self.alarm_mgr_handler.clearAlertDevice(
                    "GARAGE_OPEN", self.g_name,
                    "from determineGarageDoorOpenClosedStatus()")
                self.alarm_mgr_handler.clearAlertID("GLO01", self.g_name)
                self.alarm_mgr_handler.clearAlertID("HW002", self.g_name)
                for sensorkey in self.g_sensor_props:
                    sensordevname = self.g_name + "_" + sensorkey
                    self.alarm_mgr_handler.clearAlertDevice(
                        "SENSOR", sensordevname)
            elif self.g_status == G_ERROR:
                tmpstrerr = self.g_name + ":" + self.g_status + " ERROR status"
                log.info(tmpstrerr)
                self.g_error_time = time.time()
                # self.startLightFlash('RED')
            #self.printStatus()
            do_print_status = True
        else:
            # Status no change
            # In case of previous garage error state and if garage is currently closed
            # --> Check if garage is closed and auto close was disabled
            # --> Check if enough time has passed to see if auto close can be re-enabled
            log.debug(self.g_name + "status no change !")
            if (self.g_status.find(G_CLOSED)>=0 and self.g_error_time!=None \
                        and self.g_auto_force_ignore_garage_open_close_cmd==True \
                        and (time.time() > (self.g_error_time + float(self.config_handler.getConfigParam("GARAGE_COMMON", "GarageDoorAssumedClosedTime") ) ) )
                             ):
                if (time.time() > (self.g_close_time + float(
                        self.config_handler.getConfigParam(
                            "GARAGE_COMMON", "GarageDoorAssumedClosedTime")))):
                    self.g_auto_force_ignore_garage_open_close_cmd = False
                    self.alarm_mgr_handler.clearAlertID("GCD01", self.g_name)
                    log.info(
                        self.g_name +
                        " assumed closed. Garage back to auto close mode!")
            else:
                #here the status is set when the status didnt change
                sensor_status_text = self.g_name + ":" + self.g_status
                # log.info(sensor_status_text)

            # Send HW error
            # --> if current time is greater then last command sent time + some time
            # --> and if last command trigger time is greater then last update status change + some time
            # --> and garage is not manually locked
            if self.g_update_time != None and self.g_last_cmd_sent_time != None and self.g_last_cmd_trigger_time !=None \
                and self.g_manual_force_lock_garage_open_close_cmd == False \
                and time.time() > (self.g_last_cmd_sent_time + float(self.config_handler.getConfigParam("GARAGE_COMMON", "GarageElapsedTimeForStatusChange")))\
                and self.g_last_cmd_trigger_time > (self.g_update_time+float(self.config_handler.getConfigParam("GARAGE_COMMON", "GarageElapsedTimeForStatusChange"))):
                self.alarm_mgr_handler.clearAlertDevice(
                    "GARAGE_COMMAND", self.g_name,
                    "determineGarageDoorOpenClosedStatus() before HW002")
                status_text = self.addAlert("HW002", self.g_name)
                self.g_update_time = time.time()
            if self.g_status == G_OPEN and self.g_open_time != None and time.time(
            ) > (self.g_open_time + 15):
                self.alarm_mgr_handler.clearAlertID("GTO01", self.g_name)
            if self.g_status.find(
                    G_CLOSED) >= 0 and self.g_close_time != None and time.time(
                    ) > (self.g_close_time + 15):
                self.alarm_mgr_handler.clearAlertID("GTC01", self.g_name)

        #Trigger a print status on light changes
        self.g_lightstatus = self.getAllLightStatus()
        if self.g_lightstatus != self.g_prevlightstatus:
            do_print_status = True
            self.g_prevlightstatus = self.g_lightstatus

        if (do_print_status == True):
            self.printStatus()
        return (sensor_status_text)

    def lock(self):
        tmptxt = ""
        if self.g_manual_force_lock_garage_open_close_cmd == False:
            tmptxt = "%s Garage Lock down requested" % (self.g_name)
            self.g_manual_force_lock_garage_open_close_cmd = True
            self.g_lock_time = time.time()
        else:
            self.g_manual_force_lock_garage_open_close_cmd = False
            tmptxt = "%s Garage UnLock requested" % (self.g_name)
            self.g_lock_time = None
            # self.g_lock_time=None
        log.info(tmptxt)

        # resp = CommmandQResponse(time.time() * 1000000, "[DeviceManager] " + self.determineGarageDoorOpenClosedStatus())
        # self.tid,self.module,self.device,self.status,self.text)
        mod = "[DeviceManager]"
        stat = "[STATUS]"
        str0 = self.determineGarageDoorOpenClosedStatus().split(':')
        dev = str0[0]
        if str0.__len__() > 1:
            stat = str0[1]
        resp = CommmandQResponse(time.time() * 1000000, mod, dev, stat, "")

        return (resp)

    def status(self):
        log.debug("GarageDoor status called !")
        self.updateSensor()
        rsptxt = self.getCmdQResponseStatusStr()

        #resp = CommmandQResponse(time.time() * 1000000, "[DeviceManager] " + self.determineGarageDoorOpenClosedStatus())
        # self.tid,self.module,self.device,self.status,self.text)
        mod = "[DeviceManager]"
        stat = "[STATUS]"
        str0 = self.determineGarageDoorOpenClosedStatus().split(':')
        dev = str0[0]
        if str0.__len__() > 1:
            stat = str0[1]
        resp = CommmandQResponse(time.time() * 1000000, mod, dev, stat, rsptxt)

        return (resp)

    def getCmdQResponseStatusStr(self):
        resp_json = None
        try:
            rspstr = {}
            if self.g_open_time != None:
                rspstr[G_OPEN] = GarageUtil.getTimePrintOut(
                    self,
                    time.time() - self.g_open_time)

            else:
                rspstr[G_OPEN] = ""
            if self.g_close_time != None and self.g_open_time != None:
                rspstr[G_CLOSED] = GarageUtil.getTimePrintOut(
                    self,
                    time.time() - self.g_close_time)
            else:
                rspstr[G_CLOSED] = ""
            if self.g_error_time != None:
                rspstr[G_ERROR] = GarageUtil.getTimePrintOut(
                    self,
                    time.time() - self.g_close_time)
            else:
                rspstr[G_ERROR] = "Check!"

            if self.g_lock_time != None:
                rspstr[G_LOCK] = GarageUtil.getTimePrintOut(
                    self,
                    time.time() - self.g_lock_time)
            else:
                rspstr[G_LOCK] = ""

            resp_json = json.dumps(rspstr)

        except Exception:
            log.error("Bug handling of getCmdQResponseStatusStr JSON convert")
            traceback.print_exc()
            resp_json = "Error!"

        return json.dumps(resp_json)

    def clear(self):
        # resp = CommmandQResponse(time.time()*1000000, "Garage alarm cleared" )
        resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]", "", "",
                                 "Garage alarm cleared")
        return (resp)

    def addSensor(self, key, sensor_props):
        self.g_sensor_props[key] = sensor_props
        self.initBoardPinModeInput(self.g_sensor_props[key].board_pin_id)
        log.debug(str(sensor_props))
        self.s_update_time = time.time()
        pass

    def addLight(self, key, lightobj):
        self.g_light_list[key] = lightobj
        self.initBoardPinModeOutput(self.g_light_list[key].board_pin_id)
        self.turnOffLight(key)
        log.debug(str(lightobj))
        pass

    def initBoardPinModeOutput(self, pin):
        log.info("Init Board Pin %d Mode Output %s" % (pin, self.g_name))
        self.usbConnectHandler.pinMode(pin, self.usbConnectHandler.OUTPUT)
        self.s_update_time = time.time()

    def initBoardPinModeInput(self, pin):
        log.info("Init Board Pin %d Mode Input %s" % (pin, self.g_name))
        self.usbConnectHandler.pinMode(pin, self.usbConnectHandler.INPUT)
        self.s_update_time = time.time()

    def open(self):
        status_text = "Open"
        self.alarm_mgr_handler.clearAlertDevice("GARAGE_COMMAND", self.g_name,
                                                "from open() 1")
        try:
            if self.g_manual_force_lock_garage_open_close_cmd == False:
                if (self.g_status == G_CLOSED):
                    if time.time() > self.g_next_manual_cmd_allowed_time:
                        # status_text+=" open. Trigger garage door !"
                        self.alarm_mgr_handler.clearAlertDevice(
                            "GARAGE_OPEN", self.g_name, "from open() 2")
                        self.triggerGarageDoor()
                        status_text = self.addAlert("GTO01", self.g_name)
                        self.g_next_manual_cmd_allowed_time = time.time(
                        ) + float(
                            self.config_handler.getConfigParam(
                                "GARAGE_COMMON",
                                "TimeBetweenButtonManualPressed"))
                        # self.startLightFlash('GREEN')
                    else:
                        # open denied. Too early to retry!
                        status_text = self.addAlert("GTO02", self.g_name)

                else:
                    # open denied. current status is " + self.g_status
                    status_text = self.addAlert("GTO03", self.g_name,
                                                self.g_status)

            else:  #Lock!
                status_text = self.addAlert("GTO04", self.g_name,
                                            self.g_status)

            self.g_last_cmd_trigger_time = time.time()

        except Exception:
            traceback.print_exc()
            logstr = "open() Garage %s Status = %s Fatal Exception" % (
                self.g_name, self.g_status)
            log.error(logstr)
            os._exit(-1)
        # resp=CommmandQResponse(0, status_text)
        resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]", "", "",
                                 status_text)
        log.warning(status_text)
        return resp

    def manualopen(self):
        self.open()
        status_text = "manualopen"
        resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]", "", "",
                                 status_text)
        log.debug(status_text)
        return resp

    def close(self):
        status_text = "Close"

        try:
            if self.g_auto_force_ignore_garage_open_close_cmd == True:
                status_text = self.g_name + " " + self.alarm_mgr_handler.alertFileListJSON[
                    "Fr"]["GCD01"]["text"] + " "
                # log.warning(status_text)
            else:
                if (self.g_status == G_OPEN
                        and self.g_manual_force_lock_garage_open_close_cmd
                        == False):
                    if time.time() > self.g_next_manual_cmd_allowed_time:
                        # close. Trigger garage door !
                        self.alarm_mgr_handler.clearAlertDevice(
                            "GARAGE_COMMAND", self.g_name, "from close() 1")
                        status_text = self.addAlert("GTC01", self.g_name)
                        self.triggerGarageDoor()
                        self.g_next_manual_cmd_allowed_time = time.time(
                        ) + float(
                            self.config_handler.getConfigParam(
                                "GARAGE_COMMON",
                                "TimeBetweenButtonManualPressed"))
                        # self.startLightFlash('RED')
                    else:
                        # status_text += "close denied. Too early to retry!"
                        self.alarm_mgr_handler.clearAlertDevice(
                            "GARAGE_COMMAND", self.g_name, "from close() 2")
                        status_text = self.addAlert("GTC02", self.g_name)
                else:
                    # status_text += "close denied. current status is " + self.g_status
                    self.alarm_mgr_handler.clearAlertDevice(
                        "GARAGE_COMMAND", self.g_name, "from close() 3")
                    status_text = self.addAlert("GTC03", self.g_name,
                                                self.g_status)
            self.g_last_cmd_trigger_time = time.time()

        except Exception:
            traceback.print_exc()
            logstr = "close() Garage %s Status = %s Fatal Exception" % (
                self.g_name, self.g_status)
            log.error(logstr)
            os._exit(-1)
        # resp=CommmandQResponse(0, status_text)
        resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]", "", "",
                                 status_text)

        log.warning(status_text)

        return resp

    '''
    return True if OK, False if problem.
    OS exit if fatal !
    '''

    def triggerGarageDoor(self):

        #GarageManager Check Policy will not call this because status os LOCKOPEN and OPEN in this mode !
        if (self.g_manual_force_lock_garage_open_close_cmd):
            logtxt = "Trigger garage Door refused because of Manual Override"
            log.error(logtxt)
            return False

        try:
            self.usbConnectHandler.digitalWrite(self.g_board_pin_relay,
                                                self.usbConnectHandler.HIGH)
            log.debug(self.g_name + " Press button!")
            sleep(
                float(
                    self.config_handler.getConfigParam(
                        "GARAGE_COMMON", "TimeToKeepButtonPressedMilliSec")) /
                1000)
            self.usbConnectHandler.digitalWrite(self.g_board_pin_relay,
                                                self.usbConnectHandler.LOW)
            log.debug(self.g_name + " Release button!")
            sleep(
                float(
                    self.config_handler.getConfigParam(
                        "GARAGE_COMMON", "TimeToKeepButtonPressedMilliSec")) /
                1000)
            self.g_next_auto_cmd_allowed_time = time.time() + float(
                self.config_handler.getConfigParam(
                    "GARAGE_COMMON", "TimeBeforeAutoRetryCloseDoor"))
            self.g_next_manual_cmd_allowed_time = time.time() + float(
                self.config_handler.getConfigParam(
                    "GARAGE_COMMON", "TimeBetweenButtonManualPressed"))
            self.g_last_cmd_sent_time = time.time()
            log.info("%s Open/Close door button pressed" % (self.g_name))
        except Exception:
            log.error("triggerGarageDoor Open or Close button problem !")
            traceback.print_exc()
            os._exit(-1)

        return True

    def printStatus(self):
        logstr = "%s:%s " % (self.g_name, self.g_status)
        sensor_status_str = ""

        all_light_status = self.getAllLightStatus()

        for sensor in self.g_sensor_props:
            sensor_status_str = sensor_status_str + sensor + "=" + self.g_sensor_props[
                sensor].status + " "

        if self.g_update_time != None:
            printut = datetime.datetime.fromtimestamp(
                self.g_update_time).strftime("%Y%m%d-%H%M%S")
        else:
            printut = "None"
        if self.g_open_time != None:
            printot = datetime.datetime.fromtimestamp(
                self.g_open_time).strftime("%Y%m%d-%H%M%S")
        else:
            printot = "None"
        if self.g_close_time != None:
            printct = datetime.datetime.fromtimestamp(
                self.g_close_time).strftime("%Y%m%d-%H%M%S")
        else:
            printct = "None"
        if self.g_lock_time != None:
            printlock = datetime.datetime.fromtimestamp(
                self.g_lock_time).strftime("%Y%m%d-%H%M%S")
        else:
            printlock = "None"
        if self.g_error_time != None:
            printerrt = datetime.datetime.fromtimestamp(
                self.g_error_time).strftime("%Y%m%d-%H%M%S")
        else:
            printerrt = "None"
        if self.g_last_alert_time != None:
            printlast = datetime.datetime.fromtimestamp(
                self.g_last_alert_time).strftime("%Y%m%d-%H%M%S")
        else:
            printlast = "None"
        try:
            logstr = logstr + sensor_status_str + "updte=" + printut + " opn=" + printot + " clse=" + printct + " lock=" + printlock + " err=" + printerrt + " Alert=" + printlast + " Lights:" + all_light_status
            log.info(logstr)
        except Exception:
            log.error("Time Stamp print error ?!?  print to stdout ")
            print(logstr)

    def getAllLightStatus(self):
        all_light_status = ""
        for color in sorted({"GREEN", "RED", "WHITE"}):
            key_dev_color = self.g_name + "_" + color
            if key_dev_color in self.g_light_list:
                light_status = color[0]
                if self.g_light_list[key_dev_color].getLightStatus() == "ON":
                    all_light_status += light_status.lower()
                elif self.g_light_list[key_dev_color].getLightStatus(
                ) == "FLASH":
                    all_light_status += light_status.upper()
                else:
                    all_light_status += "-"
            else:
                log.debug("%s %s light defined yet" % (color, self.g_name))
        return all_light_status

    def test(self):
        self.initBoardPinModeOutput(self.g_board_pin_relay)
        logbuf = "Arduino Init Pin=%d" % self.g_board_pin_relay
        log.info(logbuf)
        for n in range(0, 1):
            self.usbConnectHandler.digitalWrite(self.g_board_pin_relay,
                                                self.usbConnectHandler.HIGH)
            log.info("ON")
            sleep(3)
            self.usbConnectHandler.digitalWrite(self.g_board_pin_relay,
                                                self.usbConnectHandler.LOW)
            log.info("OFF")
            sleep(2)
            n += 1
        resp = CommmandQResponse(time.time() * 1000000, "[MESSAGE]", "", "",
                                 "test!")

        return resp

    def get_serialdevicename(self):
        return self.usbConnectHandler.connection.device

    def get_g_name(self):
        return self.g_name