initial app helpers completed - moving to make the default apps next.
This commit is contained in:
@@ -1,16 +1,32 @@
|
||||
import storage
|
||||
|
||||
from macropad_os.config import Config, ConfigVars
|
||||
from macropad_os import Config
|
||||
|
||||
config = Config("config.json")
|
||||
|
||||
print("test")
|
||||
print(config.data)
|
||||
default_config = Config("default_config.json").load()
|
||||
config = Config("config.json").load(default_config)
|
||||
dev_mode = config.get_item_by_name("dev_mode")
|
||||
|
||||
if not config.data.get(ConfigVars.DEV_MODE.name):
|
||||
if dev_mode :
|
||||
if not dev_mode.value:
|
||||
print("file system should not be writable - dev mode not active")
|
||||
storage.disable_usb_drive()
|
||||
print("file system should be writable")
|
||||
storage.remount("/", False)
|
||||
config.data[ConfigVars.DEV_MODE.name] = True
|
||||
dev_mode_active = config.get_item_by_name("dev_mode_active")
|
||||
dev_mode_active.value = False
|
||||
config.set_item(dev_mode_active)
|
||||
config.save()
|
||||
print("success")
|
||||
else:
|
||||
# dev mode is on and needs to be disabled
|
||||
print("Disabling dev mode before boot so that settings will work")
|
||||
storage.disable_usb_drive()
|
||||
storage.remount("/", False)
|
||||
dev_mode = config.get_item_by_name("dev_mode")
|
||||
dev_mode.value = False
|
||||
config.set_item(dev_mode)
|
||||
dev_mode_active = config.get_item_by_name("dev_mode_active")
|
||||
dev_mode_active.value = True
|
||||
config.set_item(dev_mode_active)
|
||||
config.save()
|
||||
storage.enable_usb_drive()
|
||||
storage.remount("/", True)
|
||||
@@ -1,16 +1,19 @@
|
||||
from macropad_os import AppRouter, DebugApp, SerialComms
|
||||
from macropad_os.config import Config
|
||||
from macropad_os import AppRouter, SerialComms, Config
|
||||
from macropad_os.system_apps import DebugApp
|
||||
|
||||
from adafruit_macropad import MacroPad
|
||||
|
||||
from python_apps import NumpadApp
|
||||
|
||||
macropad = MacroPad()
|
||||
config = Config("config.json")
|
||||
|
||||
default_config = Config("default_config.json").load()
|
||||
config = Config("config.json").load(default_config)
|
||||
|
||||
ar = AppRouter(macropad, config, [
|
||||
DebugApp(macropad, config, "DEBUG 1"),
|
||||
DebugApp(macropad, config, "DEBUG 2")
|
||||
NumpadApp(macropad, config),
|
||||
# Arrow Keys
|
||||
# Script Runner
|
||||
])
|
||||
|
||||
sc = SerialComms(config)
|
||||
|
||||
+85
-4
@@ -1,6 +1,87 @@
|
||||
{
|
||||
"dev_mode": true,
|
||||
"debug_app_enabled": true,
|
||||
"brightness": 100,
|
||||
"default_tone": 523
|
||||
"rgb_mode": {
|
||||
"current_value": "rainbow",
|
||||
"available_values": [
|
||||
"rainbow",
|
||||
"static"
|
||||
]
|
||||
},
|
||||
"rgb_green": {
|
||||
"current_value": 100,
|
||||
"value_range": {
|
||||
"end": 1500,
|
||||
"start": 0
|
||||
},
|
||||
"dependency": {
|
||||
"rgb_mode": "static"
|
||||
}
|
||||
},
|
||||
"rgb_red": {
|
||||
"current_value": 100,
|
||||
"value_range": {
|
||||
"end": 1500,
|
||||
"start": 0
|
||||
},
|
||||
"dependency": {
|
||||
"rgb_mode": "static"
|
||||
}
|
||||
},
|
||||
"key_tone_hz": {
|
||||
"current_value": 690,
|
||||
"value_range": {
|
||||
"end": 1500,
|
||||
"start": 0
|
||||
},
|
||||
"dependency": {
|
||||
"key_tone": true
|
||||
}
|
||||
},
|
||||
"brightness": {
|
||||
"current_value": 75,
|
||||
"value_range": {
|
||||
"end": 100,
|
||||
"start": 1
|
||||
}
|
||||
},
|
||||
"rgb_blue": {
|
||||
"current_value": 100,
|
||||
"value_range": {
|
||||
"end": 1500,
|
||||
"start": 0
|
||||
},
|
||||
"dependency": {
|
||||
"rgb_mode": "static"
|
||||
}
|
||||
},
|
||||
"key_tone": {
|
||||
"current_value": false,
|
||||
"available_values": [
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"dev_mode": {
|
||||
"current_value": false,
|
||||
"available_values": [
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"debug_app": {
|
||||
"current_value": true,
|
||||
"available_values": [
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"dev_mode_active": {
|
||||
"current_value": false,
|
||||
"available_values": [
|
||||
true,
|
||||
false
|
||||
],
|
||||
"dependency": {
|
||||
"never resolving dependency": "asdf"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"rgb_mode": {
|
||||
"current_value": "rainbow",
|
||||
"available_values": [
|
||||
"rainbow",
|
||||
"static"
|
||||
]
|
||||
},
|
||||
"rgb_green": {
|
||||
"current_value": 100,
|
||||
"value_range": {
|
||||
"end": 1500,
|
||||
"start": 0
|
||||
},
|
||||
"dependency": {
|
||||
"rgb_mode": "static"
|
||||
}
|
||||
},
|
||||
"rgb_red": {
|
||||
"current_value": 100,
|
||||
"value_range": {
|
||||
"end": 1500,
|
||||
"start": 0
|
||||
},
|
||||
"dependency": {
|
||||
"rgb_mode": "static"
|
||||
}
|
||||
},
|
||||
"key_tone_hz": {
|
||||
"current_value": 523,
|
||||
"value_range": {
|
||||
"end": 1500,
|
||||
"start": 0
|
||||
},
|
||||
"dependency": {
|
||||
"key_tone": true
|
||||
}
|
||||
},
|
||||
"brightness": {
|
||||
"current_value": 100,
|
||||
"value_range": {
|
||||
"end": 100,
|
||||
"start": 0
|
||||
}
|
||||
},
|
||||
"rgb_blue": {
|
||||
"current_value": 100,
|
||||
"value_range": {
|
||||
"end": 1500,
|
||||
"start": 0
|
||||
},
|
||||
"dependency": {
|
||||
"rgb_mode": "static"
|
||||
}
|
||||
},
|
||||
"key_tone": {
|
||||
"current_value": true,
|
||||
"available_values": [
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"dev_mode": {
|
||||
"current_value": false,
|
||||
"available_values": [
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"dev_mode_active": {
|
||||
"current_value": false,
|
||||
"available_values": [
|
||||
true,
|
||||
false
|
||||
],
|
||||
"dependency": {
|
||||
"never resolving dependency": "asdf"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
from .abstract_app import App
|
||||
from .app_state import AppState, InvalidStateUpdateError
|
||||
from .app_router import AppRouter
|
||||
from macropad_os.default_apps.debug_app import DebugApp
|
||||
from .serial_communications import SerialComms
|
||||
from .configuration import Config, ConfigItem
|
||||
from .app_state import AppState, InvalidStateUpdateError
|
||||
from .abstract_app import App
|
||||
from .app_router import AppRouter
|
||||
+61
-34
@@ -1,8 +1,10 @@
|
||||
import time
|
||||
|
||||
import displayio
|
||||
import terminalio
|
||||
from adafruit_display_text import label
|
||||
|
||||
from .app_state import AppState, InvalidStateUpdateError
|
||||
from macropad_os import AppState, InvalidStateUpdateError, Config
|
||||
|
||||
DISPLAY = displayio.Group()
|
||||
|
||||
@@ -13,7 +15,7 @@ def convert_to_keynum(x, y):
|
||||
|
||||
class App(object):
|
||||
|
||||
def __init__(self, macropad, config):
|
||||
def __init__(self, macropad, config: Config):
|
||||
"""
|
||||
|
||||
:param macropad:
|
||||
@@ -31,28 +33,29 @@ class App(object):
|
||||
text=f"",
|
||||
background_color=0xFFFFFF,
|
||||
)
|
||||
self._key_tones = [config.data["default_tone"] for _ in range(12)]
|
||||
self._enabled_key_sounds = True
|
||||
self._pressed_keys = []
|
||||
self._key_pressed_callbacks = []
|
||||
self._key_released_callbacks = []
|
||||
self._encoder_changed_callbacks = []
|
||||
self._encoder_state = 0
|
||||
self._labels = []
|
||||
self._state = AppState.STOPPED
|
||||
self._name = "app"
|
||||
self._key_tones = {}
|
||||
|
||||
self._current_brightness = config.brightness()
|
||||
|
||||
self.macropad = macropad
|
||||
self.config = config
|
||||
self.name = "app"
|
||||
self.state = AppState.STOPPED
|
||||
|
||||
def start(self) -> None:
|
||||
print("Start from base class ")
|
||||
if self.state is not AppState.STOPPED:
|
||||
raise InvalidStateUpdateError(f"Start called but the current app state is {self.state}")
|
||||
self.state = AppState.STARTING
|
||||
if self._state is not AppState.STOPPED:
|
||||
raise InvalidStateUpdateError(f"Start called but the current app state is {self._state}")
|
||||
self._state = AppState.STARTING
|
||||
self._on_start()
|
||||
self.on_start()
|
||||
self.state = AppState.PAUSED
|
||||
self._state = AppState.PAUSED
|
||||
|
||||
def _on_start(self) -> None:
|
||||
pass
|
||||
@@ -61,12 +64,12 @@ class App(object):
|
||||
raise NotImplementedError("on_start not implemented")
|
||||
|
||||
def resume(self) -> None:
|
||||
if self.state is not AppState.PAUSED:
|
||||
raise InvalidStateUpdateError(f"Resume called but the current app state is {self.state}")
|
||||
self.state = AppState.RESUMING
|
||||
if self._state is not AppState.PAUSED:
|
||||
raise InvalidStateUpdateError(f"Resume called but the current app state is {self._state}")
|
||||
self._state = AppState.RESUMING
|
||||
self._on_resume()
|
||||
self.on_resume()
|
||||
self.state = AppState.RUNNING
|
||||
self._state = AppState.RUNNING
|
||||
|
||||
def _on_resume(self) -> None:
|
||||
self.add_displays_to_group()
|
||||
@@ -75,12 +78,12 @@ class App(object):
|
||||
raise NotImplementedError("on_resume not implemented")
|
||||
|
||||
def pause(self) -> None:
|
||||
if self.state is not AppState.RUNNING:
|
||||
raise InvalidStateUpdateError(f"Pause called but the current app state is {self.state}")
|
||||
self.state = AppState.PAUSING
|
||||
if self._state is not AppState.RUNNING:
|
||||
raise InvalidStateUpdateError(f"Pause called but the current app state is {self._state}")
|
||||
self._state = AppState.PAUSING
|
||||
self._on_pause()
|
||||
self.on_pause()
|
||||
self.state = AppState.PAUSED
|
||||
self._state = AppState.PAUSED
|
||||
|
||||
def _on_pause(self) -> None:
|
||||
self.macropad.keyboard.release_all()
|
||||
@@ -113,20 +116,34 @@ class App(object):
|
||||
if key_event.key_number < 12:
|
||||
if key_event.pressed:
|
||||
self.macropad.stop_tone()
|
||||
self.macropad.start_tone(self._key_tones[key_event.key_number])
|
||||
self._pressed_keys.append(key_event.key_number)
|
||||
print(self.config.get_items())
|
||||
self._play_tone_for_key(key_event.key_number)
|
||||
if self._key_pressed_callbacks:
|
||||
for callback in self._key_pressed_callbacks:
|
||||
callback(key_event.key_number)
|
||||
else:
|
||||
self.macropad.stop_tone()
|
||||
self._pressed_keys.remove(key_event.key_number)
|
||||
if self._pressed_keys:
|
||||
self.macropad.start_tone(self._key_tones[self._pressed_keys[0]])
|
||||
self._stop_tone_for_key(key_event.key_number)
|
||||
if self._key_released_callbacks:
|
||||
for callback in self._key_pressed_callbacks:
|
||||
callback(key_event.key_number)
|
||||
|
||||
def _play_tone_for_key(self, key_number):
|
||||
if self.config.key_tone_enabled():
|
||||
if key_number in self._key_tones:
|
||||
self.macropad.start_tone(self._key_tones[key_number])
|
||||
else:
|
||||
self.macropad.start_tone(self.config.key_tone_hz())
|
||||
self._pressed_keys.append(key_number)
|
||||
|
||||
def _stop_tone_for_key(self, key_number):
|
||||
self.macropad.stop_tone()
|
||||
self._pressed_keys.remove(key_number)
|
||||
if self._pressed_keys and self.config.key_tone_enabled():
|
||||
if key_number in self._key_tones:
|
||||
self.macropad.start_tone(self._key_tones[self._pressed_keys[0]])
|
||||
else:
|
||||
self.macropad.start_tone(self.config.key_tone_hz())
|
||||
|
||||
def _process_wheel_changes(self) -> None:
|
||||
encoder = self.macropad.encoder
|
||||
if self._encoder_state != encoder:
|
||||
@@ -138,12 +155,12 @@ class App(object):
|
||||
self._encoder_state = encoder
|
||||
|
||||
def stop(self) -> None:
|
||||
if self.state is not AppState.PAUSED:
|
||||
raise InvalidStateUpdateError(f"Stop called but the current app state is {self.state}")
|
||||
self.state = AppState.STOPPING
|
||||
if self._state is not AppState.PAUSED:
|
||||
raise InvalidStateUpdateError(f"Stop called but the current app state is {self._state}")
|
||||
self._state = AppState.STOPPING
|
||||
self._on_stop()
|
||||
self.on_stop()
|
||||
self.state = AppState.STOPPED
|
||||
self._state = AppState.STOPPED
|
||||
|
||||
def _on_stop(self) -> None:
|
||||
pass
|
||||
@@ -152,6 +169,14 @@ class App(object):
|
||||
raise NotImplementedError("on_stop not implemented.")
|
||||
|
||||
def _update_lighting(self) -> None:
|
||||
new_brightness = self.config.brightness()
|
||||
if self._current_brightness != new_brightness:
|
||||
print("SETTING BRIGHTNESS!!!!")
|
||||
print(self._key_lights)
|
||||
self._key_lights = [tuple(rgb_val * new_brightness / self._current_brightness for rgb_val in color) for
|
||||
color in self._key_lights]
|
||||
print(self._key_lights)
|
||||
self._current_brightness = self.config.brightness()
|
||||
for index, color in enumerate(self._key_lights):
|
||||
self.macropad.pixels[index] = color
|
||||
|
||||
@@ -165,6 +190,8 @@ class App(object):
|
||||
self._display_group.remove(self._layout)
|
||||
|
||||
def set_color(self, x, y, color) -> None:
|
||||
print("setting color")
|
||||
color = tuple(rgb_val * self._current_brightness for rgb_val in color)
|
||||
key_value = convert_to_keynum(x, y)
|
||||
if key_value >= 12:
|
||||
raise ValueError("color index out of range")
|
||||
@@ -172,12 +199,14 @@ class App(object):
|
||||
self._key_lights[key_value] = color
|
||||
|
||||
def set_colors(self, colors) -> None:
|
||||
print(time.time())
|
||||
if len(colors) != 12:
|
||||
raise ValueError("Colors must be passed in as a 12 len array")
|
||||
for color in colors:
|
||||
if len(color) != 3:
|
||||
raise ValueError("Color format error - color must be length 3")
|
||||
self._key_lights = colors
|
||||
self._key_lights = [tuple(round(rgb_val * self._current_brightness / 100) for rgb_val in color) for color in
|
||||
colors]
|
||||
|
||||
def get_colors(self) -> [(int, int, int)]:
|
||||
return self._key_lights
|
||||
@@ -195,12 +224,10 @@ class App(object):
|
||||
for tone in tones:
|
||||
if tone < 20 or tone > 20000:
|
||||
raise ValueError("Tone format error - tone out of human hearing range (20 - 20000)")
|
||||
self._key_tones = tones
|
||||
for index, hz in enumerate(tones):
|
||||
self._key_tones[index] = hz
|
||||
|
||||
def set_tone_status(self, enable) -> None:
|
||||
self._enabled_key_sounds = enable
|
||||
|
||||
def get_tones(self) -> [int]:
|
||||
def get_tones(self) -> {int: int}:
|
||||
return self._key_tones
|
||||
|
||||
def set_title(self, title) -> None:
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import time
|
||||
|
||||
from .app_state import AppState
|
||||
from .default_apps.options_app import OptionsApp
|
||||
|
||||
from macropad_os.system_apps import OptionsApp, DebugApp
|
||||
|
||||
|
||||
class AppRouter(object):
|
||||
def __init__(self, macropad, config, apps):
|
||||
@@ -15,6 +16,10 @@ class AppRouter(object):
|
||||
self.encoder_state = False
|
||||
self.options_time = 500000000 # .5 seconds in nanoseconds
|
||||
self.click_time = 0
|
||||
self.debug_app = DebugApp(macropad, config, "DEBUG APP")
|
||||
self.debug_app_active = self.config.debug_app_enabled()
|
||||
if self.debug_app_active:
|
||||
self.apps.append(self.debug_app)
|
||||
|
||||
def swap_to_app(self, app):
|
||||
"""
|
||||
@@ -26,10 +31,10 @@ class AppRouter(object):
|
||||
self.current_app.pause()
|
||||
print("Selecting new app")
|
||||
self.current_app = app
|
||||
if self.current_app.state is AppState.STOPPED:
|
||||
if self.current_app._state is AppState.STOPPED:
|
||||
print("Starting new app")
|
||||
self.current_app.start()
|
||||
if self.current_app.state is AppState.PAUSED:
|
||||
if self.current_app._state is AppState.PAUSED:
|
||||
print("Starting new app")
|
||||
self.current_app.resume()
|
||||
|
||||
@@ -51,6 +56,12 @@ class AppRouter(object):
|
||||
self.encoder_state = False
|
||||
if self.current_app is self.options:
|
||||
print("Moving from options to the last opened app.")
|
||||
if self.debug_app_active != self.config.debug_app_enabled():
|
||||
if self.debug_app_active:
|
||||
self.apps.append(self.debug_app)
|
||||
else:
|
||||
self.apps.remove(self.debug_app)
|
||||
self.debug_app_active = self.config.debug_app_enabled()
|
||||
else:
|
||||
self.app_index += 1
|
||||
print("Moving to the next app on the list. ")
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
def clamp(num, min_value, max_value):
|
||||
return max(min(num, max_value), min_value)
|
||||
|
||||
|
||||
|
||||
def rgb_from_int(rgb):
|
||||
blue = rgb & 255
|
||||
green = (rgb >> 8) & 255
|
||||
red = (rgb >> 16) & 255
|
||||
return red, green, blue
|
||||
@@ -0,0 +1,20 @@
|
||||
arrows_with_enter = [
|
||||
(0, 0, 0), (0, 0, 0), (0, 0, 0),
|
||||
(0, 0, 0), (0, 0, 100), (0, 0, 0),
|
||||
(0, 0, 100), (100, 0, 0), (0, 0, 100),
|
||||
(0, 0, 0), (0, 0, 100), (0, 0, 0)
|
||||
]
|
||||
|
||||
up_down_enter = [
|
||||
(0, 0, 0), (0, 0, 0), (0, 0, 0),
|
||||
(0, 0, 0), (0, 0, 100), (0, 0, 0),
|
||||
(0, 0, 0), (100, 0, 0), (0, 0, 0),
|
||||
(0, 0, 0), (0, 0, 100), (0, 0, 0)
|
||||
]
|
||||
|
||||
arrows_yes_no = [
|
||||
(0, 0, 0), (0, 0, 100), (0, 0, 0),
|
||||
(100, 0, 0), (0, 0, 100), (0, 100, 0),
|
||||
(0, 0, 0), (0, 0, 0), (0, 0, 0),
|
||||
(0, 0, 0), (0, 0, 0), (0, 0, 0),
|
||||
]
|
||||
@@ -1,2 +0,0 @@
|
||||
from .config_vars import ConfigVars, ConVar
|
||||
from .config import Config
|
||||
@@ -1,21 +0,0 @@
|
||||
import json
|
||||
|
||||
from .config_vars import ConfigVars
|
||||
|
||||
|
||||
class Config(object):
|
||||
def __init__(self, save_file):
|
||||
self.data = {}
|
||||
self.save_file = save_file
|
||||
self.load()
|
||||
|
||||
def load(self):
|
||||
with open(self.save_file, "r") as f:
|
||||
self.data = json.load(f)
|
||||
for convar in ConfigVars.__dict__.keys():
|
||||
print(convar)
|
||||
|
||||
def save(self):
|
||||
with open(self.save_file, "w") as f:
|
||||
json.dump(self.data, f)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
class ConVar(object):
|
||||
def __init__(self, name, default):
|
||||
self.name = name
|
||||
self.default = default
|
||||
|
||||
|
||||
class ConfigVars(object):
|
||||
DEV_MODE = ConVar("dev_mode", True)
|
||||
TEST_VAR = ConVar("test_var", False)
|
||||
@@ -0,0 +1,2 @@
|
||||
from .config import Config
|
||||
from .config_item import ConfigItem
|
||||
@@ -0,0 +1,61 @@
|
||||
import json
|
||||
|
||||
from .config_item import ConfigItem
|
||||
|
||||
|
||||
class Config(object):
|
||||
def __init__(self, save_file):
|
||||
self._data = {}
|
||||
self._items = {}
|
||||
self._save_file = save_file
|
||||
|
||||
def load(self, default_config=None):
|
||||
try:
|
||||
f = open(self._save_file, "r")
|
||||
self._data = json.load(f)
|
||||
self._items = {}
|
||||
for key, value in self._data.items():
|
||||
self._items[key] = ConfigItem.from_json(key, value)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
self._items = {}
|
||||
self._data = {}
|
||||
if default_config:
|
||||
for item in default_config._items:
|
||||
if not (item in self._items):
|
||||
self._items[item] = default_config._items[item]
|
||||
return self
|
||||
|
||||
def get_items(self):
|
||||
return self._items
|
||||
|
||||
def get_item_by_name(self, key):
|
||||
if key in self._items:
|
||||
return self._items[key]
|
||||
else:
|
||||
return None
|
||||
|
||||
def set_item(self, config_item: ConfigItem):
|
||||
self._items[config_item.name] = config_item
|
||||
return self
|
||||
|
||||
def save(self):
|
||||
save_format = {}
|
||||
for name, item in self._items.items():
|
||||
save_format[name] = item.to_json()
|
||||
with open(self._save_file, "w") as f:
|
||||
json.dump(save_format, f)
|
||||
self._data = save_format
|
||||
return self
|
||||
|
||||
def key_tone_enabled(self):
|
||||
return self.get_item_by_name("key_tone").value
|
||||
|
||||
def key_tone_hz(self):
|
||||
return self.get_item_by_name("key_tone_hz").value if self.key_tone_enabled() else 0
|
||||
|
||||
def brightness(self):
|
||||
return self.get_item_by_name("brightness").value
|
||||
|
||||
def debug_app_enabled(self):
|
||||
return self.get_item_by_name("debug_app").value
|
||||
@@ -0,0 +1,50 @@
|
||||
class ValueRange(object):
|
||||
def __init__(self, start, end):
|
||||
self.start = start
|
||||
self.end = end
|
||||
|
||||
@staticmethod
|
||||
def from_json(json_dict: dict):
|
||||
if not ("start" in json_dict and "end" in json_dict):
|
||||
raise ValueError("Value Range must be provided a start and end value in the provided json dictionary")
|
||||
return ValueRange(json_dict["start"], json_dict["end"])
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
"start": self.start,
|
||||
"end": self.end
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.to_json()}"
|
||||
|
||||
|
||||
class ConfigItem(object):
|
||||
def __init__(self, name: str, value, available_values: list = None, value_range: ValueRange = None,
|
||||
dependency: dict = None):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.available_values = available_values
|
||||
self.value_range = value_range
|
||||
self.dependency = dependency
|
||||
|
||||
@staticmethod
|
||||
def from_json(key: str, json_dict: dict):
|
||||
return ConfigItem(
|
||||
key,
|
||||
json_dict["current_value"],
|
||||
available_values=json_dict["available_values"] if "available_values" in json_dict else None,
|
||||
value_range=ValueRange.from_json(json_dict["value_range"]) if "value_range" in json_dict else None,
|
||||
dependency=json_dict["dependency"] if "dependency" in json_dict else None,
|
||||
)
|
||||
|
||||
def to_json(self):
|
||||
json_dict = {"current_value": self.value}
|
||||
if self.value_range:
|
||||
json_dict["value_range"] = self.value_range.to_json()
|
||||
if self.available_values:
|
||||
json_dict["available_values"] = self.available_values
|
||||
if self.dependency:
|
||||
json_dict["dependency"] = self.dependency
|
||||
|
||||
return json_dict
|
||||
@@ -1,20 +0,0 @@
|
||||
arrows_with_enter = [
|
||||
[(0, 0, 0), (0, 0, 0), (0, 0, 0)],
|
||||
[(0, 0, 0), (0, 0, 100), (0, 0, 0)],
|
||||
[(0, 0, 100), (100, 0, 0), (0, 0, 100)],
|
||||
[(0, 0, 0), (0, 0, 100), (0, 0, 0)]
|
||||
]
|
||||
|
||||
up_down_enter = [
|
||||
[(0, 0, 0), (0, 0, 0), (0, 0, 0)],
|
||||
[(0, 0, 0), (0, 0, 100), (0, 0, 0)],
|
||||
[(0, 0, 0), (100, 0, 0), (0, 0, 0)],
|
||||
[(0, 0, 0), (0, 0, 100), (0, 0, 0)]
|
||||
]
|
||||
|
||||
arrows_yes_no = [
|
||||
[(0, 0, 0), (0, 0, 0), (0, 0, 0)],
|
||||
[(0, 0, 0), (0, 0, 100), (0, 0, 0)],
|
||||
[(0, 0, 0), (100, 0, 0), (0, 0, 0)],
|
||||
[(0, 100, 0), (0, 0, 100), (100, 0, 0)]
|
||||
]
|
||||
@@ -1,49 +0,0 @@
|
||||
from adafruit_displayio_layout.layouts.grid_layout import GridLayout
|
||||
|
||||
from .common import arrows_yes_no, arrows_with_enter, up_down_enter
|
||||
from ..abstract_app import App
|
||||
|
||||
|
||||
class OptionsApp(App):
|
||||
|
||||
def __init__(self, macropad, config):
|
||||
super().__init__(macropad, config)
|
||||
self.send_keyboard_inputs = 0
|
||||
self.labels = []
|
||||
self.key_colors = arrows_yes_no
|
||||
self.possible_key_colors = [arrows_yes_no, arrows_with_enter, up_down_enter]
|
||||
self.counter = 0
|
||||
self.title = "Options"
|
||||
|
||||
def on_start(self):
|
||||
print("on start from the options app!")
|
||||
self.set_title(self.title)
|
||||
self.set_layout(GridLayout(x=0, y=9, width=128, height=54, grid_size=(4, 4), cell_padding=1))
|
||||
|
||||
def on_resume(self):
|
||||
print("resume from the options app!")
|
||||
|
||||
def on_pause(self):
|
||||
print("Pausing")
|
||||
|
||||
|
||||
def on_stop(self):
|
||||
print("Stopping")
|
||||
|
||||
def on_loop(self):
|
||||
pass
|
||||
|
||||
def on_key_pressed(self, keyevent):
|
||||
print("ON KEY PRESSED CALLBACK FROM OPTIONS")
|
||||
print(keyevent)
|
||||
|
||||
def on_key_released(self, keyevent):
|
||||
print("ON KEY RELEASED CALLBACK FROM OPTIONS")
|
||||
print(keyevent)
|
||||
|
||||
def on_wheel_change(self, event):
|
||||
print("ON WHEEL CHANGED CALLBACK")
|
||||
print(event)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
from .debug_app import DebugApp
|
||||
from .options_app import OptionsApp
|
||||
@@ -1,17 +1,11 @@
|
||||
import terminalio
|
||||
|
||||
from adafruit_display_text import bitmap_label as label
|
||||
from adafruit_display_text.scrolling_label import ScrollingLabel
|
||||
from adafruit_displayio_layout.layouts.grid_layout import GridLayout
|
||||
from rainbowio import colorwheel
|
||||
|
||||
from macropad_os.abstract_app import App
|
||||
|
||||
|
||||
def rgb_from_int(rgb):
|
||||
blue = rgb & 255
|
||||
green = (rgb >> 8) & 255
|
||||
red = (rgb >> 16) & 255
|
||||
return red, green, blue
|
||||
from macropad_os import App
|
||||
from macropad_os.app_utils import rgb_from_int
|
||||
|
||||
|
||||
class DebugApp(App):
|
||||
@@ -26,17 +20,15 @@ class DebugApp(App):
|
||||
|
||||
def on_start(self):
|
||||
print("on start from the app!")
|
||||
self.set_layout(GridLayout(x=0, y=9, width=128, height=54, grid_size=(4, 4), cell_padding=1))
|
||||
self.set_layout(GridLayout(x=0, y=9, width=128, height=54, grid_size=(3, 4), cell_padding=1))
|
||||
self.set_title(self.title)
|
||||
self.lit_keys = [True] * 12
|
||||
|
||||
for _ in range(12):
|
||||
self.labels.append(label.Label(terminalio.FONT, text=""))
|
||||
for i in range(12):
|
||||
self.labels.append(ScrollingLabel(terminalio.FONT, text=f"{i}{i}{i}{i}{i}", max_characters=6, animate_time=0.5))
|
||||
for index in range(12):
|
||||
x = index % 3
|
||||
y = index // 3
|
||||
self._layout.add_content(self.labels[index], grid_position=(x, y), cell_size=(1, 1))
|
||||
self.set_tone_status(True)
|
||||
self.set_tones([196, 220, 246, 262, 294, 330, 349, 392, 440, 494, 523, 587])
|
||||
self.register_on_key_pressed(self.process_keys_pressed_callback)
|
||||
self.register_on_key_released(self.process_keys_released_callback)
|
||||
@@ -53,16 +45,18 @@ class DebugApp(App):
|
||||
|
||||
def on_loop(self):
|
||||
self.update_key_colors()
|
||||
for label in self.labels:
|
||||
label.update()
|
||||
|
||||
def update_key_colors(self):
|
||||
self.wheel_offset += 1
|
||||
colors = self.get_colors()
|
||||
colors = []
|
||||
for pixel in range(12):
|
||||
if self.lit_keys[pixel]:
|
||||
(r, g, b) = rgb_from_int(colorwheel((pixel / 12 * 256) + self.wheel_offset))
|
||||
colors[pixel] = (r, g, b)
|
||||
colors.append((r, g, b))
|
||||
else:
|
||||
colors[pixel] = 0
|
||||
colors.append((0, 0, 0))
|
||||
self.set_colors(colors)
|
||||
|
||||
def process_keys_pressed_callback(self, keyevent):
|
||||
@@ -0,0 +1,210 @@
|
||||
import terminalio
|
||||
|
||||
from adafruit_display_text import label
|
||||
from adafruit_display_text.scrolling_label import ScrollingLabel
|
||||
from adafruit_displayio_layout.layouts.grid_layout import GridLayout
|
||||
|
||||
from macropad_os import App, Config
|
||||
from macropad_os.app_utils import arrows_yes_no, arrows_with_enter, up_down_enter, clamp
|
||||
|
||||
|
||||
class OptionsState(object):
|
||||
UNINIT = "uninit"
|
||||
VIEWING = "viewing"
|
||||
SELECTING = "selecting"
|
||||
|
||||
|
||||
down_arrow_text = "vvvvvvvvvvvvvvvvvvvv"
|
||||
down_arrow_not_shown = ""
|
||||
|
||||
|
||||
class OptionsApp(App):
|
||||
def __init__(self, macropad, config: Config):
|
||||
super().__init__(macropad, config)
|
||||
self.send_keyboard_inputs = 0
|
||||
self.key_colors = arrows_yes_no
|
||||
self.possible_key_colors = [arrows_yes_no, arrows_with_enter, up_down_enter]
|
||||
self.counter = 0
|
||||
self.title = "Options"
|
||||
self.cursor_index = -1
|
||||
self.next_cursor_index = 0
|
||||
self.bottom_nav_label = label.Label(
|
||||
terminalio.FONT,
|
||||
text=down_arrow_text,
|
||||
background_tight=False,
|
||||
padding_left=100,
|
||||
padding_right=100
|
||||
)
|
||||
self.sorted_settings_keys = []
|
||||
self.state = OptionsState.UNINIT
|
||||
self.display_labels = [
|
||||
[ScrollingLabel(terminalio.FONT, text=" ", max_characters=11, animate_time=0.5) for _ in range(3)],
|
||||
[label.Label(terminalio.FONT, text="") for _ in range(3)]
|
||||
]
|
||||
self.selected_item = None
|
||||
self.selected_item_new_value = None
|
||||
self.is_dev_mode_active = self.config.get_item_by_name("dev_mode_active").value
|
||||
|
||||
def on_start(self):
|
||||
self.set_title(self.title)
|
||||
if not self.is_dev_mode_active:
|
||||
self.set_layout(GridLayout(x=0, y=9, width=128, height=54, grid_size=(3, 4), cell_padding=1))
|
||||
self._update_settings_items()
|
||||
self.update_settings_menu()
|
||||
self.set_colors(arrows_yes_no)
|
||||
self.register_on_key_pressed(self.on_key_pressed)
|
||||
self.register_on_encoder_changed(self.on_wheel_change)
|
||||
else:
|
||||
self.set_layout(GridLayout(x=0, y=9, width=128, height=54, grid_size=(1, 4), cell_padding=1))
|
||||
self._layout.add_content(label.Label(terminalio.FONT, text="Disabled in dev-mode"), grid_position=(0, 0),
|
||||
cell_size=(1, 1))
|
||||
self._layout.add_content(label.Label(terminalio.FONT, text="reboot device to exit"), grid_position=(0, 1),
|
||||
cell_size=(1, 1))
|
||||
|
||||
def _update_settings_items(self):
|
||||
self.sorted_settings_keys = []
|
||||
for config_item_name in self.config.get_items().keys():
|
||||
dependency = self.config.get_item_by_name(config_item_name).dependency
|
||||
dep_check = True
|
||||
if dependency is not None:
|
||||
for k, v in dependency.items():
|
||||
config_item = self.config.get_item_by_name(k)
|
||||
if not config_item or config_item.value != v:
|
||||
dep_check = False
|
||||
if dep_check:
|
||||
self.sorted_settings_keys.append(config_item_name)
|
||||
else:
|
||||
self.sorted_settings_keys.append(config_item_name)
|
||||
print("SORTED KEYS")
|
||||
self.sorted_settings_keys = sorted(self.sorted_settings_keys)
|
||||
print(self.sorted_settings_keys)
|
||||
self._scroll()
|
||||
|
||||
def _initialize_menu(self):
|
||||
for y in range(3):
|
||||
self._layout.add_content(self.display_labels[0][y], grid_position=(0, y), cell_size=(2, 1))
|
||||
self._layout.add_content(self.display_labels[1][y], grid_position=(2, y), cell_size=(1, 1))
|
||||
self._layout.add_content(self.bottom_nav_label, grid_position=(0, 3), cell_size=(3, 1))
|
||||
self.state = OptionsState.VIEWING
|
||||
|
||||
def update_settings_menu(self):
|
||||
self.next_cursor_index = clamp(self.next_cursor_index, 0, len(self.sorted_settings_keys) - 1)
|
||||
if self.state == OptionsState.UNINIT:
|
||||
self._initialize_menu()
|
||||
if self.state == OptionsState.VIEWING and self.next_cursor_index != self.cursor_index:
|
||||
self._scroll()
|
||||
if self.state == OptionsState.SELECTING:
|
||||
self._update_selected_item()
|
||||
|
||||
def _update_selected_item(self):
|
||||
if self.state == OptionsState.VIEWING:
|
||||
self.display_labels[1][0].background_color = None
|
||||
self.display_labels[1][0].color = 0xffffff
|
||||
if self.state == OptionsState.SELECTING:
|
||||
self.display_labels[1][0].background_color = 0xffffff
|
||||
self.display_labels[1][0].color = 0x000000
|
||||
|
||||
def _up(self):
|
||||
if self.state == OptionsState.VIEWING:
|
||||
self.next_cursor_index -= 1
|
||||
elif self.state == OptionsState.SELECTING:
|
||||
if self.selected_item.available_values != None:
|
||||
avail_values = self.selected_item.available_values
|
||||
item_index = avail_values.index(self.selected_item_new_value) - 1
|
||||
item_index = clamp(item_index, 0, len(avail_values) - 1)
|
||||
self.selected_item_new_value = avail_values[item_index]
|
||||
elif self.selected_item.value_range != None:
|
||||
self.selected_item_new_value += 1
|
||||
self.selected_item_new_value = clamp(
|
||||
self.selected_item_new_value,
|
||||
self.selected_item.value_range.start,
|
||||
self.selected_item.value_range.end
|
||||
)
|
||||
self.display_labels[1][0].text = f"{self.selected_item_new_value}"
|
||||
|
||||
def _down(self):
|
||||
if self.state == OptionsState.VIEWING:
|
||||
self.next_cursor_index += 1
|
||||
elif self.state == OptionsState.SELECTING:
|
||||
if self.selected_item.available_values != None:
|
||||
avail_values = self.selected_item.available_values
|
||||
item_index = avail_values.index(self.selected_item_new_value) + 1
|
||||
item_index = clamp(item_index, 0, len(avail_values) - 1)
|
||||
self.selected_item_new_value = avail_values[item_index]
|
||||
elif self.selected_item.value_range != None:
|
||||
self.selected_item_new_value -= 1
|
||||
self.selected_item_new_value = clamp(
|
||||
self.selected_item_new_value,
|
||||
self.selected_item.value_range.start,
|
||||
self.selected_item.value_range.end
|
||||
)
|
||||
self.display_labels[1][0].text = f"{self.selected_item_new_value}"
|
||||
|
||||
def _select(self):
|
||||
if self.state == OptionsState.VIEWING:
|
||||
self.state = OptionsState.SELECTING
|
||||
self.selected_item = self.config.get_item_by_name(self.sorted_settings_keys[self.cursor_index])
|
||||
self.selected_item_new_value = self.selected_item.value
|
||||
elif self.state == OptionsState.SELECTING:
|
||||
self.state = OptionsState.VIEWING
|
||||
self.selected_item.value = self.selected_item_new_value
|
||||
self.config.set_item(self.selected_item)
|
||||
if not self.is_dev_mode_active:
|
||||
self.config.save()
|
||||
self._update_settings_items()
|
||||
self._update_selected_item()
|
||||
|
||||
def _cancel(self):
|
||||
if self.state == OptionsState.VIEWING:
|
||||
print("Cancel called during viewing")
|
||||
elif self.state == OptionsState.SELECTING:
|
||||
self.state = OptionsState.VIEWING
|
||||
self.selected_item_new_value = self.selected_item.value
|
||||
self._update_selected_item()
|
||||
|
||||
def _scroll(self):
|
||||
self.cursor_index = self.next_cursor_index
|
||||
keys = self.sorted_settings_keys[self.cursor_index:self.cursor_index + 3]
|
||||
print(keys)
|
||||
for y in range(3):
|
||||
if y < len(keys):
|
||||
self.display_labels[0][y].text = f">{keys[y]}" if y == 0 else keys[y]
|
||||
self.display_labels[1][y].text = f"{self.config.get_item_by_name(keys[y]).value}"
|
||||
else:
|
||||
self.display_labels[0][y].text = " "
|
||||
self.display_labels[1][y].text = " "
|
||||
if self.display_labels[0][2].text == " ":
|
||||
self.bottom_nav_label.text = down_arrow_not_shown
|
||||
else:
|
||||
self.bottom_nav_label.text = down_arrow_text
|
||||
|
||||
def on_resume(self):
|
||||
print("resume from the options app!")
|
||||
|
||||
def on_pause(self):
|
||||
print("Pausing")
|
||||
|
||||
def on_stop(self):
|
||||
print("Stopping")
|
||||
|
||||
def on_loop(self):
|
||||
pass
|
||||
|
||||
def on_key_pressed(self, keyevent):
|
||||
print(keyevent)
|
||||
if keyevent == 4:
|
||||
self._down()
|
||||
elif keyevent == 1:
|
||||
self._up()
|
||||
elif keyevent == 5:
|
||||
self._select()
|
||||
elif keyevent == 3:
|
||||
self._cancel()
|
||||
self.update_settings_menu()
|
||||
|
||||
def on_wheel_change(self, event):
|
||||
if event > 0:
|
||||
self._up()
|
||||
else:
|
||||
self._down()
|
||||
self.update_settings_menu()
|
||||
@@ -0,0 +1 @@
|
||||
from .numpad import NumpadApp
|
||||
@@ -0,0 +1,85 @@
|
||||
import terminalio
|
||||
from adafruit_display_text.bitmap_label import Label
|
||||
|
||||
from adafruit_display_text.scrolling_label import ScrollingLabel
|
||||
from adafruit_displayio_layout.layouts.grid_layout import GridLayout
|
||||
from rainbowio import colorwheel
|
||||
|
||||
from macropad_os import App
|
||||
from macropad_os.app_utils import rgb_from_int
|
||||
|
||||
|
||||
labels = [
|
||||
"7", "8", "9",
|
||||
"4", "5", "6",
|
||||
"1", "2", "3",
|
||||
"0", ".", "SHIFT"
|
||||
]
|
||||
|
||||
modified_labels = [
|
||||
"<", ">", "&",
|
||||
"(", ")", "%",
|
||||
"/", "+", "-",
|
||||
"*", "=", "SHIFT"
|
||||
]
|
||||
|
||||
|
||||
class NumpadApp(App):
|
||||
|
||||
def __init__(self, macropad, config):
|
||||
super().__init__(macropad, config)
|
||||
self.name = "Numpad"
|
||||
self.wheel_offset = 0
|
||||
self.lit_keys = [False] * 12
|
||||
self.labels = []
|
||||
self.title = "Numpad"
|
||||
self.modifier_pressed = False
|
||||
|
||||
def on_start(self):
|
||||
print("on start from the app!")
|
||||
self.set_layout(GridLayout(x=0, y=9, width=128, height=54, grid_size=(3, 4), cell_padding=1))
|
||||
self.set_title(self.title)
|
||||
self.lit_keys = [True] * 12
|
||||
for i in range(12):
|
||||
self.labels.append(Label(terminalio.FONT, text=labels[i]))
|
||||
for index in range(12):
|
||||
x = index % 3
|
||||
y = index // 3
|
||||
self._layout.add_content(self.labels[index], grid_position=(x, y), cell_size=(1, 1))
|
||||
self.register_on_key_pressed(self.process_keys_pressed_callback)
|
||||
self.register_on_key_released(self.process_keys_released_callback)
|
||||
self.register_on_encoder_changed(self.process_enbcoder_changed)
|
||||
|
||||
def on_resume(self):
|
||||
print("resume from the debug app!")
|
||||
|
||||
def on_pause(self):
|
||||
print("Pausing")
|
||||
|
||||
def on_stop(self):
|
||||
print("Stopping")
|
||||
|
||||
def on_loop(self):
|
||||
self.update_key_colors()
|
||||
|
||||
def update_key_colors(self):
|
||||
self.wheel_offset += 1
|
||||
colors = []
|
||||
for pixel in range(12):
|
||||
if self.lit_keys[pixel]:
|
||||
(r, g, b) = rgb_from_int(colorwheel((pixel / 12 * 256) + self.wheel_offset))
|
||||
colors.append((r, g, b))
|
||||
else:
|
||||
colors.append((0, 0, 0))
|
||||
self.set_colors(colors)
|
||||
|
||||
def process_keys_pressed_callback(self, keyevent):
|
||||
print("PROCESS KEYS CALLBACK FROM DEBUG")
|
||||
print(keyevent)
|
||||
|
||||
def process_keys_released_callback(self, keyevent):
|
||||
print("PROCESS KEYS RELEASED CALLBACK FROM DEBUG")
|
||||
print(keyevent)
|
||||
def process_enbcoder_changed(self, keyevent):
|
||||
print("PROCESS Encoder Changed Callback FROM DEBUG")
|
||||
print(keyevent)
|
||||
Reference in New Issue
Block a user