def main(trellis: NeoTrellis): for i in range(BUTTON_COUNT): trellis.activate_key(i, NeoTrellis.EDGE_RISING) trellis.activate_key(i, NeoTrellis.EDGE_FALLING) trellis.callbacks[i] = button_callback(trellis, i) while True: for i in range(BUTTON_COUNT): trellis.pixels[i] = button_target_color(i) trellis.sync() time.sleep(0.02)
def blink(event): # turn the LED on when a rising edge is detected if event.edge == NeoTrellis.EDGE_RISING: if not button.value: print("try redraw board") elif event.edge == NeoTrellis.EDGE_FALLING: print("falling") redrawBoard(COLORS) pass for i in range(16): # activate rising edge events on all keys trellis.activate_key(i, NeoTrellis.EDGE_RISING) # activate falling edge events on all keys trellis.activate_key(i, NeoTrellis.EDGE_FALLING) # set all keys to trigger the blink callback trellis.callbacks[i] = blink time.sleep(.02) trellis.pixels[i] = (0, 0, 0) time.sleep(.02) while True: # TODO debounce if not button.value: resetBoard() # call the sync function call any triggered callbacks trellis.sync() # try:
class Nightstand: def __init__(self): self.config = NightStandConfig(CONFIGFILE) self.audioPlayer = None self.states = NightstandStates() self.btctl = None self.states.registerMenuStateChangeListener(self.onMenuChanged) self.states.registerResetListener(self.reset) self.states.registerSleepListener(self.onGoToSleep) self.timer = RepeatedTimer(TIMER_INTERVAL, self.onTimerTick) def init(self): self.states.enterMenu(MENUSTATE_INIT) self.config.load() self.states.sleepEnabled = self.config.isSleepEnabled() self.config.registerFilechangedListener(self.onConfigChanged) self.btctl = BluetoothCtl(self.config.getBluetoothMacs()) # create the i2c object for the trellis i2c_bus = busio.I2C(SCL, SDA) # create the trellis self.trellis = NeoTrellis(i2c_bus) for i in range(16): # activate rising edge events on all keys self.trellis.activate_key(i, NeoTrellis.EDGE_RISING) # activate falling edge events on all keys self.trellis.activate_key(i, NeoTrellis.EDGE_FALLING) # set all keys to trigger the blink callback self.trellis.callbacks[i] = self.onKeyPressed def doConnect(self): self.btctl.doConnectAsync(BTCONNECT_RETRIES, self.onConnectFinished, self.onConnectFailed) def doDisconnect(self): self.btctl.doDisconnect() def onConnectFinished(self): print("bt connections established") self.states.enterMenu(MENUSTATE_IDLE) def onConnectFailed(self): print("failed to connect to bt devices") self.states.enterMenu(MENUSTATE_ERROR) def onMenuChanged(self, oldMenuState, newMenuState): print("menu changed from " + str(oldMenuState) + " to " + str(newMenuState)) if oldMenuState == MENUSTATE_SLEEPCONFIG: self.states.setSleepConfig(self.config.isSleepEnabled(), self.config.getSleepTime()) self.states.enterMenu( MENUSTATE_PLAYING if self.audioPlayer is not None else MENUSTATE_IDLE, True) return self.initColors() def reset(self, startupSequence=True, stopAudio=False): self.states.enterMenu(MENUSTATE_INIT) self.states.resetCounter = RESET_COUNTER_START self.btctl = BluetoothCtl(self.config.getBluetoothMacs()) if stopAudio: self.playAudio(None) if startupSequence: for i in range(16): self.trellis.pixels[i] = RED time.sleep(INIT_DELAY) for i in range(16): self.trellis.pixels[i] = YELLOW time.sleep(INIT_DELAY) for i in range(16): self.trellis.pixels[i] = GREEN time.sleep(INIT_DELAY) for i in range(16): self.trellis.pixels[i] = OFF time.sleep(INIT_DELAY) self.initColors() if self.config.doBluetoothAutoConnect(): self.states.enterMenu(MENUSTATE_CONNECTING) self.doConnect() else: self.states.enterMenu(MENUSTATE_IDLE) def onGoToSleep(self): self.enterMenu(MENUSTATE_SLEEPING) self.playAudio(None) self.doDisconnect() def getKeyColor(self, index): if self.states.menuState == MENUSTATE_SLEEPCONFIG: time = self.config.getSleepTime() if index < 12: return YELLOW if time > index * 300 else OFF elif index == 12: return WHITE elif index == 13: return GREEN if self.config.isSleepEnabled() else RED elif index == 14: return GREEN if self.config.isSleepEnabled() else RED elif index == 15: return WHITE elif self.states.menuState == MENUSTATE_DIMMING or self.states.menuState == MENUSTATE_STOPPED or self.states.menuState == MENUSTATE_SLEEPING: return OFF else: kc = self.config.getKeyConfig(index) if not kc.isPlayerButton( ) or self.states.menuState == MENUSTATE_PLAYING: return kc.color else: return OFF def initColors(self): for i in range(16): self.trellis.pixels[i] = self.getKeyColor(i) def onTimerTick(self): self.states.onResetTimerTick() self.states.onSleepTimerTick() self.states.onDimmingTimerTick() def onConfigChanged(self): print("config changed") self.reset(False) def onKeyPressed(self, event): self.states.resetDimmingTimer() # turn the LED on when a rising edge is detected if event.edge == NeoTrellis.EDGE_RISING: self.trellis.pixels[event.number] = self.config.getKeyConfig( event.number).keyPressedColor self.states.keyStates[event.number] = KEYSTATE_PRESSED # turn the LED off when a rising edge is detected elif event.edge == NeoTrellis.EDGE_FALLING: if self.states.menuState == MENUSTATE_DIMMING: self.states.menuBack() return kc = self.config.getKeyConfig(event.number) if not kc.isPlayerButton( ) or self.states.menuState == MENUSTATE_PLAYING: self.trellis.pixels[event.number] = kc.color else: self.trellis.pixels[event.number] = OFF if self.states.menuState == MENUSTATE_SLEEPCONFIG: self.onSleepConfigKeyPressed(event.number) else: self.onAudioKeyPressed(event.number) self.states.keyStates[event.number] = KEYSTATE_NONE def onSleepConfigKeyPressed(self, index): curSleep = self.config.getSleepTime() incdec = 300 if index == 12: self.config.setSleepTime(curSleep - incdec if curSleep > incdec else 0) if self.config.getSleepTime() == 0: self.config.enableSleep(False) if index == 15: self.config.setSleepTime(curSleep + incdec if curSleep < 11 * incdec else 12 * incdec) if curSleep == 0: self.config.enableSleep(True) if index == 13 or index == 14: self.config.enableSleep(not self.config.isSleepEnabled()) self.states.stayinSleepConfig() self.initColors() def onAudioKeyPressed(self, index): if self.states.menuState == MENUSTATE_IDLE or ( self.states.menuState == MENUSTATE_PLAYING and not self.config.getKeyConfig(index).isPlayerButton()): print("start audio") self.playAudio(self.config.getMedia(index)) elif self.states.menuState == MENUSTATE_PLAYING: keyConfig = self.config.getKeyConfig(index) if keyConfig.isPlayerButton(): self.audioPlayerButtonClick(keyConfig.playerButton) def audioPlayerButtonClick(self, button): print("exec audio button ", button) if not self.audioPlayer is None: if button == KeyConfig.AUDIOBUTTON_PLAY_PAUSE: if self.audioPlayer.is_playing(): self.audioPlayer.pause() else: self.audioPlayer.play() elif button == KeyConfig.AUDIOBUTTON_STOP: self.playAudio(None) elif button == KeyConfig.AUDIOBUTTON_VOLUMEUP: vol = self.audioPlayer.audio_get_volume() + VOL_INCDEC self.audioPlayer.audio_set_volume(vol) elif button == KeyConfig.AUDIOBUTTON_VOLUMEDOWN: vol = self.audioPlayer.audio_get_volume() - VOL_INCDEC self.audioPlayer.audio_set_volume(vol) elif button == KeyConfig.AUDIOBUTTON_NEXT: self.audioPlayer.next_chapter() elif button == KeyConfig.AUDIOBUTTON_PREV: self.audioPlayer.previous_chapter() def playAudio(self, audio): if not self.audioPlayer is None: print("stopping current audio") self.audioPlayer.stop() self.states.enterMenu(MENUSTATE_IDLE) if audio is None: return self.states.enterMenu(MENUSTATE_PLAYING) print("start playing ", audio) self.audioPlayer = vlc.MediaPlayer(audio) self.audioPlayer.play() def startServer(self): print('Hello from the Nightstand Service') try: while True: # call the sync function call any triggered callbacks self.trellis.sync() # the trellis can only be read every 17 millisecons or so time.sleep(0.02) except: self.config.stop() self.stop() def startCLI(self): print('Hello from the Nightstand Server') self.reset() self.timer.start() try: while True: # call the sync function call any triggered callbacks self.trellis.sync() # the trellis can only be read every 17 millisecons or so time.sleep(0.02) except KeyboardInterrupt: self.config.stop() self.stop() def stop(self): self.config.join() self.timer.stop() self.states.enterMenu(MENUSTATE_STOPPED) self.doDisconnect() self.states.unregisterMenuStateChangeListener(self.onMenuChanged)
class Trellis: """ relays button presses by adding them to a queue buttons can be referred to by name, index, or color group name """ def __init__(self, startup_color='random', debug=True): self.debug = debug self.nbuttons = 16 self.colors = { 'off': (0, 0, 0), 'purple': (180, 0, 255), 'red': (255, 0, 0), 'orange': (255, 164, 0), 'green': (0, 255, 0), 'yellow': (158, 152, 17), 'gray': (100, 100, 100), 'blue': (0, 0, 255), 'lightblue': (7, 34, 81), 'blueish': (33, 211, 237), 'darkgray': (10, 10, 10), 'seagreen': (30, 255, 30), 'lightseagreen': (39, 239, 120), 'salmon': (206, 28, 41), 'lightorange': (176, 76, 9), 'lightpurple': (87, 20, 174), 'lighterpurple': (70, 27, 87), 'pink': (100, 0, 100) } # create the i2c object for the trellis self.i2c_bus = busio.I2C(SCL, SDA) # create the trellis self.trellis = NeoTrellis( self.i2c_bus) # can set interrupt=True here... # for handling colors of groups of buttons self.startup_color = startup_color self.color_map = {} # to ensure callback set self.button_handler = None def set_color_map(self, color_map): self.color_map = color_map def set_callback(self, fcn): # callback for when buttons are pressed self.button_handler = fcn # set handlers for button press self.activate(self.startup_color, lightshow=True) def activate(self, startup_color=None, lightshow=False): if self.button_handler is None: print("Error: callback must be set using 'set_callback'") for i in range(self.nbuttons): # activate rising edge events on all keys self.trellis.activate_key(i, BUTTON_PRESSED) # activate falling edge events on all keys self.trellis.activate_key(i, BUTTON_RELEASED) # set all keys to trigger the blink callback self.trellis.callbacks[i] = self.button_handler if not lightshow: continue #cycle the LEDs on startup if startup_color is not None: if startup_color == 'random': color = random_color() else: color = self.colors[startup_color] self.trellis.pixels[i] = color time.sleep(.03) for i in range(self.nbuttons): self.trellis.pixels[i] = self.colors['off'] if lightshow: time.sleep(.03) def end_lightshow(self, event=None): # reset callbacks and turn lights off if event is None or event.edge == BUTTON_PRESSED: self.activate() self.lightshow_on = False def lightshow(self): self.lightshow_on = True # first, set callback to interrupt the show for i in range(self.nbuttons): self.trellis.callbacks[i] = self.end_lightshow # now pick buttons and flash lights on/off in random order while True: button_indices = list(range(self.nbuttons)) random.shuffle(button_indices) for i in button_indices: self.trellis.pixels[i] = random_color() time.sleep(.07) if not self.lightshow_on: return for i in button_indices: self.trellis.pixels[i] = self.colors['off'] time.sleep(.07) if not self.lightshow_on: return self.sync() time.sleep(.02) def set_color_all_buttons(self, color): for i in range(self.nbuttons): self.set_color(i, color) def set_color(self, index, color): if color in self.color_map: color = self.color_map[color] self.trellis.pixels[index] = self.colors[color] def sync(self): self.trellis.sync() def terminate(self): for i in range(self.nbuttons): self.trellis.pixels[i] = self.colors['off'] self.sync()
CYAN = (0, 255, 255) BLUE = (0, 0, 255) PURPLE = (180, 0, 255) #this will be called when button events are received def blink(event): #turn the LED on when a rising edge is detected if event.edge == NeoTrellis.EDGE_RISING: trellis.pixels[event.number] = CYAN #turn the LED off when a rising edge is detected elif event.edge == NeoTrellis.EDGE_FALLING: trellis.pixels[event.number] = OFF for i in range(16): #activate rising edge events on all keys trellis.activate_key(i, NeoTrellis.EDGE_RISING) #activate falling edge events on all keys trellis.activate_key(i, NeoTrellis.EDGE_FALLING) #set all keys to trigger the blink callback trellis.callbacks[i] = blink #cycle the LEDs on startup trellis.pixels[i] = PURPLE time.sleep(.05) for i in range(16): trellis.pixels[i] = OFF time.sleep(.05) while True: #call the sync function call any triggered callbacks