From b63283a213f7cbf279d03e3d88a962dd1ab9793e Mon Sep 17 00:00:00 2001 From: James Meyer Date: Wed, 7 Dec 2011 13:41:34 -0600 Subject: powermate_myth: add ability to control the FE using a geffen powermate. --- abs/core/powermate_myth/PKGBUILD | 16 +++ abs/core/powermate_myth/powermate-myth.py | 214 ++++++++++++++++++++++++++++++ abs/core/powermate_myth/powermate.py | 106 +++++++++++++++ 3 files changed, 336 insertions(+) create mode 100644 abs/core/powermate_myth/PKGBUILD create mode 100644 abs/core/powermate_myth/powermate-myth.py create mode 100644 abs/core/powermate_myth/powermate.py diff --git a/abs/core/powermate_myth/PKGBUILD b/abs/core/powermate_myth/PKGBUILD new file mode 100644 index 0000000..1581477 --- /dev/null +++ b/abs/core/powermate_myth/PKGBUILD @@ -0,0 +1,16 @@ +pkgname=powermate_myth +pkgver=1.0 +pkgrel=1 +arch=('i686') +pkgdesc="Provides support and sample program for using the powermate with mtythv" +url="" +depends=(python2 xdotool) +source=(powermate-myth.py powermate.py) + +build() { + cd $startdir/src + install -D -m0755 powermate.py $startdir/pkg/usr/LH/bin/powermate.py + install -D -m0755 powermate-myth.py $startdir/pkg/usr/LH/bin/powermate-myth.py +} +md5sums=('44a03725355ed53d9f81099923d94329' + '90bed7b80570ad6e63ebff827830e14b') diff --git a/abs/core/powermate_myth/powermate-myth.py b/abs/core/powermate_myth/powermate-myth.py new file mode 100644 index 0000000..85e5165 --- /dev/null +++ b/abs/core/powermate_myth/powermate-myth.py @@ -0,0 +1,214 @@ +#!/usr/bin/python2 + +import powermate +import time +import os + +EVENT_BUTTON_PRESS = 1 +EVENT_RELATIVE_MOTION = 2 + + +button_pressed = False +single_tap = False +mode = "nav" + +current_event=(0,0,time.time()) +last_event=current_event +skipped_event = 0 +pressed_time = 0 +button_held = False +led_brightness = 255 +#pm = powermate.PowerMate("/dev/powermate") + +pm = powermate.PowerMate() +pm.SetLEDState(led_brightness,0,0,0,0) + + +def runcmd(cmd): +# print cmd + os.system(cmd) + + +def volume_action(action): + global mode + global led_brightness + if action == "L" : + print "Volume down" + + if action == "R" : + print "Volume UP" + + if action == "tap" : + print "toggle mute" + + if action == "double_tap" : + mode = "nav" + led_brightness = 500 + print "mode is now nav" + + return + + +def nav_action(action): + global mode + global led_brightness + keycmd = '''xdotool search --name "Mythtv Frontend" key %s''' + if action == "L" : + #print "prev" + cmd = keycmd %"Up" + runcmd(cmd) + + if action == "R" : + #print "next" + cmd = keycmd %"Down" + runcmd(cmd) + + if action == "LP" : + #print "back" + cmd = keycmd %"Left" + runcmd(cmd) + + if action == "RP" : + #print "tab" + cmd = keycmd %"Right" + runcmd(cmd) + + + if action == "tap" : + #print "select" + cmd = keycmd %"Return" + runcmd(cmd) + + if action == "double_tap" : + mode = "volume" + led_brightness = 10 + print "mode is now vol" + + if action == "button_held" : + cmd = keycmd %"Escape" + runcmd(cmd) + + + + return + + + + +def act_on_event(action): + + if mode == "volume": + volume_action(action) + + elif mode == "nav": + nav_action(action) + + + + +event_stack=[current_event] +while 1: + #process_events = True + reset_lastevent = False + event = pm.WaitForEvent(2) + # print event + if event: + #event that happens after every button push, can be thrown away. + if event[2] == 0: + continue + + + elif single_tap: + pressed_diff = time.time() - pressed_time + #This takes care of the holding down the button for X amount of time + #print event, button_pressed, pressed_diff, current_event + #if pressed_diff >= 2 and button_pressed and current_event[0] == EVENT_BUTTON_PRESS: + if pressed_diff >= 2 and button_pressed : + act_on_event("button_held") + single_tap = False + pressed_time = 0 + button_pressed = False + last_event_time = time.time() + last_event=(0,0,time.time()) + pm.SetLEDState(led_brightness,0,0,0,0) + #print "resetting last event:", last_event + + + #This handles a single press of the button. + elif not button_pressed : + act_on_event("tap") + single_tap = False + last_event_time = time.time() + last_event=(0,0,time.time()) + + if event: + #print "processing:" , event + current_event = event[2],event[4],time.time() + + last_event_time = last_event[2] + current_event_time = current_event[2] + + last_event_only = last_event[0], last_event[1] + current_event_only = current_event[0], current_event[1] + + repeat_rate = 10 + repeat_time = .50 + + if last_event_only == current_event_only and current_event[0] == EVENT_RELATIVE_MOTION : + #check for time diff + event_time_diff = current_event_time - last_event_time + + if skipped_event <= repeat_rate and event_time_diff <= repeat_time: + skipped_event = skipped_event + 1 + #print "skipped a repeat event : %s" %skipped_event + continue + #event occured, reset skip counter + skipped_event = 0 + + #rotate dial + if current_event[0] == EVENT_RELATIVE_MOTION: + reset_lastevent = True + single_tap = False + if current_event[1] >= 0 : + if button_pressed : + #print "dial turned Counter clockwise and button is pressed" + act_on_event("RP") + else: + #print "dial turned Counter clockwise" + act_on_event("R") + + if current_event[1] <=0 : + if button_pressed : + #print "dial turned clockwise and button is pressed" + act_on_event("LP") + else: + #print "dial turned clockwise" + act_on_event("L") + + + #button was pushed + if current_event[0] == EVENT_BUTTON_PRESS : + pm.SetLEDState(0,0,0,0,0) + if current_event[1] == 1: + button_pressed = True + pressed_time = current_event_time + tap_diff = current_event_time - last_event_time + #double tap + if tap_diff < 0.2 and last_event[0] == EVENT_BUTTON_PRESS: + act_on_event("double_tap") + single_tap = False + else: + single_tap = True + reset_lastevent = True + + #button was released + elif current_event[1] == 0: + button_pressed = False + pressed_time = 0 + pm.SetLEDState(led_brightness,0,0,0,0) + + if reset_lastevent: + last_event = current_event + + + diff --git a/abs/core/powermate_myth/powermate.py b/abs/core/powermate_myth/powermate.py new file mode 100644 index 0000000..8e40078 --- /dev/null +++ b/abs/core/powermate_myth/powermate.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python + +import select +import os +import fcntl +import struct +import exceptions + +#struct input_event { +# struct timeval time; = {long seconds, long microseconds} +# unsigned short type; +# unsigned short code; +# unsigned int value; +#}; + +input_event_struct = "@llHHi" +input_event_size = struct.calcsize(input_event_struct) + +EVENT_BUTTON_PRESS = 1 +EVENT_RELATIVE_MOTION = 2 +RELATIVE_AXES_DIAL = 7 +BUTTON_MISC = 0x100 + +def report(x): + sys.stderr.write(x + "\n") + +class PowerMate: + def __init__(self, filename = None): + self.handle = -1 + if filename: + if not self.OpenDevice(filename): + raise exceptions.RuntimeError, 'Unable to find powermate' + else: + ok = 0 + for d in range(0, 16): + if self.OpenDevice("/dev/input/event%d" % d): + ok = 1 + break + if not ok: + raise exceptions.RuntimeError, 'Unable to find powermate' + self.poll = select.poll() + self.poll.register(self.handle, select.POLLIN) + self.event_queue = [] # queue used to reduce kernel/userspace context switching + + def __del__(self): + if self.handle >= 0: + self.poll.unregister(self.handle) + os.close(self.handle) + self.handle = -1 + del self.poll + + def OpenDevice(self, filename): + try: + self.handle = os.open(filename, os.O_RDWR) + if self.handle < 0: + return 0 + name = fcntl.ioctl(self.handle, 0x80ff4506, chr(0) * 256) # read device name + name = name.replace(chr(0), '') + if name == 'Griffin PowerMate' or name == 'Griffin SoundKnob': + fcntl.fcntl(self.handle, fcntl.F_SETFL, os.O_NDELAY) + return 1 + os.close(self.handle) + self.handle = -1 + return 0 + except exceptions.OSError: + return 0 + + def WaitForEvent(self, timeout): # timeout in seconds + if len(self.event_queue) > 0: + return self.event_queue.pop(0) + if self.handle < 0: + return None + r = self.poll.poll(int(timeout*100)) + if len(r) == 0: + return None + return self.GetEvent() + + def GetEvent(self): # only call when descriptor is readable + if self.handle < 0: + return None + try: + data = os.read(self.handle, input_event_size * 32) + while data != '': + self.event_queue.append(struct.unpack(input_event_struct, data[0:input_event_size])) + data = data[input_event_size:] + return self.event_queue.pop(0) + except exceptions.OSError, e: # Errno 11: Resource temporarily unavailable + #if e.errno == 19: # device has been disconnected + # report("PowerMate disconnected! Urgent!"); + return None + + def SetLEDState(self, static_brightness, pulse_speed, pulse_table, pulse_on_sleep, pulse_on_wake): + static_brightness &= 0xff; + if pulse_speed < 0: + pulse_speed = 0 + if pulse_speed > 510: + pulse_speed = 510 + if pulse_table < 0: + pulse_table = 0 + if pulse_table > 2: + pulse_table = 2 + pulse_on_sleep = not not pulse_on_sleep # not not = convert to 0/1 + pulse_on_wake = not not pulse_on_wake + magic = static_brightness | (pulse_speed << 8) | (pulse_table << 17) | (pulse_on_sleep << 19) | (pulse_on_wake << 20) + data = struct.pack(input_event_struct, 0, 0, 0x04, 0x01, magic) + os.write(self.handle, data) -- cgit v0.12