class Monitor(MySupport): def __init__(self, ConfigFilePath = ProgramDefaults.ConfPath): super(Monitor, self).__init__() self.ProgramName = "Generator Monitor" self.Version = "Unknown" self.log = None self.IsStopping = False self.ProgramComplete = False if ConfigFilePath == None or ConfigFilePath == "": self.ConfigFilePath = ProgramDefaults.ConfPath else: self.ConfigFilePath = ConfigFilePath self.ConnectionList = [] # list of incoming connections for heartbeat # defautl values self.SiteName = "Home" self.ServerSocket = None self.ServerSocketPort = ProgramDefaults.ServerPort # server socket for nagios heartbeat and command/status self.IncomingEmailFolder = "Generator" self.ProcessedEmailFolder = "Generator/Processed" self.FeedbackLogFile = self.ConfigFilePath + "feedback.json" self.LogLocation = ProgramDefaults.LogPath self.LastLogFileSize = 0 self.NumberOfLogSizeErrors = 0 # set defaults for optional parameters self.NewInstall = False # True if newly installed or newly upgraded version self.FeedbackEnabled = False # True if sending autoated feedback on missing information self.FeedbackMessages = {} self.OneTimeMessages = {} self.MailInit = False # set to true once mail is init self.CommunicationsActive = False # Flag to let the heartbeat thread know we are communicating self.Controller = None self.ControllerSelected = None self.bDisablePlatformStats = False self.ReadOnlyEmailCommands = False self.SlowCPUOptimization = False # weather parameters self.WeatherAPIKey = None self.WeatherLocation = None self.UseMetric = False self.WeatherMinimum = True self.DisableWeather = False self.MyWeather = None # Time Sync Related Data self.bSyncTime = False # Sync gen to system time self.bSyncDST = False # sync time at DST change self.bDST = False # Daylight Savings Time active if True # simulation self.Simulation = False self.SimulationFile = None self.console = SetupLogger("genmon_console", log_file = "", stream = True) if os.geteuid() != 0: self.LogConsole("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'.") sys.exit(1) if not os.path.isfile(self.ConfigFilePath + 'genmon.conf'): self.LogConsole("Missing config file : " + self.ConfigFilePath + 'genmon.conf') sys.exit(1) if not os.path.isfile(self.ConfigFilePath + 'mymail.conf'): self.LogConsole("Missing config file : " + self.ConfigFilePath + 'mymail.conf') sys.exit(1) self.config = MyConfig(filename = self.ConfigFilePath + 'genmon.conf', section = "GenMon", log = self.console) # read config file if not self.GetConfig(): self.LogConsole("Failure in Monitor GetConfig") sys.exit(1) # log errors in this module to a file self.log = SetupLogger("genmon", self.LogLocation + "genmon.log") self.config.log = self.log if self.IsLoaded(): self.LogConsole("ERROR: genmon.py is already loaded.") self.LogError("ERROR: genmon.py is already loaded.") sys.exit(1) if self.NewInstall: self.LogError("New version detected: Old = %s, New = %s" % (self.Version, GENMON_VERSION)) self.Version = GENMON_VERSION self.ProgramStartTime = datetime.datetime.now() # used for com metrics atexit.register(self.Close) signal.signal(signal.SIGTERM, self.Close) signal.signal(signal.SIGINT, self.Close) # start thread to accept incoming sockets for nagios heartbeat and command / status clients self.Threads["InterfaceServerThread"] = MyThread(self.InterfaceServerThread, Name = "InterfaceServerThread") # init mail, start processing incoming email self.mail = MyMail(monitor=True, incoming_folder = self.IncomingEmailFolder, processed_folder =self.ProcessedEmailFolder,incoming_callback = self.ProcessCommand, loglocation = self.LogLocation, ConfigFilePath = ConfigFilePath) self.Threads = self.MergeDicts(self.Threads, self.mail.Threads) self.MailInit = True self.FeedbackPipe = MyPipe("Feedback", self.FeedbackReceiver, log = self.log, ConfigFilePath = self.ConfigFilePath) self.Threads = self.MergeDicts(self.Threads, self.FeedbackPipe.Threads) self.MessagePipe = MyPipe("Message", self.MessageReceiver, log = self.log, nullpipe = self.mail.DisableSNMP, ConfigFilePath = self.ConfigFilePath) self.Threads = self.MergeDicts(self.Threads, self.MessagePipe.Threads) try: #Starting device connection if self.Simulation: self.LogError("Simulation Running") if not self.ControllerSelected == None and len(self.ControllerSelected): self.LogError("Selected Controller: " + str(self.ControllerSelected)) else: self.ControllerSelected = "generac_evo_nexus" if self.ControllerSelected.lower() == "h_100" : self.Controller = HPanel(self.log, newinstall = self.NewInstall, simulation = self.Simulation, simulationfile = self.SimulationFile, message = self.MessagePipe, feedback = self.FeedbackPipe, config = self.config) else: self.Controller = Evolution(self.log, self.NewInstall, simulation = self.Simulation, simulationfile = self.SimulationFile, message = self.MessagePipe, feedback = self.FeedbackPipe, config = self.config) self.Threads = self.MergeDicts(self.Threads, self.Controller.Threads) except Exception as e1: self.LogErrorLine("Error opening controller device: " + str(e1)) sys.exit(1) self.StartThreads() self.ProcessFeedbackInfo() # send mail to tell we are starting self.MessagePipe.SendMessage("Generator Monitor Starting at " + self.SiteName, "Generator Monitor Starting at " + self.SiteName , msgtype = "info") self.LogError("GenMon Loaded for site: " + self.SiteName) # ------------------------ Monitor::StartThreads---------------------------- def StartThreads(self, reload = False): try: # start thread to accept incoming sockets for nagios heartbeat self.Threads["ComWatchDog"] = MyThread(self.ComWatchDog, Name = "ComWatchDog") if self.bSyncDST or self.bSyncTime: # Sync time thread self.Threads["TimeSyncThread"] = MyThread(self.TimeSyncThread, Name = "TimeSyncThread") if not self.DisableWeather and not self.WeatherAPIKey == None and len(self.WeatherAPIKey) and not self.WeatherLocation == None and len(self.WeatherLocation): Unit = 'metric' if self.UseMetric else 'imperial' self.MyWeather = MyWeather(self.WeatherAPIKey, location = self.WeatherLocation, unit = Unit, log = self.log) self.Threads = self.MergeDicts(self.Threads, self.MyWeather.Threads) except Exception as e1: self.LogErrorLine("Error in StartThreads: " + str(e1)) # -------------------- Monitor::GetConfig----------------------------------- def GetConfig(self): try: if self.config.HasOption('sitename'): self.SiteName = self.config.ReadValue('sitename') if self.config.HasOption('incoming_mail_folder'): self.IncomingEmailFolder = self.config.ReadValue('incoming_mail_folder') # imap folder for incoming mail if self.config.HasOption('processed_mail_folder'): self.ProcessedEmailFolder = self.config.ReadValue('processed_mail_folder') # imap folder for processed mail # server_port, must match value in myclient.py and check_monitor_system.py and any calling client apps if self.config.HasOption('server_port'): self.ServerSocketPort = self.config.ReadValue('server_port', return_type = int) self.LogLocation = self.config.ReadValue('loglocation', default = ProgramDefaults.LogPath) if self.config.HasOption('syncdst'): self.bSyncDST = self.config.ReadValue('syncdst', return_type = bool) if self.config.HasOption('synctime'): self.bSyncTime = self.config.ReadValue('synctime', return_type = bool) if self.config.HasOption('disableplatformstats'): self.bDisablePlatformStats = self.config.ReadValue('disableplatformstats', return_type = bool) if self.config.HasOption('simulation'): self.Simulation = self.config.ReadValue('simulation', return_type = bool) if self.config.HasOption('simulationfile'): self.SimulationFile = self.config.ReadValue('simulationfile') if self.config.HasOption('controllertype'): self.ControllerSelected = self.config.ReadValue('controllertype') if self.config.HasOption('disableweather'): self.DisableWeather = self.config.ReadValue('disableweather', return_type = bool) else: self.DisableWeather = False if self.config.HasOption('weatherkey'): self.WeatherAPIKey = self.config.ReadValue('weatherkey') if self.config.HasOption('weatherlocation'): self.WeatherLocation = self.config.ReadValue('weatherlocation') if self.config.HasOption('metricweather'): self.UseMetric = self.config.ReadValue('metricweather', return_type = bool) if self.config.HasOption('minimumweatherinfo'): self.WeatherMinimum = self.config.ReadValue('minimumweatherinfo', return_type = bool) if self.config.HasOption('readonlyemailcommands'): self.ReadOnlyEmailCommands = self.config.ReadValue('readonlyemailcommands', return_type = bool) if self.config.HasOption('optimizeforslowercpu'): self.SlowCPUOptimization = self.config.ReadValue('optimizeforslowercpu', return_type = bool) if self.config.HasOption('version'): self.Version = self.config.ReadValue('version') if not self.Version == GENMON_VERSION: self.config.WriteValue('version', GENMON_VERSION) self.NewInstall = True else: self.config.WriteValue('version', GENMON_VERSION) self.NewInstall = True self.Version = GENMON_VERSION if self.config.HasOption("autofeedback"): self.FeedbackEnabled = self.config.ReadValue('autofeedback', return_type = bool) else: self.config.WriteValue('autofeedback', "False") self.FeedbackEnabled = False # Load saved feedback log if log is present if os.path.isfile(self.FeedbackLogFile): try: with open(self.FeedbackLogFile) as infile: self.FeedbackMessages = json.load(infile) except Exception as e1: os.remove(self.FeedbackLogFile) except Exception as e1: self.Console("Missing config file or config file entries (genmon): " + str(e1)) return False return True #--------------------------------------------------------------------------- def ProcessFeedbackInfo(self): try: if self.FeedbackEnabled: for Key, Entry in self.FeedbackMessages.items(): self.MessagePipe.SendMessage("Generator Monitor Submission", Entry , recipient = self.MaintainerAddress, files = self.GetLogFileNames(), msgtype = "error") # delete unsent Messages if os.path.isfile(self.FeedbackLogFile): os.remove(self.FeedbackLogFile) except Exception as e1: self.LogErrorLine("Error in ProcessFeedbackInfo: " + str(e1)) #--------------------------------------------------------------------------- def FeedbackReceiver(self, Message): try: FeedbackDict = {} FeedbackDict = json.loads(Message) self.SendFeedbackInfo(FeedbackDict["Reason"], Always = FeedbackDict["Always"], Message = FeedbackDict["Message"], FullLogs = FeedbackDict["FullLogs"], NoCheck = FeedbackDict["NoCheck"]) except Exception as e1: self.LogErrorLine("Error in FeedbackReceiver: " + str(e1)) self.LogError("Size : " + str(len(Message))) self.LogError("Message : " + str(Message)) #--------------------------------------------------------------------------- def MessageReceiver(self, Message): try: MessageDict = {} MessageDict = json.loads(Message) if MessageDict["onlyonce"]: Subject = self.OneTimeMessages.get(MessageDict["subjectstr"], None) if Subject == None: self.OneTimeMessages[MessageDict["subjectstr"]] = MessageDict["msgstr"] else: return self.mail.sendEmail(MessageDict["subjectstr"], MessageDict["msgstr"], recipient = MessageDict["recipient"], files = MessageDict["files"], deletefile= MessageDict["deletefile"], msgtype= MessageDict["msgtype"]) except Exception as e1: self.LogErrorLine("Error in MessageReceiver: " + str(e1)) #--------------------------------------------------------------------------- def SendFeedbackInfo(self, Reason, Always = False, Message = None, FullLogs = True, NoCheck = False): try: if self.NewInstall or Always: CheckedSent = self.FeedbackMessages.get(Reason, "") if not CheckedSent == "" and not NoCheck: return if not NoCheck: self.LogError(Reason + " : " + Message) msgbody = "Reason = " + Reason + "\n" if Message != None: msgbody += "Message : " + Message + "\n" msgbody += self.printToString(self.ProcessDispatch(self.GetStartInfo(NoTile = True),"")) if not self.bDisablePlatformStats: msgbody += self.printToString(self.ProcessDispatch({"Platform Stats" : self.GetPlatformStats()},"")) msgbody += self.Controller.DisplayRegisters(AllRegs = FullLogs) msgbody += "\n" + self.GetSupportData() + "\n" if self.FeedbackEnabled: self.MessagePipe.SendMessage("Generator Monitor Submission", msgbody , recipient = self.MaintainerAddress, files = self.GetLogFileNames(), msgtype = "error") self.FeedbackMessages[Reason] = msgbody # if feedback not enabled, save the log to file if not self.FeedbackEnabled: with open(self.FeedbackLogFile, 'w') as outfile: json.dump(self.FeedbackMessages, outfile, sort_keys = True, indent = 4, ensure_ascii = False) except Exception as e1: self.LogErrorLine("Error in SendFeedbackInfo: " + str(e1)) #---------- Monitor::EmailSendIsEnabled------------------------------------- def EmailSendIsEnabled(self): EmailThread = self.Threads.get("SendMailThread",None) if EmailThread == None: return False return True #---------- Monitor::GetSupportData----------------------------------------- def GetSupportData(self): SupportData = collections.OrderedDict() SupportData["Program Run Time"] = self.GetProgramRunTime() SupportData["Monitor Health"] = self.GetSystemHealth() SupportData["StartInfo"] = self.GetStartInfo(NoTile = True) if not self.bDisablePlatformStats: SupportData["PlatformStats"] = self.GetPlatformStats() SupportData["Data"] = self.Controller.DisplayRegisters(AllRegs = True, DictOut = True) # Raw Modbus data SupportData["Registers"] = self.Controller.Registers SupportData["Strings"] = self.Controller.Strings SupportData["FileData"] = self.Controller.FileData return json.dumps(SupportData, sort_keys=False) #---------- Monitor::GetLogFileNames---------------------------------------- def GetLogFileNames(self): try: LogList = [] FilesToSend = ["genmon.log", "genserv.log", "mymail.log", "myserial.log", "mymodbus.log", "gengpio.log", "gengpioin.log", "gensms.log", "gensms_modem.log", "genmqtt.log", "genpushover.log", "gensyslog.log", "genloader.log", "myserialtcp.log", "genlog.log", "genslack.log", "genexercise.log","genemail2sms.log", "gentankutil.log"] for File in FilesToSend: LogFile = self.LogLocation + File if os.path.isfile(LogFile): LogList.append(LogFile) return LogList except Exception as e1: return None #---------- Monitor::SendSupportInfo---------------------------------------- def SendSupportInfo(self, SendLogs = True): try: if not self.EmailSendIsEnabled(): self.LogError("Error in SendSupportInfo: send email is not enabled") return "Send Email is not enabled." msgbody = "" msgbody += self.printToString(self.ProcessDispatch(self.GetStartInfo(NoTile = True),"")) if not self.bDisablePlatformStats: msgbody += self.printToString(self.ProcessDispatch({"Platform Stats" : self.GetPlatformStats()},"")) msgbody += self.Controller.DisplayRegisters(AllRegs = True) msgbody += "\n" + self.GetSupportData() + "\n" msgtitle = "Generator Monitor Log File Submission" if SendLogs == True: LogList = self.GetLogFileNames() else: msgtitle = "Generator Monitor Register Submission" LogList = None self.MessagePipe.SendMessage(msgtitle, msgbody , recipient = self.MaintainerAddress, files = LogList, msgtype = "error") return "Log files submitted" except Exception as e1: self.LogErrorLine("Error in SendSupportInfo: " + str(e1)) #---------- process command from email and socket -------------------------- def ProcessCommand(self, command, fromsocket = False): LocalError = False command = command.decode('ascii') msgsubject = "Generator Command Response at " + self.SiteName if not fromsocket: msgbody = "\n" else: msgbody = "" if(len(command)) == 0: msgsubject = "Error in Generator Command (Lenght is zero)" msgbody += "Invalid GENERATOR command: zero length command." LocalError = True if not LocalError: if(not command.lower().startswith( 'generator:' )): msgsubject = "Error in Generator Command (command prefix)" msgbody += "Invalid GENERATOR command: all commands must be prefixed by \"generator: \"" LocalError = True if LocalError: if not fromsocket: self.MessagePipe.SendMessage(msgsubject, msgbody, msgtype = "error") return "" # ignored by email module else: msgbody += "EndOfMessage" return msgbody if command.lower().startswith('generator:'): command = command[len('generator:'):] CommandDict = { "registers" : [self.Controller.DisplayRegisters,(False,), False], # display registers "allregs" : [self.Controller.DisplayRegisters, (True,), False], # display registers "logs" : [self.Controller.DisplayLogs, (True, False), False], "status" : [self.Controller.DisplayStatus, (), False], # display decoded generator info "maint" : [self.Controller.DisplayMaintenance, (), False], "monitor" : [self.DisplayMonitor, (), False], "outage" : [self.Controller.DisplayOutage, (), False], "settime" : [self.StartTimeThread, (), False], # set time and date "setexercise" : [self.Controller.SetGeneratorExerciseTime, (command.lower(),), False], "setquiet" : [self.Controller.SetGeneratorQuietMode, ( command.lower(),), False], "setremote" : [self.Controller.SetGeneratorRemoteCommand, (command.lower(),), False], "testcommand" : [self.Controller.TestCommand, (command.lower(),), False], "network_status": [self.InternetConnected, (), False], "help" : [self.DisplayHelp, (), False], # display help screen ## These commands are used by the web / socket interface only "power_log_json" : [self.Controller.GetPowerHistory, (command.lower(),), True], "power_log_clear" : [self.Controller.ClearPowerLog, (), True], "start_info_json" : [self.GetStartInfo, (), True], "registers_json" : [self.Controller.DisplayRegisters, (False, True), True], # display registers "allregs_json" : [self.Controller.DisplayRegisters, (True, True), True], # display registers "logs_json" : [self.Controller.DisplayLogs, (True, True), True], "status_json" : [self.Controller.DisplayStatus, (True,), True], "status_num_json" : [self.Controller.DisplayStatus, (True,True), True], "maint_json" : [self.Controller.DisplayMaintenance, (True,), True], "monitor_json" : [self.DisplayMonitor, (True,), True], "weather_json" : [self.DisplayWeather, (True,), True], "outage_json" : [self.Controller.DisplayOutage, (True,), True], "gui_status_json" : [self.GetStatusForGUI, (), True], "get_maint_log_json": [self.Controller.GetMaintLog, (), True], "add_maint_log" : [self.Controller.AddEntryToMaintLog, (command,), True], # Do not do command.lower() since this input is JSON "clear_maint_log" : [self.Controller.ClearMaintLog, (), True], "getsitename" : [self.GetSiteName, (), True], "getbase" : [self.Controller.GetBaseStatus, (), True], # (UI changes color based on exercise, running , ready status) "gethealth" : [self.GetSystemHealth, (), True], "getregvalue" : [self.Controller.GetRegValue, (command.lower(),), True], # only used for debug purposes, read a cached register value "readregvalue" : [self.Controller.ReadRegValue, (command.lower(),), True], # only used for debug purposes, Read Register Non Cached "getdebug" : [self.GetDeadThreadName, (), True], # only used for debug purposes. If a thread crashes it tells you the thread name "sendregisters" : [self.SendSupportInfo, (False,), True], "sendlogfiles" : [self.SendSupportInfo, (True,), True], "set_tank_data" : [self.Controller.SetExternalTankData, (command,), True] } CommandList = command.split(' ') ValidCommand = False try: for item in CommandList: if not len(item): continue item = item.strip() LookUp = item if "=" in item: BaseCmd = item.split('=') LookUp = BaseCmd[0] # check if we disallow write commands via email if self.ReadOnlyEmailCommands and not fromsocket and LookUp in ["settime", "setexercise", "setquiet", "setremote"]: continue ExecList = CommandDict.get(LookUp.lower(),None) if ExecList == None: continue if ExecList[0] == None: continue if not fromsocket and ExecList[2]: continue # Execut Command ReturnMessage = ExecList[0](*ExecList[1]) ValidCommand = True if LookUp.lower().endswith("_json") and not isinstance(ReturnMessage, str): msgbody += json.dumps(ReturnMessage, sort_keys=False) else: msgbody += ReturnMessage if not fromsocket: msgbody += "\n" except Exception as e1: self.LogErrorLine("Error Processing Commands: " + command + ": "+ str(e1)) if not ValidCommand: msgbody += "No valid command recognized." if not fromsocket: self.MessagePipe.SendMessage(msgsubject, msgbody, msgtype = "warn") return "" # ignored by email module else: msgbody += "EndOfMessage" return msgbody #------------ Monitor::DisplayHelp ----------------------------------------- def DisplayHelp(self): outstring = "" outstring += "Help:\n" outstring += self.printToString("\nCommands:") outstring += self.printToString(" status - display engine and line information") outstring += self.printToString(" maint - display maintenance and service information") outstring += self.printToString(" outage - display current and last outage (since program launched)") outstring += self.printToString(" info, also shows utility min and max values") outstring += self.printToString(" monitor - display communication statistics and monitor health") outstring += self.printToString(" logs - display all alarm, on/off, and maintenance logs") outstring += self.printToString(" registers - display contents of registers being monitored") outstring += self.printToString(" settime - set generator time to system time") outstring += self.printToString(" setexercise - set the exercise time of the generator. ") outstring += self.printToString(" i.e. setexercise=Monday,13:30,Weekly") outstring += self.printToString(" if Enhanced Exercise Frequency is supported by your generator:") outstring += self.printToString(" i.e. setexercise=Monday,13:30,BiWeekly") outstring += self.printToString(" i.e. setexercise=15,13:30,Monthly") outstring += self.printToString(" setquiet - enable or disable exercise quiet mode, ") outstring += self.printToString(" i.e. setquiet=on or setquiet=off") outstring += self.printToString(" setremote - issue remote command. format is setremote=command, ") outstring += self.printToString(" where command is start, stop, starttransfer,") outstring += self.printToString(" startexercise. i.e. setremote=start") outstring += self.printToString(" help - Display help on commands") outstring += self.printToString("\n") outstring += self.printToString("To clear the Alarm/Warning message, press OFF on the control panel keypad") outstring += self.printToString("followed by the ENTER key. To access Dealer Menu on the Evolution") outstring += self.printToString("controller, from the top menu selection (SYSTEM, DATE/TIME,BATTERY, SUB-MENUS)") outstring += self.printToString("enter UP UP ESC DOWN UP ESC UP, then go to the dealer menu and press enter.") outstring += self.printToString("For liquid cooled models a level 2 dealer code can be entered, ESC UP UP DOWN") outstring += self.printToString("DOWN ESC ESC, then navigate to the dealer menu and press enter.") outstring += self.printToString("Passcode for Nexus controller is ESC, UP, UP ESC, DOWN, UP, ESC, UP, UP, ENTER.") outstring += self.printToString("\n") return outstring #------------ Monitor::GetProgramRunTime ----------------------------------- def GetProgramRunTime(self): try: ProgramRunTime = datetime.datetime.now() - self.ProgramStartTime outstr = str(ProgramRunTime).split(".")[0] # remove microseconds from string return self.ProgramName + " running for " + outstr + "." except Exception as e1: self.LogErrorLine("Error in GetProgramRunTime:" + str(e1)) return "Unknown" #------------ Monitor::GetWeatherData -------------------------------------- def GetWeatherData(self, ForUI = False): if self.MyWeather == None: return None ReturnData = self.MyWeather.GetWeather(minimum = self.WeatherMinimum, ForUI = ForUI) if not len(ReturnData): return None return ReturnData #------------ Monitor::GetUserDefinedData ---------------------------------- # this assumes one json object, the file can be formatted (i.e. on multiple # lines) or can be on a single line def GetUserDefinedData(self): try: FileName = os.path.dirname(os.path.realpath(__file__)) + "/userdefined.json" if not os.path.isfile(FileName): return None if os.path.getsize(FileName) == 0: return None with open(FileName) as f: data = json.load(f,object_pairs_hook=collections.OrderedDict) return data except Exception as e1: self.LogErrorLine("Error in GetUserDefinedData: " + str(e1)) return None #------------ Monitor::DisplayWeather -------------------------------------- def DisplayWeather(self, DictOut = False): WeatherData = collections.OrderedDict() try: ReturnData = self.GetWeatherData() if not ReturnData == None and len(ReturnData): WeatherData["Weather"] = ReturnData if not DictOut: return self.printToString(self.ProcessDispatch(WeatherData,"")) except Exception as e1: self.LogErrorLine("Error in DisplayWeather: " + str(e1)) return WeatherData #------------ Monitor::DisplayMonitor -------------------------------------- def DisplayMonitor(self, DictOut = False): try: Monitor = collections.OrderedDict() MonitorData = [] Monitor["Monitor"] = MonitorData GenMonStats = [] SerialStats = [] MonitorData.append({"Generator Monitor Stats" : GenMonStats}) MonitorData.append({"Serial Stats" : self.Controller.GetCommStatus()}) GenMonStats.append({"Monitor Health" : self.GetSystemHealth()}) GenMonStats.append({"Controller" : self.Controller.GetController(Actual = False)}) GenMonStats.append({"Run time" : self.GetProgramRunTime()}) if self.Controller.PowerMeterIsSupported(): GenMonStats.append({"Power log file size" : self.Controller.GetPowerLogFileDetails()}) GenMonStats.append({"Generator Monitor Version" : GENMON_VERSION}) if not self.bDisablePlatformStats: PlatformStats = self.GetPlatformStats() if not PlatformStats == None: MonitorData.append({"Platform Stats" : PlatformStats}) WeatherData = self.GetWeatherData() if not WeatherData == None and len(WeatherData): MonitorData.append({"Weather" : WeatherData}) UserData = self.GetUserDefinedData() if not UserData == None and len(UserData): MonitorData.append({"External Data" : UserData}) if not DictOut: return self.printToString(self.ProcessDispatch(Monitor,"")) except Exception as e1: self.LogErrorLine("Error in DisplayMonitor: " + str(e1)) return Monitor #------------ Monitor::GetStartInfo----------------------------------------- def GetStartInfo(self, NoTile = False): StartInfo = collections.OrderedDict() StartInfo["version"] = GENMON_VERSION StartInfo["sitename"] = self.SiteName ControllerStartInfo = self.Controller.GetStartInfo(NoTile = NoTile) StartInfo = self.MergeDicts(StartInfo, ControllerStartInfo) return StartInfo #------------ Monitor::GetStatusForGUI ------------------------------------- def GetStatusForGUI(self): Status = {} Status["SystemHealth"] = self.GetSystemHealth() Status["UnsentFeedback"] = str(os.path.isfile(self.FeedbackLogFile)) if not self.bDisablePlatformStats: PlatformStats = self.GetPlatformStats(usemetric = True) if not PlatformStats == None and len(PlatformStats): Status["PlatformStats"] = PlatformStats WeatherData = self.GetWeatherData(ForUI = True) if not WeatherData == None and len(WeatherData): Status["Weather"] = WeatherData # Monitor Time Status["MonitorTime"] = datetime.datetime.now().strftime("%m/%d/%Y %H:%M") # Engine run hours Status["RunHours"] = self.Controller.GetRunHours() ReturnDict = self.MergeDicts(Status, self.Controller.GetStatusForGUI()) return ReturnDict #-------------Monitor::GetSystemHealth-------------------------------------- # returns the health of the monitor program def GetSystemHealth(self): outstr = "" if not self.Controller.InitComplete: outstr += "System Initializing. " if not self.AreThreadsAlive(): outstr += " Threads are dead. " if not self.CommunicationsActive: outstr += " Not receiving data. " if not self.LogFileIsOK(): outstr += " Log file is reporting errors." if len(outstr) == 0: outstr = "OK" return outstr #---------- Monitor::StartTimeThread--------------------------------------- def StartTimeThread(self): # This is done is a separate thread as not to block any return email processing # since we attempt to sync with generator time MyThread(self.Controller.SetGeneratorTimeDate, Name = "SetTimeThread") return "Time Set: Command Sent\n" #---------- Monitor::TimeSyncThread---------------------------------------- def TimeSyncThread(self): self.bDST = self.is_dst() # set initial DST state time.sleep(0.25) while True: if self.WaitForExit("TimeSyncThread", 1): # ten min return if self.Controller.InitComplete: break # if we are not always syncing, then set the time once if not self.bSyncTime: self.StartTimeThread() while True: if self.bSyncDST: if self.bDST != self.is_dst(): # has DST changed? self.bDST = self.is_dst() # update Flag # time changed so some comm stats may be off self.Controller.ResetCommStats() # set new time self.StartTimeThread() # start settime thread self.MessagePipe.SendMessage("Generator Time Update at " + self.SiteName, "Time updated due to daylight savings time change", msgtype = "info") if self.bSyncTime: # update gen time self.StartTimeThread() if self.WaitForExit("TimeSyncThread", 60 * 60): # 1 hour return #---------- Monitor::is_dst------------------------------------------------ def is_dst(self): #Determine whether or not Daylight Savings Time (DST) is currently in effect t = time.localtime() isdst = t.tm_isdst return (isdst != 0) #---------- Monitor::ComWatchDog------------------------------------------- #---------- monitors receive data status to make sure we are still communicating def ComWatchDog(self): self.CommunicationsActive = False time.sleep(0.25) NoticeSent = False LastActiveTime = datetime.datetime.now() while True: if self.WaitForExit("ComWatchDog", 1): return if self.Controller.InitComplete: break if self.Controller.ModBus.UseTCP: WatchDogPollTime = 8 else: WatchDogPollTime = 2 while True: self.CommunicationsActive = self.Controller.ComminicationsIsActive() if self.CommunicationsActive: LastActiveTime = datetime.datetime.now() if NoticeSent: NoticeSent = False self.MessagePipe.SendMessage("Generator Monitor Communication Restored at " + self.SiteName, "Generator Monitor communications with the controller has been restored at " + self.SiteName , msgtype = "info") else: if self.GetDeltaTimeMinutes(datetime.datetime.now() - LastActiveTime) > 1 : if not NoticeSent: NoticeSent = True self.MessagePipe.SendMessage("Generator Monitor Communication WARNING at " + self.SiteName, "Generator Monitor is not communicating with the controller at " + self.SiteName , msgtype = "error") if self.WaitForExit("ComWatchDog", WatchDogPollTime): return #---------- Monitor::LogFileIsOK-------------------------------------------- def LogFileIsOK(self): try: if not self.Controller.InitComplete: return True LogFile = self.LogLocation + "genmon.log" LogFileSize = os.path.getsize(LogFile) if LogFileSize <= self.LastLogFileSize: # log is unchanged or has rotated self.LastLogFileSize = LogFileSize self.NumberOfLogSizeErrors = 0 return True LogFileSizeDiff = LogFileSize - self.LastLogFileSize self.LastLogFileSize = LogFileSize if LogFileSizeDiff >= 100: self.NumberOfLogSizeErrors += 1 if self.NumberOfLogSizeErrors > 3: return False else: self.NumberOfLogSizeErrors = 0 return True except Exception as e1: self.LogErrorLine("Error in LogFileIsOK: " + str(e1)) return True #---------- Monitor::SocketWorkThread------------------------------------- # This thread spawns for each connection established by a client # in InterfaceServerThread def SocketWorkThread(self, conn): try: statusstr = "" if self.Controller == None: outstr = "WARNING: System Initializing" conn.sendall(outstr.encode()) else: if self.Controller.SystemInAlarm(): statusstr += "CRITICAL: System in alarm! " HealthStr = self.GetSystemHealth() if HealthStr != "OK": statusstr += "WARNING: " + HealthStr if statusstr == "": statusstr = "OK " outstr = statusstr + ": "+ self.Controller.GetOneLineStatus() conn.sendall(outstr.encode()) while True: try: data = conn.recv(1024) if len(data): if self.Controller == None: outstr = "Retry, System Initializing" else: outstr = self.ProcessCommand(data, True) conn.sendall(outstr.encode()) else: # socket closed remotely break except socket.timeout: if self.IsStopping: break continue except socket.error as msg: try: self.ConnectionList.remove(conn) conn.close() except: pass break except socket.error as msg: self.LogError("Error in SocketWorkThread: " + str(msg)) pass try: self.ConnectionList.remove(conn) conn.close() except: pass # end SocketWorkThread #---------- interface for heartbeat server thread ------------------------- def InterfaceServerThread(self): #create an INET, STREAMing socket self.ServerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # set some socket options so we can resuse the port self.ServerSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.ServerSocket.settimeout(.5) #bind the socket to a host, and a port self.ServerSocket.bind(('', self.ServerSocketPort)) #become a server socket self.ServerSocket.listen(5) #wait to accept a connection - blocking call while True: try: conn, addr = self.ServerSocket.accept() #self.LogError('Connected with ' + addr[0] + ':' + str(addr[1])) conn.settimeout(0.5) self.ConnectionList.append(conn) SocketThread = threading.Thread(target=self.SocketWorkThread, args = (conn,), name = "SocketWorkThread") SocketThread.daemon = True SocketThread.start() # start server thread except socket.timeout: if self.IsStopping: break continue except Exception as e1: if self.IsStopping: break self.LogErrorLine("Excpetion in InterfaceServerThread" + str(e1)) if self.WaitForExit("InterfaceServerThread", 0.5 ): break continue if self.ServerSocket != None: if len(self.ConnectionList): try: self.ServerSocket.shutdown(socket.SHUT_RDWR) except: pass self.ServerSocket.close() self.ServerSocket = None # #---------------------Monitor::Close---------------------------------------- def Close(self): # we dont really care about the errors that may be generated on shutdown try: self.IsStopping = True try: if self.MyWeather != None: self.MyWeather.Close() except: pass try: if self.bSyncDST or self.bSyncTime: self.KillThread("TimeSyncThread") except: pass try: self.KillThread("ComWatchDog") except: pass try: if not self.Controller == None: self.Controller.Close() except: pass # try: self.mail.Close() except: pass try: for item in self.ConnectionList: try: item.close() except: continue self.ConnectionList.remove(item) except: pass try: if(self.ServerSocket != None): self.ServerSocket.shutdown(socket.SHUT_RDWR) self.ServerSocket.close() self.KillThread("InterfaceServerThread") except: pass try: self.FeedbackPipe.Close() except: pass try: self.MessagePipe.Close() except: pass # Tell any remaining threads to stop for name, object in self.Threads.items(): try: if self.Threads[name].IsAlive(): self.Threads[name].Stop() except Exception as e1: self.LogErrorLine("Error killing thread in Monitor Close: " + name + ":" + str(e1)) except Exception as e1: self.LogErrorLine("Error Closing Monitor: " + str(e1)) with self.CriticalLock: self.LogError("Generator Monitor Shutdown") try: self.ProgramComplete = True sys.exit(0) except: pass
console.error("Error in SendNotice: " + str(e1)) return False #------------------- Command-line interface for gengpio ------------------------ if __name__ == '__main__': console, ConfigFilePath, address, port, loglocation, log = MySupport.SetupAddOnProgram( "genslack") # Set the signal handler signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) try: config = MyConfig(filename=os.path.join(ConfigFilePath, 'genslack.conf'), section='genslack', log=log) webhook_url = config.ReadValue('webhook_url', default=None) channel = config.ReadValue('channel', default=None) username = config.ReadValue('username', default=None) icon_emoji = config.ReadValue('icon_emoji', default=":red_circle:") title_link = config.ReadValue('title_link', default=None) if webhook_url == None or not len(webhook_url): log.error("Error: invalid webhoot_url setting") console.error("Error: invalid webhoot_url setting") sys.exit(2) if channel == None or not len(channel): log.error("Error: invalid channel setting")
def __init__(self, log = None, loglocation = ProgramDefaults.LogPath, ConfigFilePath = MyCommon.DefaultConfPath, host = ProgramDefaults.LocalHost, port = ProgramDefaults.ServerPort, console = None): super(GenExercise, self).__init__() self.AccessLock = threading.Lock() self.log = log self.console = console self.MonitorAddress = host self.PollTime = 2 self.ExerciseActive = False self.Debug = False try: self.config = MyConfig(filename = os.path.join(ConfigFilePath, 'genexercise.conf'), section = 'genexercise', log = self.log) self.ExerciseType = self.config.ReadValue('exercise_type', default = "Normal") self.ExerciseHour = self.config.ReadValue('exercise_hour', return_type = int, default = 12) self.ExerciseMinute = self.config.ReadValue('exercise_minute', return_type = int, default = 0) self.ExerciseDayOfMonth = self.config.ReadValue('exercise_day_of_month', return_type = int, default = 1) self.ExerciseDayOfWeek = self.config.ReadValue('exercise_day_of_week', default = "Monday") self.ExerciseDuration = self.config.ReadValue('exercise_duration', return_type = float, default = 12) self.ExerciseWarmup = self.config.ReadValue('exercise_warmup', return_type = float, default = 0) self.ExerciseFrequency = self.config.ReadValue('exercise_frequency', default = "Monthly") self.MonitorAddress = self.config.ReadValue('monitor_address', default = ProgramDefaults.LocalHost) self.LastExerciseTime = self.config.ReadValue('last_exercise', default = None) self.UseGeneratorTime = self.config.ReadValue('use_gen_time', return_type = bool, default = False) self.Debug = self.config.ReadValue('debug', return_type = bool, default = False) # Validate settings if not self.ExerciseType.lower() in ["normal","quiet", "transfer"]: self.ExerciseType = "normal" if self.ExerciseHour > 23 or self.ExerciseHour < 0: self.ExerciseHour = 12 if self.ExerciseMinute > 59 or self.ExerciseMinute < 0: self.ExerciseMinute = 0 if not self.ExerciseDayOfWeek.lower() in ["monday", "tuesday", "wednesday","thursday","friday","saturday","sunday"]: self.ExerciseDayOfWeek = "Monday" if self.ExerciseDayOfMonth > 28 or self.ExerciseDayOfMonth < 1: self.ExerciseDayOfMonth = 1 if self.ExerciseDuration > 60: self.ExerciseDuration = 60 if self.ExerciseDuration < 5: self.ExerciseDuration = 5 if self.ExerciseWarmup > 30: self.ExerciseWarmup = 30 if self.ExerciseWarmup < 0: self.ExerciseWarmup = 0 if not self.ExerciseFrequency.lower() in ["weekly","biweekly", "monthly"]: self.ExerciseFrequency = "Monthly" if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + os.path.join(ConfigFilePath, "genexercise.conf") + ": " + str(e1)) self.console.error("Error reading " + os.path.join(ConfigFilePath, "genexercise.conf") + ": " + str(e1)) sys.exit(1) try: self.Generator = ClientInterface(host = self.MonitorAddress, port = port, log = self.log) if not self.CheckGeneratorRequirement(): self.LogError("Requirements not met. Exiting.") sys.exit(1) # start thread monitor time for exercise self.Threads["ExerciseThread"] = MyThread(self.ExerciseThread, Name = "ExerciseThread", start = False) self.Threads["ExerciseThread"].Start() try: if self.ExerciseFrequency.lower() == "monthly": DayStr = "Day " + str(self.ExerciseDayOfMonth) else: DayStr = str(self.ExerciseDayOfWeek) self.LogError("Execise: " + self.ExerciseType + ", " + self.ExerciseFrequency + " at " + str(self.ExerciseHour) + ":" + str(self.ExerciseMinute) + " on " + DayStr + " for " + str(self.ExerciseDuration) + " min. Warmup: " + str(self.ExerciseWarmup)) self.DebugOutput("Debug Enabled") except Exception as e1: self.LogErrorLine(str(e1)) signal.signal(signal.SIGTERM, self.SignalClose) signal.signal(signal.SIGINT, self.SignalClose) except Exception as e1: self.LogErrorLine("Error in GenExercise init: " + str(e1)) self.console.error("Error in GenExercise init: " + str(e1)) sys.exit(1)
class Loader(MySupport): def __init__(self, start=False, stop=False, hardstop=False, loglocation="/var/log/", log=None, localinit=False, ConfigFilePath=None): self.Start = start self.Stop = stop self.HardStop = hardstop if ConfigFilePath == None: self.ConfigFilePath = "/etc/" else: self.ConfigFilePath = ConfigFilePath self.ConfigFileName = "genloader.conf" # log errors in this module to a file if localinit == True: self.configfile = self.ConfigFileName else: self.configfile = self.ConfigFilePath + self.ConfigFileName self.ModulePath = os.path.dirname(os.path.realpath(__file__)) + "/" self.ConfPath = os.path.dirname(os.path.realpath(__file__)) + "/conf/" # log errors in this module to a file if log == None: self.log = SetupLogger("genloader", loglocation + "genloader.log") else: self.log = log self.console = SetupLogger("genloader_console", log_file="", stream=True) try: if self.Start: if not self.CheckSystem(): self.LogInfo("Error check system readiness. Exiting") sys.exit(2) self.CachedConfig = {} # check to see if genloader.conf is present, if not copy it from genmon directory if not os.path.isfile(self.configfile): self.LogInfo("Warning: unable to find config file: " + self.configfile + " Copying file to /etc/ directory.") if os.path.isfile(self.ConfPath + self.ConfigFileName): copyfile(self.ConfPath + self.ConfigFileName, self.configfile) else: self.LogInfo("Unable to find config file.") sys.exit(2) self.config = MyConfig(filename=self.configfile, section="genmon", log=self.log) if not self.GetConfig(): self.LogInfo("Error reading config file. Exiting") sys.exit(2) if not self.ValidateConfig(): self.LogInfo("Error validating config. Exiting") sys.exit(2) self.LoadOrder = self.GetLoadOrder() if self.Stop: self.StopModules() time.sleep(2) if self.Start: self.StartModules() except Exception as e1: self.LogErrorLine("Error in init: " + str(e1)) #--------------------------------------------------------------------------- def CheckSystem(self): # this function checks the system to see if the required libraries are # installed. If they are not then an attempt is made to install them. ModuleList = [ # [import name , install name] ['flask', 'flask'], ['configparser', 'configparser'], ['serial', 'pyserial'], ['crcmod', 'crcmod'], ['pyowm', 'pyowm'], ['pytz', 'pytz'] ] try: ErrorOccured = False for Module in ModuleList: if not self.LibraryIsInstalled(Module[0]): self.LogInfo("Warning: required library " + Module[1] + " not installed. Attempting to install....") if not self.InstallLibrary(Module[1]): self.LogInfo("Error: unable to install library " + Module[1]) ErrorOccured = True return not ErrorOccured except Exception as e1: self.LogInfo("Error in CheckSystem: " + str(e1), LogLine=True) return False #--------------------------------------------------------------------------- def LibraryIsInstalled(self, libraryname): try: import importlib my_module = importlib.import_module(libraryname) return True except Exception as e1: return False #--------------------------------------------------------------------------- def InstallLibrary(self, libraryname): try: process = Popen(['pip', 'install', libraryname], stdout=PIPE, stderr=PIPE) output, _error = process.communicate() if _error: self.LogInfo("Error in InstallLibrary using pip : " + libraryname + " : " + str(_error)) rc = process.returncode return True except Exception as e1: self.LogInfo("Error installing module: " + libraryname + ": " + str(e1), LogLine=True) return False #--------------------------------------------------------------------------- def ValidateConfig(self): ErrorOccured = False if not len(self.CachedConfig): self.LogInfo("Error: Empty configruation found.") return False for Module, Settiings in self.CachedConfig.items(): try: if self.CachedConfig[Module]["enable"]: if not os.path.isfile(self.ModulePath + self.CachedConfig[Module]["module"]): self.LogInfo("Enable to find file " + self.ModulePath + self.CachedConfig[Module]["module"]) ErrorOccured = True # validate config file and if it is not there then copy it. if not self.CachedConfig[Module]["conffile"] == None and len( self.CachedConfig[Module]["conffile"]): ConfFileList = self.CachedConfig[Module]["conffile"].split( ",") for ConfigFile in ConfFileList: ConfigFile = ConfigFile.strip() if not os.path.isfile(self.ConfigFilePath + ConfigFile): if os.path.isfile(self.ConfPath + ConfigFile): self.LogInfo("Copying " + ConfigFile + " to " + self.ConfigFilePath) copyfile(self.ConfPath + ConfigFile, self.ConfigFilePath + ConfigFile) else: self.LogInfo("Enable to find config file " + self.ConfPath + ConfigFile) ErrorOccured = True except Exception as e1: self.LogInfo("Error validating config for " + Module + " : " + str(e1), LogLine=True) return False return not ErrorOccured #--------------------------------------------------------------------------- def AddEntry(self, section=None, module=None, conffile="", args="", priority='2'): try: if section == None or module == None: return self.config.WriteSection(section) self.config.WriteValue('module', module, section=section) self.config.WriteValue('enable', 'False', section=section) self.config.WriteValue('hardstop', 'False', section=section) self.config.WriteValue('conffile', conffile, section=section) self.config.WriteValue('args', args, section=section) self.config.WriteValue('priority', priority, section=section) except Exception as e1: self.LogInfo("Error in AddEntry: " + str(e1), LogLine=True) return #--------------------------------------------------------------------------- def GetConfig(self): try: Sections = self.config.GetSections() ValidSections = [ 'genmon', 'genserv', 'gengpio', 'gengpioin', 'genlog', 'gensms', 'gensms_modem', 'genpushover', 'gensyslog', 'genmqtt', 'genslack' ] for entry in ValidSections: if not entry in Sections: if entry == 'genslack': self.LogError("Warning: Missing entry: " + entry + " , adding entry") self.AddEntry(section=entry, module='genslack.py', conffile='genslack.conf') else: self.LogError("Warning: Missing entry: " + entry) Sections = self.config.GetSections() for SectionName in Sections: TempDict = {} self.config.SetSection(SectionName) if self.config.HasOption('module'): TempDict['module'] = self.config.ReadValue('module') else: TempDict['module'] = None if self.config.HasOption('enable'): TempDict['enable'] = self.config.ReadValue( 'enable', return_type=bool) else: TempDict['enable'] = False if self.config.HasOption('hardstop'): TempDict['hardstop'] = self.config.ReadValue( 'hardstop', return_type=bool) else: TempDict['hardstop'] = False if self.config.HasOption('conffile'): TempDict['conffile'] = self.config.ReadValue('conffile') else: TempDict['conffile'] = None if self.config.HasOption('args'): TempDict['args'] = self.config.ReadValue('args') else: TempDict['args'] = None if self.config.HasOption('priority'): TempDict['priority'] = self.config.ReadValue( 'priority', return_type=int, default=None) else: TempDict['priority'] = None if self.config.HasOption('postloaddelay'): TempDict['postloaddelay'] = self.config.ReadValue( 'postloaddelay', return_type=int, default=0) else: TempDict['postloaddelay'] = 0 self.CachedConfig[SectionName] = TempDict return True except Exception as e1: self.LogInfo("Error parsing config file: " + str(e1), LogLine=True) return False #--------------------------------------------------------------------------- def ConvertToInt(self, value, default=None): try: return int(str(value)) except: return default #--------------------------------------------------------------------------- def GetLoadOrder(self): LoadOrder = [] LoadDict = {} try: for Module, Settiings in self.CachedConfig.items(): # get the load order of all modules, even if they are disabled # since we need to stop all modules (even disabled ones) if the # conf file changed try: if self.CachedConfig[Module]["priority"] == None: LoadDict[Module] = 99 elif self.CachedConfig[Module]["priority"] >= 0: LoadDict[Module] = self.CachedConfig[Module][ "priority"] else: LoadDict[Module] = 99 except Exception as e1: self.LogInfo("Error reading load order (retrying): " + str(e1), LogLine=True) #lambda kv: (-kv[1], kv[0]) for key, value in sorted(LoadDict.iteritems(), key=lambda kv: (-kv[1], kv[0])): #for key, value in sorted(LoadDict.iteritems(), key=lambda (k,v): (v,k)): LoadOrder.append(key) except Exception as e1: self.LogInfo("Error reading load order: " + str(e1), LogLine=True) return LoadOrder #--------------------------------------------------------------------------- def StopModules(self): self.LogConsole("Stopping....") if not len(self.LoadOrder): self.LogInfo("Error, nothing to stop.") return False ErrorOccured = False for Module in reversed(self.LoadOrder): try: if not self.UnloadModule( self.CachedConfig[Module]["module"], HardStop=self.CachedConfig[Module]["hardstop"]): self.LogInfo("Error stopping " + Module) ErrorOccured = True except Exception as e1: self.LogInfo("Error stopping module " + Module + " : " + str(e1), LogLine=True) return False return not ErrorOccured #--------------------------------------------------------------------------- def StartModules(self): self.LogConsole("Starting....") if not len(self.LoadOrder): self.LogInfo("Error, nothing to start.") return False ErrorOccured = False for Module in self.LoadOrder: try: if self.CachedConfig[Module]["enable"]: if not self.LoadModule( self.ModulePath + self.CachedConfig[Module]["module"], args=self.CachedConfig[Module]["args"]): self.LogInfo("Error starting " + Module) ErrorOccured = True if not self.CachedConfig[Module][ "postloaddelay"] == None and self.CachedConfig[ Module]["postloaddelay"] > 0: time.sleep(self.CachedConfig[Module]["postloaddelay"]) except Exception as e1: self.LogInfo("Error starting module " + Module + " : " + str(e1), LogLine=True) return False return not ErrorOccured #--------------------------------------------------------------------------- def LoadModuleAlt(self, modulename, args=None): try: self.LogConsole("Starting " + modulename) # to load as a background process we just use os.system since Popen # is problematic in doing this CommandString = "python " + modulename if args != None and len(args): CommandString += " " + args CommandString += " &" os.system(CommandString) return True except Exception as e1: self.LogInfo("Error loading module: " + str(e1), LogLine=True) return False #--------------------------------------------------------------------------- def LoadModule(self, modulename, args=None): try: self.LogConsole("Starting " + modulename) try: from subprocess import DEVNULL # py3k except ImportError: import os DEVNULL = open(os.devnull, 'wb') if not len(args): args = None if "genserv.py" in modulename: OutputStream = DEVNULL else: OutputStream = subprocess.PIPE if args == None: # close_fds=True pid = subprocess.Popen([sys.executable, modulename], stdout=OutputStream, stderr=OutputStream, stdin=OutputStream) else: pid = subprocess.Popen([sys.executable, modulename, args], stdout=OutputStream, stderr=OutputStream, stdin=OutputStream) return True except Exception as e1: self.LogInfo("Error loading module: " + str(e1), LogLine=True) return False #--------------------------------------------------------------------------- def UnloadModule(self, modulename, HardStop=False): try: self.LogConsole("Stopping " + modulename) LoadInfo = [] LoadInfo.append('pkill') if HardStop or self.HardStop: LoadInfo.append('-9') LoadInfo.append('-u') LoadInfo.append('root') LoadInfo.append('-f') LoadInfo.append(modulename) process = Popen(LoadInfo, stdout=PIPE) output, _error = process.communicate() rc = process.returncode return True except Exception as e1: self.LogInfo("Error loading module: " + str(e1), LogLine=True) return False
class GenTankData(MySupport): #------------ GenTankData::init--------------------------------------------- def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, ConfigFilePath=MyCommon.DefaultConfPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort, console=None): super(GenTankData, self).__init__() self.LogFileName = os.path.join(loglocation, "gentankdiy.log") self.AccessLock = threading.Lock() self.log = log self.console = console self.MonitorAddress = host configfile = os.path.join(ConfigFilePath, 'gentankdiy.conf') try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename=configfile, section='gentankdiy', log=self.log) self.gauge_type = self.config.ReadValue('gauge_type', return_type=int, default=1) self.nb_tanks = self.config.ReadValue('nb_tanks', return_type=int, default=1) if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) try: if self.gauge_type == 1: self.gauge = GaugeDIY1(self.config, log=self.log, console=self.console) elif self.gauge_type == 2: self.gauge = GaugeDIY2(self.config, log=self.log, console=self.console) else: self.LogError("Invalid gauge type: " + str(self.gauge_type)) sys.exit(1) if not self.nb_tanks in [1, 2, 3, 4]: self.LogError( "Invalid Number of tanks (nb_tanks), 1, 2, 3 or 4 accepted: " + str(self.nb_tanks)) sys.exit(1) self.debug = self.gauge.debug self.simulate = self.gauge.simulate self.LogDebug("Num Tanks: " + str(self.nb_tanks)) self.LogDebug("Gauge Type: " + str(self.gauge_type)) self.Generator = ClientInterface(host=self.MonitorAddress, port=port, log=self.log) # start thread monitor time for exercise self.Threads["TankCheckThread"] = MyThread(self.TankCheckThread, Name="TankCheckThread", start=False) if not self.gauge.InitADC(): self.LogError("InitADC failed, exiting") sys.exit(1) self.Threads["TankCheckThread"].Start() signal.signal(signal.SIGTERM, self.SignalClose) signal.signal(signal.SIGINT, self.SignalClose) except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) self.console.error("Error in GenTankData init: " + str(e1)) sys.exit(1) #---------- GenTankData::SendCommand -------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data # ---------- GenTankData::TankCheckThread----------------------------------- def TankCheckThread(self): time.sleep(1) while True: try: dataforgenmon = {} tankdata = self.gauge.GetGaugeData() if tankdata != None: dataforgenmon["Tank Name"] = "External Tank" dataforgenmon["Capacity"] = 0 dataforgenmon["Percentage"] = tankdata if self.nb_tanks >= 2: tankdata2 = self.gauge.GetGaugeData(tanknum=1) if tankdata2 != None: dataforgenmon["Percentage2"] = tankdata2 if self.nb_tanks >= 3: tankdata2 = self.gauge.GetGaugeData(tanknum=2) if tankdata2 != None: dataforgenmon["Percentage3"] = tankdata2 if self.nb_tanks >= 4: tankdata2 = self.gauge.GetGaugeData(tanknum=3) if tankdata2 != None: dataforgenmon["Percentage4"] = tankdata2 retVal = self.SendCommand("generator: set_tank_data=" + json.dumps(dataforgenmon)) self.LogDebug(json.dumps(dataforgenmon)) self.LogDebug(retVal) if self.WaitForExit("TankCheckThread", float(self.gauge.PollTime * 60)): return except Exception as e1: self.LogErrorLine("Error in TankCheckThread: " + str(e1)) if self.WaitForExit("TankCheckThread", float(self.gauge.PollTime * 60)): return # ----------GenTankData::SignalClose---------------------------------------- def SignalClose(self, signum, frame): self.Close() sys.exit(1) # ----------GenTankData::Close---------------------------------------------- def Close(self): self.KillThread("TankCheckThread") self.gauge.Close() self.Generator.Close()
# Set the signal handler signal.signal(signal.SIGINT, signal_handler) if os.geteuid() != 0: print( "You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting." ) sys.exit(2) console = SetupLogger("pushover_console", log_file="", stream=True) log = SetupLogger("client", "/var/log/genpushover.log") try: config = MyConfig(filename='/etc/genpushover.conf', section='genpushover', log=log) appid = config.ReadValue('appid') userid = config.ReadValue('userid') pushsound = config.ReadValue('pushsound', default='updown') if appid == None or not len(appid): log.error("Error: invalid app ID") console.error("Error: invalid app ID") sys.exit(2) if userid == None or not len(userid): log.error("Error: invalid user ID") console.error("Error: invalid user ID") sys.exit(2)
class GenTankData(MySupport): #------------ GenTankData::init--------------------------------------------- def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, ConfigFilePath=MyCommon.DefaultConfPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort, console=None): super(GenTankData, self).__init__() self.LogFileName = os.path.join(loglocation, "gentankutil.log") self.AccessLock = threading.Lock() self.log = log self.console = console self.MonitorAddress = host self.PollTime = 2 self.TankID = "" self.debug = False configfile = os.path.join(ConfigFilePath, 'gentankutil.conf') try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename=configfile, section='gentankutil', log=self.log) self.PollTime = self.config.ReadValue('poll_frequency', return_type=float, default=60) self.debug = self.config.ReadValue('debug', return_type=bool, default=False) self.username = self.config.ReadValue('username', default="") self.password = self.config.ReadValue('password', default="") self.tank_name = self.config.ReadValue('tank_name', default="") if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) if self.username == "" or self.username == None or self.password == "" or self.password == None: self.LogError("Invalid user name or password, exiting") sys.exit(1) try: self.Generator = ClientInterface(host=self.MonitorAddress, port=port, log=self.log) #if not self.CheckGeneratorRequirement(): # self.LogError("Requirements not met. Exiting.") # sys.exit(1) self.tank = tankutility(self.username, self.password, self.log, debug=self.debug) # start thread monitor time for exercise self.Threads["TankCheckThread"] = MyThread(self.TankCheckThread, Name="TankCheckThread", start=False) self.Threads["TankCheckThread"].Start() signal.signal(signal.SIGTERM, self.SignalClose) signal.signal(signal.SIGINT, self.SignalClose) except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) self.console.error("Error in GenTankData init: " + str(e1)) sys.exit(1) #---------- GenTankData::SendCommand -------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data #---------- GenTankData::CheckGeneratorRequirement ------------------------ def CheckGeneratorRequirement(self): try: data = self.SendCommand("generator: start_info_json") StartInfo = {} StartInfo = json.loads(data) if not "evolution" in StartInfo["Controller"].lower( ) and not "nexus" in StartInfo["Controller"].lower(): self.LogError( "Error: Only Evolution or Nexus controllers are supported for this feature: " + StartInfo["Controller"]) return False return True except Exception as e1: self.LogErrorLine("Error in CheckGeneratorRequirement: " + str(e1)) return False # ---------- GenTankData::Login--------------------------------------------- def Login(self, force=False): if force: self.TankID = "" if len(self.TankID): # already logged in return True if not self.tank.Login(): return False self.TankID = self.tank.GetIDFromName(self.tank_name) if not len(self.TankID): return False return True # ---------- GenTankData::TankCheckThread----------------------------------- def TankCheckThread(self): time.sleep(1) LastLoginTime = datetime.datetime.now() while True: try: NUMBER_OF_SECONDS = 60 * 60 * 12 # 12 hours if ((datetime.datetime.now() - LastLoginTime).total_seconds() > NUMBER_OF_SECONDS) or not len(self.TankID): self.LogDebug("Login ") if not self.Login(force=True): self.LogError( "Error logging in in TankCheckThread, retrying") dataforgenmon = {} tankdata = self.tank.GetData(self.TankID) if tankdata != None: dataforgenmon["Tank Name"] = tankdata["name"] dataforgenmon["Capacity"] = self.tank.GetCapacity() dataforgenmon["Percentage"] = self.tank.GetPercentage() retVal = self.SendCommand("generator: set_tank_data=" + json.dumps(dataforgenmon)) self.LogDebug(retVal) if self.WaitForExit("TankCheckThread", float(self.PollTime * 60)): return except Exception as e1: self.LogErrorLine("Error in TankCheckThread: " + str(e1)) if self.WaitForExit("TankCheckThread", float(self.PollTime * 60)): return # ----------GenTankData::SignalClose---------------------------------------- def SignalClose(self, signum, frame): self.Close() sys.exit(1) # ----------GenTankData::Close---------------------------------------------- def Close(self): self.KillThread("TankCheckThread") self.Generator.Close()
class MyModem(MySupport): def __init__(self, port="/dev/ttyAMA0", rate=115200, loglocation=ProgramDefaults.LogPath, log=None, localinit=False, ConfigFilePath=ProgramDefaultsConfPath, recipient=None): super(MyModem, self).__init__() self.MessagesSent = 0 self.Errors = 0 self.SendActive = False self.ModemLock = threading.RLock() self.Sending = False self.SendQueue = [] if ConfigFilePath == None: self.ConfigFilePath = ProgramDefaults.ConfPath else: self.ConfigFilePath = ConfigFilePath # log errors in this module to a file if localinit == True: self.configfile = "mymodem.conf" else: self.configfile = self.ConfigFilePath + "mymodem.conf" # log errors in this module to a file if log == None: self.log = SetupLogger("mymodem", loglocation + "mymodem.log") else: self.log = log self.console = SetupLogger("mymodem_console", log_file="", stream=True) try: self.config = MyConfig(filename=self.configfile, section="MyModem", log=self.log) self.LogAtCommands = self.config.ReadValue('log_at_commands', return_type=bool, default=False) self.MessageLevel = self.config.ReadValue('message_level', default='error') self.Rate = self.config.ReadValue('rate', return_type=int, default=115200) self.Port = self.config.ReadValue('port', default="/dev/ttyAMA0") self.Recipient = self.config.ReadValue('recipient', default=recipient) self.ModemType = self.config.ReadValue('modem_type', default="LTEPiHat") except Exception as e1: self.LogErrorLine("Error reading config file: " + str(e1)) self.LogConsole("Error reading config file: " + str(e1)) return if self.Recipient == None or not len(self.Recipient): self.LogErrorLine("Error invalid recipient") self.LogConsole("Error invalid recipient") if self.Port == None or not len(self.Port): self.LogErrorLine("Error invalid port") self.LogConsole("Error invalid port") return if self.Rate == None or self.Rate <= 0: self.LogErrorLine("Error invalid rate") self.LogConsole("Error invalid rate") return # rate * 10 bits then convert to MS self.CharacterTimeMS = (((1 / self.Rate) * 10) * 1000) self.InitComplete = False try: self.SerialDevice = SerialDevice(port, rate=rate, log=self.log, loglocation=loglocation) self.Threads = self.MergeDicts(self.Threads, self.SerialDevice.Threads) self.Threads["SendMessageThread"] = MyThread( self.SendMessageThread, Name="SendMessageThread") except Exception as e1: self.LogErrorLine("Error opening serial device in MyModem: " + str(e1)) #------------MyModem::SendMessageThread------------------------------------- def SendMessageThread(self): # once SendMessage is called messages are queued and then sent from this thread time.sleep(0.5) while True: try: self.SendActive = False if self.WaitForExit("SendMessageThread", 2): return while self.SendQueue != []: SendError = False if not self.InitComplete: if self.WaitForExit("SendMessageThread", 5): return else: continue self.SendActive = True MessageItems = self.SendQueue.pop() try: if not (self.SendSMS(message=MessageItems[0], recipient=MessageItems[1], msgtype=MessageItems[2])): self.LogError( "Error in SendMessageThread, SendSMS failed, retrying" ) SendError = True except Exception as e1: # put the time back at the end of the queue self.LogErrorLine( "Error in SendMessageThread, retrying (2): " + str(e1)) SendError = True if SendError: self.SendQueue.insert(len(self.SendQueue), MessageItems) self.SendActive = False # sleep for 10 sec and try again if self.WaitForExit("SendMessageThread", 10): return except Exception as e1: self.LogErrorLine("Error in SendMessageThread: " + str(e1)) #------------MyModem::SendMessage------------------------------------------- # msgtype must be one of "outage", "error", "warn", "info" def SendMessage(self, message=None, recipient=None, msgtype="error"): try: self.SendQueue.insert(0, [message, recipient, msgtype]) return True except Exception as e1: self.LogErrorLine("Error in SendMessage: " + str(e1)) return False #------------------MyModem::MessagesPending--------------------------------- def MessagesPending(self): return self.SendQueue != [] or self.SendActive #------------------MyModem::SendCommand------------------------------------- def SendATCommand(self, command, response=None, retries=1, NoAddCRLF=False): try: if self.SerialDevice == None: self.LogError("Serial device is not open!") return False if not NoAddCRLF: command = str(command) + b"\r\n" if response != None: response = str(response) + b"\r\n" else: command = str(command) if response != None: response = str(response) with self.ModemLock: self.Sending = True self.SerialDevice.Flush() attempts = retries while self.Sending and attempts >= 0: self.SerialDevice.Write(command) if self.LogAtCommands: self.LogError("->" + command) time.sleep(0.75) if None != response: SerialBuffer = self.SerialDevice.GetRxBufferAsString() if self.LogAtCommands and len(SerialBuffer): self.LogError("<-" + SerialBuffer) if SerialBuffer.find("ERROR") >= 0: self.Sending = False self.LogError( "Error returned SendATCommand: CMD: " + str(command)) return False if SerialBuffer.find(response) >= 0: self.Sending = False attempts += 1 elif None == response: self.Sending = False if self.Sending: time.sleep(0.5) attempts = attempts - 1 else: break return (attempts >= 0) except Exception as e1: self.LogErrorLine("Error in SendATCommand: " + "CMD: " + str(command) + " : " + str(e1)) return False #------------------MyModem::Send-------------------------------------------- def SendSMS(self, message=None, recipient=None, msgtype="error"): try: if recipient == None: recipient = self.Recipient if recipient == None or not len(recipient): self.LogError("Invalid recipient in SendSMS.") return False with self.ModemLock: # set default config if not self.SendATCommand("ATZ", "OK"): self.LogError( "Failed setting default config in MySMS:Send") self.Errors += 1 # set text message mode if not self.SendATCommand("AT+CMGF=1", "OK"): self.LogError("Failed setting message mode in MySMS:Send") self.Errors += 1 return False StartMessage = str("AT+CMGS=" + '"' + str(recipient) + '"' + "\r") if not self.SendATCommand( StartMessage, ">", retries=0, NoAddCRLF=True): self.SendATCommand("\x1b", "OK", retries=1, NoAddCRLF=True) self.LogError("Failed sending CMGS in MySMS:Send") self.Errors += 1 return False if not self.SendATCommand( str(message) + "\r", ">", retries=0, NoAddCRLF=True): self.SendATCommand("\x1b", "OK", retries=1, NoAddCRLF=True) self.LogError("Failed sending Message Body in MySMS:Send") self.Errors += 1 return False if not self.SendATCommand( "\x1a", "OK", retries=1, NoAddCRLF=True): self.SendATCommand("\x1b", "OK", retries=1, NoAddCRLF=True) self.LogError("Failed sending EOM in MySMS:Send") self.Errors += 1 return False self.SendATCommand("AT", 'OK') self.MessagesSent += 1 return True except Exception as e1: self.Errors += 1 self.LogErrorLine("Error in MySMS:Send: " + str(e1)) return False #------------------MyModem::GetQuotedString--------------------------------- def GetQuotedString(self, InputString): try: quoted = re.compile('"[^"]*"') for value in quoted.findall(InputString): newline = "".join(c for c in value if c not in '"') return newline return None except Exception as e1: self.LogErrorLine("Error in GetQuotedString: " + str(InputString) + ": " + str(e1)) return "" #------------------MyModem::GetNumbersFromString---------------------------- def GetNumbersFromString(self, InputString): # return list of numbers try: return re.findall(r'\d+', InputString) except Exception as e1: self.LogErrorLine("Error in GetNumbersFromString: " + str(InputString) + ": " + str(e1)) return [] #------------------MyModem::GetItemsFromCommand----------------------------- def GetItemsFromCommand(self, InputString): try: ReturnString = InputString Index = ReturnString.find(":") if Index > 1 or len(ReturnString) < 2: ListItems = ReturnString[Index + 1:].split(",") ListItems = map(str.strip, ListItems) return ListItems else: return [InputString.split('\r\n')[1].strip()] except Exception as e1: self.LogErrorLine("Error in GetItemsFromCommand: " + str(InputString) + ": " + str(e1)) self.LogErrorLine("Input: " + str(InputString)) return [] #------------------MyModem::GetInfo----------------------------------------- def GetInfo(self, ReturnString=False): ModemInfo = collections.OrderedDict() try: with self.ModemLock: ModemInfo["Port"] = str(self.Port) ModemInfo["Rate"] = str(self.Rate) ModemInfo["Messages Sent"] = str(self.MessagesSent) ModemInfo["Errors"] = str(self.Errors) # get operator name if self.SendATCommand("AT+COPS?", "OK"): Buffer = self.SerialDevice.GetRxBufferAsString() ModemInfo["Carrier"] = self.GetQuotedString( Buffer.split('\r\n')[1]) # AT+CIMI IMSI (International mobile subscriber identification) if self.SendATCommand("AT+CIMI", "OK"): Buffer = self.SerialDevice.GetRxBufferAsString() ReturnValue = self.GetItemsFromCommand(Buffer) if len(ReturnValue): ModemInfo["IMSI"] = ReturnValue[0] ## get SIM card state if self.SendATCommand("AT+UUICC?", "OK"): Buffer = self.SerialDevice.GetRxBufferAsString() ReturnValue = self.GetItemsFromCommand(Buffer) if len(ReturnValue): SIMType = self.GetNumbersFromString(ReturnValue[0])[0] if SIMType == "0": ModemInfo["SIM Type"] = "2G" elif SIMType == "1": ModemInfo["SIM Type"] = "3G or 4G" # name of manufacturer (AT+CGMI) if self.SendATCommand("AT+CGMI", "OK"): Buffer = self.SerialDevice.GetRxBufferAsString() ReturnValue = self.GetItemsFromCommand(Buffer) if len(ReturnValue): ModemInfo["Manufacturer"] = ReturnValue[0] # show module/model name if self.SendATCommand("AT+CGMM", "OK"): Buffer = self.SerialDevice.GetRxBufferAsString() ReturnValue = self.GetItemsFromCommand(Buffer) if len(ReturnValue): ModemInfo["Model"] = ReturnValue[0] # phone / modem info # IMEI number (International Mobile Equipment Identity) (AT+CGSN) if self.SendATCommand("AT+CGSN", "OK"): Buffer = self.SerialDevice.GetRxBufferAsString() ReturnValue = self.GetItemsFromCommand(Buffer) if len(ReturnValue): ModemInfo["IMEI"] = ReturnValue[0] # software version (AT+CGMR) if self.SendATCommand("AT+CGMR", "OK"): Buffer = self.SerialDevice.GetRxBufferAsString() ReturnValue = self.GetItemsFromCommand(Buffer) if len(ReturnValue): ModemInfo["Firmware Version"] = ReturnValue[0] ## subscriber info MSISDN (AT+CNUM) if self.SendATCommand("AT+CNUM", "OK"): Buffer = self.SerialDevice.GetRxBufferAsString() ReturnValue = self.GetItemsFromCommand(Buffer) if len(ReturnValue) >= 2: ModemInfo["MSISDN"] = self.GetQuotedString( ReturnValue[1]) # mobile phone activity status (AT+CPAS), returns "+CPAS: 0" where number is: # 0: ready (MT allows commands from DTE) # 1: unavailable (MT does not allow commands from # 2: unknown (MT is not guaranteed to respond to instructions) # 3: ringing (MT is ready for commands from DTE, but the ringer is active) # 4:callinprogress(MTisreadyforcommandsfromDTE,butacallisinprogress,e.g.callactive, # hold, disconnecting) # 5:asleep(MEisunabletoprocesscommandsfromDTEbecauseitisinalowfunctionalitystate) if self.SendATCommand("AT+CPAS", "OK"): Buffer = self.SerialDevice.GetRxBufferAsString() NumberList = self.GetItemsFromCommand(Buffer) if len(NumberList) >= 1: Status = self.GetNumbersFromString(NumberList[0])[0] if Status == "0": ModemInfo["Status"] = "Ready" elif Status == "1": ModemInfo["Status"] = "Unavailable" elif Status == "2": ModemInfo["Status"] = "Unknown" elif Status == "3": ModemInfo["Status"] = "Ringing" elif Status == "4": ModemInfo["Status"] = "Call In Progress" elif Status == "5": ModemInfo["Status"] = "Asleep" else: ModemInfo["Status"] = "Unknown" # mobile network registration status (AT+CREG?) returns "+CREG: 0,0" or +CREG: n,stat # <n> # 0 (default value and factory-programmed value): network registration URC disabled # 1: network registration URC +CREG: <stat> enabled # 2: network registration and location information URC +CREG: <stat>[,<lac>,<ci>[,<AcTStatus>]] enabled # <stat> # 0: not registered, the MT is not currently searching a new operator to register to # 1: registered, home network # 2: not registered, but the MT is currently searching a new operator to register to # 3: registration denied # 4: unknown (e.g. out of GERAN/UTRAN/E-UTRAN coverage) # 5: registered, roaming # 6:registeredfor"SMSonly",homenetwork(applicableonlywhen<AcTStatus>indicates E-UTRAN) # 7:registeredfor"SMSonly",roaming(applicableonlywhen<AcTStatus>indicatesE-UTRAN) # 8: attached for emergency bearer services only # 9:registeredfor"CSFBnotpreferred",homenetwork(applicableonlywhen<AcTStatus> indicates E-UTRAN) # 10:registeredfor"CSFBnotpreferred",roaming(applicableonlywhen<AcTStatus>indicates E-UTRAN) ## AT+REG=2 self.SendATCommand("AT+CREG=2", "OK") ## +CREG, +CEREG and +CGREG. if self.SendATCommand("AT+CREG?", "OK"): Buffer = self.SerialDevice.GetRxBufferAsString() NumberList = self.GetItemsFromCommand(Buffer) if len(NumberList) >= 1: if NumberList[0] == "0": ModemInfo["Registration Status"] = "Disabled" elif NumberList[0] == "1" or NumberList[0] == "2": ModemInfo["Registration Status"] = "Enabled" if len(NumberList) >= 2: NetworkRegistrationLookup = { "0": "Not Registered, Not searching", "1": "Registered, Home network", "2": "Not Registered, Searching", "3": "Registration Denied", "4": "Unknown, Out of Coverage", "5": "Registered, Roaming", "6": "Registered, Home network, SMS Only", "7": "Registered, Roaming, SMS Only", "8": "Attached for Emergency Bearer Services Only", "9": "Registered, Home network, CSFB Not Preferred", "10": "Registered, Roaming, CSFB Not Preferred", } ModemInfo[ "Network Registration"] = NetworkRegistrationLookup.get( NumberList[1], "Unknown") if len(NumberList) > 5: NetworkTech = { "0": "GSM", # 2G "1": "GSM Compact", # 2G "2": "UTRAN", # 3G "3": "GSM w/EGPRS", # 2.5G "4": "UTRAN w/HSDPA", # 3G "5": "UTRAN w/HSUPA", # 3G "6": "UTRAN w/HSDPA and HSUPA", # 3G "7": "E-UTRAN", # 4G "255": "Unknown" } ModemInfo["Cell Network Technology"] = NetworkTech.get( self.GetNumbersFromString(NumberList[4])[0], "Unknown") # radio signal strength (AT+CSQ), returns "+CSQ: 2,5" # first number is RSSI: # The allowed range is 0-31 and 99. Remapped indication of the following parameters: # the Received Signal Strength Indication (RSSI) in GSM RAT # the Received Signal Code Power (RSCP) in UMTS RAT # the Reference Signal Received Power (RSRP) in LTE RAT # When the RF power level of the received signal is the highest possible, the value 31 is reported. When it is not known, not detectable or currently not available, 99 is returned. # second number is signal quality: # The allowed range is 0-7 and 99. The information provided depends on the selected RAT: # In 2G RAT CS dedicated and GPRS packet transfer mode indicates the BitErrorRate(BER)as # specified in 3GPP TS 45.008 [148] # In 2G RAT EGPRS packet transfer mode indicates the Mean BitErrorProbability(BEP) of a radio # block. 3GPP TS 45.008 [148] specifies the range 0-31 for the Mean BEP which is mapped to # the range 0-7 of <qual> # In UMTS RAT indicates the Energy per Chip/Noise(ECN0) ratioin dB levels of the current cell. # 3GPP TS 25.133 [106] specifies the range 0-49 for EcN0 which is mapped to the range 0-7 # of <qual> # In LTE RAT indicates the Reference Signal Received Quality(RSRQ). TS36.133[105] specifies # the range 0-34 for RSRQ which is mapped to the range 0-7 of <qual> if self.SendATCommand("AT+CSQ", "OK"): Buffer = self.SerialDevice.GetRxBufferAsString() NumberList = self.GetItemsFromCommand(Buffer) if len(NumberList) >= 2: RSSIList = self.ParseRSSI(NumberList[0]) ModemInfo[ "RSSI"] = RSSIList[0] + " dBm" + ", " + RSSIList[1] ModemInfo[ "Signal Quality"] = self.GetNumbersFromString( NumberList[1])[0] if self.SendATCommand("AT+CCLK?", "OK"): Buffer = self.SerialDevice.GetRxBufferAsString() if len(Buffer): ModemInfo["Network Time"] = self.GetQuotedString( Buffer) except Exception as e1: self.LogErrorLine("Error in MyModem:GetInfo: " + str(e1)) if ReturnString: return self.DictToString(ModemInfo) return ModemInfo #------------------MyModem::ParseRSSI------------------------------------------- def ParseRSSI(self, Value): RSSILookup = { "0": ["-113", "Poor"], "1": ["-111", "Poor"], "2": ["-109", "Marginal"], "3": ["-107", "Marginal"], "4": ["-105", "Marginal"], "5": ["-103", "Marginal"], "6": ["-101", "Marginal"], "7": ["-99", "Marginal"], "8": ["-97", "Marginal"], "9": ["-95", "Marginal"], "10": ["-93", "OK"], "11": ["-91", "OK"], "12": ["-89", "OK"], "13": ["-87", "OK"], "14": ["-85", "OK"], "15": ["-83", "Good"], "16": ["-81", "Good"], "17": ["-79", "Good"], "18": ["-77", "Good"], "19": ["-75", "Good"], "20": ["-73", "Excellent"], "21": ["-71", "Excellent"], "22": ["-69", "Excellent"], "23": ["-67", "Excellent"], "24": ["-65", "Excellent"], "25": ["-63", "Excellent"], "26": ["-61", "Excellent"], "27": ["-59", "Excellent"], "28": ["-57", "Excellent"], "29": ["-55", "Excellent"], "30": ["-53", "Excellent"], "99": ["Unknown", "Unknown"] } return RSSILookup.get(Value, ["Unknown", "Unknown"]) #------------------MyModem::Close------------------------------------------- def Close(self): try: try: self.KillThread("SendMessageThread") except: pass try: self.SerialDevice.Close() except: pass except Exception as e1: self.LogErrorLine("Error Closing Modem: " + str(e1))
def __init__(self, port="/dev/ttyAMA0", rate=115200, loglocation=ProgramDefaults.LogPath, log=None, localinit=False, ConfigFilePath=ProgramDefaultsConfPath, recipient=None): super(MyModem, self).__init__() self.MessagesSent = 0 self.Errors = 0 self.SendActive = False self.ModemLock = threading.RLock() self.Sending = False self.SendQueue = [] if ConfigFilePath == None: self.ConfigFilePath = ProgramDefaults.ConfPath else: self.ConfigFilePath = ConfigFilePath # log errors in this module to a file if localinit == True: self.configfile = "mymodem.conf" else: self.configfile = self.ConfigFilePath + "mymodem.conf" # log errors in this module to a file if log == None: self.log = SetupLogger("mymodem", loglocation + "mymodem.log") else: self.log = log self.console = SetupLogger("mymodem_console", log_file="", stream=True) try: self.config = MyConfig(filename=self.configfile, section="MyModem", log=self.log) self.LogAtCommands = self.config.ReadValue('log_at_commands', return_type=bool, default=False) self.MessageLevel = self.config.ReadValue('message_level', default='error') self.Rate = self.config.ReadValue('rate', return_type=int, default=115200) self.Port = self.config.ReadValue('port', default="/dev/ttyAMA0") self.Recipient = self.config.ReadValue('recipient', default=recipient) self.ModemType = self.config.ReadValue('modem_type', default="LTEPiHat") except Exception as e1: self.LogErrorLine("Error reading config file: " + str(e1)) self.LogConsole("Error reading config file: " + str(e1)) return if self.Recipient == None or not len(self.Recipient): self.LogErrorLine("Error invalid recipient") self.LogConsole("Error invalid recipient") if self.Port == None or not len(self.Port): self.LogErrorLine("Error invalid port") self.LogConsole("Error invalid port") return if self.Rate == None or self.Rate <= 0: self.LogErrorLine("Error invalid rate") self.LogConsole("Error invalid rate") return # rate * 10 bits then convert to MS self.CharacterTimeMS = (((1 / self.Rate) * 10) * 1000) self.InitComplete = False try: self.SerialDevice = SerialDevice(port, rate=rate, log=self.log, loglocation=loglocation) self.Threads = self.MergeDicts(self.Threads, self.SerialDevice.Threads) self.Threads["SendMessageThread"] = MyThread( self.SendMessageThread, Name="SendMessageThread") except Exception as e1: self.LogErrorLine("Error opening serial device in MyModem: " + str(e1))
class GenTemp(MySupport): #------------ GenTemp::init------------------------------------------------- def __init__(self, log = None, loglocation = ProgramDefaults.LogPath, ConfigFilePath = MyCommon.DefaultConfPath, host = ProgramDefaults.LocalHost, port = ProgramDefaults.ServerPort, console = None): super(GenTemp, self).__init__() self.LogFileName = os.path.join(loglocation, "gentemp.log") self.AccessLock = threading.Lock() # log errors in this module to a file self.log = log self.console = console self.LastValues = {} self.MonitorAddress = host self.debug = False self.PollTime = 1 self.BlackList = None configfile = os.path.join(ConfigFilePath, 'gentemp.conf') try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename = configfile, section = 'gentemp', log = self.log) self.UseMetric = self.config.ReadValue('use_metric', return_type = bool, default = False) self.PollTime = self.config.ReadValue('poll_frequency', return_type = float, default = 1) self.debug = self.config.ReadValue('debug', return_type = bool, default = False) self.DeviceLabels = self.GetParamList(self.config.ReadValue('device_labels', default = None)) self.BlackList = self.GetParamList(self.config.ReadValue('blacklist', default = None)) if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) try: self.Generator = ClientInterface(host = self.MonitorAddress, port = port, log = self.log) self.DeviceList = self.EnumDevices() if not len(self.DeviceList): self.LogConsole("No sensors found.") self.LogError("No sensors found.") sys.exit(1) # start thread monitor time for exercise self.Threads["GenTempThread"] = MyThread(self.GenTempThread, Name = "GenTempThread", start = False) self.Threads["GenTempThread"].Start() signal.signal(signal.SIGTERM, self.SignalClose) signal.signal(signal.SIGINT, self.SignalClose) except Exception as e1: self.LogErrorLine("Error in GenTemp init: " + str(e1)) self.LogConsole("Error in GenTemp init: " + str(e1)) sys.exit(1) #---------- GenTemp::GetParamList ----------------------------------------- def GetParamList(self, input_string): ReturnValue = [] try: if input_string != None: if len(input_string): ReturnList = input_string.strip().split(",") if len(ReturnList): for Items in ReturnList: Items = Items.strip() if len(Items): ReturnValue.append(Items) if len(ReturnValue): return ReturnValue return None except Exception as e1: self.LogErrorLine("Error in GetParamList: " + str(e1)) return None #---------- GenTemp::EnumDevices ------------------------------------------ def EnumDevices(self): DeviceList = [] try: # enum DS18B20 temp sensors for sensor in glob.glob("/sys/bus/w1/devices/28-*/w1_slave"): if not self.CheckBlackList(sensor) and self.DeviceValid(sensor): self.LogDebug("Found DS18B20 : " + sensor) DeviceList.append(sensor) # enum type K thermocouples #for sensor in glob.glob("/sys/bus/w1/devices/w1_bus_master*/3b-*/w1_slave"): for sensor in glob.glob("/sys/bus/w1/devices/3b-*/w1_slave"): if not self.CheckBlackList(sensor) and self.DeviceValid(sensor): self.LogDebug("Found type K thermocouple : " + sensor) DeviceList.append(sensor) return DeviceList except Exception as e1: self.LogErrorLine("Error in EnumDevices: " + str(e1)) return DeviceList #------------ GenTemp::ReadData -------------------------------------------- def ReadData(self, device): try: f = open(device, "r") data = f.read() f.close() return data except Exception as e1: self.LogErrorLine("Error in ReadData for " + device + " : " + str(e1)) return None #------------ GenTemp::DeviceValid ----------------------------------------- def DeviceValid(self, device): try: data = self.ReadData(device) if isinstance(data, str) and "YES" in data and " crc=" in data and " t=" in data: return True return False except Exception as e1: self.LogErrorLine("Error in DeviceValid for " + device + " : " + str(e1)) return False #------------ GenTemp::ParseData ------------------------------------------- def ParseData(self, data): try: if self.UseMetric: units = "C" else: units = "F" if not isinstance(data, str): return None, units if not len(data): return None, units (discard, sep, reading) = data.partition(' t=') t = float(reading) / 1000.0 if not self.UseMetric: return self.ConvertCelsiusToFahrenheit(t), units else: return t, units except Exception as e1: self.LogErrorLine("Error in ParseData: " + str(e1)) return None, units #------------ GenTemp::GetIDFromDeviceName --------------------------------- def GetIDFromDeviceName(self, device): try: if "28-" in device or "3b-" in device: id = device.split("/")[5] return id except Exception as e1: self.LogErrorLine("Error in GetIDFromDeviceName for " + device + " : " + str(e1)) return "UNKNOWN_ID" #---------- GenTemp::SendCommand ------------------------------------------ def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data # ---------- GenTemp::CheckBlackList----------------------------------------- def CheckBlackList(self, device): if not isinstance(self.BlackList, list): return False return any(blacklistitem in device for blacklistitem in self.BlackList) # ---------- GenTemp::GenTempThread----------------------------------------- def GenTempThread(self): time.sleep(1) while True: try: labelIndex = 0 ReturnDeviceData = [] for sensor in self.DeviceList: temp, units = self.ParseData(self.ReadData(sensor)) if temp != None: self.LogDebug("Device: %s Reading: %.2f %s" % (self.GetIDFromDeviceName(sensor), temp, units)) if isinstance(self.DeviceLabels, list) and len(self.DeviceLabels) and (labelIndex < len(self.DeviceLabels)): device_label = self.DeviceLabels[labelIndex] else: device_label = self.GetIDFromDeviceName(sensor) ReturnDeviceData.append({device_label : "%.2f %s" % (temp, units)}) labelIndex += 1 return_string = json.dumps({"External Temperature Sensors": ReturnDeviceData}) self.SendCommand("generator: set_temp_data=" + return_string ) self.LogDebug(return_string) if self.WaitForExit("GenTempThread", float(self.PollTime)): return except Exception as e1: self.LogErrorLine("Error in GenTempThread: " + str(e1)) if self.WaitForExit("GenTempThread", float(self.PollTime * 60)): return # ----------GenTemp::SignalClose-------------------------------------------- def SignalClose(self, signum, frame): self.Close() sys.exit(1) # ----------GenTemp::Close---------------------------------------------- def Close(self): self.LogError("GenTemp Exit") self.KillThread("GenTempThread") self.Generator.Close()
def __init__(self, log = None, loglocation = ProgramDefaults.LogPath, ConfigFilePath = MyCommon.DefaultConfPath, host = ProgramDefaults.LocalHost, port = ProgramDefaults.ServerPort, console = None): super(GenTemp, self).__init__() self.LogFileName = os.path.join(loglocation, "gentemp.log") self.AccessLock = threading.Lock() # log errors in this module to a file self.log = log self.console = console self.LastValues = {} self.MonitorAddress = host self.debug = False self.PollTime = 1 self.BlackList = None configfile = os.path.join(ConfigFilePath, 'gentemp.conf') try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename = configfile, section = 'gentemp', log = self.log) self.UseMetric = self.config.ReadValue('use_metric', return_type = bool, default = False) self.PollTime = self.config.ReadValue('poll_frequency', return_type = float, default = 1) self.debug = self.config.ReadValue('debug', return_type = bool, default = False) self.DeviceLabels = self.GetParamList(self.config.ReadValue('device_labels', default = None)) self.BlackList = self.GetParamList(self.config.ReadValue('blacklist', default = None)) if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) try: self.Generator = ClientInterface(host = self.MonitorAddress, port = port, log = self.log) self.DeviceList = self.EnumDevices() if not len(self.DeviceList): self.LogConsole("No sensors found.") self.LogError("No sensors found.") sys.exit(1) # start thread monitor time for exercise self.Threads["GenTempThread"] = MyThread(self.GenTempThread, Name = "GenTempThread", start = False) self.Threads["GenTempThread"].Start() signal.signal(signal.SIGTERM, self.SignalClose) signal.signal(signal.SIGINT, self.SignalClose) except Exception as e1: self.LogErrorLine("Error in GenTemp init: " + str(e1)) self.LogConsole("Error in GenTemp init: " + str(e1)) sys.exit(1)
try: console = SetupLogger("gengpioin_console", log_file="", stream=True) log = SetupLogger("gengpioin_log", "/var/log/gengpioin.log") # Set the signal handler signal.signal(signal.SIGINT, signal_handler) ConfigFilePath = "/etc/" Threads = {} UseCallbacks = False DefaultTrigger = GPIO.FALLING DefaultPullup = GPIO.PUD_UP DefaultBounce = 0 if os.path.isfile(ConfigFilePath + 'gengpioin.conf'): config = MyConfig(filename=ConfigFilePath + 'gengpioin.conf', section='gengpioin', log=log) Trigger = config.ReadValue('trigger', default="falling") if Trigger.lower() == "rising": DefaultTrigger = GPIO.RISING elif Trigger.lower() == "both": DefaultTrigger = GPIO.BOTH else: DefaultTrigger = GPIO.FALLING ResistorPull = config.ReadValue('resistorpull', default="up") if ResistorPull.lower() == "down": DefaultPullup = GPIO.PUD_DOWN elif ResistorPull.lower() == "off": DefaultPullup = GPIO.PUD_OFF else:
sys.exit(1) try: port, loglocation = MySupport.GetGenmonInitInfo(ConfigFilePath, log = console) log = SetupLogger("client", loglocation + "genalexa.log") except Exception as e1: print("Error setting up log: " + str(e1)) sys.exit(1) try: # Set the signal handler signal.signal(signal.SIGINT, signal_handler) if not os.path.isfile(ConfigFilePath + 'genalexa.conf'): console.error("Error: config file not found") log.error("Error: config file not found") sys.exit(1) config = MyConfig(filename = ConfigFilePath + 'genalexa.conf', section = 'genalexa', log = log) FauxmoName = config.ReadValue('name', default = " generator") FauxmoPort = config.ReadValue('port', return_type = int, default = 52004) Debug = config.ReadValue('debug', return_type = bool, default = False) log.error("Key word: " + FauxmoName + "; Port: "+str(FauxmoPort) + "; " + "Debug: " + str(Debug)) MyClientInterface = ClientInterface(host = address, port = port, log = log) data = MyClientInterface.ProcessMonitorCommand("generator: start_info_json") StartInfo = {} StartInfo = json.loads(data) remoteCommands = False if 'RemoteCommands' in StartInfo: remoteCommands = StartInfo['RemoteCommands'] if remoteCommands == False:
def __init__(self, ConfigFilePath = ProgramDefaults.ConfPath): super(Monitor, self).__init__() self.ProgramName = "Generator Monitor" self.Version = "Unknown" self.log = None self.IsStopping = False self.ProgramComplete = False if ConfigFilePath == None or ConfigFilePath == "": self.ConfigFilePath = ProgramDefaults.ConfPath else: self.ConfigFilePath = ConfigFilePath self.ConnectionList = [] # list of incoming connections for heartbeat # defautl values self.SiteName = "Home" self.ServerSocket = None self.ServerSocketPort = ProgramDefaults.ServerPort # server socket for nagios heartbeat and command/status self.IncomingEmailFolder = "Generator" self.ProcessedEmailFolder = "Generator/Processed" self.FeedbackLogFile = self.ConfigFilePath + "feedback.json" self.LogLocation = ProgramDefaults.LogPath self.LastLogFileSize = 0 self.NumberOfLogSizeErrors = 0 # set defaults for optional parameters self.NewInstall = False # True if newly installed or newly upgraded version self.FeedbackEnabled = False # True if sending autoated feedback on missing information self.FeedbackMessages = {} self.OneTimeMessages = {} self.MailInit = False # set to true once mail is init self.CommunicationsActive = False # Flag to let the heartbeat thread know we are communicating self.Controller = None self.ControllerSelected = None self.bDisablePlatformStats = False self.ReadOnlyEmailCommands = False self.SlowCPUOptimization = False # weather parameters self.WeatherAPIKey = None self.WeatherLocation = None self.UseMetric = False self.WeatherMinimum = True self.DisableWeather = False self.MyWeather = None # Time Sync Related Data self.bSyncTime = False # Sync gen to system time self.bSyncDST = False # sync time at DST change self.bDST = False # Daylight Savings Time active if True # simulation self.Simulation = False self.SimulationFile = None self.console = SetupLogger("genmon_console", log_file = "", stream = True) if os.geteuid() != 0: self.LogConsole("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'.") sys.exit(1) if not os.path.isfile(self.ConfigFilePath + 'genmon.conf'): self.LogConsole("Missing config file : " + self.ConfigFilePath + 'genmon.conf') sys.exit(1) if not os.path.isfile(self.ConfigFilePath + 'mymail.conf'): self.LogConsole("Missing config file : " + self.ConfigFilePath + 'mymail.conf') sys.exit(1) self.config = MyConfig(filename = self.ConfigFilePath + 'genmon.conf', section = "GenMon", log = self.console) # read config file if not self.GetConfig(): self.LogConsole("Failure in Monitor GetConfig") sys.exit(1) # log errors in this module to a file self.log = SetupLogger("genmon", self.LogLocation + "genmon.log") self.config.log = self.log if self.IsLoaded(): self.LogConsole("ERROR: genmon.py is already loaded.") self.LogError("ERROR: genmon.py is already loaded.") sys.exit(1) if self.NewInstall: self.LogError("New version detected: Old = %s, New = %s" % (self.Version, GENMON_VERSION)) self.Version = GENMON_VERSION self.ProgramStartTime = datetime.datetime.now() # used for com metrics atexit.register(self.Close) signal.signal(signal.SIGTERM, self.Close) signal.signal(signal.SIGINT, self.Close) # start thread to accept incoming sockets for nagios heartbeat and command / status clients self.Threads["InterfaceServerThread"] = MyThread(self.InterfaceServerThread, Name = "InterfaceServerThread") # init mail, start processing incoming email self.mail = MyMail(monitor=True, incoming_folder = self.IncomingEmailFolder, processed_folder =self.ProcessedEmailFolder,incoming_callback = self.ProcessCommand, loglocation = self.LogLocation, ConfigFilePath = ConfigFilePath) self.Threads = self.MergeDicts(self.Threads, self.mail.Threads) self.MailInit = True self.FeedbackPipe = MyPipe("Feedback", self.FeedbackReceiver, log = self.log, ConfigFilePath = self.ConfigFilePath) self.Threads = self.MergeDicts(self.Threads, self.FeedbackPipe.Threads) self.MessagePipe = MyPipe("Message", self.MessageReceiver, log = self.log, nullpipe = self.mail.DisableSNMP, ConfigFilePath = self.ConfigFilePath) self.Threads = self.MergeDicts(self.Threads, self.MessagePipe.Threads) try: #Starting device connection if self.Simulation: self.LogError("Simulation Running") if not self.ControllerSelected == None and len(self.ControllerSelected): self.LogError("Selected Controller: " + str(self.ControllerSelected)) else: self.ControllerSelected = "generac_evo_nexus" if self.ControllerSelected.lower() == "h_100" : self.Controller = HPanel(self.log, newinstall = self.NewInstall, simulation = self.Simulation, simulationfile = self.SimulationFile, message = self.MessagePipe, feedback = self.FeedbackPipe, config = self.config) else: self.Controller = Evolution(self.log, self.NewInstall, simulation = self.Simulation, simulationfile = self.SimulationFile, message = self.MessagePipe, feedback = self.FeedbackPipe, config = self.config) self.Threads = self.MergeDicts(self.Threads, self.Controller.Threads) except Exception as e1: self.LogErrorLine("Error opening controller device: " + str(e1)) sys.exit(1) self.StartThreads() self.ProcessFeedbackInfo() # send mail to tell we are starting self.MessagePipe.SendMessage("Generator Monitor Starting at " + self.SiteName, "Generator Monitor Starting at " + self.SiteName , msgtype = "info") self.LogError("GenMon Loaded for site: " + self.SiteName)
class GenTankData(MySupport): # The device is the ADS1115 I2C ADC # reference python http://www.smartypies.com/projects/ads1115-with-raspberrypi-and-python/ads1115runner/ RESET_ADDRESS = 0b0000000 RESET_COMMAND = 0b00000110 POINTER_CONVERSION = 0x0 POINTER_CONFIGURATION = 0x1 POINTER_LOW_THRESHOLD = 0x2 POINTER_HIGH_THRESHOLD = 0x3 #------------ GenTankData::init--------------------------------------------- def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, ConfigFilePath=MyCommon.DefaultConfPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort): super(GenTankData, self).__init__() self.LogFileName = os.path.join(loglocation, "gentankdiy.log") self.AccessLock = threading.Lock() # log errors in this module to a file self.log = SetupLogger("gentankdiy", self.LogFileName) self.console = SetupLogger("gentankdiy_console", log_file="", stream=True) self.MonitorAddress = host self.PollTime = 2 self.debug = False configfile = os.path.join(ConfigFilePath, 'gentankdiy.conf') try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename=configfile, section='gentankdiy', log=self.log) self.PollTime = self.config.ReadValue('poll_frequency', return_type=float, default=60) self.debug = self.config.ReadValue('debug', return_type=bool, default=False) self.i2c_address = self.config.ReadValue('i2c_address', return_type=int, default=72) self.mv_per_step = self.config.ReadValue('mv_per_step', return_type=int, default=125) self.Multiplier = self.config.ReadValue( 'volts_to_percent_multiplier', return_type=float, default=20.0) # I2C channel 1 is connected to the GPIO pins self.i2c_channel = self.config.ReadValue('i2c_channel', return_type=int, default=1) if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) try: try: startcount = 0 while startcount <= 10: try: self.Generator = ClientInterface( host=self.MonitorAddress, port=port, log=self.log) break except Exception as e1: startcount += 1 if startcount >= 10: self.console.info("genmon not loaded.") self.LogError("Unable to connect to genmon.") sys.exit(1) time.sleep(1) continue except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) # start thread monitor time for exercise self.Threads["TankCheckThread"] = MyThread(self.TankCheckThread, Name="TankCheckThread", start=False) if not self.InitADC(): self.LogError("InitADC failed, exiting") sys.exit(1) self.Threads["TankCheckThread"].Start() atexit.register(self.Close) signal.signal(signal.SIGTERM, self.Close) signal.signal(signal.SIGINT, self.Close) except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) self.console.error("Error in GenTankData init: " + str(e1)) sys.exit(1) #---------- GenTankData::SendCommand -------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data # ---------- GenTankData::InitADC------------------------------------------- def InitADC(self): try: # I2C channel 1 is connected to the GPIO pins self.I2Cbus = smbus.SMBus(self.i2c_channel) # Reset ADC self.I2Cbus.write_byte(self.RESET_ADDRESS, self.RESET_COMMAND) # set config register and start conversion # ANC1 and GND, 4.096v, 128s/s # Customized - Port A0 and 4.096 V input # 0b11000011; # bit 15-8 = 0xC3 # bit 15 flag bit for single shot # Bits 14-12 input selection: # 100 ANC0; 101 ANC1; 110 ANC2; 111 ANC3 # Bits 11-9 Amp gain. Default to 010 here 001 P19 # Bit 8 Operational mode of the ADS1115. # 0 : Continuous conversion mode # 1 : Power-down single-shot mode (default) CONFIG_VALUE_1 = 0xC3 # bits 7-0 0b10000101 = 0x85 # Bits 7-5 data rate default to 100 for 128SPS # Bits 4-0 comparator functions see spec sheet. CONFIG_VALUE_2 = 0x85 self.I2Cbus.write_i2c_block_data(self.i2c_address, self.POINTER_CONFIGURATION, [CONFIG_VALUE_1, CONFIG_VALUE_2]) self.LogDebug("I2C Init complete: success") except Exception as e1: self.LogErrorLine("Error calling InitADC: " + str(e1)) return False return True # ---------- GenTankData::GetGaugeData-------------------------------------- def GetGaugeData(self): try: val = self.I2Cbus.read_i2c_block_data(self.i2c_address, self.POINTER_CONVERSION, 2) self.LogDebug(str(val)) # convert display results reading = val[0] << 8 | val[1] if (reading < 0): reading = 0 #reading = self.I2Cbus.read_word_data(self.i2c_address, self.i2c_channel) volts = round( float(reading * (float(self.mv_per_step) / 1000000.0)), 2) gauge_data = float(self.Multiplier) * volts self.LogDebug("Reading Gauge Data: %4.2f%%" % gauge_data) return gauge_data except Exception as e1: self.LogErrorLine("Error calling GetGaugeData: " + str(e1)) return 0.0 # ---------- GenTankData::TankCheckThread----------------------------------- def TankCheckThread(self): time.sleep(1) while True: try: dataforgenmon = {} tankdata = self.GetGaugeData() if tankdata != None: dataforgenmon["Tank Name"] = "External Tank" dataforgenmon["Capacity"] = 0 dataforgenmon["Percentage"] = self.GetGaugeData() retVal = self.SendCommand("generator: set_tank_data=" + json.dumps(dataforgenmon)) self.LogDebug(retVal) if self.WaitForExit("TankCheckThread", float(self.PollTime * 60)): return except Exception as e1: self.LogErrorLine("Error in TankCheckThread: " + str(e1)) if self.WaitForExit("TankCheckThread", float(self.PollTime * 60)): return # ----------GenTankData::Close---------------------------------------------- def Close(self): self.KillThread("TankCheckThread") self.Generator.Close()
def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, ConfigFilePath=MyCommon.DefaultConfPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort): super(GenTankData, self).__init__() self.LogFileName = loglocation + "gentankutil.log" self.AccessLock = threading.Lock() # log errors in this module to a file self.log = SetupLogger("gentankutil", self.LogFileName) self.console = SetupLogger("gentankutil_console", log_file="", stream=True) self.MonitorAddress = host self.PollTime = 2 self.TankID = "" self.Debug = False configfile = ConfigFilePath + 'gentankutil.conf' try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename=configfile, section='gentankutil', log=self.log) self.PollTime = self.config.ReadValue('poll_frequency', return_type=float, default=60) self.Debug = self.config.ReadValue('debug', return_type=bool, default=False) self.username = self.config.ReadValue('username', default="") self.password = self.config.ReadValue('password', default="") self.tank_name = self.config.ReadValue('tank_name', default="") if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) if self.username == "" or self.username == None or self.password == "" or self.password == None: self.LogError("Invalid user name or password, exiting") sys.exit(1) try: try: startcount = 0 while startcount <= 10: try: self.Generator = ClientInterface( host=self.MonitorAddress, port=port, log=self.log) break except Exception as e1: startcount += 1 if startcount >= 10: self.console.info("genmon not loaded.") self.LogError("Unable to connect to genmon.") sys.exit(1) time.sleep(1) continue except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) #if not self.CheckGeneratorRequirement(): # self.LogError("Requirements not met. Exiting.") # sys.exit(1) self.tank = tankutility(self.username, self.password, self.log, debug=self.Debug) # start thread monitor time for exercise self.Threads["TankCheckThread"] = MyThread(self.TankCheckThread, Name="TankCheckThread", start=False) self.Threads["TankCheckThread"].Start() atexit.register(self.Close) signal.signal(signal.SIGTERM, self.Close) signal.signal(signal.SIGINT, self.Close) except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) self.console.error("Error in GenTankData init: " + str(e1)) sys.exit(1)
def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, ConfigFilePath=MyCommon.DefaultConfPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort): super(GenTankData, self).__init__() self.LogFileName = os.path.join(loglocation, "gentankdiy.log") self.AccessLock = threading.Lock() # log errors in this module to a file self.log = SetupLogger("gentankdiy", self.LogFileName) self.console = SetupLogger("gentankdiy_console", log_file="", stream=True) self.MonitorAddress = host self.PollTime = 2 self.debug = False configfile = os.path.join(ConfigFilePath, 'gentankdiy.conf') try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename=configfile, section='gentankdiy', log=self.log) self.PollTime = self.config.ReadValue('poll_frequency', return_type=float, default=60) self.debug = self.config.ReadValue('debug', return_type=bool, default=False) self.i2c_address = self.config.ReadValue('i2c_address', return_type=int, default=72) self.mv_per_step = self.config.ReadValue('mv_per_step', return_type=int, default=125) self.Multiplier = self.config.ReadValue( 'volts_to_percent_multiplier', return_type=float, default=20.0) # I2C channel 1 is connected to the GPIO pins self.i2c_channel = self.config.ReadValue('i2c_channel', return_type=int, default=1) if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) try: try: startcount = 0 while startcount <= 10: try: self.Generator = ClientInterface( host=self.MonitorAddress, port=port, log=self.log) break except Exception as e1: startcount += 1 if startcount >= 10: self.console.info("genmon not loaded.") self.LogError("Unable to connect to genmon.") sys.exit(1) time.sleep(1) continue except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) # start thread monitor time for exercise self.Threads["TankCheckThread"] = MyThread(self.TankCheckThread, Name="TankCheckThread", start=False) if not self.InitADC(): self.LogError("InitADC failed, exiting") sys.exit(1) self.Threads["TankCheckThread"].Start() atexit.register(self.Close) signal.signal(signal.SIGTERM, self.Close) signal.signal(signal.SIGINT, self.Close) except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) self.console.error("Error in GenTankData init: " + str(e1)) sys.exit(1)
os.path.join(ConfigFilePath, 'genmon.conf')) log.error("Missing config file : " + os.path.join(ConfigFilePath, 'genmon.conf')) sys.exit(1) if not os.path.isfile(os.path.join(ConfigFilePath, 'mymail.conf')): console.error("Missing config file : " + os.path.join(ConfigFilePath, 'mymail.conf')) log.error("Missing config file : " + os.path.join(ConfigFilePath, 'mymail.conf')) sys.exit(1) try: SiteName = GetSiteName() config = MyConfig(filename=os.path.join(ConfigFilePath, 'genemail2sms.conf'), section='genemail2sms', log=log) DestinationEmail = config.ReadValue('destination', default="") if DestinationEmail == "" or (not "@" in DestinationEmail): log.error("Missing parameter in " + os.path.join(ConfigFilePath, 'genemail2sms.conf')) console.error("Missing parameter in " + os.path.join(ConfigFilePath, 'genemail2sms.conf')) sys.exit(1) # init mail, start processing incoming email MyMail = MyMail(loglocation=loglocation, log=log, ConfigFilePath=ConfigFilePath)
configfile = os.path.join(MyCommon.DefaultConfPath, configfilename) for opt, arg in opts: if opt == '-h': print(HelpStr) sys.exit() elif opt in ("-c"): configfile = os.path.join(arg, configfilename) if not os.path.isfile(configfile): console.error("Missing config file: {0}".format(configfile)) sys.exit(1) console.error("Using config file %s" % configfile) config = MyConfig(filename=configfile, section='gentankdiy') gauge = GaugeDIY2(config, console=console, log=console) if not gauge.InitADC(): console.error("Error initializing ADC, exiting") exit(2) average_n = 10 while True: print(""" Enter command: 1 - Show current calibration table 2 - Clear all entries from calibration table 3 - Enter lowest and highest readings possible
if __name__=='__main__': # usage program.py [server_address] address='127.0.0.1' if len(sys.argv)<2 else sys.argv[1] # Set the signal handler signal.signal(signal.SIGINT, signal_handler) if os.geteuid() != 0: print("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.") sys.exit(2) console = SetupLogger("sms_console", log_file = "", stream = True) log = SetupLogger("client", "/var/log/gensms.log") try: config = MyConfig(filename = '/etc/gensms.conf', section = 'gensms', log = log) account_sid = config.ReadValue('accountsid', default = "") auth_token = config.ReadValue('authtoken', default = "") to_number = config.ReadValue('to_number', default = "") from_number = config.ReadValue('from_number', default = "") if account_sid == "" or auth_token == "" or to_number == "" or from_number == "": log.error("Missing parameter in /etc/gensms.conf") console.error("Missing parameter in /etc/gensms.conf") sys.exit(1) except Exception as e1: log.error("Error reading /etc/gensms.conf: " + str(e1)) console.error("Error reading /etc/gensms.conf: " + str(e1)) sys.exit(1)
def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, ConfigFilePath=MyCommon.DefaultConfPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort, console=None): super(GenTankData, self).__init__() self.LogFileName = os.path.join(loglocation, "gentankutil.log") self.AccessLock = threading.Lock() self.log = log self.console = console self.MonitorAddress = host self.PollTime = 2 self.TankID = "" self.debug = False configfile = os.path.join(ConfigFilePath, 'gentankutil.conf') try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename=configfile, section='gentankutil', log=self.log) self.PollTime = self.config.ReadValue('poll_frequency', return_type=float, default=60) self.debug = self.config.ReadValue('debug', return_type=bool, default=False) self.username = self.config.ReadValue('username', default="") self.password = self.config.ReadValue('password', default="") self.tank_name = self.config.ReadValue('tank_name', default="") if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) if self.username == "" or self.username == None or self.password == "" or self.password == None: self.LogError("Invalid user name or password, exiting") sys.exit(1) try: self.Generator = ClientInterface(host=self.MonitorAddress, port=port, log=self.log) #if not self.CheckGeneratorRequirement(): # self.LogError("Requirements not met. Exiting.") # sys.exit(1) self.tank = tankutility(self.username, self.password, self.log, debug=self.debug) # start thread monitor time for exercise self.Threads["TankCheckThread"] = MyThread(self.TankCheckThread, Name="TankCheckThread", start=False) self.Threads["TankCheckThread"].Start() signal.signal(signal.SIGTERM, self.SignalClose) signal.signal(signal.SIGINT, self.SignalClose) except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) self.console.error("Error in GenTankData init: " + str(e1)) sys.exit(1)
class Loader(MySupport): def __init__(self, start = False, stop = False, hardstop = False, loglocation = ProgramDefaults.LogPath, log = None, localinit = False, ConfigFilePath = ProgramDefaults.ConfPath): self.Start = start self.Stop = stop self.HardStop = hardstop self.ConfigFilePath = ConfigFilePath self.ConfigFileName = "genloader.conf" # log errors in this module to a file if localinit == True: self.configfile = self.ConfigFileName else: self.configfile = os.path.join(self.ConfigFilePath, self.ConfigFileName) self.ModulePath = os.path.dirname(os.path.realpath(__file__)) self.ConfPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "conf") # log errors in this module to a file if log == None: self.log = SetupLogger("genloader", os.path.join(loglocation, "genloader.log")) else: self.log = log self.console = SetupLogger("genloader_console", log_file = "", stream = True) try: if self.Start: if not self.CheckSystem(): self.LogInfo("Error check system readiness. Exiting") sys.exit(2) self.CachedConfig = {} if not os.path.isdir(self.ConfigFilePath): try: os.mkdir(self.ConfigFilePath) except Exception as e1: self.LogInfo("Error creating target config directory: " + str(e1), LogLine = True) # check to see if genloader.conf is present, if not copy it from genmon directory if not os.path.isfile(self.configfile): self.LogInfo("Warning: unable to find config file: " + self.configfile + " Copying file to " + self.ConfigFilePath + " directory.") if not self.CopyConfFile(): sys.exit(2) self.config = MyConfig(filename = self.configfile, section = "genmon", log = self.log) if not self.GetConfig(): self.LogInfo("Error reading config file. Exiting") sys.exit(2) if not self.ValidateConfig(): self.CopyConfFile() self.LogInfo("Error validating config. Retrying..") self.config = MyConfig(filename = self.configfile, section = "genmon", log = self.log) if not self.GetConfig(): self.LogInfo("Error reading config file, 2nd attempt, Exiting") sys.exit(2) if not self.ValidateConfig(): self.LogInfo("Error validating config file, Exiting") sys.exit(2) self.LoadOrder = self.GetLoadOrder() if self.Stop: self.StopModules() time.sleep(2) if self.Start: self.StartModules() except Exception as e1: self.LogErrorLine("Error in init: " + str(e1)) #--------------------------------------------------------------------------- def CopyConfFile(self): if os.path.isfile(os.path.join(self.ConfPath, self.ConfigFileName)): copyfile(os.path.join(self.ConfPath, self.ConfigFileName) , self.configfile) return True else: self.LogInfo("Unable to find config file.") return False sys.exit(2) #--------------------------------------------------------------------------- def CheckSystem(self): # this function checks the system to see if the required libraries are # installed. If they are not then an attempt is made to install them. ModuleList = [ # [import name , install name, Python2.7 version] ['flask','flask', None], # Web server # we will not use the check for configparser as this look like it is in backports on 2.7 # and our myconfig modules uses the default so this generates an error that is not warranted #['configparser','configparser',None], # reading config files ['serial','pyserial',None], # Serial ['crcmod','crcmod',None], # Modbus CRC ['pyowm','pyowm','2.9.0'], # Open Weather API ['pytz','pytz',None], # Time zone support ['pysnmp','pysnmp',None], # SNMP ['ldap3','ldap3',None], # LDAP ['smbus','smbus',None], # SMBus reading of temp sensors ['pyotp','pyotp','2.3.0'] # 2FA support ] try: ErrorOccured = False # This will check if pip is installed #if "linux" in sys.platform: # self.CheckBaseSoftware() for Module in ModuleList: if not self.LibraryIsInstalled(Module[0]): self.LogInfo("Warning: required library " + Module[1] + " not installed. Attempting to install....") if not self.InstallLibrary(Module[1], version = Module[2]): self.LogInfo("Error: unable to install library " + Module[1]) ErrorOccured = True if Module[0] == "ldap3": # This will correct and issue with the ldap3 modbule not being recogonized in LibrayIsInstalled self.InstallLibrary("pyasn1", update = True) return not ErrorOccured except Exception as e1: self.LogInfo("Error in CheckSystem: " + str(e1), LogLine = True) return False #--------------------------------------------------------------------------- def CheckBaseSoftware(self): try: if sys.version_info[0] < 3: pipProgram = "pip" else: pipProgram = "pip3" install_list = [pipProgram, '-V'] process = Popen(install_list, stdout=PIPE, stderr=PIPE) output, _error = process.communicate() if _error: self.LogInfo("Error in CheckBaseSoftware : " + libraryname + " : " + str(_error)) rc = process.returncode return True except Exception as e1: self.LogInfo("Error in CheckBaseSoftware: " + str(e1), LogLine = True) self.InstallBaseSoftware() return False #--------------------------------------------------------------------------- def InstallBaseSoftware(self): try: if sys.version_info[0] < 3: pipProgram = "python-pip" else: pipProgram = "python3-pip" install_list = ["sudo","apt-get","-yqq","update"] process = Popen(install_list, stdout=PIPE, stderr=PIPE) output, _error = process.communicate() if _error: self.LogInfo("Error in InstallBaseSoftware : " + libraryname + " : " + str(_error)) rc = process.returncode install_list = ["sudo","apt-get","-yqq","install", pipProgram] process = Popen(install_list, stdout=PIPE, stderr=PIPE) output, _error = process.communicate() return True except Exception as e1: self.LogInfo("Error in CheckBaseSoftware: " + str(e1), LogLine = True) return False #--------------------------------------------------------------------------- @staticmethod def OneTimeMaint(ConfigFilePath, log): FileList = { "feedback.json" : os.path.dirname(os.path.realpath(__file__)) + "/", "outage.txt" : os.path.dirname(os.path.realpath(__file__)) + "/", "kwlog.txt" : os.path.dirname(os.path.realpath(__file__)) + "/", "maintlog.json" : os.path.dirname(os.path.realpath(__file__)) + "/", "Feedback_dat" : os.path.dirname(os.path.realpath(__file__)) + "/genmonlib/", "Message_dat" : os.path.dirname(os.path.realpath(__file__)) + "/genmonlib/", 'genmon.conf' : "/etc/", 'genserv.conf': "/etc/", 'gengpio.conf' : "/etc/", 'gengpioin.conf': "/etc/", 'genlog.conf' : "/etc/", 'gensms.conf' : "/etc/", 'gensms_modem.conf': "/etc/", 'genpushover.conf': "/etc/", 'gensyslog.conf' : "/etc/", 'genmqtt.conf' : "/etc/", 'genslack.conf': "/etc/", 'genexercise.conf' : "/etc/", 'genemail2sms.conf' : "/etc/", 'genloader.conf' : "/etc/", 'mymail.conf' : "/etc/", 'mymodem.conf' : "/etc/" } try: # Check to see if we have done this already by checking files in the genmon source directory if (not os.path.isfile(os.path.dirname(os.path.realpath(__file__)) + "/genmonlib/Message_dat") and not os.path.isfile(os.path.dirname(os.path.realpath(__file__)) + "/maintlog.json") and not os.path.isfile(os.path.dirname(os.path.realpath(__file__)) + "/outage.txt") and not os.path.isfile(os.path.dirname(os.path.realpath(__file__)) + "/kwlog.txt") and not os.path.isfile("/etc/genmon.conf")): return False # validate target directory if not os.path.isdir(ConfigFilePath): try: os.mkdir(ConfigFilePath) if not os.access(ConfigFilePath + File, os.R_OK): pass except Exception as e1: log.error("Error validating target directory: " + str(e1), LogLine = True) # move files for File, Path in FileList.items(): try: SourceFile = Path + File if os.path.isfile(SourceFile): log.error("Moving " + SourceFile + " to " + ConfigFilePath ) if not MySupport.CopyFile(SourceFile, ConfigFilePath + File, move = True, log = log): log.error("Error: using alternate move method") move(SourceFile , ConfigFilePath + File) if not os.access(ConfigFilePath + File, os.R_OK): pass except Exception as e1: log.error("Error moving " + SourceFile) except Exception as e1: log.error("Error moving files: " + str(e1), LogLine = True) return True #--------------------------------------------------------------------------- def LibraryIsInstalled(self, libraryname): try: import importlib my_module = importlib.import_module(libraryname) return True except Exception as e1: return False #--------------------------------------------------------------------------- def InstallLibrary(self, libraryname, update = False, version = None): try: if sys.version_info[0] < 3: pipProgram = "pip" if version != None: libraryname = libraryname + "=="+ version else: pipProgram = "pip3" if update: install_list = [pipProgram, 'install', libraryname, '-U'] else: install_list = [pipProgram, 'install', libraryname] process = Popen(install_list, stdout=PIPE, stderr=PIPE) output, _error = process.communicate() if _error: self.LogInfo("Error in InstallLibrary using pip : " + libraryname + " : " + str(_error)) rc = process.returncode return True except Exception as e1: self.LogInfo("Error installing module: " + libraryname + ": "+ str(e1), LogLine = True) return False #--------------------------------------------------------------------------- def ValidateConfig(self): ErrorOccured = False if not len(self.CachedConfig): self.LogInfo("Error: Empty configruation found.") return False for Module, Settiings in self.CachedConfig.items(): try: if self.CachedConfig[Module]["enable"]: if not os.path.isfile(os.path.join(self.ModulePath, self.CachedConfig[Module]["module"])): self.LogInfo("Enable to find file " + os.path.join(self.ModulePath, self.CachedConfig[Module]["module"])) ErrorOccured = True # validate config file and if it is not there then copy it. if not self.CachedConfig[Module]["conffile"] == None and len(self.CachedConfig[Module]["conffile"]): ConfFileList = self.CachedConfig[Module]["conffile"].split(",") for ConfigFile in ConfFileList: ConfigFile = ConfigFile.strip() if not os.path.isfile(os.path.join(self.ConfigFilePath, ConfigFile)): if os.path.isfile(os.path.join(self.ConfPath, ConfigFile)): self.LogInfo("Copying " + ConfigFile + " to " + self.ConfigFilePath ) copyfile(os.path.join(self.ConfPath, ConfigFile) , os.path.join(self.ConfigFilePath, ConfigFile)) else: self.LogInfo("Enable to find config file " + os.path.join(self.ConfPath, ConfigFile)) ErrorOccured = True except Exception as e1: self.LogInfo("Error validating config for " + Module + " : " + str(e1), LogLine = True) return False try: if not self.CachedConfig["genmon"]["enable"]: self.LogError("Warning: Genmon is not enabled, assume corrupt file.") ErrorOccured = True if not self.CachedConfig["genserv"]["enable"]: self.LogError("Warning: Genserv is not enabled") except Exception as e1: self.LogErrorLine("Error in ValidateConfig, possible corrupt file. " + str(e1)) ErrorOccured = True return not ErrorOccured #--------------------------------------------------------------------------- def AddEntry(self, section = None, module = None, conffile = "", args = "", priority = '2'): try: if section == None or module == None: return self.config.WriteSection(section) self.config.WriteValue('module', module, section = section) self.config.WriteValue('enable', 'False', section = section) self.config.WriteValue('hardstop', 'False', section = section) self.config.WriteValue('conffile', conffile, section = section) self.config.WriteValue('args', args, section = section) self.config.WriteValue('priority', priority, section = section) except Exception as e1: self.LogInfo("Error in AddEntry: " + str(e1), LogLine = True) return #--------------------------------------------------------------------------- def UpdateIfNeeded(self): try: self.config.SetSection("gengpioin") if not self.config.HasOption('conffile'): self.config.WriteValue('conffile', "gengpioin.conf", section = "gengpioin") self.LogError("Updated entry gengpioin.conf") else: defValue = self.config.ReadValue('conffile', default = "") if not len(defValue): self.config.WriteValue('conffile', "gengpioin.conf", section = "gengpioin") self.LogError("Updated entry gengpioin.conf") except Exception as e1: self.LogInfo("Error in UpdateIfNeeded: " + str(e1), LogLine = True) #--------------------------------------------------------------------------- def GetConfig(self): try: Sections = self.config.GetSections() ValidSections = ['genmon', 'genserv', 'gengpio', 'gengpioin', 'genlog', 'gensms', 'gensms_modem', 'genpushover', 'gensyslog', 'genmqtt', 'genslack', 'genexercise', 'genemail2sms', 'gentankutil', 'gentankdiy','genalexa', 'gensnmp', 'gentemp'] for entry in ValidSections: if not entry in Sections: if entry == 'genslack': self.LogError("Warning: Missing entry: " + entry + " , adding entry") self.AddEntry(section = entry, module = 'genslack.py', conffile = 'genslack.conf') if entry == 'genexercise': self.LogError("Warning: Missing entry: " + entry + " , adding entry") self.AddEntry(section = entry, module = 'genexercise.py', conffile = 'genexercise.conf') if entry == 'genemail2sms': self.LogError("Warning: Missing entry: " + entry + " , adding entry") self.AddEntry(section = entry, module = 'genemail2sms.py', conffile = 'genemail2sms.conf') if entry == 'gentankutil': self.LogError("Warning: Missing entry: " + entry + " , adding entry") self.AddEntry(section = entry, module = 'gentankutil.py', conffile = 'gentankutil.conf') if entry == 'genalexa': self.LogError("Warning: Missing entry: " + entry + " , adding entry") self.AddEntry(section = entry, module = 'genalexa.py', conffile = 'genalexa.conf') if entry == 'gensnmp': self.LogError("Warning: Missing entry: " + entry + " , adding entry") self.AddEntry(section = entry, module = 'gensnmp.py', conffile = 'gensnmp.conf') if entry == 'gentemp': self.LogError("Warning: Missing entry: " + entry + " , adding entry") self.AddEntry(section = entry, module = 'gentemp.py', conffile = 'gentemp.conf') if entry == 'gentankdiy': self.LogError("Warning: Missing entry: " + entry + " , adding entry") self.AddEntry(section = entry, module = 'gentankdiy.py', conffile = 'gentankdiy.conf') else: self.LogError("Warning: Missing entry: " + entry) self.UpdateIfNeeded() Sections = self.config.GetSections() for SectionName in Sections: TempDict = {} self.config.SetSection(SectionName) if self.config.HasOption('module'): TempDict['module'] = self.config.ReadValue('module') else: TempDict['module'] = None if self.config.HasOption('enable'): TempDict['enable'] = self.config.ReadValue('enable', return_type = bool) else: TempDict['enable'] = False if self.config.HasOption('hardstop'): TempDict['hardstop'] = self.config.ReadValue('hardstop', return_type = bool) else: TempDict['hardstop'] = False if self.config.HasOption('conffile'): TempDict['conffile'] = self.config.ReadValue('conffile') else: TempDict['conffile'] = None if self.config.HasOption('args'): TempDict['args'] = self.config.ReadValue('args') else: TempDict['args'] = None if self.config.HasOption('priority'): TempDict['priority'] = self.config.ReadValue('priority', return_type = int, default = None) else: TempDict['priority'] = None if self.config.HasOption('postloaddelay'): TempDict['postloaddelay'] = self.config.ReadValue('postloaddelay', return_type = int, default = 0) else: TempDict['postloaddelay'] = 0 if self.config.HasOption('pid'): TempDict['pid'] = self.config.ReadValue('pid', return_type = int, default = 0, NoLog = True) else: TempDict['pid'] = 0 self.CachedConfig[SectionName] = TempDict return True except Exception as e1: self.LogInfo("Error parsing config file: " + str(e1), LogLine = True) return False #--------------------------------------------------------------------------- def ConvertToInt(self, value, default = None): try: return int(str(value)) except: return default #--------------------------------------------------------------------------- def GetLoadOrder(self): LoadOrder = [] LoadDict = {} try: for Module, Settiings in self.CachedConfig.items(): # get the load order of all modules, even if they are disabled # since we need to stop all modules (even disabled ones) if the # conf file changed try: if self.CachedConfig[Module]["priority"] == None: LoadDict[Module] = 99 elif self.CachedConfig[Module]["priority"] >= 0: LoadDict[Module] = self.CachedConfig[Module]["priority"] else: LoadDict[Module] = 99 except Exception as e1: self.LogInfo("Error reading load order (retrying): " + str(e1), LogLine = True) #lambda kv: (-kv[1], kv[0]) for key, value in sorted(LoadDict.items(), key=lambda kv: (-kv[1], kv[0])): #for key, value in sorted(LoadDict.iteritems(), key=lambda (k,v): (v,k)): LoadOrder.append(key) except Exception as e1: self.LogInfo("Error reading load order: " + str(e1), LogLine = True) return LoadOrder #--------------------------------------------------------------------------- def StopModules(self): self.LogConsole("Stopping....") if not len(self.LoadOrder): self.LogInfo("Error, nothing to stop.") return False ErrorOccured = False for Module in self.LoadOrder: try: if not self.UnloadModule(self.ModulePath, self.CachedConfig[Module]["module"], pid = self.CachedConfig[Module]["pid"],HardStop = self.CachedConfig[Module]["hardstop"], UsePID = True): self.LogInfo("Error stopping " + Module) ErrorOccured = True except Exception as e1: self.LogInfo("Error stopping module " + Module + " : " + str(e1), LogLine = True) return False return not ErrorOccured #--------------------------------------------------------------------------- def StartModules(self): self.LogConsole("Starting....") if not len(self.LoadOrder): self.LogInfo("Error, nothing to start.") return False ErrorOccured = False for Module in reversed(self.LoadOrder): try: if self.CachedConfig[Module]["enable"]: if not self.LoadModule(self.ModulePath, self.CachedConfig[Module]["module"], args = self.CachedConfig[Module]["args"]): self.LogInfo("Error starting " + Module) ErrorOccured = True if not self.CachedConfig[Module]["postloaddelay"] == None and self.CachedConfig[Module]["postloaddelay"] > 0: time.sleep(self.CachedConfig[Module]["postloaddelay"]) except Exception as e1: self.LogInfo("Error starting module " + Module + " : " + str(e1), LogLine = True) return False return not ErrorOccured #--------------------------------------------------------------------------- def LoadModuleAlt(self, modulename, args = None): try: self.LogConsole("Starting " + modulename) # to load as a background process we just use os.system since Popen # is problematic in doing this CommandString = sys.executable + " " + modulename if args != None and len(args): CommandString += " " + args CommandString += " &" os.system(CommandString) return True except Exception as e1: self.LogInfo("Error loading module: " + str(e1), LogLine = True) return False #--------------------------------------------------------------------------- def LoadModule(self, path, modulename, args = None): try: try: import os fullmodulename = os.path.join(path, modulename) except Exception as e1: fullmodulename = path + "/" + modulename if args != None: self.LogConsole("Starting " + fullmodulename + " " + args) else: self.LogConsole("Starting " + fullmodulename) try: from subprocess import DEVNULL # py3k except ImportError: import os DEVNULL = open(os.devnull, 'wb') if not len(args): args = None if "genserv.py" in modulename: OutputStream = DEVNULL else: OutputStream = subprocess.PIPE executelist = [sys.executable, fullmodulename] if args != None: executelist.extend(args.split(" ")) # This will make all the programs use the same config files executelist.extend(["-c", self.ConfigFilePath]) # close_fds=True pid = subprocess.Popen(executelist, stdout=OutputStream, stderr=OutputStream, stdin=OutputStream) self.UpdatePID(modulename, pid.pid) return True except Exception as e1: self.LogInfo("Error loading module " + path + ": "+ modulename + ": " + str(e1), LogLine = True) return False #--------------------------------------------------------------------------- def UnloadModule(self, path, modulename, pid = None, HardStop = False, UsePID = False): try: LoadInfo = [] if UsePID: if pid == None or pid == "" or pid == 0: return True LoadInfo.append("kill") if HardStop or self.HardStop: LoadInfo.append('-9') LoadInfo.append(str(pid)) else: LoadInfo.append('pkill') if HardStop or self.HardStop: LoadInfo.append('-9') LoadInfo.append('-u') LoadInfo.append('root') LoadInfo.append('-f') LoadInfo.append(modulename) self.LogConsole("Stopping " + modulename) process = Popen(LoadInfo, stdout=PIPE) output, _error = process.communicate() rc = process.returncode self.UpdatePID(modulename, "") return True except Exception as e1: self.LogInfo("Error loading module: " + str(e1), LogLine = True) return False #--------------------------------------------------------------------------- def UpdatePID(self, modulename, pid = None): try: filename = os.path.splitext(modulename)[0] # remove extension self.config.SetSection(filename) self.config.WriteValue("pid", str(pid)) except Exception as e1: self.LogInfo("Error writing PID for " + modulename + " : " + str(e1))
def __init__(self, start=False, stop=False, hardstop=False, loglocation="/var/log/", log=None, localinit=False, ConfigFilePath=None): self.Start = start self.Stop = stop self.HardStop = hardstop if ConfigFilePath == None: self.ConfigFilePath = "/etc/" else: self.ConfigFilePath = ConfigFilePath self.ConfigFileName = "genloader.conf" # log errors in this module to a file if localinit == True: self.configfile = self.ConfigFileName else: self.configfile = self.ConfigFilePath + self.ConfigFileName self.ModulePath = os.path.dirname(os.path.realpath(__file__)) + "/" self.ConfPath = os.path.dirname(os.path.realpath(__file__)) + "/conf/" # log errors in this module to a file if log == None: self.log = SetupLogger("genloader", loglocation + "genloader.log") else: self.log = log self.console = SetupLogger("genloader_console", log_file="", stream=True) try: if self.Start: if not self.CheckSystem(): self.LogInfo("Error check system readiness. Exiting") sys.exit(2) self.CachedConfig = {} # check to see if genloader.conf is present, if not copy it from genmon directory if not os.path.isfile(self.configfile): self.LogInfo("Warning: unable to find config file: " + self.configfile + " Copying file to /etc/ directory.") if os.path.isfile(self.ConfPath + self.ConfigFileName): copyfile(self.ConfPath + self.ConfigFileName, self.configfile) else: self.LogInfo("Unable to find config file.") sys.exit(2) self.config = MyConfig(filename=self.configfile, section="genmon", log=self.log) if not self.GetConfig(): self.LogInfo("Error reading config file. Exiting") sys.exit(2) if not self.ValidateConfig(): self.LogInfo("Error validating config. Exiting") sys.exit(2) self.LoadOrder = self.GetLoadOrder() if self.Stop: self.StopModules() time.sleep(2) if self.Start: self.StartModules() except Exception as e1: self.LogErrorLine("Error in init: " + str(e1))
def __init__(self, start = False, stop = False, hardstop = False, loglocation = ProgramDefaults.LogPath, log = None, localinit = False, ConfigFilePath = ProgramDefaults.ConfPath): self.Start = start self.Stop = stop self.HardStop = hardstop self.ConfigFilePath = ConfigFilePath self.ConfigFileName = "genloader.conf" # log errors in this module to a file if localinit == True: self.configfile = self.ConfigFileName else: self.configfile = os.path.join(self.ConfigFilePath, self.ConfigFileName) self.ModulePath = os.path.dirname(os.path.realpath(__file__)) self.ConfPath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "conf") # log errors in this module to a file if log == None: self.log = SetupLogger("genloader", os.path.join(loglocation, "genloader.log")) else: self.log = log self.console = SetupLogger("genloader_console", log_file = "", stream = True) try: if self.Start: if not self.CheckSystem(): self.LogInfo("Error check system readiness. Exiting") sys.exit(2) self.CachedConfig = {} if not os.path.isdir(self.ConfigFilePath): try: os.mkdir(self.ConfigFilePath) except Exception as e1: self.LogInfo("Error creating target config directory: " + str(e1), LogLine = True) # check to see if genloader.conf is present, if not copy it from genmon directory if not os.path.isfile(self.configfile): self.LogInfo("Warning: unable to find config file: " + self.configfile + " Copying file to " + self.ConfigFilePath + " directory.") if not self.CopyConfFile(): sys.exit(2) self.config = MyConfig(filename = self.configfile, section = "genmon", log = self.log) if not self.GetConfig(): self.LogInfo("Error reading config file. Exiting") sys.exit(2) if not self.ValidateConfig(): self.CopyConfFile() self.LogInfo("Error validating config. Retrying..") self.config = MyConfig(filename = self.configfile, section = "genmon", log = self.log) if not self.GetConfig(): self.LogInfo("Error reading config file, 2nd attempt, Exiting") sys.exit(2) if not self.ValidateConfig(): self.LogInfo("Error validating config file, Exiting") sys.exit(2) self.LoadOrder = self.GetLoadOrder() if self.Stop: self.StopModules() time.sleep(2) if self.Start: self.StartModules() except Exception as e1: self.LogErrorLine("Error in init: " + str(e1))
def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, ConfigFilePath=MyCommon.DefaultConfPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort, console=None): super(GenTankData, self).__init__() self.LogFileName = os.path.join(loglocation, "gentankdiy.log") self.AccessLock = threading.Lock() self.log = log self.console = console self.MonitorAddress = host configfile = os.path.join(ConfigFilePath, 'gentankdiy.conf') try: if not os.path.isfile(configfile): self.LogConsole("Missing config file : " + configfile) self.LogError("Missing config file : " + configfile) sys.exit(1) self.config = MyConfig(filename=configfile, section='gentankdiy', log=self.log) self.gauge_type = self.config.ReadValue('gauge_type', return_type=int, default=1) self.nb_tanks = self.config.ReadValue('nb_tanks', return_type=int, default=1) if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + configfile + ": " + str(e1)) self.LogConsole("Error reading " + configfile + ": " + str(e1)) sys.exit(1) try: if self.gauge_type == 1: self.gauge = GaugeDIY1(self.config, log=self.log, console=self.console) elif self.gauge_type == 2: self.gauge = GaugeDIY2(self.config, log=self.log, console=self.console) else: self.LogError("Invalid gauge type: " + str(self.gauge_type)) sys.exit(1) if not self.nb_tanks in [1, 2, 3, 4]: self.LogError( "Invalid Number of tanks (nb_tanks), 1, 2, 3 or 4 accepted: " + str(self.nb_tanks)) sys.exit(1) self.debug = self.gauge.debug self.simulate = self.gauge.simulate self.LogDebug("Num Tanks: " + str(self.nb_tanks)) self.LogDebug("Gauge Type: " + str(self.gauge_type)) self.Generator = ClientInterface(host=self.MonitorAddress, port=port, log=self.log) # start thread monitor time for exercise self.Threads["TankCheckThread"] = MyThread(self.TankCheckThread, Name="TankCheckThread", start=False) if not self.gauge.InitADC(): self.LogError("InitADC failed, exiting") sys.exit(1) self.Threads["TankCheckThread"].Start() signal.signal(signal.SIGTERM, self.SignalClose) signal.signal(signal.SIGINT, self.SignalClose) except Exception as e1: self.LogErrorLine("Error in GenTankData init: " + str(e1)) self.console.error("Error in GenTankData init: " + str(e1)) sys.exit(1)
console.error("Error: " + str(e1)) #------------------- Command-line interface for gengpio ------------------------ if __name__ == '__main__': console, ConfigFilePath, address, port, loglocation, log = MySupport.SetupAddOnProgram( "gensms") # Set the signal handler signal.signal(signal.SIGINT, signal_handler) try: config = MyConfig(filename=os.path.join(ConfigFilePath, 'gensms.conf'), section='gensms', log=log) account_sid = config.ReadValue('accountsid', default="") auth_token = config.ReadValue('authtoken', default="") to_number = config.ReadValue('to_number', default="") from_number = config.ReadValue('from_number', default="") if account_sid == "" or auth_token == "" or to_number == "" or from_number == "": log.error("Missing parameter in " + os.path.join(ConfigFilePath, 'gensms.conf')) console.error("Missing parameter in " + os.path.join(ConfigFilePath, 'gensms.conf')) sys.exit(1) except Exception as e1:
class GenExercise(MySupport): #------------ GenExercise::init--------------------------------------------- def __init__(self, log = None, loglocation = ProgramDefaults.LogPath, ConfigFilePath = MyCommon.DefaultConfPath, host = ProgramDefaults.LocalHost, port = ProgramDefaults.ServerPort, console = None): super(GenExercise, self).__init__() self.AccessLock = threading.Lock() self.log = log self.console = console self.MonitorAddress = host self.PollTime = 2 self.ExerciseActive = False self.Debug = False try: self.config = MyConfig(filename = os.path.join(ConfigFilePath, 'genexercise.conf'), section = 'genexercise', log = self.log) self.ExerciseType = self.config.ReadValue('exercise_type', default = "Normal") self.ExerciseHour = self.config.ReadValue('exercise_hour', return_type = int, default = 12) self.ExerciseMinute = self.config.ReadValue('exercise_minute', return_type = int, default = 0) self.ExerciseDayOfMonth = self.config.ReadValue('exercise_day_of_month', return_type = int, default = 1) self.ExerciseDayOfWeek = self.config.ReadValue('exercise_day_of_week', default = "Monday") self.ExerciseDuration = self.config.ReadValue('exercise_duration', return_type = float, default = 12) self.ExerciseWarmup = self.config.ReadValue('exercise_warmup', return_type = float, default = 0) self.ExerciseFrequency = self.config.ReadValue('exercise_frequency', default = "Monthly") self.MonitorAddress = self.config.ReadValue('monitor_address', default = ProgramDefaults.LocalHost) self.LastExerciseTime = self.config.ReadValue('last_exercise', default = None) self.UseGeneratorTime = self.config.ReadValue('use_gen_time', return_type = bool, default = False) self.Debug = self.config.ReadValue('debug', return_type = bool, default = False) # Validate settings if not self.ExerciseType.lower() in ["normal","quiet", "transfer"]: self.ExerciseType = "normal" if self.ExerciseHour > 23 or self.ExerciseHour < 0: self.ExerciseHour = 12 if self.ExerciseMinute > 59 or self.ExerciseMinute < 0: self.ExerciseMinute = 0 if not self.ExerciseDayOfWeek.lower() in ["monday", "tuesday", "wednesday","thursday","friday","saturday","sunday"]: self.ExerciseDayOfWeek = "Monday" if self.ExerciseDayOfMonth > 28 or self.ExerciseDayOfMonth < 1: self.ExerciseDayOfMonth = 1 if self.ExerciseDuration > 60: self.ExerciseDuration = 60 if self.ExerciseDuration < 5: self.ExerciseDuration = 5 if self.ExerciseWarmup > 30: self.ExerciseWarmup = 30 if self.ExerciseWarmup < 0: self.ExerciseWarmup = 0 if not self.ExerciseFrequency.lower() in ["weekly","biweekly", "monthly"]: self.ExerciseFrequency = "Monthly" if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost except Exception as e1: self.LogErrorLine("Error reading " + os.path.join(ConfigFilePath, "genexercise.conf") + ": " + str(e1)) self.console.error("Error reading " + os.path.join(ConfigFilePath, "genexercise.conf") + ": " + str(e1)) sys.exit(1) try: self.Generator = ClientInterface(host = self.MonitorAddress, port = port, log = self.log) if not self.CheckGeneratorRequirement(): self.LogError("Requirements not met. Exiting.") sys.exit(1) # start thread monitor time for exercise self.Threads["ExerciseThread"] = MyThread(self.ExerciseThread, Name = "ExerciseThread", start = False) self.Threads["ExerciseThread"].Start() try: if self.ExerciseFrequency.lower() == "monthly": DayStr = "Day " + str(self.ExerciseDayOfMonth) else: DayStr = str(self.ExerciseDayOfWeek) self.LogError("Execise: " + self.ExerciseType + ", " + self.ExerciseFrequency + " at " + str(self.ExerciseHour) + ":" + str(self.ExerciseMinute) + " on " + DayStr + " for " + str(self.ExerciseDuration) + " min. Warmup: " + str(self.ExerciseWarmup)) self.DebugOutput("Debug Enabled") except Exception as e1: self.LogErrorLine(str(e1)) signal.signal(signal.SIGTERM, self.SignalClose) signal.signal(signal.SIGINT, self.SignalClose) except Exception as e1: self.LogErrorLine("Error in GenExercise init: " + str(e1)) self.console.error("Error in GenExercise init: " + str(e1)) sys.exit(1) #---------- GenExercise::SendCommand -------------------------------------- def SendCommand(self, Command): if len(Command) == 0: return "Invalid Command" try: with self.AccessLock: data = self.Generator.ProcessMonitorCommand(Command) except Exception as e1: self.LogErrorLine("Error calling ProcessMonitorCommand: " + str(Command)) data = "" return data #---------- GenExercise::CheckGeneratorRequirement ------------------------ def CheckGeneratorRequirement(self): try: data = self.SendCommand("generator: start_info_json") StartInfo = {} StartInfo = json.loads(data) if not "evolution" in StartInfo["Controller"].lower() and not "nexus" in StartInfo["Controller"].lower(): self.LogError("Error: Only Evolution or Nexus controllers are supported for this feature: " + StartInfo["Controller"]) return False return True except Exception as e1: self.LogErrorLine("Error in CheckGeneratorRequirement: " + str(e1)) return False # ---------- GenExercise::PostWarmup---------------------------------------- def PostWarmup(self): # check to see if the generator is running status = self.SendCommand("generator: getbase") if not status.lower() in ["running", "exercising"]: self.LogError("WARNING: generator not running post warmup. Transfer switch not activated.") self.SendCommand("generator: setremote=stop") return self.SendCommand("generator: setremote=starttransfer") self.DebugOutput("Starting transfer exercise cycle (post warmup).") # set timer to stop self.StopTimer = threading.Timer(float(self.ExerciseDuration * 60.0), self.StopExercise) self.StopTimer.start() # ---------- GenExercise::ReadyToExercise----------------------------------- def ReadyToExercise(self): status = self.SendCommand("generator: getbase") if not status.lower() in ["ready"]: self.LogError("Generator not in Ready state, exercise cycle not started") return False return True # ---------- GenExercise::StartExercise------------------------------------- def StartExercise(self): if self.ExerciseActive: # already active return # Start generator if self.ExerciseType.lower() == "normal" and self.ReadyToExercise(): self.SendCommand("generator: setremote=start") self.DebugOutput("Starting normal exercise cycle.") self.StopTimer = threading.Timer(float(self.ExerciseDuration * 60.0), self.StopExercise) self.StopTimer.start() elif self.ExerciseType.lower() == "quiet" and self.ReadyToExercise(): self.SendCommand("generator: setremote=startexercise") self.DebugOutput("Starting quiet exercise cycle.") self.StopTimer = threading.Timer(float(self.ExerciseDuration * 60.0), self.StopExercise) self.StopTimer.start() elif self.ExerciseType.lower() == "transfer" and self.ReadyToExercise(): if self.ExerciseWarmup == 0: self.SendCommand("generator: setremote=starttransfer") self.DebugOutput("Starting transfer exercise cycle.") self.StopTimer = threading.Timer(float(self.ExerciseDuration * 60.0), self.StopExercise) self.StopTimer.start() else: self.SendCommand("generator: setremote=start") self.DebugOutput("Starting warmup for transfer exercise cycle.") # start timer for post warmup transition to starttransfer command self.WarmupTimer = threading.Timer(float(self.ExerciseWarmup * 60.0), self.PostWarmup) self.WarmupTimer.start() else: self.LogError("Invalid mode in StartExercise: " + str(self.ExerciseType)) return self.WriteLastExerciseTime() self.ExerciseActive = True # ---------- GenExercise::StopExercise-------------------------------------- def StopExercise(self): if self.ExerciseActive: self.SendCommand("generator: setremote=stop") self.DebugOutput("Stopping exercise cycle.") self.ExerciseActive = False else: self.DebugOutput("Calling Stop Exercise (not needed)") # ---------- GenExercise::DebugOutput----------------------------- def DebugOutput(self, Message): if self.Debug: self.LogError(Message) # ---------- GenExercise::WriteLastExerciseTime----------------------------- def WriteLastExerciseTime(self): try: NowString = datetime.datetime.now().strftime("%A %B %d, %Y %H:%M:%S") if self.ExerciseFrequency.lower() == "biweekly": self.config.WriteValue("last_exercise", NowString) self.config.LastExerciseTime = NowString self.DebugOutput("Last Exercise Cycle: " + NowString) except Exception as e1: self.LogErrorLine("Error in WriteLastExerciseTime: " + str(e1)) # ---------- GenExercise::TimeForExercise----------------------------------- def TimeForExercise(self): try: if self.UseGeneratorTime: TimeNow = self.GetGeneratorTime() else: TimeNow = datetime.datetime.now() if TimeNow.hour != self.ExerciseHour or TimeNow.minute != self.ExerciseMinute: return False weekDays = ("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday") WeekDayString = weekDays[TimeNow.weekday()] if not self.ExerciseFrequency.lower() in ["weekly", "biweekly", "monthly"]: self.LogError("Invalid Exercise Frequency in TimeForExercise: " + str(self.ExerciseFrequency)) return False if self.ExerciseFrequency.lower() == "weekly" and self.ExerciseDayOfWeek.lower() == WeekDayString.lower(): return True elif self.ExerciseFrequency.lower() == "biweekly" and self.ExerciseDayOfWeek.lower() == WeekDayString.lower(): if self.LastExerciseTime == None: return True LastExerciseTime = datetime.datetime.strptime(self.LastExerciseTime, "%A %B %d, %Y %H:%M:%S") if (TimeNow - LastExerciseTime).days >= 14: return True return False elif self.ExerciseFrequency.lower() == "monthly" and TimeNow.day == self.ExerciseDayOfMonth: return True else: return False except Exception as e1: self.LogErrorLine("Error in TimeForExercise: " + str(e1)) return False # ---------- GenExercise::GetGeneratorTime---------------------------------- def GetGeneratorTime(self): try: GenTimeStr = "" data = self.SendCommand("generator: status_json") Status = {} Status = json.loads(data) TimeDict = self.FindDictValueInListByKey("Time", Status["Status"]) if TimeDict != None: TimeDictStr = self.FindDictValueInListByKey("Generator Time", TimeDict) if TimeDictStr != None or not len(TimeDictStr): GenTimeStr = TimeDictStr # Format is "Wednesday March 6, 2019 13:10" or " "Friday May 3, 2019 11:11" GenTime = datetime.datetime.strptime(GenTimeStr, "%A %B %d, %Y %H:%M") else: self.LogError("Error getting generator time! Genmon may be starting up.") GenTime = datetime.datetime.now() else: self.LogError("Error getting generator time (2)!") GenTime = datetime.datetime.now() return GenTime except Exception as e1: self.LogErrorLine("Error in GetGeneratorTime: " + str(e1) + ": " + GenTimeStr) return datetime.datetime.now() # ---------- GenExercise::ExerciseThread------------------------------------ def ExerciseThread(self): time.sleep(1) while True: try: if not self.ExerciseActive: if self.TimeForExercise(): self.StartExercise() if self.WaitForExit("ExerciseThread", float(self.PollTime)): return except Exception as e1: self.LogErrorLine("Error in ExerciseThread: " + str(e1)) if self.WaitForExit("ExerciseThread", float(self.PollTime)): return # ----------GenExercise::SignalClose---------------------------------------- def SignalClose(self, signum, frame): self.Close() sys.exit(1) # ----------GenExercise::Close---------------------------------------------- def Close(self): self.KillThread("ExerciseThread") if self.ExerciseActive: try: self.WarmupTimer.cancel() except: pass try: self.StopTimer.cancel() except: pass self.StopExercise() self.Generator.Close()
console.error("Send Notice Error: " + str(e1)) #------------------- Command-line interface for gengpio ------------------------ if __name__ == '__main__': console, ConfigFilePath, address, port, loglocation, log = MySupport.SetupAddOnProgram( "genpushover") # Set the signal handler signal.signal(signal.SIGINT, signal_handler) try: config = MyConfig(filename=os.path.join(ConfigFilePath, 'genpushover.conf'), section='genpushover', log=log) appid = config.ReadValue('appid') userid = config.ReadValue('userid') pushsound = config.ReadValue('pushsound', default='updown') if appid == None or not len(appid): log.error("Error: invalid app ID") console.error("Error: invalid app ID") sys.exit(2) if userid == None or not len(userid): log.error("Error: invalid user ID") console.error("Error: invalid user ID") sys.exit(2)
def __init__(self, log=None, loglocation=ProgramDefaults.LogPath, host=ProgramDefaults.LocalHost, port=ProgramDefaults.ServerPort, configfilepath=ProgramDefaults.ConfPath): super(MyMQTT, self).__init__() self.LogFileName = os.path.join(loglocation, "genmqtt.log") if log != None: self.log = log else: # log errors in this module to a file self.log = SetupLogger("client", self.LogFileName) # cleanup # test self.console = SetupLogger("mymqtt_console", log_file="", stream=True) self.Username = None self.Password = None self.MQTTAddress = None self.MonitorAddress = host self.MQTTPort = 1883 self.TopicRoot = None self.BlackList = None self.UseNumeric = False self.RemoveSpaces = False self.PollTime = 2 self.FlushInterval = float( 'inf') # default to inifite flush interval (e.g., never) self.debug = False try: config = MyConfig(filename=os.path.join(configfilepath, 'genmqtt.conf'), section='genmqtt', log=log) self.Username = config.ReadValue('username') self.Password = config.ReadValue('password') self.ClientID = config.ReadValue('client_id', default="genmon") self.MQTTAddress = config.ReadValue('mqtt_address') if self.MQTTAddress == None or not len(self.MQTTAddress): log.error("Error: invalid MQTT server address") console.error("Error: invalid MQTT server address") sys.exit(1) self.MonitorAddress = config.ReadValue('monitor_address', default=self.MonitorAddress) if self.MonitorAddress == None or not len(self.MonitorAddress): self.MonitorAddress = ProgramDefaults.LocalHost self.MQTTPort = config.ReadValue('mqtt_port', return_type=int, default=1883) self.PollTime = config.ReadValue('poll_interval', return_type=float, default=2.0) self.UseNumeric = config.ReadValue('numeric_json', return_type=bool, default=False) self.RemoveSpaces = config.ReadValue('remove_spaces', return_type=bool, default=False) self.TopicRoot = config.ReadValue('root_topic') if self.TopicRoot != None: self.TopicRoot = self.TopicRoot.strip() self.LogDebug("Root Topic : " + self.TopicRoot) if self.TopicRoot == None or not len(self.TopicRoot): self.TopicRoot = None #http://www.steves-internet-guide.com/mosquitto-tls/ self.CertificateAuthorityPath = config.ReadValue( 'cert_authority_path', default="") self.TLSVersion = config.ReadValue('tls_version', return_type=str, default="1.0") self.CertReqs = config.ReadValue('cert_reqs', return_type=str, default="Required") BlackList = config.ReadValue('blacklist') if BlackList != None: if len(BlackList): BList = BlackList.strip().split(",") if len(BList): self.BlackList = [] for Items in BList: self.BlackList.append(Items.strip()) self.debug = config.ReadValue('debug', return_type=bool, default=False) if config.HasOption('flush_interval'): self.FlushInterval = config.ReadValue('flush_interval', return_type=float, default=float('inf')) if self.FlushInterval == 0: self.FlushInterval = float('inf') else: self.FlushInterval = float('inf') except Exception as e1: self.LogErrorLine("Error reading " + os.path.join(configfilepath, "genmqtt.conf") + " : " + str(e1)) self.console.error("Error reading " + os.path.join(configfilepath, "genmqtt.conf") + " : " + str(e1)) sys.exit(1) try: self.MQTTclient = mqtt.Client(client_id=self.ClientID) if self.Username != None and len( self.Username) and self.Password != None: self.MQTTclient.username_pw_set(self.Username, password=self.Password) self.MQTTclient.on_connect = self.on_connect self.MQTTclient.on_message = self.on_message if len(self.CertificateAuthorityPath): if os.path.isfile(self.CertificateAuthorityPath): cert_reqs = ssl.CERT_REQUIRED if self.CertReqs.lower() == "required": cert_reqs = ssl.CERT_REQUIRED elif self.CertReqs.lower() == "optional": cert_reqs = ssl.CERT_REQUIRED elif self.CertReqs.lower() == "none": cert_reqs = ssl.CERT_NONE else: self.LogError( "Error: invalid cert required specified, defaulting to required: " + self.CertReq) use_tls = ssl.PROTOCOL_TLSv1 if self.TLSVersion == "1.0" or self.TLSVersion == "1": use_tls = ssl.PROTOCOL_TLSv1 elif self.TLSVersion == "1.1": use_tls = ssl.PROTOCOL_TLSv1_1 elif self.TLSVersion == "1.2": use_tls = ssl.PROTOCOL_TLSv1_2 else: self.LogError( "Error: invalid TLS version specified, defaulting to 1.0: " + self.TLSVersion) self.MQTTclient.tls_set( ca_certs=self.CertificateAuthorityPath, cert_reqs=cert_reqs, tls_version=use_tls) self.MQTTPort = 8883 # port for SSL else: self.LogError("Error: Unable to find CA cert file: " + self.CertificateAuthorityPath) self.MQTTclient.connect(self.MQTTAddress, self.MQTTPort, 60) self.Push = MyGenPush(host=self.MonitorAddress, log=self.log, callback=self.PublishCallback, polltime=self.PollTime, blacklist=self.BlackList, flush_interval=self.FlushInterval, use_numeric=self.UseNumeric, debug=self.debug, port=port, loglocation=loglocation) atexit.register(self.Close) signal.signal(signal.SIGTERM, self.Close) signal.signal(signal.SIGINT, self.Close) self.MQTTclient.loop_start() except Exception as e1: self.LogErrorLine("Error in MyMQTT init: " + str(e1)) self.console.error("Error in MyMQTT init: " + str(e1)) sys.exit(1)
def __init__(self, monitor = False, incoming_folder = None, processed_folder = None, incoming_callback = None, localinit = False, loglocation = ProgramDefaults.LogPath, ConfigFilePath = "/etc/", log = None, start = True): self.Monitor = monitor # true if we receive IMAP email self.IncomingFolder = incoming_folder # folder to look for incoming email self.ProcessedFolder = processed_folder # folder to move mail to once processed self.IncomingCallback = incoming_callback # called back with mail subject as a parameter if ConfigFilePath == None or ConfigFilePath == "": self.ConfigFilePath = "/etc/" else: self.ConfigFilePath = ConfigFilePath self.Mailbox = 0 self.EmailSendQueue = [] # queue for email to send self.DisableEmail = False self.DisableIMAP = False self.DisableSNMP = False self.DisableSmtpAuth = False self.SSLEnabled = False self.TLSDisable = False self.UseBCC = False self.ExtendWait = 0 self.Threads = {} # Dict of mythread objects self.ModulePath = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) # log errors in this module to a file if localinit == True: self.logfile = "mymail.log" self.configfile = "mymail.conf" else: self.logfile = os.path.join(loglocation, "mymail.log") self.configfile = os.path.join(self.ConfigFilePath, "mymail.conf") if log == None: self.log = SetupLogger("mymail", self.logfile) else: self.log = log # if mymail.conf is present attempt to copy it from the # main source directory if not os.path.isfile(self.configfile): if os.path.join(os.path.isfile(self.ModulePath, "mymail.conf")): copyfile(os.path.join(self.ModulePath, "mymail.conf") , self.configfile) else: self.LogError("Missing config file : " + self.configfile) sys.exit(1) self.config = MyConfig(filename = self.configfile, section = "MyMail", log = self.log) self.GetConfig() if self.DisableEmail: self.DisableIMAP = True self.DisableSNMP = True self.Monitor = False if not len(self.SMTPServer): self.DisableSNMP = True if not len(self.IMAPServer): self.DisableIMAP = True self.Monitor = False atexit.register(self.Close) if not self.DisableEmail: if not self.DisableSMTP and self.SMTPServer != "": self.Threads["SendMailThread"] = MyThread(self.SendMailThread, Name = "SendMailThread", start = start) else: self.LogError("SMTP disabled") if not self.DisableIMAP and self.Monitor and self.IMAPServer != "": # if True then we will have an IMAP monitor thread if incoming_callback and incoming_folder and processed_folder: self.Threads["EmailCommandThread"] = MyThread(self.EmailCommandThread, Name = "EmailCommandThread", start = start) else: self.FatalError("ERROR: incoming_callback, incoming_folder and processed_folder are required if receive IMAP is used") else: self.LogError("IMAP disabled")