예제 #1
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.btn1Pin = LEFT_BTN_PIN
        self.btn2Pin = RIGHT_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)

        # configure screen
        spi_bus = 0
        spi_device = 0

        #Load the display driver. Attention: 128_64 is the display size. Needed to be changed if different in your setup
        self.led = disp = Adafruit_SSD1306.SSD1306_128_64(
            rst=RST,
            dc=DC,
            spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000)
        )  # Change rows & cols values depending on your display dimensions.

        # Initialize library.
        self.led.begin()

        # Clear display.
        self.led.clear()
        self.led.display()

        # Create image buffer.
        # Make sure to create image with mode '1' for 1-bit color.
        self.image = Image.new('1', (self.screen_width, self.screen_height))

        # Load default font.
        #self.font = ImageFont.load_default()
        self.font = ImageFont.truetype(FONTFILE, FONTSIZE)

        # Create drawing object.
        self.draw = ImageDraw.Draw(self.image)

        # 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)

        # 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)
        #Auskommentiert solange noch kein LED Strip angebracht.
        #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"

    @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)

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

        # add drink options
        drink_opts = []
        for d in 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 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'))
        # adds an option that shuts down the rpi
        configuration_menu.addOption(MenuItem('shutdown', 'Shutdown'))

        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"):
            self.makeDrink(menuItem.name, menuItem.attributes["ingredients"])
            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"):
            self.clean()
            return True
        elif (menuItem.type == "shutdown"):
            self.shutdown()
            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)

    def shutdown(self):
        shutdowntext = "Shutdown takes 10 seconds. Bye!"
        self.led.clear()
        self.draw.rectangle((0, 0, self.screen_width, self.screen_height),
                            outline=0,
                            fill=0)

        words_list = WRAPPER.wrap(text=shutdowntext)
        TextNew = ''
        for ii in words_list[:-1]:
            TextNew = TextNew + ii + "\n"
        TextNew += words_list[-1]
        self.draw.text((0, 10), str(TextNew), font=self.font, fill=255)
        self.led.image(self.image)
        self.led.display()
        time.sleep(5)

        #Clean shutdown device
        subprocess.Popen(['shutdown', '-h', 'now'])

    def displayMenuItem(self, menuItem):
        print menuItem.name
        self.led.clear()
        self.draw.rectangle((0, 0, self.screen_width, self.screen_height),
                            outline=0,
                            fill=0)

        words_list = WRAPPER.wrap(text=menuItem.name)
        MenuItemNew = ''
        for ii in words_list[:-1]:
            MenuItemNew = MenuItemNew + ii + "\n"
        MenuItemNew += words_list[-1]
        self.draw.text((0, 10), str(MenuItemNew), font=self.font, fill=255)
        self.led.image(self.image)
        self.led.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 / 100.0
        for x in range(1, 101):
            self.led.clear()
            self.draw.rectangle((0, 0, self.screen_width, self.screen_height),
                                outline=0,
                                fill=0)
            self.updateProgressBar(x, y=35)
            self.led.image(self.image)
            self.led.display()
            time.sleep(interval)

    def makeDrink(self, drink, ingredients):
        # cancel any button presses while the drink is being made
        # self.stopInterrupts()
        self.running = True

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

        # Parse the drink ingredients and spawn threads for pumps
        maxTime = 0
        pumpThreads = []
        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_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(maxTime)

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

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

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

        # show the ending sequence lights
        self.lightsEndingSequence()

        # 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 left_btn(self, ctx):
        print("LEFT_BTN pressed")
        if not self.running:
            self.running = True
            self.menuContext.advance()
            print("Finished processing button press")
        self.running = False

    def right_btn(self, ctx):
        print("RIGHT_BTN pressed")
        if not self.running:
            self.running = True
            self.menuContext.select()
            print("Finished processing button press")
            self.running = 2
            print("Starting button timeout")

    def updateProgressBar(self, percent, x=15, y=15):
        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)

    def run(self):
        self.startInterrupts()
        # main loop
        try:

            try:

                while True:
                    letter = raw_input(">")
                    if letter == "l":
                        self.left_btn(False)
                    if letter == "r":
                        self.right_btn(False)

            except EOFError:
                while True:
                    time.sleep(0.1)
                    if self.running not in (True, False):
                        self.running -= 0.1
                        if self.running == 0:
                            self.running = False
                            print("Finished button timeout")

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

        traceback.print_exc()
예제 #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.btn1Pin = LEFT_BTN_PIN
        self.btn2Pin = RIGHT_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)  

        # Very important... This lets py-gaugette 'know' what pins to use in order to reset the display
        self.led = ssd1306(I2CBUS) # Change rows & cols values depending on your display dimensions.
        logo = Image.open('pi_logo.png')
        self.led.canvas.bitmap((32, 0), logo, fill=0)
        self.led.display()

        # 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):
        self.running = True
        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)
        time.sleep(1)
        self.running = False

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

        # add drink options
        drink_opts = []
        for d in 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 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"):
            self.makeDrink(menuItem.name, menuItem.attributes["ingredients"])
            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"):
            self.clean()
            return True
        return False

    def clean(self):
        pins = []

        for pump in self.pump_configuration.keys():
            pins.append(self.pump_configuration[pump]["pin"])

        self.startProgressBar()
        GPIO.output(pins, GPIO.LOW)
        self.sleepAndProgress(time.time(),20,20)
        GPIO.output(pins, GPIO.HIGH)

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

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

    def displayMenuItem(self, menuItem):
        print (menuItem.name)
        self.led.cls()
        self.led.canvas.text((0,20),menuItem.name, font=FONT, fill=1)
        self.led.display()

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

    def startProgressBar(self,x=15,y=20):
        start_time = time.time()
        self.led.cls()
        self.led.canvas.text((10,20),"Dispensing...", font=FONT, fill=1)

    def sleepAndProgress(self, startTime, waitTime, totalTime, x=15, y=35):
        localStartTime = time.time()
        height = 10
        width = self.screen_width-2*x

        while time.time() - localStartTime < waitTime:
            progress = (time.time() - startTime)/totalTime
            p_loc = int(progress*width)
            self.led.canvas.rectangle((x,y,x+width,y+height), outline=255, fill=0)
            self.led.canvas.rectangle((x+1,y+1,x+p_loc,y+height-1), outline=255, fill=1)
            try:
                self.led.display()
            except IOError:
                print("Failed to talk to screen")
            time.sleep(0.2)

    def makeDrink(self, drink, ingredients):
        # cancel any button presses while the drink is being made
        self.running = True

        # Parse the drink ingredients and create pouring data
        pumpTimes = []
        for ing in ingredients.keys():
            for pump in self.pump_configuration.keys():
                if ing == self.pump_configuration[pump]["value"]:
                    waitTime = ingredients[ing] * FLOW_RATE
                    pumpTimes.append([self.pump_configuration[pump]["pin"], waitTime])

        # Put the drinkjs in the order they'll stop pouring
        pumpTimes.sort(key=lambda x: x[1])

        # Note the total time required to pour the drink
        totalTime = pumpTimes[-1][1]

        # Change the times to be relative to the previous not absolute
        for i in range(1,len(pumpTimes)):
            pumpTimes[i][1] -= pumpTimes[i-1][1]

        print(pumpTimes)

        self.startProgressBar()
        startTime = time.time()
        print("starting all")
        GPIO.output([p[0] for p in pumpTimes], GPIO.LOW)
        for p in pumpTimes:
            pin, delay = p
            if delay > 0: 
                self.sleepAndProgress(startTime, delay, totalTime)
            GPIO.output(pin, GPIO.HIGH)
            print("stopping {}".format(pin))

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

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


    def left_btn(self, ctx):
        print("LEFT_BTN pressed")
        if not self.running:
            self.running = True
            self.menuContext.advance()
            print("Finished processing button press")
        self.running = False

    def right_btn(self, ctx):
        print("RIGHT_BTN pressed")
        if not self.running:
            self.running = True
            self.menuContext.select()
            print("Finished processing button press")
            self.running = 2
            print("Starting button timeout")

    def run(self):
        self.startInterrupts()
        # main loop
        try:
            try:
                while True:
                    letter = raw_input(">")
                    if letter == "l":
                        self.left_btn(False)
                    if letter == "r":
                        self.right_btn(False)
            except EOFError:
                while True:
                    time.sleep(0.1)
                    if self.running not in (True,False):
                        self.running -= 0.1
                        if self.running == 0:
                            self.running = False
                            print("Finished button timeout")

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

        traceback.print_exc()
예제 #3
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.btn1Pin = LEFT_BTN_PIN
        self.btn2Pin = RIGHT_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)

        # configure screen
        # spi_bus = 0
        # spi_device = 0
        # gpio = gaugette.gpio.GPIO()
        # spi = gaugette.spi.SPI(spi_bus, spi_device)

        serial = i2c(port=1, address=0x3C)
        device = sh1106(serial)

        # Very important... This lets py-gaugette 'know' what pins to use in order to reset the display
        # self.led = gaugette.ssd1306.SSD1306(gpio, spi, reset_pin=OLED_RESET_PIN, dc_pin=OLED_DC_PIN, rows=self.screen_height, cols=self.screen_width) # Change rows & cols values depending on your display dimensions.
        self.led = device
        self.oled_font = ImageFont.truetype('FreeSans.ttf', 12)
        # self.led.begin()
        # self.led.clear_display()
        # self.led.display()
        # self.led.invert_display()
        # time.sleep(0.5)
        # self.led.normal_display()
        time.sleep(0.5)

        # 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)

        # 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
        # Limit brightness to ~1/4 duty cycle
        #self.strip.setBrightness(NEOPIXEL_BRIGHTNESS)

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

        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)

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

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

        # add drink options
        drink_opts = []
        for d in 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 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"):
            self.makeDrink(menuItem.name, menuItem.attributes["ingredients"])
            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"):
            self.clean()
            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 displayMenuItem(self, menuItem):
        print(menuItem.name)
        # self.led.clear_display()
        # self.led.draw_text2(0,20,menuItem.name,2)
        # self.led.display()
        with canvas(self.led) as draw:
            draw.rectangle(self.led.bounding_box,
                           outline="white",
                           fill="black")
            draw.text((10, 10),
                      menuItem.name,
                      font=self.oled_font,
                      fill="white")

    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 / 100.0
        # for x in range(1, 101):
        #     self.led.clear_display()
        #     self.updateProgressBar(x, y=35)
        #     self.led.display()
        #     time.sleep(interval)

    def makeDrink(self, drink, ingredients):
        # cancel any button presses while the drink is being made
        # self.stopInterrupts()
        self.running = True

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

        # Parse the drink ingredients and spawn threads for pumps
        maxTime = 0
        pumpThreads = []
        for ing in ingredients.keys():
            for pump in self.pump_configuration.keys():
                if ing == self.pump_configuration[pump]["value"]:
                    waitTime = ingredients[ing] / float(
                        self.pump_configuration[pump]["mlpersec"])
                    if (waitTime > maxTime):
                        maxTime = waitTime
                    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(maxTime)

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

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

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

        # show the ending sequence lights
        # self.lightsEndingSequence()

        # 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 left_btn(self, ctx):
        if not self.running:
            self.menuContext.advance()

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

    def updateProgressBar(self, percent, x=15, y=15):
        height = 10
        width = self.screen_width - 2 * x
        # for w in range(0, width):
        # 	self.led.draw_pixel(w + x, y)
        # 	self.led.draw_pixel(w + x, y + height)
        # for h in range(0, height):
        # 	self.led.draw_pixel(x, h + y)
        # 	self.led.draw_pixel(self.screen_width-x, h + y)
        # 	for p in range(0, percent):
        # 		p_loc = int(p/100.0*width)
        # 		self.led.draw_pixel(x + p_loc, h + y)

    def run(self):
        self.startInterrupts()
        # main loop
        try:
            while True:
                time.sleep(0.1)

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

        traceback.print_exc()
예제 #4
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()
예제 #5
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.btn1Pin = LEFT_BTN_PIN
        self.btn2Pin = RIGHT_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)

        # configure screen
        spi_bus = 0
        spi_device = 0

        # Very important... This lets py-gaugette 'know' what pins to use in order to reset the display
        self.led = disp = Adafruit_SSD1306.SSD1306_128_64(
            rst=RST,
            dc=DC,
            spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000)
        )  # Change rows & cols values depending on your display dimensions.

        # Initialize library.
        self.led.begin()

        # Clear display.
        self.led.clear()
        self.led.display()

        # Create image buffer.
        # Make sure to create image with mode '1' for 1-bit color.
        self.image = Image.new('1', (self.screen_width, self.screen_height))

        # Load default font.
        self.font = ImageFont.truetype("FreeMono.ttf", 15)

        # Create drawing object.
        self.draw = ImageDraw.Draw(self.image)

        # 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)

        # 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

        # Set the Default or "StandBy Light" to Blue in this case
        for i in range(0, self.numpixels):
            self.strip.setPixelColor(i, COLOR_BLUE)
        self.strip.show()

        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):
        self.running = True
        GPIO.add_event_detect(self.btn1Pin,
                              GPIO.FALLING,
                              callback=self.left_btn,
                              bouncetime=LEFT_PIN_BOUNCE)
        time.sleep(1)
        GPIO.add_event_detect(self.btn2Pin,
                              GPIO.FALLING,
                              callback=self.right_btn,
                              bouncetime=RIGHT_PIN_BOUNCE)
        time.sleep(1)
        self.running = False

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

        # add drink options
        drink_opts = []
        for d in 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 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"):
            self.makeDrink(menuItem.name, menuItem.attributes["ingredients"])
            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"):
            self.clean()
            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)

    def displayMenuItem(self, menuItem):
        print menuItem.name
        self.led.clear()
        self.draw.rectangle((0, 0, self.screen_width, self.screen_height),
                            outline=0,
                            fill=0)
        self.draw.text((0, 20), str(menuItem.name), font=self.font, fill=255)
        self.led.image(self.image)
        self.led.display()

    def cycleLights(self):
        t = threading.currentThread()
        head = 0  # Index of first 'on' pixel
        tail = -10  # Index of last 'off' pixel
        color = COLOR_RED  # '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
                if (color == COLOR_RED):
                    color = COLOR_YELLOW  # if red,set to yellow
                elif (color == COLOR_YELLOW):
                    color = COLOR_BLUE  # if yellow,set to blue
                elif (color == COLOR_BLUE):
                    color = COLOR_RED  # if blue,set back to red
            tail += 1  # Advance tail position
            if (tail >= self.numpixels): tail = 0  # Off end? Reset

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

        os.system("mpg123 " + DONESOUND)

        #		time.sleep(5)

        # set them back to blue "StandBy Light"
        for i in range(0, self.numpixels):
            self.strip.setPixelColor(i, COLOR_BLUE)
        self.strip.show()

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

        # other way of dealing with Display delay, Thanks Yogesh
    def progressBar(self, waitTime):
        #-with the outcommented version, it updates faster, but there is a limit with the delay, you have to figure out-#
        #mWaitTime = waitTime - 7
        #interval = mWaitTime/ 100.0
        #if interval < 0.07:
        #	interval = 0
        #for x in range(1, 101):
        interval = waitTime / 10.0
        for x in range(1, 11):
            self.led.clear()
            self.draw.rectangle((0, 0, self.screen_width, self.screen_height),
                                outline=0,
                                fill=0)
            #	self.updateProgressBar(x, y=35)
            self.updateProgressBar(x * 10, y=35)
            self.led.image(self.image)
            self.led.display()
            time.sleep(interval)

    def makeDrink(self, drink, ingredients):
        # cancel any button presses while the drink is being made
        # self.stopInterrupts()

        os.system("mpg123 " + DRINKME)

        self.running = True

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

        # Parse the drink ingredients and spawn threads for pumps
        maxTime = 0
        pumpThreads = []
        for ing in ingredients.keys():
            for pump in self.pump_configuration.keys():
                if ing == self.pump_configuration[pump]["value"]:
                    vWaitTime = self.pump_configuration[pump]["flowrate"]
                    waitTime = ingredients[ing] * vWaitTime
                    if (waitTime > maxTime):
                        maxTime = waitTime
                    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()
    #Show in Console how long the Pumps running
        print("The pumps run for", maxTime, "seconds")
        # start the progress bar
        self.progressBar(maxTime)

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

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

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

        # show the ending sequence lights
        self.lightsEndingSequence()

        # 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 left_btn(self, ctx):
        print("LEFT_BTN pressed")
        if not self.running:
            self.running = True
            self.menuContext.advance()
            print("Finished processing button press")
        self.running = False

    def right_btn(self, ctx):
        print("RIGHT_BTN pressed")
        if not self.running:
            self.running = True
            self.menuContext.select()
            print("Finished processing button press")
            self.running = 2
            print("Starting button timeout")

    def updateProgressBar(self, percent, x=15, y=15):
        height = 25
        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)

    def run(self):
        self.startInterrupts()
        # main loop
        try:

            try:

                while True:
                    letter = raw_input(">")
                    if letter == "l":
                        self.left_btn(False)
                    if letter == "r":
                        self.right_btn(False)

            except EOFError:
                while True:
                    time.sleep(0.1)
                    if self.running not in (True, False):
                        self.running -= 0.1
                        if self.running == 0:
                            self.running = False
                            print("Finished button timeout")

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

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

        traceback.print_exc()
예제 #6
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.btn1Pin = LEFT_BTN_PIN
        self.btn2Pin = RIGHT_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)

        # configure screen
        spi_bus = 0
        spi_device = 0
        gpio = gaugette.gpio.GPIO()
        spi = gaugette.spi.SPI(spi_bus, spi_device)

        self.led = ssd1306(
            I2CBUS
        )  # Change rows & cols values depending on your display dimensions.
        logo = Image.open('logo.png')
        self.led.canvas.bitmap((0, 0), logo, fill=1)
        self.led.canvas.rectangle(
            (0, 0, self.led.width - 1, self.led.height - 1), outline=1)
        self.led.display()

        # 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)

        # 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

        time.sleep(2)

        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)

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

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

        # add drink options
        drink_opts = []
        for d in 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 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)
        self.led.cls()
        return True

    def menuItemClicked(self, menuItem):
        if (menuItem.type == "drink"):
            self.makeDrink(menuItem.name, menuItem.attributes["ingredients"])
            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"):
            self.clean()
            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 displayMenuTitle(self, title):
        self.led.canvas.text((0, 0), title, font=TITLE_FONT, fill=1)

    def displayMenuItem(self, menuItem):
        print menuItem.name
        self.led.canvas.text((0, 20), menuItem.name, font=ITEM_FONT, fill=1)
        self.led.display()

    def displayRender(self):
        self.led.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
            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):
        start_time = time.time()
        self.led.cls()
        self.led.canvas.text((30, 20), "Dispensing...", font=FONT, fill=1)
        while time.time() - start_time < waitTime:
            prog = (time.time() - start_time) / waitTime
            self.updateProgressBar(prog, y=35)
            self.led.display()
            time.sleep(0.5)

    def makeDrink(self, drink, ingredients):
        # cancel any button presses while the drink is being made
        # self.stopInterrupts()
        self.running = True

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

        # Parse the drink ingredients and spawn threads for pumps
        maxTime = 0
        pumpThreads = []
        for ing in ingredients.keys():
            for pump in self.pump_configuration.keys():
                if ing == self.pump_configuration[pump]["value"]:
                    waitTime = ingredients[
                        ing] * 60.0 / self.pump_configuration[pump]["flow_rate"]
                    if (waitTime > maxTime):
                        maxTime = waitTime
                    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(maxTime)

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

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

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

        # show the ending sequence lights
        self.lightsEndingSequence()

        # 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 left_btn(self, ctx):
        if not self.running:
            self.menuContext.advance()

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

    def updateProgressBar(self, percent, x=15, y=15):
        h = 10
        w = self.screen_width - 2 * x
        p_loc = int(percent * w)
        self.led.canvas.rectangle((x, y, x + w, y + h), outline=255, fill=0)
        self.led.canvas.rectangle((x + 1, y + 1, x + p_loc, y + h - 1),
                                  outline=255,
                                  fill=1)

    def run(self):
        self.startInterrupts()
        # main loop
        try:
            while True:
                time.sleep(0.1)

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

        traceback.print_exc()
예제 #7
0
class Bartender(MenuDelegate):
    def __init__(self):
        GPIO.setmode(GPIO.BCM)

        self.running = False

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

        self.btnMenuPlusPin = MENU_PLUS_BTN_PIN
        self.btnMenuMinPin = MENU_MIN_BTN_PIN
        self.btnSelectPin = SELECT_BTN_PIN
        self.btnAdminPin = ADMIN_BTN_PIN
        self.btnAlcoholPin = ALCOHOL_BTN_PIN

        # vars for toggle
        self.alcohol_enabled = False
        self.admin_enabled = False

        self.weborders = False

        # configure interrups for buttons
        GPIO.setup(self.btnMenuPlusPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.setup(self.btnMenuMinPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.setup(self.btnSelectPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.setup(self.btnAdminPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.setup(self.btnAlcoholPin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

        # configure screen
        spi_bus = 0
        spi_device = 0
        gpio = gaugette.gpio.GPIO()
        spi = gaugette.spi.SPI(spi_bus, spi_device)

        # Very important... This lets py-gaugette 'know' what pins to use in order to reset the display
        self.led = gaugette.ssd1306.SSD1306(
            gpio,
            spi,
            reset_pin=OLED_RESET_PIN,
            dc_pin=OLED_DC_PIN,
            rows=self.screen_height,
            cols=self.screen_width
        )  # Change rows & cols values depending on your display dimensions.
        self.led.begin()
        self.led.clear_display()
        self.led.display()
        self.led.invert_display()
        time.sleep(0.5)
        self.led.normal_display()
        time.sleep(0.5)

        # 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)

        # 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()

        # add var so the system can go back
        self.lastpagecommand = ""

        print("Done initializing")

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

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

    def startInterrupts(self):
        GPIO.add_event_detect(self.btnMenuPlusPin,
                              GPIO.FALLING,
                              callback=self.menu_plus_btn,
                              bouncetime=MENU_PLUS_BTN_BOUNCE)
        GPIO.add_event_detect(self.btnMenuMinPin,
                              GPIO.FALLING,
                              callback=self.menu_min_btn,
                              bouncetime=MENU_MIN_BTN_BOUNCE)
        GPIO.add_event_detect(self.btnSelectPin,
                              GPIO.FALLING,
                              callback=self.select_btn,
                              bouncetime=SELECT_PIN_BOUNCE)

        GPIO.add_event_detect(self.btnAlcoholPin,
                              GPIO.FALLING,
                              callback=self.alcohol_btn,
                              bouncetime=ALCOHOL_PIN_BOUNCE)
        GPIO.add_event_detect(self.btnAdminPin,
                              GPIO.FALLING,
                              callback=self.admin_btn,
                              bouncetime=ADMIN_PIN_BOUNCE)

    def stopInterrupts(self):
        GPIO.remove_event_detect(self.btnAlcoholPin)
        GPIO.remove_event_detect(self.btnAdminPin)
        GPIO.remove_event_detect(self.btnSelectPin)
        GPIO.remove_event_detect(self.btnMenuMinPin)
        GPIO.remove_event_detect(self.btnMenuPlusPin)

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

        # add drink options
        drink_opts = []
        for d in drink_list:
            # check if allowed by admin button
            if alcoholic_drinks_enabled == False:
                # check if the drink has alcohol
                if d["alcoholic"] == False:  # -----!!!!-----
                    drink_opts.append(
                        MenuItem('drink', d["name"],
                                 {"ingredients": d["ingredients"]
                                  }))  # -----!!!!-----
            else:
                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 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'))
        # adds the option to power off
        configuration_menu.addOption(MenuItem('poweroff', 'Power off'))
        configuration_menu.setParent(m)

        # add drinks to options
        m.addOptions(drink_opts)

        # check if admin is enabled
        if admin_options_enabled == True:
            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"):
            self.makeDrink(menuItem.name, menuItem.attributes["ingredients"])
            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"):
            self.clean()
            return True
        elif (menuItem.type == "poweroff"):
            self.shutdown()
            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 shutdown(self):
        print("system will go off")
        os.system("sudo shutdown -h now")

    def displayMenuItem(self, menuItem):
        print menuItem.name
        self.led.clear_display()
        self.led.draw_text2(0, 20, menuItem.name, 2)
        self.led.display()

        print("set on oled")
        print(menuItem.type)
        print(menuItem.name)

        self.displayInBrowser(menuItem)

    def displayInBrowser(self, menuItem):
        # check the type and open the matching page
        if (menuItem.type == "drink"):
            path = "/drink?drink={0}".format(base64.encodestring(
                menuItem.name))
            self.create_exectute_display_command(path)

        else:
            path = "/basic-menu?item={0}&type={1}".format(
                base64.encodestring(menuItem.name),
                base64.encodestring(menuItem.type))
            self.create_exectute_display_command(path)

    def create_exectute_display_command(self, webpath):
        # makes the command with the given parms
        command = "DISPLAY=:0 firefox '{0}:{1}{2}' &".format(
            server_host, server_port, webpath)
        # execute
        os.system(command)

    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 / 100.0
        for x in range(1, 101):
            self.led.clear_display()
            self.updateProgressBar(x, y=35)
            self.led.display()
            time.sleep(interval)

    def makeDrink(self, drink, ingredients):
        # cancel any button presses while the drink is being made
        # self.stopInterrupts()
        self.running = True

        # show a page so the user knows it's buzzy
        self.create_exectute_display_command("/making?drink={0}".format(
            base64.encodestring(drink)))
        self.served_drink = drink

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

        # Parse the drink ingredients and spawn threads for pumps
        maxTime = 0
        pumpThreads = []

        # store the longes wait time to sleep later
        longestWaitTime = 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
                    pump_t = threading.Thread(
                        target=self.pour,
                        args=(self.pump_configuration[pump]["pin"], waitTime))
                    pumpThreads.append(pump_t)

                    # chek waiting time
                    if longestWaitTime < waitTime:
                        longestWaitTime = waitTime

        # 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(longestWaitTime)

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

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

        # show the ending sequence lights
        self.lightsEndingSequence()

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

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

    def menu_plus_btn(self, ctx):
        print("plus hit")

        if not self.running:
            self.menuContext.advance()

    def menu_min_btn(self, ctx):
        print("min hit")

        if not self.running:
            self.menuContext.previous()

    def select_btn(self, ctx):

        if not self.running:
            print("cocktail selected")
            self.menuContext.select()
        else:
            #could be better but due the fact:
            #   thread can't be stopped
            #   error ignored
            #no better solution
            print("Cancel selected")

            self.create_exectute_display_command("/cancel?drink={0}".format(
                base64.encodestring(self.served_drink)))

            for pump in self.pump_configuration.keys():
                GPIO.output(self.pump_configuration[pump]["pin"], GPIO.HIGH)

            print("cancel done")

    def alcohol_btn(self, ctx):
        print("alcohol hit")

        # rebuild the menu
        self.alcohol_enabled = self.alcohol_enabled ^ 1
        self.buildMenu(drink_list, drink_options, self.alcohol_enabled,
                       self.admin_enabled)  # -----!!!!-----

    def admin_btn(self, ctx):
        print("admin hit")

        # rebuild the menu
        self.admin_enabled = self.admin_enabled ^ 1
        self.buildMenu(drink_list, drink_options, self.alcohol_enabled,
                       self.admin_enabled)  # -----!!!!-----

    def updateProgressBar(self, percent, x=15, y=15):
        height = 10
        width = self.screen_width - 2 * x
        for w in range(0, width):
            self.led.draw_pixel(w + x, y)
            self.led.draw_pixel(w + x, y + height)
        for h in range(0, height):
            self.led.draw_pixel(x, h + y)
            self.led.draw_pixel(self.screen_width - x, h + y)
            for p in range(0, percent):
                p_loc = int(p / 100.0 * width)
                self.led.draw_pixel(x + p_loc, h + y)

    def run(self):
        self.startInterrupts()

    def start_operation(self):
        self.buildMenu(drink_list, drink_options)
        self.run()

    @staticmethod
    def set_gpio():
        GPIO.setmode(GPIO.BCM)

    def clean_gpio(self):
        self.stopInterrupts()
        GPIO.cleanup()