Macropad hotkeys: Add screensaver mode to prevent OLED burn-in

This commit introduces a screensaver mode to the Adafruit Macropad
hotkeys script, addressing issue #2882.

Key Changes:
- Screensaver Activation: Added a new configurable parameter
  SCREENSAVER_START_TIME, which specifies the duration of inactivity
  (in seconds) before the screensaver activates.
- Class-Level Variables: Introduced class-level variables in the App
  class to track the last activity time, screensaver mode status, and
  breathing effect brightness.
- Screensaver Functions: Added helper functions:
  * enter_screensaver_mode(): Turns off the OLED display and LEDs,
    entering screensaver mode.
  * wake_from_screensaver(): Exits screensaver mode and restores the
    previous state of the display and LEDs.
  * screensaver_breathing_effect(): Implements a breathing LED effect
    during screensaver mode.
- Main Loop Integration: Modified the main loop to monitor inactivity
  and trigger the screensaver mode based on the specified timeout.

This update ensures the Macropad's OLED display is protected from
burn-in during extended periods of inactivity.

Fixes issue #2882.
Signed-off-by: Flavio Fernandes <flavio@flaviof.com>
This commit is contained in:
Flavio Fernandes 2024-09-14 09:26:09 -04:00
parent 24406bfb99
commit fd92dab98c

View file

@ -23,7 +23,7 @@ from adafruit_macropad import MacroPad
# CONFIGURABLES ------------------------
MACRO_FOLDER = '/macros'
SCREENSAVER_START_TIME = 10 * 60 # seconds of inactivity will clear oled to avoid burn in
# CLASSES AND FUNCTIONS ----------------
@ -31,6 +31,13 @@ class App:
""" Class representing a host-side application, for which we have a set
of macro sequences. Project code was originally more complex and
this was helpful, but maybe it's excessive now?"""
# Class-level variables (shared among all instances)
last_activity_time = time.monotonic()
in_screensaver_mode = False
breathing_brightness = 0.1 # Initial brightness
breathing_direction = 1 # 1 for increasing brightness, -1 for decreasing
def __init__(self, appdata):
self.name = appdata['name']
self.macros = appdata['macros']
@ -38,6 +45,7 @@ class App:
def switch(self):
""" Activate application settings; update OLED labels and LED
colors. """
App.last_activity_time = time.monotonic()
group[13].text = self.name # Application name
if self.name:
rect.fill = 0xFFFFFF
@ -57,6 +65,29 @@ class App:
macropad.pixels.show()
macropad.display.refresh()
# SCREENSAVER MODE HELPERS -------------
def enter_screensaver_mode():
macropad.display.auto_refresh = False
macropad.display_sleep = True
for i in range(12):
macropad.pixels[i] = 0 # Turn off all key LEDs
macropad.pixels.show()
App.in_screensaver_mode = True
def wake_from_screensaver():
App.in_screensaver_mode = False
macropad.display_sleep = False
macropad.display.auto_refresh = True
apps[app_index].switch() # Redraw the OLED and LEDs
def screensaver_breathing_effect():
App.breathing_brightness += 0.001 * App.breathing_direction
if App.breathing_brightness >= 1.0 or App.breathing_brightness <= 0.1:
App.breathing_direction *= -1 # Reverse direction
pixel_brightness = int(255 * App.breathing_brightness)
macropad.pixels[0] = (0, pixel_brightness, 0) # Green key for breathing effect
macropad.pixels.show()
# INITIALIZATION -----------------------
@ -111,9 +142,13 @@ apps[app_index].switch()
# MAIN LOOP ----------------------------
while True:
current_time = time.monotonic()
# Read encoder position. If it's changed, switch apps.
position = macropad.encoder
if position != last_position:
if App.in_screensaver_mode:
wake_from_screensaver()
app_index = position % len(apps)
apps[app_index].switch()
last_position = position
@ -132,6 +167,12 @@ while True:
else:
event = macropad.keys.events.get()
if not event or event.key_number >= len(apps[app_index].macros):
if App.in_screensaver_mode:
screensaver_breathing_effect() # Continue breathing effect in screensaver mode
else:
time_since_last_activity = current_time - App.last_activity_time
if time_since_last_activity > SCREENSAVER_START_TIME:
enter_screensaver_mode()
continue # No key events, or no corresponding macro, resume loop
key_number = event.key_number
pressed = event.pressed
@ -140,8 +181,13 @@ while True:
# and there IS a corresponding macro available for it...other situations
# are avoided by 'continue' statements above which resume the loop.
App.last_activity_time = current_time # Reset inactivity timer
sequence = apps[app_index].macros[key_number][2]
if pressed:
if App.in_screensaver_mode:
wake_from_screensaver()
continue # Skip this event, as it was used for screen wake up
# 'sequence' is an arbitrary-length list, each item is one of:
# Positive integer (e.g. Keycode.KEYPAD_MINUS): key pressed
# Negative integer: (absolute value) key released