import os if not os.geteuid() == 0: sys.exit('Script must be run as root') import argparse import re import subprocess import sys import time import sqlite3 import signal import readline from datetime import datetime from evdev import ecodes as e from config import gpio, menus, SQL from config.constants import * parser = argparse.ArgumentParser(description='GPIOnext Configuration Manager') parser.add_argument('--pins', metavar = '3,5,7,11', type = str, default = AVAILABLE_PINS_STRING, help='Comma delimited pin numbers to watch') parser.add_argument('--debounce', metavar='20', default = 20, type = int, help = 'Time in milliseconds for button debounce') parser.add_argument('--pulldown', dest='pulldown', default = False, action='store_true', help = 'Use PullDown resistors instead of PullUp') parser.add_argument('--dev', dest='dev', default = False, action='store_true', help='Show Warnings') parser.add_argument('--debug', dest='debug', default = False, action='store_true', help='Print data for debugging purposes') args = parser.parse_args() def pcolor( color, string ): color = color.lower() colors = { 'red': '\033[31m', 'green': '\033[32m', 'yellow': '\033[33m', 'blue': '\033[34m', 'fuschia': '\033[35m', 'cyan': '\033[36m' } return '{0}{1}\033[0m'.format( colors[color], string ) class ConfigurationManager: def __init__( self, args ): self.args = args # Stop any running GPIOnext components subprocess.call(('systemctl', 'stop', 'gpionext')) time.sleep(1) #give time to stop processes try: active = subprocess.check_output(['systemctl', 'is-active', 'gpionext']) except: active = 'active' if 'active' in active: self.DEBUG('ERROR: systemctl stop gpionext has failed!') self.DEBUG('Please stop gpionext before running config!') else: self.DEBUG('gpionext service has been successfully stopped') self.DEBUG('Initializing SIGNAL HANDLERS') for sig in [signal.SIGTERM, signal.SIGQUIT, signal.SIGINT]: signal.signal(sig, self.signal_handler) gpio.pinPressMethods.append( self.setTimer ) gpio.pinReleaseMethods.append( self.clearTimer ) self.set_args() SQL.init() gpio.setupGPIO( self.args ) self.getControllerType() def DEBUG(self, msg = '', addSeparator = False): if self.args.debug or self.args.dev: if msg: date = datetime.fromtimestamp( time.time() ) date = time.strftime('%Y-%m-%d %I:%M:%S%p') msg = '{0} {1} - {2}\n'.format( date, 'SYSTEM', msg ) if addSeparator: msg += ('-' * 50) + '\n' if self.args.debug: self.args.log.write( msg ) self.args.log.flush() if self.args.dev: print( msg ) def signal_handler(self, signal, frame): # Reset console in case of abrupt exit os.system('reset') self.DEBUG( addSeparator = True ) self.DEBUG( addSeparator = True ) self.DEBUG( "Shutting down. Received signal {0}".format( signal )) self.DEBUG("Cleanup GPIO Pins") gpio.cleanup() print() print ('Kaaaaahhhnn!') print() print("Type: 'gpionext start' to run the daemon") sys.exit(0) def set_args( self ): self.args.pins = [ int(x) for x in self.args.pins.split(',') ] #Use Board position numbering self.DEBUG('GPIO mode: BOARD') def getControllerType( self ): ''' currentDevice = Dictionary with keys: name, axisCount, buttonCount - or - currentDevice = None, Exit''' currentDevice = menus.GOTO_MAIN while currentDevice == menus.GOTO_MAIN: currentDevice = menus.showMainMenu() # If in main menu and user selects 'Exit' if currentDevice == None: gpio.cleanup() print("Type: 'gpionext start' to run the daemon") sys.exit(0) if currentDevice['name'] == 'Keyboard': self.configureKeyboard( currentDevice ) elif currentDevice['name'] == 'Commands': self.configureCommands( ) else: self.configureJoypad( currentDevice ) def setTimer( self, bitmask, channel ): holdTimeRequired = 1.0 self.timeout = time.time() + holdTimeRequired def clearTimer( self, bitmask, channel ): self.timeout = None def wait_for_pin( self, poll_time = 0.05 ): self.timeout = None while True: if self.timeout and self.timeout < time.time(): self.timeout = None return gpio.bitmaskToList() time.sleep(poll_time) def waitForButtonRelease( self ): startTime = time.time() prompt = pcolor('cyan', 'Please release all buttons to continue') while gpio.bitmask: time.sleep(0.05) if prompt: if time.time() - startTime > 3: print( prompt ) prompt = None def configureKeyboard( self, currentDevice ): menus.clearPreviousMenu() print( 'Configuring {0}'.format( currentDevice['name'] )) device = [] # Append new controls to this list deviceName = currentDevice['name'] # Current controller being configured # Second, Configure Buttons for button in currentDevice['buttons']: cmdName = button[0] command = button[1] unit = pcolor( 'cyan', cmdName) print( 'Press and hold GPIO pin(s) to map {0}'.format( unit ), end = ' ') sys.stdout.flush() pressed = self.wait_for_pin() pressed = ', '.join( map(str, pressed) ) print( '- Pins(s):', pressed ) self.waitForButtonRelease() device.append( (deviceName, cmdName, 'KEY', command, pressed) ) # Save to Database print( 'Saving Configuration!' ) # Delete Old Entries SQL.deleteDevice( deviceName ) # Create New Entries SQL.createDevice( device ) time.sleep(1) self.getControllerType() def getInput( self, prompt, prefill='' ): readline.set_startup_hook(lambda: readline.insert_text('\n' + prefill)) try: return input(prompt).replace('\n','') finally: readline.set_startup_hook() def configureCommands( self ): while True: cmd = menus.editCommandButton() if cmd == menus.GOTO_MAIN: return self.getControllerType() if cmd[0] == 'EDIT': self.editCommand( cmd[1] ) elif cmd[0] == 'DELETE': SQL.deleteEntry( cmd[1] ) def editCommand( self, cmd ): # User input Prompts promptButton = ( pcolor("fuschia", "Hold a button ") + "to configure this command" ) promptCommand = ( pcolor("fuschia", "Enter a command ") + "to map to this button: ") promptAdditonal = ("Map an additional command " + pcolor( "cyan", "to this button? " )) promptName = (pcolor("fuschia", "Enter a name ") + "for this command: ") # name command cmd['name'] = self.getInput( promptName, cmd['name'] ) # Press Button to map print( promptButton, end = ' ') sys.stdout.flush() pressed = self.wait_for_pin() cmd['pins'] = ', '.join( map(str, pressed) ) print( '- Pin(s):', cmd['pins'] ) self.waitForButtonRelease() # get commands print() answer = 'Y' commandList = [] while 'Y' in answer.upper(): # Enter new command commandList.append( self.getInput( promptCommand, cmd['command'] ) ) cmd['command'] = '' # add another command? answer = input( promptAdditonal ) cmd['command'] = '; '.join( commandList ) cmd['device'] = 'Commands' SQL.updateEntry( cmd ) def defineAxis( self, direction, dpad, deviceName, offset = 0 ): cmdName = '{0} {1}'.format( direction, dpad + 1 ) colorDirection = pcolor( 'cyan', direction ) colorDpad = pcolor( 'fuschia', dpad + 1 ) print( 'Hold {0} on Dpad/Joystick {1}'.format( colorDirection, colorDpad), end = ' ') sys.stdout.flush() pressed = self.wait_for_pin() pressed = ', '.join( map(str, pressed) ) print( '- Pin(s):', pressed ) self.waitForButtonRelease() if direction in ["DOWN", "RIGHT"]: value = JOYSTICK_AXIS.max else: value = JOYSTICK_AXIS.min command = '(e.EV_ABS, {0}, {1})'.format( dpad * 2 + offset, value ) return deviceName, cmdName, 'AXIS', command, pressed def configureJoypad( self, currentDevice ): menus.clearPreviousMenu() print( 'Configuring {0}'.format( currentDevice['name'] )) device = [] # Append new controls to this list deviceName = currentDevice['name'] # Current controller being configured # First, Configure Joysticks for dpad in range( currentDevice['axisCount'] ): # Define Axes device.append( self.defineAxis("UP", dpad, deviceName, offset=1) ) device.append( self.defineAxis("DOWN", dpad, deviceName, offset=1) ) device.append( self.defineAxis("LEFT", dpad, deviceName) ) device.append( self.defineAxis("RIGHT", dpad, deviceName) ) # Second, Configure Buttons for button in currentDevice['buttons']: cmdName = button[0] command = button[1] unit = pcolor( 'cyan', cmdName) print( 'Hold {0}'.format( unit ), end = ' ') sys.stdout.flush() pressed = self.wait_for_pin() pressed = ', '.join( map(str, pressed) ) print( '- Pins(s):', pressed ) self.waitForButtonRelease() device.append( (deviceName, cmdName, 'BUTTON', command, pressed) ) # Save to Database print( 'Saving Configuration!' ) # Delete Old Entries SQL.deleteDevice( deviceName ) # Create New Entries SQL.createDevice( device ) time.sleep(1) self.getControllerType() if __name__ == '__main__': ConfigurationManager(args)