def __del__(self): """ Close all devices and stop timers """ try: self.stop() except Exception as error: logger.error( 'Error in destructor of main control! {0}'.format(error))
def open(self): """ Open an input event device for reading Tries to open the device path given to the constructor or search for all available input event devices. """ device_paths = [] if len(self.__inputDevicePath): device_paths = ["{0}".format(self.__inputDevicePath)] else: device_paths = glob.glob( '/dev/input/event*') # search all available devices for path in device_paths: try: self.close() self.__inputDevice = InputDevice(path) self.__deviceOpenRetries = 0 self.__inputDevicePath = path logger.debug('Opened RFID reader device \'{0}\''.format(path)) time.sleep(1.0) # prevent timing issues return except: if self.__deviceOpenRetries < 1: logger.error( 'Unable to open USB RFID reader \'{0}\'!'.format(path)) self.__inputDevice = None self.__deviceOpenRetries += 1
def createTable(self, tableName, fields, primaryKey=""): """ Create a table of given name with given fields name = SQLite table name, MUST NOT contain special chars+whitespaces fields = array of SQL table field names and type: e.g.: ['id INTEGER', 'msg', 'price REAL', 'name TEXT'] Return True on successs, False on any error """ if tableName is None: logger.error('Invalid table name "{0}" given!'.format(tableName)) return False if fields is None: logger.error('Invalid table fields "{0}" given!'.format(fields)) return False if self.tableExists(tableName) is True: logger.warning('DB table "{0}" cannot be created! ' 'Table already exists!'.format(tableName)) return False fieldStr = None for field in fields: if fieldStr is None: fieldStr = '{0}'.format(field) else: fieldStr = '{0}, {1}'.format(fieldStr, field) sqls = [] sqls.append('CREATE TABLE {0} ({1})'.format(tableName, fieldStr)) if primaryKey and not primaryKey.isspace(): sqls.append('CREATE UNIQUE INDEX index_{0}_{1} ' 'ON {0}({1})'.format(tableName, primaryKey)) retval = self._exec_queries(sqls) return True if retval is not None else False
def _exec_queries(self, sqlQueries=[]): """ Execute given SQL statements Return results as one extended array """ results = [] sql = "" if self._dbfile is None: logger.error('Cannot execute SQL query! ' 'Database filename "{0}" invalid!'.format( self._dbfile)) return results try: with sqlite3.connect(self._dbfile) as connection: cursor = connection.cursor() for query in sqlQueries: sql = query for row in cursor.execute(sql): results.append(row) if 0: logger.debug('added query "{0}" results "{1}" ' 'to return value'.format(sql, row)) logger.debug('executed query "{0}"'.format(sql)) except sqlite3.OperationalError as err: logger.error('{0}! query: {1}'.format(err, sql)) return None return results
def readline(self): """ Try reading a code from the input device and return it as string """ if self.__inputDevice is None: self.open() if self.__inputDevice is None: return None line = '' try: while True: event = self.__inputDevice.read_one() if event is None and line == '': # There are blank events in between characters, so we don't want # to break if we've started reading them break # start a new read. if event is None or event.type != ecodes.EV_KEY: continue data = categorize(event) # catch only keyup, and not Enter if data.keystate == 0 and data.scancode != 42: if data.scancode == 28: # looking return key to be pressed if len(line) < self.__minCodeLength: logger.warning( 'ignoring to small code: {0}'.format(line)) break else: logger.debug('code read: \'{0}\''.format(line)) return line else: line += self.__scancodes[data.scancode] except: logger.error('Parsing input stream failed!') self.close() return None
def clear(self): """ Clears all text from LCD """ try: if self.__outputDevice is not None: self.__outputDevice.lcd_clear() except: logger.error('Clearing lcd display failed!') self.close()
def backlight(self, on=True): """ Switch LCD backlight on/off """ try: if self.__outputDevice is not None: self.__outputDevice.lcd_backlight('ON' if on else 'OFF') except: logger.error('Switching display backlight failed!') self.close()
def show(self, text, line=1): """ Displays given text on LCD at given line (1..n) """ try: if self.__outputDevice is None: self.open() if self.__outputDevice is not None: self.__outputDevice.lcd_display_string(text, line) except: logger.error('Showing text on lcd failed!') self.close()
def start(self): """ Init timers, reader and display user message """ self.__running = True Thread.start(self) try: logger.debug('starting shower control...') time.sleep(1.0) except Exception as error: logger.error( 'Error during starting main control! {0}'.format(error))
def open(self): """ Open connection to LCD display """ try: self.__outputDevice = lcddriver.lcd() if self.__outputDevice is None: raise RuntimeWarning self.__outputDevice.lcd_clear() time.sleep(1.0) # prevent timing issues except: if self.__deviceOpenRetries < 1: logger.error('Unable to open HD44780 LCD display!') self.__outputDevice = None self.__deviceOpenRetries += 1
def __update_sensors(self): """ Check for 1wire drivers and read available sensors """ try: # open 1-wire slaves list to read sensor ids file = open('/sys/devices/w1_bus_master1/w1_master_slaves') w1_slaves = file.read().splitlines() file.close() self.__sensors = w1_slaves if w1_slaves[0] is None: logger.warning('no 1-wire sensors found!') else: for s in w1_slaves: logger.debug('sensor found: {}'.format(s)) except: logger.error('Unable to read 1-wire device list!')
def tableExists(self, tableName): """ Check if given table exists Return True for existing table, False on any error """ if tableName is None: logger.error('Invalid table name "{0}" given!'.format(tableName)) return False sqls = [ 'SELECT name FROM sqlite_master ' 'WHERE type=\'table\' AND name=\'{0}\''.format(tableName) ] retval = self._exec_queries(sqls) return True if len(retval) and retval[0] is not None else False
def stop(self): """ Close all valves (relays), stop timers and display warning """ try: logger.warning('stopping and locking shower control...') self.__running = False if self._showerTimer: self._showerTimer.stop() if self._relays: self._relays.toggle_output(self._gpio_cold_water, False) self._relays.toggle_output(self._gpio_warm_water, False) if self._display: self.show_message_error() except Exception as error: logger.error( 'Error during stopping main control! {0}'.format(error))
def deleteTable(self, tableName): """ Delete the given table Return True on success, False on any error """ if tableName is None: logger.error('Invalid table name "{0}" given!'.format(tableName)) return False if self.tableExists(tableName) is False: logger.warning('DB table "{0}" cannot be deleted! ' 'Table does not exist!'.format(tableName)) return False sqls = ['DROP TABLE {0}'.format(tableName)] retval = self._exec_queries(sqls) return True if retval is not None else False
def __init__(self, shower_id=1, shower_seconds=420): """ Init card reader, display and I/O controls """ Thread.__init__(self) try: if shower_seconds < 30: logger.error('invalid shower time given! time must be > 30s') raise AttributeError() self.shower_id = shower_id self.shower_time = shower_seconds self._cardReader = UsbRfidReader() self._showerTimer = IOControlTimer(self.shower_time) self._relays = RelayControl( [self._gpio_cold_water, self._gpio_warm_water]) self._display = LcdControl() self.__running = False time.sleep(1.0) logger.debug('shower control ready') except Exception as error: logger.error( 'Error during init of main control! {0}'.format(error))
def writeEntry(self, tableName, primaryKey, values={}): """ Insert or update a table entry with given values tableName - name of target table for insert/update of entry primaryKey - name of table primary key or selector column values - dictionary with field:value pairs of entry to write Return True on success, False on any error """ if tableName is None: logger.error('Invalid table name given!') return False if primaryKey is None: logger.error('Invalid table entry primary key given!') return False if values is False or primaryKey not in values: logger.error('Invalid table entry primary key given!') return False id = values[primaryKey] if self.tableExists(tableName) is False: logger.warning('Entry cannot be written! ' 'Table does not exist!'.format(tableName)) return False sqls = [ 'SELECT * FROM {0} WHERE {1} ' '= \'{2}\''.format(tableName, primaryKey, id) ] retval = self._exec_queries(sqls) if len(retval) and retval[0] is not None: logger.debug('Updating entry {0}="{1}"'.format(primaryKey, id)) sqls = [ 'UPDATE {0} SET {1} WHERE {2} = \'{3}\''.format( tableName, ', '.join("%s='%s'" % (k, v) for k, v in values.items()), primaryKey, id) ] else: logger.debug('Inserting new entry {0}="{1}"'.format( primaryKey, id)) sqls = [ 'INSERT INTO {0} ({1}) VALUES ({2})'.format( tableName, ', '.join("{0}".format(k) for k in values.keys()), ', '.join("'{0}'".format(values[k]) for k in values.keys())) ] retval = self._exec_queries(sqls) return True if retval is not None else False
if len(line) < self.__minCodeLength: logger.warning( 'ignoring to small code: {0}'.format(line)) break else: logger.debug('code read: \'{0}\''.format(line)) return line else: line += self.__scancodes[data.scancode] except: logger.error('Parsing input stream failed!') self.close() return None # command line execution if __name__ == '__main__': rfidReader = UsbRfidReader('/dev/input/event2', 10) while True: try: code = rfidReader.readline() if code is not None: logger.debug('found a code: \'{0}\''.format(code)) time.sleep(0.5) except KeyboardInterrupt: logger.debug('Key pressed - finishing now...') break except: logger.error('Unknown error received. Reading aborted!') break
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ PiShower project @copyright Christian Daehn (c) 2006, http://3dh.de @license MIT license """ import time from pishowerutils import logger try: import RPi.GPIO as GPIO except RuntimeError: logger.error('Error importing RPi.GPIO! Superuser privileges needed!') quit(-1) class RelayControl: """ Easy GPIO control to toggle relais """ invertedLevels = True _state_on = GPIO.LOW _state_off = GPIO.HIGH _registered_outs = [] def __init__(self, outports=[], inverted=True): """ Init GPIO interface """ self.invertedLevels = inverted if not inverted: self._state_on, self._state_off = self._state_off, self._state_on self._states = {self._state_on: 'ON', self._state_off: 'OFF'}
def run(self): """ Handle card reader events and logic for shower control """ if self._cardReader is None: logger.error('No card reader available! Aborting!') self.stop() return if self._relays is None: logger.error('No I/O control for relays available! Aborting!') self.stop() return try: logger.debug('running main control loop...') self.show_message_ready() shower_active = False start_time = None while self.__running: code = self._cardReader.readline( ) # codes must be read to be cleared from buffer if shower_active: # a shower process was started code = None minutes_left = 0 # TODO: add minutes left calc here if self._showerTimer is None: logger.warning( 'No timer found! Stopped shower process!') shower_active = False self.show_message_ready() else: if not self._showerTimer.is_finished(): self.show_message_processing(minutes_left) else: logger.debug('stopped shower process on timeout') shower_active = False self.show_message_stop() if self._relays: self._relays.toggle_output( self._gpio_cold_water, False) self._relays.toggle_output( self._gpio_warm_water, False) time.sleep(6.0) self.show_message_ready() elif code is not None: # wait for request to start new shower process logger.debug('found a code: \'{0}\''.format( code)) # TODO: add validator here self.show_message_processing(self.shower_time // 60) shower_active = True if self._relays: self._relays.toggle_output(self._gpio_cold_water, True) self._relays.toggle_output(self._gpio_warm_water, True) if self._showerTimer: self._showerTimer.start() time.sleep(0.2) logger.debug('stopping main control loop...') except Exception as error: logger.error('Error in main control loop! {0}'.format(error))
if self._display: self._display.show('Duschzeit {0:3d}min'.format(shower_time), 1) self._display.show('>>> Karte OK <<<', 2) def show_message_error(self): if self._display: self._display.show('Dusche {0} AUS'.format(self.shower_id), 1) self._display.show('>>> STOERUNG <<<', 2) def show_message_stop(self): if self._display: self._display.show('Dusche {0} BELEGT'.format(self.shower_id), 1) self._display.show('>>> GESPERRT <<<', 2) # exec tests if __name__ == "__main__": try: main = PiShowerMain(1, 40) main.start() while main.is_alive(): time.sleep(0.2) except KeyboardInterrupt: logger.debug('Key pressed - finishing now...') except Exception as error: logger.error('Error in main control! {0}'.format(error)) finally: main.stop()
logger.debug('added query "{0}" results "{1}" ' 'to return value'.format(sql, row)) logger.debug('executed query "{0}"'.format(sql)) except sqlite3.OperationalError as err: logger.error('{0}! query: {1}'.format(err, sql)) return None return results # Run minimal test if __name__ == '__main__': db = DBConnection() if db.tableExists('user'): db.deleteTable('user') else: logger.error('check for existing table failed!') db.createTable('user', ['id INTEGER', 'name', 'firstname', 'age INTEGER']) db.writeEntry('user', 'id', { 'id': 2, 'name': 'Wurst', 'firstname': 'Hans', 'age': 24 }) db.writeEntry('user', 'id', { 'id': 2, 'name': 'Wurst', 'firstname': 'Hänschen', 'age': 30 })
@copyright Christian Daehn (c) 2006, http://3dh.de @license MIT license """ import os import sys import time from pishowerutils import logger try: scriptPath = os.path.realpath(os.path.dirname(sys.argv[0])) os.chdir(scriptPath) sys.path.append("./hd44780") import lcddriver except RuntimeError: logger.error('Error importing hd44780 lcddriver!') quit(-1) class LcdControl: """ Easy output to HD44780 based LCD displays """ __outputDevice = None __deviceOpenRetries = 0 def __init__(self): """ Init display interface """ self.open() def __del__(self): """ Clear display interface """ self.close()