First, the libraries are imported.
import time import board import neopixel import touchio import usb_hid from adafruit_hid.keyboard import Keyboard from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS from adafruit_hid.keycode import Keycode
Then, the NeoPixels are setup. The four onboard NeoPixels can be accessed with board.NEOPIXEL.
# setup for onboard neopixels pixel_pin = board.NEOPIXEL num_pixels = 4 pixels = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.05, auto_write=False)
The two capacitive touch pads are accessed with board.TOUCH1 and board.TOUCH2. They're setup as touch inputs with the touchio library.
# setup for cap touch pads top_touch = touchio.TouchIn(board.TOUCH1) bot_touch = touchio.TouchIn(board.TOUCH2)
keyboard is setup as the USB HID keyboard object. Zoom uses the ALT key for a lot of its built-in shortcuts, so alt_key is setup as a variable to hold Keycode.ALT.
# HID keyboard input setup keyboard = Keyboard(usb_hid.devices) keyboard_layout = KeyboardLayoutUS(keyboard) # variable for the ALT key alt_key = Keycode.ALT
The classic rainbow_cycle NeoPixel animation is included, followed by RED and GREEN RGB color values that will be used to show if your inputs are muted or unmuted.
# rainbow cycle animation
def wheel(pos):
# Input a value 0 to 255 to get a color value.
# The colours are a transition r - g - b - back to r.
if pos < 0 or pos > 255:
return (0, 0, 0)
if pos < 85:
return (255 - pos * 3, pos * 3, 0)
if pos < 170:
pos -= 85
return (0, 255 - pos * 3, pos * 3)
pos -= 170
return (pos * 3, 0, 255 - pos * 3)
def rainbow_cycle(wait):
for j in range(255):
for i in range(num_pixels):
rc_index = (i * 256 // num_pixels) + j
pixels[i] = wheel(rc_index & 255)
pixels.show()
time.sleep(wait)
# variables for colors
RED = (255, 0, 0)
GREEN = (0, 255, 0)
A few state machines are used in the loop and their functions are commented in the code.
# state machines # cap touch debouncing bot_pressed = False top_pressed = False # default mute states mic_mute = True vid_mute = True # time.monotonic() tracker clock = time.monotonic() # tracking for initiating an exit from the meeting escape = False escape_1 = False escape_2 = False
The loop begins with some debouncing for the cap touch inputs.
while True:
# cap touch debouncing
if not top_touch.value and top_pressed:
top_pressed = False
if not bot_touch.value and bot_pressed:
bot_pressed = False
The mic_mute and vid_mute states affect whether the two corresponding NeoPixels are green or red.
# if your mic is muted...
if mic_mute:
# neopixels are red
pixels[0] = RED
pixels[1] = RED
pixels.show()
# if your camera is muted...
if vid_mute:
# neopixels are red
pixels[2] = RED
pixels[3] = RED
pixels.show()
# if your mic is NOT muted...
if not mic_mute:
# neopixels are green
pixels[0] = GREEN
pixels[1] = GREEN
pixels.show()
# if your camera is NOT muted...
if not vid_mute:
# neopixels are green
pixels[2] = GREEN
pixels[3] = GREEN
pixels.show()
The escape state cues the start of the rainbow_cycle animation when you exit a meeting. It also resets the escape_1, escape_2, mic_mute and vid_mute states to their defaults.
# if you are leaving the meeting...
if escape:
# neopixels are rainbow
rainbow_cycle(0)
# resets exit states
escape = False
escape_1 = False
escape_2 = False
mic_mute = True
vid_mute = True
When you press the top touch pad, time.monotonic() is called to begin a time count. This is followed by a delay of 0.12 seconds. This tracks if you are holding the touch input for an exit. Otherwise, the code would go right to muting or unmuting your webcam.
# if you press the top touch cap touch pad...
if (top_touch.value and not top_pressed):
top_pressed = True
# start time count for exit
clock = time.monotonic()
# slight delay so that you don't automatically mute/unmute
# if your intent is to exit
time.sleep(0.12)
If you are inputting a long press to trigger an exit from the meeting, the escape_1 state is set to True, which will begin initiating the exit. This also keeps your webcam's mute status unchanged so that you don't suddenly mute or unmute while you're leaving the meeting.
# if after the delay you're still pressing the cap touch pad...
if top_touch.value and top_pressed:
print("escape top")
# initial escape state is set to true
escape_1 = True
If you aren't inputting a long press, then the vid_mute status will change depending on the previous state and the shortcut for muting/unmuting the video (ALT+V) is sent.
# if you aren't still pressing the cap touch pad...
else:
# if your camera was muted...
if vid_mute:
print("top")
# your camera is NOT muted
vid_mute = False
# resets escape state just in case
escape_1 = False
# if your camera was NOT muted...
elif not vid_mute:
print("top")
# your camera is muted
vid_mute = True
# resets escape state just in case
escape_1 = False
# sends camera mute/unmute shortcut
keyboard.send(alt_key, Keycode.V)
The same logic is in place for the bottom capacitive touch input, which controls the microphone.
# if you press the top touch cap touch pad...
if (bot_touch.value and not bot_pressed):
bot_pressed = True
# start time count for exit
clock = time.monotonic()
# slight delay so that you don't automatically mute/unmute
# if your intent is to exit
time.sleep(0.12)
# if after the delay you're still pressing the cap touch pad...
if bot_touch.value and bot_pressed:
print("escape bot")
# initial escape state is set to true
escape_2 = True
# if you aren't still pressing the cap touch pad...
else:
# if your mic was muted...
if mic_mute:
print("bot")
# your mic is NOT muted
mic_mute = False
# resets escape state just in case
escape_2 = False
# if your mic was NOT muted...
elif not mic_mute:
print("bot")
# your mic is muted
mic_mute = True
# resets escape state just in case
escape_2 = False
# sends mic mute/unmute shortcut
keyboard.send(alt_key, Keycode.A)
If you hold down both capacitive touch pads at the same time, escape is set to True after 2 seconds. This delay ensures no accidental exits from the meeting.
Then, the shortcut for exiting a meeting (ALT+Q) is sent, followed by a slight delay before sending the Enter key. Zoom puts up a confirmation window before you leave or end a meeting, so by sending Enter it fully automates your exit.
# if you held down both cap touch pads and 2 seconds has passed...
if ((clock + 2) < time.monotonic()) and (escape_1 and escape_2):
print("escape")
# full escape state is set
escape = True
# sends exit meeting shortcut
keyboard.send(alt_key, Keycode.Q)
# brief delay for confirmation window to open
time.sleep(0.1)
# sends enter to confirm meeting exit
keyboard.send(Keycode.ENTER)
Page last edited March 08, 2024
Text editor powered by tinymce.