Skip to content
Snippets Groups Projects

Allow configuration of wifi credentials + hostname via settings.json

Merged Woazboat requested to merge Woazboat/flow3r-firmware:feature/wifi-config-base into main
2 unresolved threads
1 file
+ 43
3
Compare changes
  • Side-by-side
  • Inline
@@ -11,8 +11,19 @@ request.
import json
from st3m import InputState, Responder, logging
from st3m.goose import ABCBase, abstractmethod, Any, Dict, List, Optional, Callable
from st3m.ui.menu import MenuController, MenuItem, MenuItemBack
from st3m.goose import (
ABCBase,
abstractmethod,
Any,
Dict,
List,
Tuple,
Optional,
Callable,
Union,
TYPE_CHECKING,
)
from st3m.ui.menu import MenuController, MenuItem, MenuItemBack, MenuItemForeground
from st3m.ui.elements.menus import SimpleMenu
from st3m.ui.view import ViewManager
from st3m.utils import lerp, ease_out_cubic, reduce
@@ -242,6 +253,82 @@ class OnOffWidget(TunableWidget):
self._tunable.set_value(not self._state)
class StringTunable(UnaryTunable):
"""
StringTunable is a UnaryTunable that has a string value
"""
def __init__(self, name: str, key: str, default: Optional[str]) -> None:
super().__init__(name, key, default)
def get_widget(self) -> TunableWidget:
return StringWidget(self)
def press(self, vm: Optional[ViewManager]) -> None:
# Text input not supported at the moment
pass
class StringWidget(TunableWidget):
"""
StringWidget is a TunableWidget for StringTunables. It renders a string.
"""
def __init__(self, tunable: StringTunable) -> None:
self._tunable = tunable
def think(self, ins: InputState, delta_ms: int) -> None:
# Nothing to do here
pass
def draw(self, ctx: Context) -> None:
ctx.text_align = ctx.LEFT
ctx.text(str(self._tunable.value) if self._tunable.value else "")
def press(self, vm: Optional[ViewManager]) -> None:
# Text input not supported at the moment
pass
# TODO: invert Tunable <-> Widget dependency to be able to define multiple different widget renderings for the same underlying tunable type
class ObfuscatedStringTunable(UnaryTunable):
"""
ObfuscatedStringTunable is a UnaryTunable that has a string value that should not be revealed openly.
"""
def __init__(self, name: str, key: str, default: Optional[str]) -> None:
super().__init__(name, key, default)
def get_widget(self) -> TunableWidget:
return ObfuscatedValueWidget(self)
def press(self, vm: Optional[ViewManager]) -> None:
# Text input not supported at the moment
pass
class ObfuscatedValueWidget(TunableWidget):
"""
ObfuscatedValueWidget is a TunableWidget for UnaryTunables. It renders three asterisks when the tunable contains a truthy value, otherwise nothing.
"""
def __init__(self, tunable: UnaryTunable) -> None:
self._tunable = tunable
def think(self, ins: InputState, delta_ms: int) -> None:
# Nothing to do here
pass
def draw(self, ctx: Context) -> None:
ctx.text_align = ctx.LEFT
if self._tunable.value:
ctx.text("***")
def press(self, vm: Optional[ViewManager]) -> None:
# Text input not supported at the moment
pass
class SettingsMenuItem(MenuItem):
"""
A MenuItem which draws its label offset to the left, and a Tunable's widget
@@ -293,17 +380,46 @@ class SettingsMenu(SimpleMenu):
# Actual tunables / settings.
onoff_camp_wifi = OnOffTunable("Connect Camp WiFi", "system.camp_wifi_enabled", False)
onoff_button_swap = OnOffTunable("Swap Buttons", "system.swap_buttons", False)
onoff_debug = OnOffTunable("Debug Overlay", "system.debug", False)
onoff_debug_touch = OnOffTunable("Touch Overlay", "system.debug_touch", False)
onoff_show_tray = OnOffTunable("Show Icons", "system.show_icons", True)
all_settings: List[UnaryTunable] = [
onoff_camp_wifi,
onoff_wifi = OnOffTunable("Enable WiFi", "system.wifi.enabled", False)
str_wifi_ssid = StringTunable("WiFi SSID", "system.wifi.ssid", "Camp2023-open")
str_wifi_psk = ObfuscatedStringTunable("WiFi Password", "system.wifi.psk", None)
str_hostname = StringTunable("Hostname", "system.hostname", "flow3r")
# List of all settings to be loaded/saved
load_save_settings: List[UnaryTunable] = [
onoff_show_tray,
onoff_button_swap,
onoff_debug,
onoff_debug_touch,
onoff_wifi,
str_wifi_ssid,
str_wifi_psk,
str_hostname,
]
if TYPE_CHECKING:
    • Contributor

      I'd probably try to handle this with some 'tagged union' style thing here instead of a Union and isinstance call, but this is good enough. I'll fix it up to make myself happy some other day.

Please register or sign in to reply
MenuStructureEntry = Union[UnaryTunable, Tuple[str, List["MenuStructureEntry"]]]
MenuStructure = List[MenuStructureEntry]
# WiFi submenu
wifi_settings: "MenuStructure" = [
onoff_wifi,
str_wifi_ssid,
str_wifi_psk,
str_hostname,
]
# Main settings menu
settings_menu_structure: "MenuStructure" = [
onoff_show_tray,
onoff_button_swap,
onoff_debug,
onoff_debug_touch,
("WiFi", wifi_settings),
]
@@ -320,7 +436,7 @@ def load_all() -> None:
return
log.info("Loaded settings from flash")
for setting in all_settings:
for setting in load_save_settings:
setting.load(data)
@@ -341,7 +457,7 @@ def save_all() -> None:
Save all settings to flash.
"""
res: Dict[str, Any] = {}
for setting in all_settings:
for setting in load_save_settings:
res = _update(res, setting.save())
try:
with open("/flash/settings.json", "w") as f:
@@ -353,12 +469,25 @@ def save_all() -> None:
log.info("Saved settings to flash")
def build_menu() -> SimpleMenu:
def build_menu_recursive(items: "MenuStructure") -> SimpleMenu:
"""
Return a SimpleMenu for all settings.
Recursively build a menu for the given setting structure.
"""
mib: MenuItem = SettingsMenuItemBack()
positions: List[MenuItem] = [
mib,
] + [SettingsMenuItem(t) for t in all_settings]
] + [
SettingsMenuItem(t)
if isinstance(t, UnaryTunable)
else MenuItemForeground(t[0], build_menu_recursive(t[1]))
for t in items
]
return SettingsMenu(positions)
def build_menu() -> SimpleMenu:
"""
Return a SettingsMenu for all settings.
"""
return build_menu_recursive(settings_menu_structure)
Loading