Exemple #1
0
class Bartender(MenuDelegate):
    def __init__(self):

        # initialize drink attributes
        self.drink_attributes = {}

        # initialize main menu
        self.mainMenu = None

        # allow button press
        self.running = False

        # initialize the LCD
        self.lcdLayer = LCD.LCDLayer()

        self.btn1Pin = LEFT_BTN_PIN
        self.btn2Pin = RIGHT_BTN_PIN
        self.btn3Pin = UP_BTN_PIN
        self.btn4Pin = DOWN_BTN_PIN

        # configure interrups for buttons
        GPIO.setup(self.btn1Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.setup(self.btn2Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.setup(self.btn3Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.setup(self.btn4Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

        # load the pump configuration from file
        self.pump_configuration = Bartender.readPumpConfiguration()
        for pump in self.pump_configuration.keys():
            GPIO.setup(
                self.pump_configuration[pump]["pin"],
                GPIO.OUT,
                initial=GPIO.HIGH
                )

        print "Done initializing"

    @staticmethod
    def readPumpConfiguration():
        return json.load(open('pump_config.json'))

    @staticmethod
    def writePumpConfiguration(configuration):
        with open("pump_config.json", "w") as jsonFile:
            json.dump(configuration, jsonFile)

    def startInterrupts(self):
        GPIO.add_event_detect(
            self.btn1Pin,
            GPIO.FALLING,
            callback=self.left_btn,
            bouncetime=LEFT_PIN_BOUNCE
            )
        GPIO.add_event_detect(
            self.btn2Pin,
            GPIO.FALLING,
            callback=self.right_btn,
            bouncetime=RIGHT_PIN_BOUNCE
            )
        GPIO.add_event_detect(
            self.btn3Pin,
            GPIO.FALLING,
            callback=self.up_btn,
            bouncetime=UP_PIN_BOUNCE
            )
        GPIO.add_event_detect(
            self.btn4Pin,
            GPIO.FALLING,
            callback=self.down_btn,
            bouncetime=DOWN_PIN_BOUNCE
            )

    def stopInterrupts(self):
        GPIO.remove_event_detect(self.btn1Pin)
        GPIO.remove_event_detect(self.btn2Pin)
        GPIO.remove_event_detect(self.btn3Pin)
        GPIO.remove_event_detect(self.btn4Pin)

    def buildMenu(self, drink_list, drink_options):
        # create a new main menu
        m = Menu("Main Menu")

        # create a drinks menu
        drinks_menu = Menu("Drinks")

        # add drink options
        drink_opts = []
        for o in drink_options:
            drink_opts.append(
                MenuItem(
                    'drink',
                    o["name"],
                    {
                        "ingredients": {o["value"]: 1},
                        "strong": o["alcohol"],
                        "ice": 0
                    }
                )
            )
        for d in drink_list:
            try:
                drink_opts.append(
                    MenuItem(
                        'drink',
                        d["name"],
                        {
                            "ingredients": d["ingredients"],
                            "strong": d["strong"],
                            "add": d["add"],
                            "ice": d["ice"]
                        }
                    )
                )
            except KeyError:
                drink_opts.append(
                    MenuItem(
                        'drink',
                        d["name"],
                        {
                            "ingredients": d["ingredients"],
                            "strong": d["strong"],
                            "ice": d["ice"]
                        }
                    )
                )
        configuration_menu = Menu("Configure")

        # add pump configuration options
        pump_opts = []
        for p in sorted(self.pump_configuration.keys()):
            config = Menu(self.pump_configuration[p]["name"])
            # add fluid options for each pump
            for opt in drink_options:
                config.addOption(
                    MenuItem(
                        'pump_selection',
                        opt["name"],
                        {"key": p, "value": opt["value"], "name": opt["name"]}
                        )
                    )
            config.setParent(configuration_menu)
            pump_opts.append(config)

        # add pump menus to the configuration menu
        configuration_menu.addOptions(pump_opts)

        # adds an option that cleans all pumps to the configuration menu
        configuration_menu.addOption(MenuItem('clean', 'Clean'))
        configuration_menu.setParent(m)

        drinks_menu.addOptions(drink_opts)
        drinks_menu.setParent(m)
        self.filterDrinks(drinks_menu)

        m.addOption(drinks_menu)
        m.addOption(configuration_menu)

        # create a menu context
        self.menuContext = MenuContext(m, self, self.lcdLayer)

    def setMainMenu(self, menu):
        self.mainMenu = menu

    def filterDrinks(self, menu):
        """
        Removes any drinks that can't be handled by the pump configuration
        """
        for i in menu.options:
            if (i.type == "drink"):
                i.visible = False
                ingredients = i.attributes["ingredients"]
                presentIng = 0
                for ing in ingredients.keys():
                    for p in self.pump_configuration.keys():
                        if (ing == self.pump_configuration[p]["value"]):
                            presentIng += 1
                if (presentIng == len(ingredients.keys())):
                    i.visible = True
            elif (i.type == "menu"):
                self.filterDrinks(i)

    def menuItemClicked(self, menuItem):
        if (menuItem.type == "drink"):
            self.makeDrink(menuItem)
            return True
        elif(menuItem.type == "pump_selection"):
            value = menuItem.attributes["value"]
            self.pump_configuration[
                menuItem.attributes["key"]]["value"] = value
            Bartender.writePumpConfiguration(self.pump_configuration)
            self.menuContext.retreat()
            return True
        elif(menuItem.type == "pour"):
            self.pourDrink(menuItem.attributes)
            return True
        elif(menuItem.type == "clean"):
            self.clean()
            return True
        elif(menuItem.type == "menu_link"):
            if (menuItem.child is not None):
                try:
                    self.drink_attributes.update(menuItem.attributes)
                    print(self.drink_attributes)
                except TypeError:
                    print("menu item has no attributes")
                self.menuContext.currentMenu = menuItem.child
                self.menuContext.showMenu()
                return True
        return False

    def clean(self):
        waitTime = 20
        pumpThreads = []

        # cancel any button presses while the drink is being made
        # self.stopInterrupts()
        self.running = True

        for pump in self.pump_configuration.keys():
            pump_t = threading.Thread(
                target=self.pour,
                args=(self.pump_configuration[pump]["pin"], waitTime)
                )
            pumpThreads.append(pump_t)

        # start the pump threads
        for thread in pumpThreads:
            thread.start()

        # start the progress bar
        self.progressBar(waitTime)

        # wait for threads to finish
        for thread in pumpThreads:
            thread.join()

        # show the main menu
        self.menuContext.showMenu()

        # sleep for a couple seconds to make sure the interrupts
        # don't get triggered
        time.sleep(2)

        # reenable interrupts
        # self.startInterrupts()
        self.running = False

    def pour(self, pin, waitTime):
        GPIO.output(pin, GPIO.LOW)
        print(
            "Starting pour on pin " + str(pin) + " for " + str(waitTime)
            + " seconds")
        time.sleep(waitTime)
        GPIO.output(pin, GPIO.HIGH)
        print("Stopping pour on pin " + str(pin))

    def progressBar(self, waitTime):
        self.lcdLayer.lcd_blank()
        self.lcdLayer.lcd_string("Pouring", LCD.LCD_LINE_1, LCD.CENTERED)
        self.lcdLayer.lcd_byte(
            LCD.LCD_LINE_3
            | LCD.LCD_SET_DDRAM, LCD.LCD_RS_CMD)
        block = 0xff
        for i in range(20):
            self.lcdLayer.lcd_byte(block, LCD.LCD_RS_CHR)
            time.sleep(waitTime / 20)
        return True

    def makeDrink(self, drink):
        # Check for strength
        if (drink.attributes["strong"] == 1):
            strengthCheck = Menu("This drink is strong")
            strengthCheck.addOption(MenuLink(
                "Continue", strengthCheck, None, {"strength": "normal"}))
            strengthCheck.setParent(self.menuContext.currentMenu)
            self.menuContext.setMenu(strengthCheck)
        else:
            # Select strength
            strengthSelect = Menu("Select strength")
            strengthSelect.addOption(MenuLink(
                "Weak", strengthSelect, None, {"strength": "weak"}))
            strengthSelect.addOption(MenuLink(
                "Normal", strengthSelect, None, {"strength": "normal"}))
            strengthSelect.addOption(MenuLink(
                "Strong", strengthSelect, None, {"strength": "strong"}))
            strengthSelect.setParent(self.menuContext.currentMenu)
            self.menuContext.setMenu(strengthSelect)
        # Select size
        # create a size menu and point it back to strengthCheck or
        # strengthSelect
        sizeSelect = Menu("Select size")
        sizeSelect.setParent(self.menuContext.currentMenu)
        for links in self.menuContext.currentMenu.options:
            if links.type == "menu_link":
                links.setChild(sizeSelect)

        # add size options menus to strengthCheck and strengthSelect
        sizeSelect.addOption(MenuLink(
            "Shot", sizeSelect, None, {"size": "shot"}))
        sizeSelect.addOption(MenuLink(
            "4 oz", sizeSelect, None, {"size": "4 oz"}))
        sizeSelect.addOption(MenuLink(
            "6 oz", sizeSelect, None, {"size": "6 oz"}))
        sizeSelect.addOption(MenuLink(
            "8 oz", sizeSelect, None, {"size": "8 oz"}))
        sizeSelect.addOption(MenuLink(
            "10 oz", sizeSelect, None, {"size": "10 oz"}))
        sizeSelect.addOption(MenuLink(
            "12 oz", sizeSelect, None, {"size": "12 oz"}))

        # pour menu
        pour = Menu("Ready to pour?")
        pour.addOption(MenuItem("pour", "Continue", drink.attributes))

        # add ice
        if (drink.attributes["ice"] == 1):
            addIce = Menu("Add Ice")
            addIce.setParent(sizeSelect)
            pour.setParent(addIce)
            addIce.addOption(MenuLink(
                "Continue", addIce, pour))
            for option in sizeSelect.options:
                if option.type == "menu_link":
                    option.setChild(addIce)
        else:
            pour.setParent(sizeSelect)
            for option in sizeSelect.options:
                if option.type == "menu_link":
                    option.setChild(pour)

        # finally show the menu structure
        self.menuContext.showMenu()

    def pourDrink(self, drinkAttributes):
        self.running = True
        ingredients = drinkAttributes["ingredients"].copy()
        print("Ingredients: " + str(ingredients.keys()))
        size = self.drink_attributes["size"].split()[0]
        if size == "shot":
            size = 1.25
        else:
            size = float(size)
        print("Size = " + str(size) + " oz")
        strength = self.drink_attributes["strength"]
        print("Strength: " + str(strength))
        alcModifier = 1.0

        if (strength == "strong"):
            alcModifier = 1.3
        elif (strength == "weak"):
            alcModifier = 0.7

        # strength calculations
        totalIngredients = 0.0
        for ing in ingredients:
            totalIngredients += ingredients[ing]
            for opts in drink_options:
                if (ing == opts["value"]
                        and opts["alcohol"] == 1):
                    ingredients.update(
                        {ing: ingredients[ing] * 1.00 * alcModifier})

        print("Total parts: " + str(totalIngredients))

        # size calculations
        for ing in ingredients:
            ingredients.update(
                {ing: ingredients[ing] * size / totalIngredients})
            print(str(ingredients[ing]) + " oz of " + str(ing))

        maxTime = 0.00
        pumpThreads = []

        for ing in ingredients.keys():
            print("Looking for pump for ingredient: " + str(ing))
            found = False
            for pump in self.pump_configuration.keys():
                print("Checking pump " + str(pump))
                print(
                    "This pump is for: "
                    + str(self.pump_configuration[pump]["value"]))
                if ing == self.pump_configuration[pump]["value"]:
                    waitTime = (ingredients[ing] * 1.00) / FLOW_RATE
                    # oz/(oz/sec)=sec
                    if (waitTime > maxTime):
                        maxTime = waitTime
                    pump_t = threading.Thread(
                        target=self.pour,
                        args=(self.pump_configuration[pump]["pin"], waitTime)
                        )
                    print(
                        "Found pump for " + str(ing)
                        + ". Setting up pour for " + str(waitTime) + " sec")
                    pumpThreads.append(pump_t)
                    found = True
            if (not found):
                print("Could not find pump for ingredient: " + str(ing))

        print("Total pour time: " + str(maxTime))

        # start the pump threads
        for thread in pumpThreads:
            thread.start()

        # start the progress bar
        self.progressBar(maxTime)

        # wait for threads to finish
        for thread in pumpThreads:
            thread.join()

        # sleep for a couple seconds to make sure the interrupts
        # don't get triggered
        time.sleep(2)

        # reenable interrupts
        # self.startInterrupts()
        self.running = False

        # check for additions
        try:
            addMenu = Menu("Just add " + self.drink_attributes["add"])
            addMenu.addOption(MenuLink("Done", None, self.mainMenu))
            addMenu.setParent(self.mainMenu)
            self.menuContext.setMenu(addMenu)
        except KeyError:
            self.menuContext.setMenu(self.mainMenu)

        self.drink_attributes = {}
        self.menuContext.showMenu()

    def down_btn(self, ctx):
        if not self.running:
            self.menuContext.scroll_down()

    def up_btn(self, ctx):
        if not self.running:
            self.menuContext.scroll_up()

    def left_btn(self, ctx):
        if not self.running:
            self.menuContext.retreat()

    def right_btn(self, ctx):
        if not self.running:
            self.menuContext.select()

    def run(self):
        self.menuContext.showMenu()
        self.startInterrupts()

        # main loop
        try:
            while True:
                time.sleep(0.1)

        except KeyboardInterrupt:
            self.lcdLayer.lcd_cleanup()       # clean up GPIO on CTRL+C exit
        self.lcdLayer.lcd_cleanup()           # clean up GPIO on normal exit

        traceback.print_exc()
Exemple #2
0
class Bartender(MenuDelegate):
    def __init__(self):
        self.running = False

        # set the oled screen height
        self.screen_width = SCREEN_WIDTH
        self.screen_height = SCREEN_HEIGHT

        self.make_drink = MAKE_DRINK

        self.btn1Pin = LEFT_BTN_PIN
        self.btn2Pin = RIGHT_BTN_PIN

        self.clk = CLK_PIN
        self.dt = DT_PIN

        # configure interrupts for buttons
        #GPIO.setup(self.btn1Pin, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.setup(self.btn2Pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

        # configure interrupts for encoder
        GPIO.setup(self.clk, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        GPIO.setup(self.dt, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

        #Pi config
        RST = 21
        DC = 20
        SPI_PORT = 0
        SPI_DEVICE = 0

        # configure screen
        self.disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST)
        self.disp.begin()
        self.disp.clear()
        self.disp.display()

        #draw
        self.image = Image.new('1', (SCREEN_WIDTH, SCREEN_HEIGHT))
        self.draw = ImageDraw.Draw(self.image)
        self.draw.rectangle((0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
                            outline=0,
                            fill=0)
        self.font = ImageFont.load_default()

        # Very important... This lets py-gaugette 'know' what pins to use in order to reset the display
        # Change rows & cols values depending on your display dimensions.

        # load the pump configuration from file
        self.pump_configuration = Bartender.readJson(
            '/home/pi/Documents/PiDrink/static/json/pump_config.json')
        for pump in self.pump_configuration.keys():
            GPIO.setup(self.pump_configuration[pump]["pin"],
                       GPIO.OUT,
                       initial=GPIO.HIGH)

        # load the current drink list
        self.drink_list = Bartender.readJson(
            '/home/pi/Documents/PiDrink/static/json/drink_list.json')
        self.drink_list = self.drink_list["drink_list"]

        # load the current drink options
        self.drink_options = Bartender.readJson(
            '/home/pi/Documents/PiDrink/static/json/drink_options.json')
        self.drink_options = self.drink_options["drink_options"]

        # setup pixels:
        self.numpixels = NUMBER_NEOPIXELS  # Number of LEDs in strip

        # Here's how to control the strip from any two GPIO pins:
        datapin = NEOPIXEL_DATA_PIN
        clockpin = NEOPIXEL_CLOCK_PIN
        self.strip = Adafruit_DotStar(self.numpixels, datapin, clockpin)
        self.strip.begin()  # Initialize pins for output
        self.strip.setBrightness(
            NEOPIXEL_BRIGHTNESS)  # Limit brightness to ~1/4 duty cycle

        # turn everything off
        for i in range(0, self.numpixels):
            self.strip.setPixelColor(i, 0)
        self.strip.show()

        print('Done initializing')

    # def check_email(self):
    # 	status, email_ids = self.imap_server.search(None, '(UNSEEN)')
    # 	if email_ids == ['']:
    # 		mail_list = []
    # 	else:
    # 		mail_list = self.get_subjects(email_ids) #FYI when calling the get_subjects function, the email is marked as 'read'
    # 	self.imap_server.close()
    # 	return mail_list

    # def get_subjects(self, email_ids):
    # 	subjects_list = []
    # 	for e_id in email_ids[0].split():
    # 		resp, data = self.imap_server.fetch(e_id, '(RFC822)')
    # 		perf = HeaderParser().parsestr(data[0][1])
    # 		subjects_list.append(perf['Subject'])

    # 	return subjects_list

    def voice_command(self, mail_list):
        for mail in mail_list:
            found = False
            while (found == False):
                currmen = self.menuContext.currentMenu.getSelection()
                if (currmen.name == mail):
                    self.makeDrink(currmen.name,
                                   currmen.attributes["ingredients"])
                    self.make_drink = False
                    found = True
                else:
                    self.menuContext.currentMenu.nextSelection()
            found = False

    @staticmethod
    def readJson(file):
        return json.load(open(file))

    @staticmethod
    def writePumpConfiguration(configuration):
        with open("/home/pi/Documents/PiDrink/static/json/pump_config.json",
                  "w") as jsonFile:
            json.dump(configuration, jsonFile)

    def startInterrupts(self):
        #GPIO.add_event_detect(self.btn1Pin, GPIO.FALLING, callback=self.left_btn, bouncetime=LEFT_PIN_BOUNCE)
        GPIO.add_event_detect(self.btn2Pin,
                              GPIO.FALLING,
                              callback=self.right_btn,
                              bouncetime=RIGHT_PIN_BOUNCE)
        GPIO.add_event_detect(self.clk,
                              GPIO.BOTH,
                              callback=self.rotary_on_change,
                              bouncetime=1000)

    def stopInterrupts(self):
        #GPIO.remove_event_detect(self.btn1Pin)
        GPIO.remove_event_detect(self.btn2Pin)
        GPIO.remove_event_detect(self.clk)

    def buildMenu(self):
        # create a new main menu
        m = Menu("Main Menu")

        # add drink options
        drink_opts = []
        for d in self.drink_list:
            drink_opts.append(
                MenuItem('drink', d["name"],
                         {"ingredients": d["ingredients"]}))

        configuration_menu = Menu("Configure")

        # add pump configuration options
        pump_opts = []
        for p in sorted(self.pump_configuration.keys()):
            config = Menu(self.pump_configuration[p]["name"])
            # add fluid options for each pump
            for opt in self.drink_options:
                # star the selected option
                selected = "*" if opt["value"] == self.pump_configuration[p][
                    "value"] else ""
                config.addOption(
                    MenuItem('pump_selection', opt["name"], {
                        "key": p,
                        "value": opt["value"],
                        "name": opt["name"]
                    }))

            # add a back button so the user can return without modifying
            config.addOption(Back("Back"))
            config.setParent(configuration_menu)
            pump_opts.append(config)

        # add pump menus to the configuration menu
        configuration_menu.addOptions(pump_opts)
        # add a back button to the configuration menu
        configuration_menu.addOption(Back("Back"))
        # adds an option that cleans all pumps to the configuration menu
        configuration_menu.addOption(MenuItem('clean', 'Clean'))
        configuration_menu.setParent(m)

        m.addOptions(drink_opts)
        m.addOption(configuration_menu)
        # create a menu context
        self.menuContext = MenuContext(m, self)

    def filterDrinks(self, menu):
        """
		Removes any drinks that can't be handled by the pump configuration
		"""
        for i in menu.options:
            if (i.type == "drink"):
                i.visible = False
                ingredients = i.attributes["ingredients"]
                presentIng = 0
                for ing in ingredients.keys():
                    for p in self.pump_configuration.keys():
                        if (ing == self.pump_configuration[p]["value"]):
                            presentIng += 1
                if (presentIng == len(ingredients.keys())):
                    i.visible = True
            elif (i.type == "menu"):
                self.filterDrinks(i)

    def selectConfigurations(self, menu):
        """
		Adds a selection star to the pump configuration option
		"""
        for i in menu.options:
            if (i.type == "pump_selection"):
                key = i.attributes["key"]
                if (self.pump_configuration[key]["value"] ==
                        i.attributes["value"]):
                    i.name = "%s %s" % (i.attributes["name"], "*")
                else:
                    i.name = i.attributes["name"]
            elif (i.type == "menu"):
                self.selectConfigurations(i)

    def prepareForRender(self, menu):
        self.filterDrinks(menu)
        self.selectConfigurations(menu)
        return True

    def menuItemClicked(self, menuItem):
        if (menuItem.type == "drink"):
            if self.make_drink:
                self.makeDrink(menuItem.name,
                               menuItem.attributes["ingredients"])
                self.make_drink = False
            return True
        elif (menuItem.type == "pump_selection"):
            self.pump_configuration[menuItem.attributes["key"]][
                "value"] = menuItem.attributes["value"]
            Bartender.writePumpConfiguration(self.pump_configuration)
            return True
        elif (menuItem.type == "clean"):
            if self.make_drink:
                self.clean()
                self.make_drink = False
            return True
        return False

    def changeConfiguration(self, pumps):
        # Change configuration of every pump
        for i in range(1, 7):
            self.pump_configuration["pump_" + str(i)]["value"] = pumps[i - 1]

        Bartender.writePumpConfiguration(self.pump_configuration)

    def clean(self):
        waitTime = 30
        pumpProcesses = []

        # cancel any button presses while the drink is being made
        # self.stopInterrupts()
        self.running = True

        for pump in self.pump_configuration.keys():
            pump_p = threading.Thread(target=self.pour,
                                      args=(
                                          self.pump_configuration[pump]["pin"],
                                          waitTime,
                                      ))
            pumpProcesses.append(pump_p)

        disp_process = threading.Thread(target=self.progressBar,
                                        args=(waitTime, ))
        pumpProcesses.append(disp_process)

        # start the pump threads
        for process in pumpProcesses:
            process.start()

        # wait for threads to finish
        for process in pumpProcesses:
            process.join()

        # sleep for a couple seconds to make sure the interrupts don't get triggered
        time.sleep(2)

        # reenable interrupts
        # self.startInterrupts()
        self.running = False
        self.make_drink = True

        self.menuContext.showMenu()

    def displayMenuItem(self, menuItem):
        #Clear display
        self.draw.rectangle((0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
                            outline=0,
                            fill=0)
        self.draw.text((20, 10), menuItem.name, font=self.font, fill=255)
        self.disp.image(self.image)
        self.disp.display()

    def cycleLights(self):
        t = threading.currentThread()
        head = 0  # Index of first 'on' pixel
        tail = -10  # Index of last 'off' pixel
        color = 0xFF0000  # 'On' color (starts red)

        while getattr(t, "do_run", True):
            self.strip.setPixelColor(head, color)  # Turn on 'head' pixel
            self.strip.setPixelColor(tail, 0)  # Turn off 'tail'
            self.strip.show()  # Refresh strip
            time.sleep(1.0 / 50)  # Pause 20 milliseconds (~50 fps)

            head += 1  # Advance head position
            if (head >= self.numpixels):  # Off end of strip?
                head = 0  # Reset to start
                color >>= 8  # Red->green->blue->black
                if (color == 0): color = 0xFF0000  # If black, reset to red

            tail += 1  # Advance tail position
            if (tail >= self.numpixels): tail = 0  # Off end? Reset

    def lightsEndingSequence(self):
        # make lights green
        for i in range(0, self.numpixels):
            self.strip.setPixelColor(i, 0xFF0000)
        self.strip.show()

        time.sleep(5)

        # turn lights off
        for i in range(0, self.numpixels):
            self.strip.setPixelColor(i, 0)
        self.strip.show()

    def pour(self, pin, waitTime):
        GPIO.output(pin, GPIO.LOW)
        time.sleep(waitTime)
        GPIO.output(pin, GPIO.HIGH)

    def progressBar(self, waitTime):
        interval = waitTime / 116.3

        for x in range(1, 101):
            # Clear display
            self.draw.rectangle((0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
                                outline=0,
                                fill=0)
            #self.draw.text((55,20), str(x) + '%', font = self.font, fill=255)
            self.updateProgressBar(x, y=10)
            self.disp.image(self.image)
            self.disp.display()
            time.sleep(interval)
        # 	# self.disp.clear()
        # 	# self.disp.display()

        # self.image = Image.open('happycat_oled_32.ppm').convert('1')

        # self.disp.image(self.image)
        # self.disp.display()

    def makeDrink(self, drink, ingredients):
        if self.running:
            return

        # cancel any button presses while the drink is being made
        # self.stopInterrupts()
        print('Making a ' + drink)
        self.running = True

        # launch a thread to control lighting
        lightsThread = threading.Thread(target=self.cycleLights)
        lightsThread.start()

        # Make a list for each potential time

        maxTime = 0
        pumpProcesses = []
        for ing in ingredients.keys():
            for pump in self.pump_configuration.keys():
                if ing == self.pump_configuration[pump]["value"]:
                    waitTime = ingredients[ing] * FLOW_RATE
                    if (waitTime > maxTime):
                        maxTime = waitTime
                    pump_p = threading.Thread(
                        target=self.pour,
                        args=(self.pump_configuration[pump]["pin"], waitTime))
                    pumpProcesses.append(pump_p)

        disp_process = threading.Thread(target=self.progressBar,
                                        args=(maxTime, ))
        pumpProcesses.append(disp_process)

        # start the pump threads
        for process in pumpProcesses:
            process.start()

        # wait for threads to finish
        for process in pumpProcesses:
            process.join()

        # stop the light thread
        lightsThread.do_run = False
        lightsThread.join()

        # show the ending sequence lights
        self.lightsEndingSequence()

        # reenable interrupts
        # sleep for a couple seconds to make sure the interrupts don't get triggered
        time.sleep(2)
        # self.startInterrupts()
        self.running = False
        print('Finished making ' + drink)
        self.make_drink = True

        # show the main menu
        self.menuContext.showMenu()

    def left_btn(self, ctx):
        if not self.running:
            self.menuContext.advance()

    def right_btn(self, ctx):
        if not self.running:
            self.menuContext.select()

    def rotary_on_change(self, ctx):
        if not self.running:
            if GPIO.input(self.dt):
                self.menuContext.retreat()
            else:
                self.menuContext.advance()

    def get_ingredients_time(self, drink):
        # Get the ingredients for the specified drink
        found = False
        while (found == False):
            currmen = self.menuContext.currentMenu.getSelection()
            if (currmen.name == drink):
                ingredients = currmen.attributes["ingredients"]
                self.make_drink = False
                found = True
            else:
                self.menuContext.currentMenu.nextSelection()

        # Get the drink making time
        maxTime = 0
        for ing in ingredients.keys():
            for pump in self.pump_configuration.keys():
                if ing == self.pump_configuration[pump]["value"]:
                    waitTime = ingredients[ing] * FLOW_RATE
                    if (waitTime > maxTime):
                        maxTime = waitTime

        # Return making time and ingredients for drink
        return ingredients, maxTime

    def updateProgressBar(self, percent, x=15, y=10):

        height = 10
        width = self.screen_width - 2 * x

        for w in range(0, width):
            self.draw.point((w + x, y), fill=255)
            self.draw.point((w + x, y + height), fill=255)
        for h in range(0, height):
            self.draw.point((x, h + y), fill=255)
            self.draw.point((self.screen_width - x, h + y), fill=255)
            for p in range(0, percent):
                p_loc = int(p / 100.0 * width)
                self.draw.point((x + p_loc, h + y), fill=255)
                self.draw.text((55, 20),
                               str(percent) + '%',
                               font=self.font,
                               fill=255)
                #self.disp.image(self.image)
            #self.disp.display()

    def endprogram(self):
        global stopprogram

        stopprogram = True

        # Goodbye message
        self.draw.rectangle((0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
                            outline=0,
                            fill=0)
        self.draw.text((25, 10), "Goodbye...", font=self.font, fill=255)
        self.disp.image(self.image)
        self.disp.display()
        time.sleep(3)

        self.draw.rectangle((0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
                            outline=0,
                            fill=0)
        self.draw.text((15, 10), "Have a great day!", font=self.font, fill=255)
        self.disp.image(self.image)
        self.disp.display()

        time.sleep(3)
        self.disp.clear()
        self.disp.display()

    def run(self):
        self.startInterrupts()
        # main loop
        try:
            while True:
                if stopprogram:
                    return
                # self.imap_server = imaplib.IMAP4_SSL("imap.gmail.com",993)
                # self.imap_server.login(USERNAME, PASSWORD)
                # self.imap_server.select('INBOX')
                # mail_list = self.check_email()
                # if mail_list and not self.running:
                # 	self.voice_command(mail_list)
                time.sleep(0.1)

        except KeyboardInterrupt:
            GPIO.cleanup()  # clean up GPIO on CTRL+C exit
            sys.exit(0)

        GPIO.cleanup()  # clean up GPIO on normal exit

        traceback.print_exc()