Files
pylibibuddy/pybuddylib.py
2010-09-03 14:22:49 -04:00

273 lines
8.0 KiB
Python

#!/usr/bin/env python
#
# pybuddylib.py: simple library to control iBuddy USB device
# by ewall <e@ewall.org>, Sept 2010
#
# borrows code from http://code.google.com/p/pybuddy
# by Jose.Carlos.Luna@gmail.com and luis.peralta@gmail.com
# who got most of the code from http://cuntography.com/blog/?p=17
# which is based on http://scott.weston.id.au/software/pymissile/
#
# TODO: - Why does libusb require sudo/root on Linux?
# - Why does libusb-win32 error on Windoze?
#
import logging
from time import sleep
from sys import exit
import usb
### Global Configuration
DEBUG = True
### Prepare Logging
log = logging.getLogger("pybuddy")
log.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler()
if DEBUG:
console_handler.setLevel(logging.DEBUG)
else:
console_handler.setLevel(logging.ERROR)
log.addHandler(console_handler)
### General USB Device Class
class UsbDevice:
def __init__(self, vendor_id, product_id, skip):
busses = usb.busses()
self.handle = None
count = 0
for bus in busses:
devices = bus.devices
for dev in devices:
if dev.idVendor==vendor_id and dev.idProduct==product_id:
if count==skip:
log.info("USB device found (vend: %s, prod: %s)." % (dev.idVendor, dev.idProduct))
self.dev = dev
self.conf = self.dev.configurations[0]
self.intf = self.conf.interfaces[0][0]
self.endpoints = []
for endpoint in self.intf.endpoints:
self.endpoints.append(endpoint)
log.info("USB endpoint found.")
return
else:
count=count+1
raise NoBuddyException()
def open(self):
# TODO: should the HID driver detachment be optional?
self.handle = self.dev.open()
# we need to detach HID interface
try:
self.handle.detachKernelDriver(0)
self.handle.detachKernelDriver(1)
except:
pass
self.handle.setConfiguration(self.conf)
self.handle.claimInterface(self.intf)
self.handle.setAltInterface(self.intf)
### iBuddy Device Class
class iBuddyDevice:
USB_VENDOR = 0x1130
USB_PRODUCT = int(0x0001)
BATTERY = 0
SETUP = (0x22, 0x09, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00)
MESS = (0x55, 0x53, 0x42, 0x43, 0x00, 0x40, 0x02)
WAITTIME = 0.1
LEFT = 0
RIGHT = 1
UP = 0
DOWN = 1
OFF = 0
ON = 1
BLUE = (0,0,1)
GREEN = (0,1,0)
LTBLUE = (0,1,1)
PURPLE = (1,0,1)
RED = (1,0,0)
WHITE = (1,1,1)
YELLOW = (1,1,0)
CLEAR = 0xFF
command = CLEAR
# the following items deal with setup and control
def __init__(self):
try:
self.dev=UsbDevice(self.USB_VENDOR, self.USB_PRODUCT, self.BATTERY)
self.dev.open()
self.dev.handle.reset()
self.resetCmd()
self.doCmd()
except NoBuddyException, e:
raise NoBuddyException()
def __send(self, inp):
""" send your command to the device """
try:
self.dev.handle.controlMsg(0x21, 0x09, self.SETUP, 0x02, 0x01)
self.dev.handle.controlMsg(0x21, 0x09, self.MESS+(inp,), 0x02, 0x01)
except usb.USBError:
if DEBUG:
self.__init__()
#raise
else:
self.__init__()
def doCmd(self, seconds=WAITTIME):
""" send the command specified by the current command """
self.__send(self.command)
sleep(seconds)
# the following items operate on the command to be sent next
def resetCmd(self):
""" reset command to default (must pump to take effect) """
self.command = self.CLEAR
def setReverseBitValue(self,num,value):
""" commands are sent as disabled bits """
if (value==1):
temp = 0xFF - (1<<num)
self.command = self.command & temp
elif (value==0):
temp = 1 << num
self.command = self.command | temp
def getReverseBitValue(self,num):
""" what was that bit set to again? """
temp = self.command
temp = temp >> num
res = not(temp&1)
return res
def setHeadColors(self, red, green, blue):
""" colors as (red, green, blue) can be on (1) or off (0) """
self.setReverseBitValue(4,red)
self.setReverseBitValue(5,green)
self.setReverseBitValue(6,blue)
self.setReverseBitValue(2,1)
def getHeadColors (self):
""" returns color status as tuple representing (red, green, blue) as on (1) or off (0) """
return self.getReverseBitValue(4), self.getReverseBitValue(5), self.getReverseBitValue(6)
def setHeart(self, status):
""" heart-light can be on (1) or off (0) """
self.setReverseBitValue(7,status)
def getHeart(self):
""" returns heart-light status of on (1) or off (0) """
return self.getReverseBitValue(7)
def setWing(self, direction):
""" move the wings iBuddyDevice.UP (0) or iBuddyDevice.DOWN (1) """
if (direction == self.UP):
self.setReverseBitValue(3,1)
self.setReverseBitValue(2,0)
elif(direction == self.DOWN):
self.setReverseBitValue(3,0)
self.setReverseBitValue(2,1)
def getWing(self):
""" returns wing status of iBuddyDevice.UP (0) or iBuddyDevice.DOWN (1) """
return self.getReverseBitValue(2)
def setSwivel(self, direction):
""" swivel the body iBuddyDevice.LEFT (0) or iBuddyDevice.RIGHT (1) """
if (direction == self.RIGHT):
self.setReverseBitValue(1,1)
self.setReverseBitValue(0,0)
elif(direction == self.LEFT):
self.setReverseBitValue(1,0)
self.setReverseBitValue(0,1)
def getSwivel(self):
""" returns current swivel direction as iBuddyDevice.LEFT (0) or iBuddyDevice.RIGHT (1) """
return self.getReverseBitValue(1)
# the following items are macros for specific behaviors
def doReset(self, seconds=WAITTIME):
""" reset to default positions/off, run command immediately """
self.resetCmd()
self.doCmd(seconds)
def doFlap(self, times=3, seconds=0.2):
""" flap wings X times with Y seconds pause in between, run command immediately """
for i in range(times):
self.setWing(self.UP)
self.doCmd(seconds)
self.setWing(self.DOWN)
self.doCmd(seconds)
def doWiggle(self, times=3, seconds=0.2):
""" wiggle back and forth X times with Y seconds pauses, run command immediately """
for i in range(times):
self.setSwivel(self.LEFT)
self.doCmd(seconds)
self.setSwivel(self.RIGHT)
self.doCmd(seconds)
def doHeartbeat(self, times=3, seconds=0.3):
""" blink heart X times with Y seconds' pause in betwee, run command immediately """
for i in range(times):
self.setHeart(self.ON)
self.doCmd(seconds)
self.setHeart(self.OFF)
self.doCmd(seconds)
def doColorRGB(self, r, g, b, seconds=WAITTIME):
""" set head color by red/green/blue values 0 or 1, run command immediately """
self.setHeadColors(r, g, b)
self.doCmd(seconds)
def doColorName(self, rgb, seconds=WAITTIME):
""" set head color with color name tuples, run command immediately """
self.setHeadColors(*rgb)
self.doCmd(seconds)
## Custom Exceptions
class NoBuddyException(usb.USBError):
""" indicates errors communicating with iBuddy USB device """
#def __init__(self, *args):
# Exception._ _init_ _(self, *args)
# self.wrapped_exc = sys.exc_info()
pass
### Main Program
if __name__ == '__main__':
# find iBuddy device
log.info("Starting search...")
try:
buddy = iBuddyDevice()
except NoBuddyException, e:
log.exception("No iBuddy device found!")
exit(1)
# demo command macros
buddy.doColorName(iBuddyDevice.PURPLE, 0.5)
buddy.doColorName(iBuddyDevice.BLUE, 0.5)
buddy.doColorName(iBuddyDevice.LTBLUE, 0.5)
buddy.doColorName(iBuddyDevice.YELLOW, 0.5)
buddy.doColorName(iBuddyDevice.GREEN, 0.5)
buddy.doColorName(iBuddyDevice.RED, 0.5)
buddy.doColorName(iBuddyDevice.WHITE, 0.5)
buddy.doFlap()
sleep(1)
buddy.doWiggle()
sleep(1)
buddy.doHeartbeat()
sleep(1)
buddy.doReset()