Add battery level reporting (Kconfig-gated) and display orientation
Build and push server image / build-and-push (push) Successful in 32s
Build and push server image / build-and-push (push) Successful in 32s
Battery (firmware + server, disabled by default): new battery.c reads a 2x200k voltage divider via ADC oneshot with curve-fitting calibration (the ESP32-C6's scheme), maps through a piecewise LiPo discharge curve, and restores the pin to button duty after each read -- the settled XIAO ESP32-C6 design shares the back button's GPIO0/A0, time-shared per wake. Skipped entirely when on mains (a 2x100k VBUS divider into a spare digital pin -- the 5V pin is dead on battery power, so presence = mains, where the charging voltage would read misleadingly full) or when the reading is implausible. The manage overlay gains a battery region (static outline glyph + "NN%", below the manage QR, all menu levels), and the device POSTs to the new /frame/battery endpoint after a successful fetch; the server stores percent + as-of timestamp, exposed via /api/queue and shown in the web UI. FRAME_BATTERY_ADC_GPIO / FRAME_VBUS_SENSE_GPIO default to -1 (fully inert on the dev board); compile-verified both disabled and enabled, hardware bring-up deferred until the ordered XIAO + batteries arrive. Orientation (server-side only): new config setting + web UI dropdown (landscape / portrait / landscape_flipped / portrait_flipped). Photos are composed/cropped at the logical hanging shape (portrait crops at 480x800, so face-aware crops match how the frame actually hangs), then rotated losslessly into the panel's native 800x480 byte layout after dithering -- the device never knows. Face-label anchors are transformed through the same rotation (logical_to_native()) so they stay attached to faces on rotated frames. Known documented limitation: the on-device manage overlay still renders in native orientation, so it appears sideways on a portrait-hung frame (QRs scan at any rotation; text reads sideways).
This commit is contained in:
@@ -27,6 +27,10 @@ class FrameConfig(BaseModel):
|
||||
order: str = "sequential" # or "shuffle"
|
||||
refresh_interval_s: int = 3600
|
||||
smart_crop_faces: bool = True
|
||||
# How the physical frame is hung: landscape (native), portrait,
|
||||
# landscape_flipped, portrait_flipped. Purely a server-side render
|
||||
# decision -- the device always receives native 800x480 bytes.
|
||||
orientation: str = "landscape"
|
||||
|
||||
# Current photo + upcoming queue (see app/photo_queue.py). current_asset_set_at
|
||||
# is what lets the server decide "has it been long enough to advance" on its
|
||||
@@ -39,6 +43,12 @@ class FrameConfig(BaseModel):
|
||||
history: list[str] = [] # bounded stack of previously-current asset ids, most recent last
|
||||
excluded_asset_ids: list[str] = [] # permanently removed from this frame's rotation (not deleted from Immich)
|
||||
|
||||
# Last battery report from the device (POST /frame/battery); -1 = never
|
||||
# reported / not battery-powered. battery_as_of mirrors the
|
||||
# current_asset_set_at timestamp pattern.
|
||||
battery_percent: int = -1
|
||||
battery_as_of: float = 0.0
|
||||
|
||||
|
||||
def load() -> FrameConfig:
|
||||
with _lock:
|
||||
|
||||
Reference in New Issue
Block a user