Initial commit
This commit is contained in:
		
						commit
						0273973538
					
				
							
								
								
									
										210
									
								
								pyupsmon.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								pyupsmon.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,210 @@
 | 
				
			|||||||
 | 
					#!/usr/bin/python3
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Depends on pyyaml!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import shlex
 | 
				
			||||||
 | 
					import subprocess
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					import yaml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from serial import Serial
 | 
				
			||||||
 | 
					from threading import Timer
 | 
				
			||||||
 | 
					from time import sleep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConvertUPS(Serial):
 | 
				
			||||||
 | 
					    """ Class that implements monitoring for Wöhrle Convert-[123]000 UPS
 | 
				
			||||||
 | 
					        devices connected via RS232.
 | 
				
			||||||
 | 
					        Has to be initialized like serial.Serial().
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Class variables
 | 
				
			||||||
 | 
					    _isOnBattery = False    # Is UPS running on battery right now?
 | 
				
			||||||
 | 
					    _powerToggled = False   # Did power state (battery / AC) toggle recently?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, configfile = '/etc/pyupsmon.yml', port = None,
 | 
				
			||||||
 | 
					        baudrate = None, interval = None, shutdownTimeout = None,
 | 
				
			||||||
 | 
					        shutdownCommand = None, debug = None, *args, **kwargs):
 | 
				
			||||||
 | 
					        """ Read config file and/or set default config values. """
 | 
				
			||||||
 | 
					        self.serialPort = port
 | 
				
			||||||
 | 
					        self.serialBaud = baudrate
 | 
				
			||||||
 | 
					        self.interval = interval
 | 
				
			||||||
 | 
					        self.shutdownTimeout = shutdownTimeout
 | 
				
			||||||
 | 
					        self.shutdownCommand = shutdownCommand
 | 
				
			||||||
 | 
					        self.debug = debug
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            with open(configfile, 'r') as ymlfile:
 | 
				
			||||||
 | 
					                cfg = yaml.load(ymlfile)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if cfg.get('serial') is not None:
 | 
				
			||||||
 | 
					                    if self.serialPort is None:
 | 
				
			||||||
 | 
					                        self.serialPort = cfg['serial'].get('port', '/dev/ttyS0')
 | 
				
			||||||
 | 
					                    if self.serialBaud is None:
 | 
				
			||||||
 | 
					                        self.serialBaud = int(cfg['serial'].get('baudrate', 2400))
 | 
				
			||||||
 | 
					                if cfg.get('daemon') is not None:
 | 
				
			||||||
 | 
					                    if self.interval is None:
 | 
				
			||||||
 | 
					                        self.interval = float(cfg['daemon'].get('interval', 1.0))
 | 
				
			||||||
 | 
					                    if self.debug is None:
 | 
				
			||||||
 | 
					                        self.debug = bool(cfg['daemon'].get('debug', False))
 | 
				
			||||||
 | 
					                if cfg.get('shutdown') is not None:
 | 
				
			||||||
 | 
					                    if self.shutdownTimeout is None:
 | 
				
			||||||
 | 
					                        self.shutdownTimeout = float(cfg['shutdown'].get('timeout', 3600))
 | 
				
			||||||
 | 
					                    if self.shutdownCommand is None:
 | 
				
			||||||
 | 
					                        self.shutdownCommand = shlex.split(cfg['shutdown'].get('command', '/sbin/shutdown -h now'))
 | 
				
			||||||
 | 
					        except OSError:
 | 
				
			||||||
 | 
					            if debug:
 | 
				
			||||||
 | 
					                print('Could not read config file %s.' % configfile)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.serialPort is None:
 | 
				
			||||||
 | 
					            self.serialPort = '/dev/ttyS0'
 | 
				
			||||||
 | 
					        if self.serialBaud is None:
 | 
				
			||||||
 | 
					            self.serialBaud = 2400
 | 
				
			||||||
 | 
					        if self.interval is None:
 | 
				
			||||||
 | 
					            self.interval = 1.0
 | 
				
			||||||
 | 
					        if self.debug is None:
 | 
				
			||||||
 | 
					            self.debug = False
 | 
				
			||||||
 | 
					        if self.shutdownTimeout is None:
 | 
				
			||||||
 | 
					            self.shutdownTimeout = 3600.0
 | 
				
			||||||
 | 
					        if self.shutdownCommand is None:
 | 
				
			||||||
 | 
					            self.shutdownCommand = shlex.split('/sbin/shutdown -h now')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.debug:
 | 
				
			||||||
 | 
					            print('Configuration:')
 | 
				
			||||||
 | 
					            print('\tSerial Port:\t\t%s' % self.serialPort)
 | 
				
			||||||
 | 
					            print('\tBaud Rate:\t\t%d' % self.serialBaud)
 | 
				
			||||||
 | 
					            print('\tQuery Interval:\t\t%.2f s' % self.interval)
 | 
				
			||||||
 | 
					            print('\tDebugging:\t\t%s' % self.debug)
 | 
				
			||||||
 | 
					            print('\tShutdown Timeout:\t%.2f' % self.shutdownTimeout)
 | 
				
			||||||
 | 
					            print('\tShutdown Command:\t%s' % self.shutdownCommand)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super(ConvertUPS, self).__init__(port = self.serialPort,
 | 
				
			||||||
 | 
					                baudrate = self.serialBaud, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def upsInit(self):
 | 
				
			||||||
 | 
					        """ Start communicating with the UPS. """
 | 
				
			||||||
 | 
					        if not self.name:
 | 
				
			||||||
 | 
					            if self.debug:
 | 
				
			||||||
 | 
					                raise NameError('Please, set the serial communication parameters first.')
 | 
				
			||||||
 | 
					        if self.debug:
 | 
				
			||||||
 | 
					            print('Successfully connected to serial port %s' % (ups.name))
 | 
				
			||||||
 | 
					            print('Sending initialization sequence (you should hear a short beep).')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.write(b'CT\rDQ1\r')
 | 
				
			||||||
 | 
					        sleep(5.0)
 | 
				
			||||||
 | 
					        self.write(b'Rt\r')
 | 
				
			||||||
 | 
					        sleep(3.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.write(b'Q1\r')
 | 
				
			||||||
 | 
					        sleep(1.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        readbuffer = b''
 | 
				
			||||||
 | 
					        data = self.read(1)
 | 
				
			||||||
 | 
					        while b'\r' not in data:
 | 
				
			||||||
 | 
					            readbuffer += data
 | 
				
			||||||
 | 
					            data = self.read(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if readbuffer.strip().startswith(b'('):
 | 
				
			||||||
 | 
					            if self.debug:
 | 
				
			||||||
 | 
					                print('Initialization successful.')
 | 
				
			||||||
 | 
					                print('Initial values returned:', readbuffer.strip())
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def upsRead(self):
 | 
				
			||||||
 | 
					        """ Read the current status of the UPS. """
 | 
				
			||||||
 | 
					        ups.write(b'Q1\r')
 | 
				
			||||||
 | 
					        readbuffer = b''
 | 
				
			||||||
 | 
					        data = ups.read(1)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        while b'\r' not in data:
 | 
				
			||||||
 | 
					            readbuffer += data
 | 
				
			||||||
 | 
					            data = ups.read(1)
 | 
				
			||||||
 | 
					        if self.debug:
 | 
				
			||||||
 | 
					            print(readbuffer.strip())
 | 
				
			||||||
 | 
					        return readbuffer.strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def isOnBattery(self):
 | 
				
			||||||
 | 
					        """ Check whether UPS is on Battery. """
 | 
				
			||||||
 | 
					        status = self.upsRead()
 | 
				
			||||||
 | 
					        if status.startswith(b'(000.0 '):
 | 
				
			||||||
 | 
					            if not self._isOnBattery:
 | 
				
			||||||
 | 
					                self._isOnBattery = True
 | 
				
			||||||
 | 
					                self._powerToggled = True
 | 
				
			||||||
 | 
					            if self.debug:
 | 
				
			||||||
 | 
					                print('UPS is on battery.')
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            if self._isOnBattery:
 | 
				
			||||||
 | 
					                self._isOnBattery = False
 | 
				
			||||||
 | 
					                self._powerToggled = True
 | 
				
			||||||
 | 
					            if self.debug:
 | 
				
			||||||
 | 
					                print('UPS is on AC.')
 | 
				
			||||||
 | 
					        return self._isOnBattery
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def powerToggled(self):
 | 
				
			||||||
 | 
					        """ Check if a power toggle (AC -> Battery or vice versa)
 | 
				
			||||||
 | 
					            occured and reset the flag.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        onBattery = self.isOnBattery()
 | 
				
			||||||
 | 
					        if self._powerToggled:
 | 
				
			||||||
 | 
					            self._powerToggled = False
 | 
				
			||||||
 | 
					            if self.debug:
 | 
				
			||||||
 | 
					                print('Power toggle has occured recently.')
 | 
				
			||||||
 | 
					            if onBattery:
 | 
				
			||||||
 | 
					                return(True, True)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                return(True, False) 
 | 
				
			||||||
 | 
					        if self.debug:
 | 
				
			||||||
 | 
					            print('No power toggle has occured recently.')
 | 
				
			||||||
 | 
					        if onBattery:
 | 
				
			||||||
 | 
					            return (False, True)
 | 
				
			||||||
 | 
					        return (False, False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def doShutdown(self):
 | 
				
			||||||
 | 
					        """ Execute self.shutdownCommand. """
 | 
				
			||||||
 | 
					        subprocess.call(self.shutdownCommand)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def startShutdownTimer(self):
 | 
				
			||||||
 | 
					        """ Start a timer that executes self.shutdownCommand as soon as
 | 
				
			||||||
 | 
					            self.shutdownTimeout has expired.
 | 
				
			||||||
 | 
					            If self.shutdownTimeout == 0, the timer is disabled.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if self.shutdownTimeout > 0:
 | 
				
			||||||
 | 
					            self.timer = Timer(self.shutdownTimeout, self.doShutdown)
 | 
				
			||||||
 | 
					            self.timer.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def cancelShutdownTimer(self):
 | 
				
			||||||
 | 
					        """ Cancel a  previously started shutdown timer. """
 | 
				
			||||||
 | 
					        if self.timer is not None:
 | 
				
			||||||
 | 
					            self.timer.cancel()
 | 
				
			||||||
 | 
					            self.timer = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    #try:
 | 
				
			||||||
 | 
					    ups = ConvertUPS()
 | 
				
			||||||
 | 
					    #except:
 | 
				
			||||||
 | 
					    #    print('Error: Could not open UPS serial connection.')
 | 
				
			||||||
 | 
					    #    print('\tCheck the config file!')
 | 
				
			||||||
 | 
					    #    sys.exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if not ups.upsInit():
 | 
				
			||||||
 | 
					        print('Error: Could not initialize UPS.')
 | 
				
			||||||
 | 
					        sys.exit(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        (toggled, onBatt) = ups.powerToggled()
 | 
				
			||||||
 | 
					        if toggled:
 | 
				
			||||||
 | 
					            if onBatt:
 | 
				
			||||||
 | 
					                ups.startShutdownTimer()
 | 
				
			||||||
 | 
					            if not onBatt:
 | 
				
			||||||
 | 
					                ups.cancelShutdownTimer()
 | 
				
			||||||
 | 
					        sleep(ups.interval)
 | 
				
			||||||
							
								
								
									
										36
									
								
								pyupsmon.yml.example
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								pyupsmon.yml.example
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					# Serial port parameters
 | 
				
			||||||
 | 
					serial:
 | 
				
			||||||
 | 
					  # Serial port the UPS is connected to
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # Default: /dev/ttyS0
 | 
				
			||||||
 | 
					  #port: /dev/ttyS0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Baud rate to set for the serial port
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # Default: 2400
 | 
				
			||||||
 | 
					  #baudrate: 2400
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Daemon settings
 | 
				
			||||||
 | 
					daemon:
 | 
				
			||||||
 | 
					  # UPS query interval (in seconds)
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # Default: 1.0
 | 
				
			||||||
 | 
					  #interval: 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Enable debug output?
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # Default: 0
 | 
				
			||||||
 | 
					  #debug: 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Shutdown settings
 | 
				
			||||||
 | 
					shutdown:
 | 
				
			||||||
 | 
					  # Shutdown the monitoring host after this many seconds
 | 
				
			||||||
 | 
					  # on battery (0 to disable)
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # Default: 3600.0
 | 
				
			||||||
 | 
					  #timeout: 180.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Shutdown command to execute after timeout expired
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # Default: /sbin/shutdown -h now
 | 
				
			||||||
 | 
					  #command: /sbin/shutdown -h now
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user