mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-25 18:40:06 -04:00
82 lines
2.9 KiB
Python
82 lines
2.9 KiB
Python
|
|
"""A YAML config storing the user's saved firmware flashing configurations.
|
||
|
|
|
||
|
|
Each saved configuration is a named set of firmware files (media-relative paths) and their flash offsets, plus
|
||
|
|
whether to erase the flash first. This lets a user configure a multi-part flash once (e.g. a Meshtastic T-Deck:
|
||
|
|
firmware at 0x0, littlefs at 0xc90000) and re-load it later with one click.
|
||
|
|
"""
|
||
|
|
from dataclasses import dataclass, field
|
||
|
|
from typing import List
|
||
|
|
|
||
|
|
from wrolpi.common import ConfigFile, logger
|
||
|
|
|
||
|
|
logger = logger.getChild(__name__)
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass
|
||
|
|
class FlasherConfigValidator:
|
||
|
|
version: int = None
|
||
|
|
configurations: list = field(default_factory=list)
|
||
|
|
|
||
|
|
|
||
|
|
class FlasherConfig(ConfigFile):
|
||
|
|
file_name = 'flasher.yaml'
|
||
|
|
default_config = dict(
|
||
|
|
version=0,
|
||
|
|
configurations=[],
|
||
|
|
)
|
||
|
|
validator = FlasherConfigValidator
|
||
|
|
|
||
|
|
@property
|
||
|
|
def configurations(self) -> List[dict]:
|
||
|
|
return list(self._config.get('configurations', []))
|
||
|
|
|
||
|
|
@configurations.setter
|
||
|
|
def configurations(self, value: List[dict]):
|
||
|
|
# ConfigFile.update() validates, stores, and triggers a background save.
|
||
|
|
self.update({'configurations': value})
|
||
|
|
|
||
|
|
def save_configuration(self, name: str, files: List[dict], erase_all: bool = False) -> dict:
|
||
|
|
"""Add or replace (by name) a saved configuration. Returns the stored configuration."""
|
||
|
|
name = (name or '').strip()
|
||
|
|
if not name:
|
||
|
|
raise ValueError('Configuration name is required')
|
||
|
|
# Keep only the fields we persist for each file.
|
||
|
|
stored_files = [
|
||
|
|
dict(
|
||
|
|
path=f.get('path'),
|
||
|
|
address=f.get('address'),
|
||
|
|
name=f.get('name'),
|
||
|
|
size=f.get('size'),
|
||
|
|
)
|
||
|
|
for f in files
|
||
|
|
]
|
||
|
|
configuration = dict(name=name, erase_all=bool(erase_all), files=stored_files)
|
||
|
|
# Replace any existing configuration with the same name, then keep the list sorted by name.
|
||
|
|
configurations = [c for c in self.configurations if c.get('name') != name]
|
||
|
|
configurations.append(configuration)
|
||
|
|
configurations.sort(key=lambda c: (c.get('name') or '').lower())
|
||
|
|
self.configurations = configurations
|
||
|
|
return configuration
|
||
|
|
|
||
|
|
def delete_configuration(self, name: str) -> bool:
|
||
|
|
"""Delete a saved configuration by name. Returns True if one was removed."""
|
||
|
|
configurations = self.configurations
|
||
|
|
remaining = [c for c in configurations if c.get('name') != name]
|
||
|
|
if len(remaining) == len(configurations):
|
||
|
|
return False
|
||
|
|
self.configurations = remaining
|
||
|
|
return True
|
||
|
|
|
||
|
|
|
||
|
|
FLASHER_CONFIG: FlasherConfig = FlasherConfig()
|
||
|
|
|
||
|
|
# Test override (see get_flasher_config).
|
||
|
|
TEST_FLASHER_CONFIG = None
|
||
|
|
|
||
|
|
|
||
|
|
def get_flasher_config() -> FlasherConfig:
|
||
|
|
global TEST_FLASHER_CONFIG
|
||
|
|
if isinstance(TEST_FLASHER_CONFIG, ConfigFile):
|
||
|
|
return TEST_FLASHER_CONFIG
|
||
|
|
return FLASHER_CONFIG
|