Add back-photo button; consolidate reset/manage onto one hold-duration button
Build and push server image / build-and-push (push) Successful in 32s
Build and push server image / build-and-push (push) Successful in 32s
Back button (new GPIO0, POST /frame/back): the server now tracks a bounded history of previously-current photos (photo_queue.py), pushed to on every advance (auto or forced) and popped by back_forced() -- symmetric with advance, so pressing next afterwards returns to right where you were. frame_client.c's force_advance bool becomes a 3-way fetch_action_t (NORMAL/ADVANCE/BACK) threaded through the whole fetch path. Also folds the separate reset and manage buttons onto one pin (combo_button.c, replacing reset_button.c/manage_button.c entirely), disambiguated by hold duration: quick press shows the management menu (unchanged), ~3s hold-then-release soft-resets (esp_restart(), config kept -- new), ~15s hold factory-resets (today's old reset behavior, extended from 10s for clearer tier separation). Driven by a production board (Seeed XIAO ESP32-C6) exposing only 3 of the ESP32-C6's 8 deep-sleep-wakeup-capable GPIOs -- next/back keep their own dedicated pins where instant response matters most, everything else shares the third pin via timing instead of needing its own. Same three-pin layout now works on both the dev board and the production board. Fixed a fast-tap bug in combo_button_check() before shipping: it only did a live gpio_get_level() read to decide whether the button was pressed at all, so a press fast enough to already be released by the time boot reached that check was missed entirely (treated as "never pressed" rather than "quick press"). Added the same latched esp_sleep_get_gpio_wakeup_status() check the other buttons already use for exactly this reason.
This commit is contained in:
@@ -130,6 +130,7 @@ def api_config_save(
|
||||
cfg.current_asset_set_at = 0.0
|
||||
cfg.queue = []
|
||||
cfg.queue_cursor = 0
|
||||
cfg.history = []
|
||||
cfg.album_id = album_id
|
||||
cfg.order = order if order in ("sequential", "shuffle") else "sequential"
|
||||
cfg.refresh_interval_s = max(MIN_REFRESH_INTERVAL_S, min(MAX_REFRESH_INTERVAL_S, refresh_interval_s))
|
||||
@@ -211,6 +212,26 @@ def frame_advance():
|
||||
return Response(content=_render_asset(client, cfg, cfg.current_asset_id), media_type="application/octet-stream")
|
||||
|
||||
|
||||
@app.post("/frame/back", dependencies=[Depends(require_access_token)])
|
||||
def frame_back():
|
||||
"""Returns to the previously-current photo (the mirror image of
|
||||
/frame/advance -- see photo_queue.back_forced()), and resets the
|
||||
interval clock from now. A no-op (still 200, current photo
|
||||
unchanged) if there's no history to go back to -- same "always
|
||||
returns something displayable" contract as /frame/advance, rather
|
||||
than erroring. Used by the device's back-photo button."""
|
||||
cfg = config.load()
|
||||
_require_configured(cfg)
|
||||
|
||||
client = ImmichClient(cfg.immich_url, cfg.immich_api_key)
|
||||
assets = _list_assets(client, cfg)
|
||||
|
||||
photo_queue.back_forced(cfg, assets)
|
||||
config.save(cfg)
|
||||
|
||||
return Response(content=_render_asset(client, cfg, cfg.current_asset_id), media_type="application/octet-stream")
|
||||
|
||||
|
||||
LOCATION_LINE_MAX_LEN = 14
|
||||
|
||||
US_STATE_ABBR = {
|
||||
|
||||
Reference in New Issue
Block a user