159 lines
6.3 KiB
Python
159 lines
6.3 KiB
Python
# SPDX-FileCopyrightText: 2018 John Edgar Park for Adafruit Industries
|
|
#
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
# Launch Deck Trellis M4
|
|
# USB HID button box for launching applications, media control, camera switching and more
|
|
# Use it with your favorite keyboard controlled launcher, such as Quicksilver and AutoHotkey
|
|
|
|
import time
|
|
import random
|
|
import adafruit_trellism4
|
|
import usb_hid
|
|
from adafruit_hid.keyboard import Keyboard
|
|
from adafruit_hid.keycode import Keycode
|
|
from adafruit_hid.consumer_control import ConsumerControl
|
|
from adafruit_hid.consumer_control_code import ConsumerControlCode
|
|
|
|
# Rotation of the trellis. 0 is when the USB is upself.
|
|
# The grid coordinates used below require portrait mode of 90 or 270
|
|
ROTATION = 270
|
|
|
|
# the two command types -- MEDIA for ConsumerControlCodes, KEY for Keycodes
|
|
# this allows button press to send the correct HID command for the type specified
|
|
MEDIA = 1
|
|
KEY = 2
|
|
# button mappings
|
|
# customize these for your desired postitions, colors, and keyboard combos
|
|
# specify (button coordinate): (color hex value, command type, command/keycodes)
|
|
keymap = {
|
|
(0,0): (0x001100, MEDIA, ConsumerControlCode.PLAY_PAUSE),
|
|
(1,0): (0x110011, MEDIA, ConsumerControlCode.SCAN_PREVIOUS_TRACK),
|
|
(2,0): (0x110011, MEDIA, ConsumerControlCode.SCAN_NEXT_TRACK),
|
|
(3,0): (0x000033, MEDIA, ConsumerControlCode.VOLUME_INCREMENT),
|
|
|
|
(0,1): (0x110000, MEDIA, ConsumerControlCode.MUTE),
|
|
# intentional blank button
|
|
# intentional blank button
|
|
(3,1): ((0,0,10), MEDIA, ConsumerControlCode.VOLUME_DECREMENT),
|
|
|
|
(0,2): (0x551100, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.ONE)),
|
|
(1,2): (0x221100, KEY, (Keycode.CONTROL, Keycode.SHIFT, Keycode.TAB)), # back cycle tabs
|
|
(2,2): (0x221100, KEY, (Keycode.CONTROL, Keycode.TAB)), # cycle tabs
|
|
(3,2): (0x333300, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.TWO)),
|
|
|
|
(0,3): (0x001155, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.THREE)),
|
|
# intentional blank button
|
|
# intentional blank button
|
|
(3,3): (0x330000, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.FOUR)),
|
|
|
|
(0,4): (0x005511, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.FIVE)),
|
|
(1,4): (0x440000, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.SIX)),
|
|
# intentional blank button
|
|
(3,4): (0x003300, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.EIGHT)),
|
|
|
|
(0,5): (0x222222, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.W)),
|
|
(1,5): (0x000044, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.E)),
|
|
# intentional blank button
|
|
(3,5): (0x332211, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.T)),
|
|
|
|
(0,6): (0x001133, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.C)),
|
|
(1,6): (0x331100, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.V)),
|
|
(2,6): (0x111111, KEY, (Keycode.GUI, Keycode.SHIFT, Keycode.FOUR)), # screen shot
|
|
(3,6): (0x110000, KEY, (Keycode.GUI, Keycode.ALT, Keycode.CONTROL, Keycode.N)),
|
|
|
|
(0,7): (0x060606, KEY, (Keycode.GUI, Keycode.H)), # hide front app, all windows
|
|
(1,7): (0x222200, KEY, (Keycode.GUI, Keycode.GRAVE_ACCENT)), # cycle windows of app
|
|
(2,7): (0x010001, KEY, (Keycode.GUI, Keycode.SHIFT, Keycode.TAB)), # cycle apps backards
|
|
(3,7): (0x010001, KEY, (Keycode.GUI, Keycode.TAB))} # cycle apps forwards
|
|
|
|
|
|
# Time in seconds to stay lit before sleeping.
|
|
TIMEOUT = 90
|
|
|
|
# Time to take fading out all of the keys.
|
|
FADE_TIME = 1
|
|
|
|
# Once asleep, how much time to wait between "snores" which fade up and down one button.
|
|
SNORE_PAUSE = 0.5
|
|
|
|
# Time in seconds to take fading up the snoring LED.
|
|
SNORE_UP = 2
|
|
|
|
# Time in seconds to take fading down the snoring LED.
|
|
SNORE_DOWN = 1
|
|
|
|
TOTAL_SNORE = SNORE_PAUSE + SNORE_UP + SNORE_DOWN
|
|
|
|
kbd = Keyboard(usb_hid.devices)
|
|
cc = ConsumerControl(usb_hid.devices)
|
|
|
|
trellis = adafruit_trellism4.TrellisM4Express(rotation=ROTATION)
|
|
for button in keymap:
|
|
trellis.pixels[button] = keymap[button][0]
|
|
|
|
current_press = set()
|
|
last_press = time.monotonic()
|
|
snore_count = -1
|
|
while True:
|
|
pressed = set(trellis.pressed_keys)
|
|
now = time.monotonic()
|
|
sleep_time = now - last_press
|
|
sleeping = sleep_time > TIMEOUT
|
|
for down in pressed - current_press:
|
|
if down in keymap and not sleeping:
|
|
print("down", down)
|
|
# Lower the brightness so that we don't draw too much current when we turn all of
|
|
# the LEDs on.
|
|
trellis.pixels.brightness = 0.2
|
|
trellis.pixels.fill(keymap[down][0])
|
|
if keymap[down][1] == KEY:
|
|
kbd.press(*keymap[down][2])
|
|
else:
|
|
cc.send(keymap[down][2])
|
|
# else if the entry starts with 'l' for layout.write
|
|
last_press = now
|
|
for up in current_press - pressed:
|
|
if up in keymap:
|
|
print("up", up)
|
|
if keymap[up][1] == KEY:
|
|
kbd.release(*keymap[up][2])
|
|
|
|
# Reset the LEDs when there was something previously pressed (current_press) but nothing now
|
|
# (pressed).
|
|
if not pressed and current_press:
|
|
trellis.pixels.brightness = 1
|
|
trellis.pixels.fill((0, 0, 0))
|
|
for button in keymap:
|
|
trellis.pixels[button] = keymap[button][0]
|
|
|
|
if not sleeping:
|
|
snore_count = -1
|
|
else:
|
|
sleep_time -= TIMEOUT
|
|
# Fade all out
|
|
if sleep_time < FADE_TIME:
|
|
brightness = (1 - sleep_time / FADE_TIME)
|
|
# Snore by pausing and then fading a random button up and back down.
|
|
else:
|
|
sleep_time -= FADE_TIME
|
|
current_snore = int(sleep_time / TOTAL_SNORE)
|
|
# Detect a new snore and pick a new button
|
|
if current_snore > snore_count:
|
|
button = random.choice(list(keymap.keys()))
|
|
trellis.pixels.fill((0, 0, 0))
|
|
trellis.pixels[button] = keymap[button][0]
|
|
snore_count = current_snore
|
|
|
|
sleep_time = sleep_time % TOTAL_SNORE
|
|
if sleep_time < SNORE_PAUSE:
|
|
brightness = 0
|
|
else:
|
|
sleep_time -= SNORE_PAUSE
|
|
if sleep_time < SNORE_UP:
|
|
brightness = sleep_time / SNORE_UP
|
|
else:
|
|
sleep_time -= SNORE_UP
|
|
brightness = 1 - sleep_time / SNORE_DOWN
|
|
trellis.pixels.brightness = brightness
|
|
current_press = pressed
|