diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/code.py | 39 | ||||
| -rw-r--r-- | src/skeleton.py | 15 | ||||
| -rw-r--r-- | src/sleep_mode.py | 57 | ||||
| -rw-r--r-- | src/ticks.py | 4 | 
4 files changed, 100 insertions, 15 deletions
| diff --git a/src/code.py b/src/code.py index 813574a..ad634ad 100644 --- a/src/code.py +++ b/src/code.py @@ -28,6 +28,7 @@ from supervisor import runtime  from actions import Action  import skeleton  import menu +from sleep_mode import Power  # CONFIGURABLES ------------------------ @@ -43,6 +44,8 @@ macropad = MacroPad()  macropad.display.auto_refresh = False  macropad.pixels.auto_write = False +power = Power(macropad) +  Action().set_macropad(macropad)  # Load all the macro key setups from .py files in MACRO_FOLDER @@ -76,6 +79,7 @@ if not apps:  app_index = 0 +  def run_application(app):      # Read encoder position. If it's changed, declare it as a button.      position = app.macropad.encoder @@ -91,32 +95,41 @@ def run_application(app):          event = app.macropad.keys.events.get()          if not event:              app.tick() +            power.tick()              return # No key events          key_number = event.key_number          pressed = event.pressed +    power.execute_action(key_number, pressed or False)      return app.execute_action(key_number, pressed or False)  def switch_layout(configuration):      global app      app = skeleton.Handler(macropad, configuration) +    power.set_configuration(configuration)      app.start()  def search_and_switch(layout): -    found_application = False      for local_app in apps:          if local_app.title == layout and app is not local_app: -            found_application = True              switch_layout(local_app)              break +def stack(layout): +    for configuration in apps: +        if configuration.title == layout and app is not configuration: +            app.stack(configuration) +            app.start() +            break +  def send_layout(_any):      if usb_cdc.data.connected:          usb_cdc.data.write(bytes("%s\n" % app.name, "utf-8"))  actions = {      "layout" : search_and_switch, +    "stack" :  stack,      "get_layout" : send_layout,    } @@ -128,13 +141,18 @@ while True:      # First read from the serial      # If we have an application name from the command line, switch to this app. -    if usb_cdc.data.connected and usb_cdc.data.in_waiting != 0: -        line = usb_cdc.data.read(usb_cdc.data.in_waiting).strip() -        commands = json.loads(line) -        for command in commands: -            for key, value in command.items(): -                actions[key](value) -        continue +    if usb_cdc.data.connected: +        while usb_cdc.data.in_waiting != 0: +            line = usb_cdc.data.readline(usb_cdc.data.in_waiting).strip() +            try: +                commands = json.loads(line) +                for command in commands: +                    for key, value in command.items(): +                        actions[key](value) +                continue +            except ValueError: +                print(line) +                pass      # Handle encoder button. If state has changed, and if there's a      # corresponding macro, set up variables to act on this just like @@ -152,7 +170,8 @@ while True:              if configuration is None: continue              switch_layout(configuration)              break -        send_layout(None) +        if configuration.is_layout: +            send_layout(None)      run_application(app)      continue diff --git a/src/skeleton.py b/src/skeleton.py index 8497d34..1e4a03a 100644 --- a/src/skeleton.py +++ b/src/skeleton.py @@ -42,6 +42,9 @@ class Configuration(object):          # Set the application available in the menu by default          self.visible = True + +        self.is_layout = True +          self.Action = namedtuple("Action", ("name", "callback", "color"))          self.title = title          self.keys = { @@ -88,10 +91,16 @@ class Handler(object):                  9  : None,  10 : None,  11 : None,          } -        self.options = configuration.group +        self.stack(configuration) +    def stack(self, configuration): +        """ Add a new configuration on top of an existing one +        """ +        self.group = displayio.Group()          self.name = configuration.title +        self.options = configuration.group +          for index, value in configuration.keys.items():              if value is None:                  continue @@ -112,6 +121,7 @@ class Handler(object):              self._update_tick()          self.macropad.pixels.show() +      def _update_tick(self):          self.next_tick = ticks.add(                  ticks_ms(), @@ -162,8 +172,7 @@ class Handler(object):          self.macropad.consumer_control.release()          self.macropad.mouse.release_all()          self.macropad.stop_tone() -        for key in range(12): -            self._set_color(key, False) +        self.macropad.pixels.fill((0, 0, 0))          self.macropad.pixels.show()          self.macropad.display.refresh() diff --git a/src/sleep_mode.py b/src/sleep_mode.py new file mode 100644 index 0000000..9c56c24 --- /dev/null +++ b/src/sleep_mode.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# The firmware I’m using does not have the function display_sleep available. So +# I’m sending the raw code directly. +# +# https://docs.circuitpython.org/projects/macropad/en/latest/api.html#adafruit_macropad.MacroPad.display_sleep +# + +timeout = 15 * 60 * 1000 +from supervisor import ticks_ms +import ticks + +class Power(object): + +    def __init__(self, macropad): +        self.macropad = macropad +        self.last_update = ticks_ms() +        self.configuration = None +        self.sleep = False +        self.macropad.display.brightness = 0 +        self.macropad.display.bus.send(0xAF, b"") + +    def start(self): +        self.last_update = ticks_ms() +        self._wake() + +    def set_configuration(self, configuration): +        self.configuration = configuration +        self.start() + +    def _sleep(self): +        if self.sleep: +            return +        self.sleep = True +        self.macropad.pixels.fill((0, 0, 0)) +        self.macropad.pixels.show() +        self.macropad.display.bus.send(0xAE, b"") + +    def _wake(self): +        if not self.sleep: +            return +        self.sleep = False +        #self.macropad.display_sleep = False +        for index, value in self.configuration.keys.items(): +            if value is None or index >= 12: +                continue +            self.macropad.pixels[index] = value.color +        self.macropad.pixels.show() +        self.macropad.display.bus.send(0xAF, b"") + +    def tick(self): +        if ticks.diff(ticks_ms(), self.last_update) > timeout: +            self._sleep() + +    def execute_action(self, key, pressed): +        self.start() diff --git a/src/ticks.py b/src/ticks.py index ba6831a..1f7e943 100644 --- a/src/ticks.py +++ b/src/ticks.py @@ -10,7 +10,7 @@ def add(ticks, delta):      "Add a delta to a base number of ticks, performing wraparound at 2**29ms."      return (ticks + delta) % _TICKS_PERIOD -def ticks_diff(ticks1, ticks2): +def diff(ticks1, ticks2):      "Compute the signed difference between two ticks values, assuming that they are within 2**28 ticks"      diff = (ticks1 - ticks2) & _TICKS_MAX      diff = ((diff + _TICKS_HALFPERIOD) & _TICKS_MAX) - _TICKS_HALFPERIOD @@ -18,4 +18,4 @@ def ticks_diff(ticks1, ticks2):  def less(ticks):      "Return true iff ticks1 is less than ticks2, assuming that they are within 2**28 ticks" -    return ticks_diff(ticks_ms(), ticks) < 0 +    return diff(ticks_ms(), ticks) < 0 | 
