def go(self, argv): self.download_dir = ".airframe" self.get_options(argv) if self.local_dir: photo_filenames = self.local_dir_mode() elif self.facebook: photo_filenames = self.facebook_mode() else: photo_filenames = self.flickr_mode() if self.resize: self.resize_pictures(photo_filenames) self.flashair = FlashAir(self.flashair_ip) self.flashair.sync_files_on_card_to_list(photo_filenames, self.force_upload)
def connectionMade(self): logger.info('Connected to serial port') self.accumulated = '' self.transport.setDTR(False) Sleep(1) self.transport.setDTR(True) self.waiton = dict() self.waiton = {"{State:Ready}" : self.initProjector1} self.sdCard = FlashAir() self.sdCard.disconnect() self.currentSlowSeq = None self.slowSeqInterval = 2
class SerialProtocol(LineOnlyReceiver): def connectionMade(self): logger.info('Connected to serial port') self.accumulated = '' self.transport.setDTR(False) Sleep(1) self.transport.setDTR(True) self.waiton = dict() self.waiton = {"{State:Ready}" : self.initProjector1} self.sdCard = FlashAir() self.sdCard.disconnect() self.currentSlowSeq = None self.slowSeqInterval = 2 def connectionLost(self, reason): logger.info('Disconnected from serial port %s', reason) reactor.callFromThread(reactor.stop) def lineReceived(self, line): logger.debug('lineReceived %s, %d waiton entries' % (line, len(self.waiton))) self.accumulated += line for kk, vv in self.waiton.iteritems(): if -1 != self.accumulated.find(kk): logger.debug("Triggered on %s", kk) self.accumulated = '' reactor.callLater(0, vv) def send(self, data): logger.debug("Sending: %s" % data) self.transport.write(data) def generator(self, sequence): ii = 0; if sequence is None: pdb.set_trace() try: for ll in sequence: ii += 1 if None != ll.__doc__: logger.debug('Generator executing %u:%s' % (ii, ll.__doc__)) else: logger.debug('Generator executing %u:%s' % (ii, ll.__name__)) ll() yield except TypeError as tt: logger.error(tt.message) except RuntimeError as ee: logger.error("%s at generator step %u" % (ee.message, ii)) raise def initProjector1(self): self.waiton.pop('{State:Ready}', None) logger.debug("initProjector1") self.waiton['{pt:'] = self.initProjector2 sequence = [ lambda: self.send(' '), lambda: self.send('vt') # autotension ] self.slowSequence(sequence) def initProjector2(self): self.waiton.pop('{pt:}', None) logger.debug("initProjector2") def noMoreFrames(self): global exitcode exitcode = EX_TIMEOUT; self.transport.loseConnection(); def setWaiton(self): self.waiton = dict() #self.waiton['{Remaining:0}'] = lambda: self.getFileInfo(last = False) self.waiton['{Remaining:0}'] = lambda: noMoreFrames(self) self.waiton['Opto int timeout'] = self.stopProjector sequence = [ lambda: self.setSSInterval(2), lambda: self.send('c'), lambda: self.send({'8mm': 'd', 'super8': 'D'}[options.mode]), lambda: self.send("%so" % options.numframes), lambda: setWaiton(self), lambda: self.send('S'), ] self.slowSequence(sequence) def setSSInterval(self, interval): logger.debug("SlowSeq Interval %d" % interval) self.slowSeqInterval = interval def slowSequence(self, sequence = None): logger.debug("slowSequence, sequence is %s" % sequence) if sequence is not None: self.currentSlowSeq = sequence logger.debug("self.currentSlowSeq %s" % self.currentSlowSeq) self.ssIter = self.generator(sequence) if False == hasattr(self, 'ssIter') or self.ssIter is None: return try: next(self.ssIter) reactor.callLater(self.slowSeqInterval, self.slowSequence) except StopIteration: del self.ssIter del self.currentSlowSeq except RuntimeError as ee: del self.ssIter if 'restart' == ee.message and self.currentSlowSeq is not None: logger.info("Restarting sequence") # self.ssITer = self.generator(self.currentSlowSeq) reactor.callLater(2, lambda: self.slowSequence(self.currentSlowSeq)) else: pdb.set_trace() def stopProjector(self): reactor.callLater(0, lambda: self.getFileInfo(last = True)) # self.waiton = dict() # logger.debug("Projector stopped") # sequence = [ # lambda: self.send('c'), # lambda: self.send('s'), # lambda: reactor.callLater(0, lambda: self.getFileInfo(last = True)) # ] # global exitCode # exitCode = EX_TIMEOUT # self.slowSequence(sequence) # def findSDFiles(self): # self.urls = self.sdCard.getFiles() # if False == self.urls: # logger.error("getFiles failed, aborting") # raise RuntimeError("getFiles") # # if self.urls is None: # logger.info("No ready files yet") # raise RuntimeError("No ready files") def getFileInfo(self, last = False): logger.debug("getFileInfo") def testLast(self, last): if False == last: reactor.callLater(1, self.initProjector1) else: global exitCode exitCode = EX_TIMEOUT self.transport.loseConnection() self.slowSequence([ lambda: self.setSSInterval(2), lambda: self.send('C'), lambda: self.send('c'), lambda: self.setSSInterval(5), self.sdCard.turnAirportOff, self.sdCard.turnAirportOn, lambda: self.setSSInterval(10), lambda: self.sdCard.waitingForAP(FlashAir.SSID), self.sdCard.connectToCard, self.sdCard.processFiles, lambda: self.setSSInterval(2), self.sdCard.clearCard, self.sdCard.disconnect, lambda: testLast(self, last) ])
class AirFrame(object): def __init__(self): pass def _parse_csv_list(self, s): try: assert isinstance(s,str) s = s.strip() value_list = s.split(',') except: raise argparse.ArgumentTypeError("Could not parse tag list") return value_list def get_options(self, argv): """ Parse the command-line options and set the following object properties: I really need to convert this to use docopt! :param argv: usually just sys.argv[1:] :returns: Nothing :ivar debug: Enable logging debug statements :ivar verbose: Enable verbose logging """ p = argparse.ArgumentParser( description = "Push pictures from Flickr, Facebook or local files to a Toshiba FlashAir automatically", epilog = "AirFrame version %s (Copyright 2014 Virantha Ekanayake)" % __version__, ) p.add_argument('-b', '--facebook', action='store_true', default=False, help='Upload pictures from Flickr to Flashair') p.add_argument('-r', '--flickr', action='store_true', default=True, help='Upload pictures from Facebook to Flashair (default)') p.add_argument('-l', '--local-dir', type=str, help='Upload all .jpg files from this directory instead of Flickr or Facebook') p.add_argument('-s', '--resize', type=str, help='Resize all images to fit in this box (e.g. 1024x768) before uploading them to Flashair') p.add_argument('-d', '--debug', action='store_true', default=False, dest='debug', help='Turn on debugging') p.add_argument('-v', '--verbose', action='store_true', default=False, dest='verbose', help='Turn on verbose mode') p.add_argument('-f', '--force', action='store_true', default=False, help='Force upload of all pictures to Flashair (instead of only new pictures)') p.add_argument('-n', '--number', type=int, default=100, dest='number', help='Max number of photos to sync') p.add_argument('-t', '--tags', type=self._parse_csv_list, default=[], dest='tags', help='List of Flickr tags to match') p.add_argument('flashair_ip', type=str, help='The ip/hostname of your FlashAir card') args = p.parse_args(argv) self.debug = args.debug self.verbose = args.verbose self.photo_count = args.number self.photo_tags = args.tags self.force_upload = args.force self.flashair_ip = args.flashair_ip self.local_dir = args.local_dir self.resize = args.resize self.facebook = args.facebook self.flickr = args.flickr if self.debug: logging.basicConfig(level=logging.DEBUG, format='%(message)s') if self.verbose: logging.basicConfig(level=logging.INFO, format='%(message)s') def flickr_mode(self): # Connect to Flickr logging.debug("list of tags: %s" % self.photo_tags) self.flickr = Flickr() if len(self.photo_tags) > 0: photo_filenames = self.flickr.get_tagged(self.photo_tags, self.photo_count, download_dir=self.download_dir) else: photo_filenames = self.flickr.get_recent(self.photo_count,download_dir=self.download_dir) return photo_filenames def facebook_mode(self): # Connect to Facebook logging.debug("list of tags: %s" % self.photo_tags) self.facebookphotos = FacebookPhotos() # if len(self.photo_tags) > 0: # photo_filenames = self.flickr.get_tagged(self.photo_tags, self.photo_count, download_dir=self.download_dir) # else: # photo_filenames = self.flickr.get_recent(self.photo_count,download_dir=self.download_dir) photo_filenames = self.facebookphotos.get_recent(self.photo_count,download_dir=self.download_dir) return photo_filenames def local_dir_mode(self): # Copy all the files in the named directory to the cache (download_dir). # completely replaces both the local cached files and the ones on the # wifi sd card pattern = '/*.[jJ][pP][gG]' match = self.local_dir + pattern if os.path.isdir(self.download_dir): logging.debug("removing existing cache dir: %s", self.download_dir) shutil.rmtree(self.download_dir) os.mkdir(self.download_dir) logging.debug("caching files from: %s" % match) photo_filenames = glob.glob(match) for filename in photo_filenames: logging.debug("copy %s to %s" % (filename, self.download_dir)) shutil.copy(filename, self.download_dir) return photo_filenames def resize_pictures(self, photo_filenames): print 'Resizing images...' size = self.resize.split('x') size = [int(x) for x in tuple(size)] for infile in photo_filenames: im = Image.open(infile) if im.size[0] > size[0] or im.size[1] > size[1]: im.thumbnail(size, Image.ANTIALIAS) im.save(infile) im.close() def go(self, argv): self.download_dir = ".airframe" self.get_options(argv) if self.local_dir: photo_filenames = self.local_dir_mode() elif self.facebook: photo_filenames = self.facebook_mode() else: photo_filenames = self.flickr_mode() if self.resize: self.resize_pictures(photo_filenames) self.flashair = FlashAir(self.flashair_ip) self.flashair.sync_files_on_card_to_list(photo_filenames, self.force_upload)