# Twitter connector to talk to the arduino driven LEDs over serial. import sys from time import sleep import threading import serial import tweepy from config import * INTERFACES = ("/dev/ttyUSB", "/dev/ttyACM") BAUD_RATE = 9600 NO_INTERFACES = 4 HEADER_STRING = "Lightnode:" conns = {} #has all the serial connections master_keywords = [] keyword_bauble_map = {} #sets up a map between the keywords as keys to specific baubles baubles = [] class Bauble(): """ This class represents one of the baubles. It contains a reference to the serial node that controls it and the keywords associated with it """ light_period = 0 # msecs that the light should illuminate on keyword light_node = None # which node are you looking at channel = None # which PWM channel are you talking to serial_conn = None # which serial connection is this bauble connected to keywords = [] # keywords to track. debug = False # used to determine to display tweet or not on this bauble def __init__(self, light_period=500, light_node=0, channel=0, keywords=[], connections = {}, debug = False): """ Set up the bauble with the various params set """ # set up the object self.light_period = light_period self.light_node = light_node self.channel = channel self.keywords = keywords self.debug = debug for serialport in connections: if (connections[serialport]["light_node"] == self.light_node): self.serial_conn = connections[serialport]["ser"] def __str__(self): return "%s on (LN)%s:(C)%s" % (self.keywords, self.light_node, self.channel) def pulse(self): """ This function pulses the bauble according to the settings """ if self.serial_conn is not None: self.serial_conn.write("%s %s\n" % (self.channel, self.light_period) ) else: print "Tried to write to an invalid connection %s" % self.keywords class XmasListener(tweepy.StreamListener): """ Class set up to listen to and handle the messages coming from twitter and hitting the Serial connections associated with it """ keyword_mapping = None def on_status(self, status): if status.retweeted: # get rid of any retweets return if (status.text[0:1] in ('@', 'R', '#')): #gets rid of quoted RTs, @replies and # spam return # clean status up to work with status.text = status.text.lower().strip('#:.,?!"') # now we work out where to route it. for keyword in self.keyword_mapping: if keyword in status.text: # we've found one bauble = self.keyword_mapping[keyword] if bauble.debug: print "-----------GOT A TWEET --------------" print status.text print "Pulsing bauble %s for KW: %s" % (bauble, keyword) bauble.pulse() def on_error(self, status_code): print >> sys.stderr, 'Encountered error on %s with status code:' % (self.bauble, status_code) return True # Don't kill the stream def on_timeout(self): print >> sys.stderr, 'Timeout...' return True # Don't kill the stream def on_limit(self, track): print "-------RATE LIMITED --------" print "this filter %s just got rate limited" % track sleep(5) return True # don't kill the stream. def initialise_interfaces(): """ Iterates through the specified interfaces and then opens all the s.ports and tries to ignore anything a bit wacky. """ for interface in INTERFACES: for port in range(0, NO_INTERFACES): serialport = "%s%s" % (interface, port) try: ser = serial.Serial(serialport, BAUD_RATE, timeout=5) print "Connected to interface %s" % serialport except serial.SerialException as e: print e continue; # test the serial connection and make sure you get what we want. ser.flush() # get the data now. header = ser.readline() if header in (None, ""): print "Connection timed out. Let's try to get a name" ser.write("n\n") header = ser.readline() print "We got this back: %s" % header lightnode = None if HEADER_STRING in header: lightnode = header.split(':')[1] try: lightnode = int(lightnode) except ValueError: print "Total garbage off %s" % serialport ser.close() continue else: print "This is not the interface we're looking for: %s" % serialport ser.close() continue conns[serialport] = { "ser": ser, "light_node": lightnode, } if __name__ == '__main__': print "Connecting serial devices" initialise_interfaces() print "Connecting to twitter" auth = tweepy.OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret) api = tweepy.API(auth) print "Setting up the baubles" for keyword in keyword_set: #print "Keyword: %s Set: %s" % (keyword, keyword_set[keyword]) item = keyword_set[keyword] bauble = Bauble( light_period = item["light_period"], light_node = item["light_node"], channel = item["channel"], keywords = item["keywords"], debug = item["debug"], connections = conns, ) # this is used to map the keywords themselves to the baubles. This # makes it much faster to look up the keywords when we process the # tweets on the listener - we also use a keyword set so we can look # up a bit more broadly. for key in item["keywords"]: keyword_bauble_map[key] = bauble baubles.append(bauble) # set up the master keywords list so we have a big list to push # to the tweepy streaming tracker if master_keywords is None: master_keywords = item["keywords"][:] ## add all items in a new list else: master_keywords.extend(item["keywords"]) print "Authed, tracking keywords" # now we call the listener with all the baubles/ xmas_listener = XmasListener() xmas_listener.keyword_mapping = keyword_bauble_map sapi = tweepy.streaming.Stream(auth, xmas_listener) run_app = True while (run_app): try: sapi.filter(track=master_keywords) except KeyboardInterrupt as kbd: print "Keyboard interrupted - shutdown" print "Disconnect Twitter" sapi.disconnect() print "Closing the serial connections" for serialport in conns: print "Closing: %s" % serialport conns[serialport]["ser"].close() run_app = False except Exception as e: print "There was a problem %s" % e sapi.disconnect()