feat(reset): config wipe, setup screen, and re-exec into provisioning (story 4-2)

Add _handle_reset() and _make_setup_screen() to main.py; integrate ButtonHoldDetector
and LEDController into the radar loop; LED lights immediately on hold, config is wiped,
setup screen shown, then os.execvp hands off to planemapper-provision. Wipe failures
log ERROR and abort without exec. Completes epic-4.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Edholm
2026-04-22 23:59:22 -04:00
parent 8e57f2d927
commit 7f34a7b926
5 changed files with 168 additions and 3 deletions
+33
View File
@@ -2,6 +2,7 @@ from __future__ import annotations
import dataclasses
import logging
import os
import time
import requests
@@ -15,8 +16,10 @@ from planemapper.constants import (
)
from planemapper.display import DisplayInterface, WaveshareDisplay
from planemapper.fetcher import HttpFetcher
from planemapper.gpio_ctrl import ButtonHoldDetector, LEDController
from planemapper.models import Aircraft
from planemapper.provisioning.config import read as read_config
from planemapper.provisioning.config import wipe as wipe_config
from planemapper.renderer.basemap import load as load_basemap
from planemapper.renderer.projection import MapBounds
from planemapper.renderer.renderer import Renderer
@@ -37,6 +40,32 @@ def _make_startup_screen() -> Image.Image:
return image
def _make_setup_screen() -> Image.Image:
image = Image.new("RGB", (DISPLAY_WIDTH, DISPLAY_HEIGHT), color=(255, 255, 255))
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()
draw.text(
(DISPLAY_WIDTH // 2 - 60, DISPLAY_HEIGHT // 2),
"Resetting...",
fill=(0, 0, 0),
font=font,
)
return image
def _handle_reset(display: DisplayInterface, led: LEDController) -> None:
led.on()
try:
wipe_config()
except Exception:
log.error("config wipe failed — aborting reset", exc_info=True)
led.off()
return
setup_screen = _make_setup_screen()
display.show(setup_screen)
os.execvp("planemapper-provision", ["planemapper-provision"])
def _run_one_cycle(
renderer: Renderer,
fetcher: HttpFetcher,
@@ -89,9 +118,13 @@ def main() -> None:
fetcher = HttpFetcher()
renderer = Renderer(base_map, bounds)
display = WaveshareDisplay()
button = ButtonHoldDetector()
led = LEDController()
startup = _make_startup_screen()
display.show(startup)
last: list[Aircraft] = []
while True:
if button.check():
_handle_reset(display, led)
last = _run_one_cycle(renderer, fetcher, display, last)
time.sleep(REFRESH_INTERVAL_S)