You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For Raspberry Pi 2, follow the manual installation instructions as Trixie 13 Lite 32-bit is required
Key Fixes
Fixed WiFi, Captive Portal and Access Point issues.
Change Log
[1.0.5-beta.5]
Added
Pi 2 / Ethernet-only support — is_wifi_hardware_present() check prevents the controller from attempting AP activation on devices without WiFi hardware; the controller stays in CLIENT_CONNECTED or CLIENT_DISCONNECTED based on Ethernet state and never retries AP
Stale AP cleanup on boot — the controller now kills any leftover hostapd instance on initialisation, preventing the captive portal from blocking the web dashboard after an unclean shutdown
Changed
AP startup delay eliminated — removed the daemon's forced ap_timeout_seconds wait on boot; the controller now owns all timing (immediate AP when no saved networks, 5‑minute grace period when saved WiFi exists)
Boot screen message timing — 10 s delay after slideshow start ensures the boot screen fade-out completes before welcome or PIN messages appear
Setup script prompts before install — channel and WiFi country questions are now asked before git is installed; answers flow through to Phase 1 via environment variables so the user is never re‑prompted
Beta channel pins to pre-release tags — the setup script now checks out the latest v*-beta.* tag on main for beta channel installs instead of tracking the dev branch, matching the OTA updater's behaviour
Fixed
Captive portal blocking dashboard after reboot — hostapd left running from a previous AP session is now stopped at controller init
[1.0.4-beta.4]
Added
WiFi connection failed popup — when the captive portal WiFi connection fails (wrong password, out of range, etc.), an error message now appears on the photo frame display with guidance to check the password or try a different network (30 s auto-dismiss)
WiFi connection profile fallback — if the one-shot nmcli device wifi connect fails with a "key-mgmt property is missing" error (common on routers with mixed WPA2/WPA3 or certain TP-Link/ASUS models), the code now creates an explicit connection profile with WPA2-PSK settings and retries automatically
Setup script channel prompt — asks for stable or beta channel before installing; stable pins to the latest non-prerelease v* tag, beta tracks the dev branch
Setup script WiFi country prompt — asks for the regulatory domain (e.g. AU, US, GB) upfront alongside the channel choice; both answers are written to config.json for the Web UI
Changed
Boot welcome delay eliminated — the network monitor no longer waits ap_timeout_seconds when a network is already connected at boot; the welcome message appears as soon as the slideshow is ready instead of 60+ seconds later
Captive portal error messaging — the WiFi failure popup on the frame display uses a single clean message instead of concatenating the raw nmcli error with boilerplate text
Fixed
Setup script CRLF line endings — .gitattributes now enforces eol=lf for *.sh files; the setup script was renormalized so it runs correctly when downloaded from GitHub raw on a Pi
[1.0.3-beta.3]
Changed
Network Controller rewritten — NetworkPhase flag-based state machine replaced with NetworkState enum (CLIENT_CONNECTED, CLIENT_DISCONNECTED, AP_ACTIVE, AP_EXHAUSTED); all transitions go through a single _transition_to() method under lock; monitor loop drains a pending-actions queue instead of comparing phase snapshots. Ethernet connectivity is checked independently from WiFi and is always safe (different radio) — nmcli is never queried for WiFi while the AP is active, preventing hostapd beacon disruption.
WiFi connection deferred to background thread — the captive portal's /api/network/connect endpoint now returns a response immediately (before the AP is torn down) and spawns a background thread for the actual AP-stop + scan + nmcli-connect sequence. The phone receives the HTTP response while still associated with the AP.
WiFi scan delay after AP stop — connect_to_network() now performs a nmcli device wifi rescan and waits 5 s after stopping the AP before attempting the connection. Without a fresh scan wlan0 has no visible SSID list and nmcli fails with "No network with SSID 'X' found."
Controller connection guard — new begin_connection() / end_connection() methods prevent the monitor thread from treating an intentionally-stopped AP as "unexpectedly dead" during the scan gap
AP_EXHAUSTED sudo reduction — _stop_ap() now guarded by is_ap_mode_active() check; no longer runs unnecessary sudo commands on every monitor tick when the AP is already down
Fixed
WiFi connection failing after captive portal — the AP was torn down during the HTTP request, severing the client's TCP connection before the response arrived; the phone showed a network error instead of success
"WiFi Offline" popup flashing during WiFi connection — the monitor tick saw the AP was down during the scan delay and marked it as AP_EXHAUSTED, triggering an on-screen warning that was dismissed seconds later when the connection succeeded
Captive portal error messages removed — the password field no longer displays error text on failure; all feedback is shown on the photo frame display where it is always visible even after the phone disconnects from the AP
Removed
Legacy PIN state — _pin_state module-level dict and all legacy PIN functions (generate_ap_pin, clear_ap_pin, validate_ap_pin, is_pin_required, get_active_pin) removed from network_manager.py; PIN management is now exclusively owned by NetworkController with proper thread safety
SCAN_CACHE_TTL constant and force_live parameter — scan cache is now served indefinitely while the AP is active; live scan toggle no longer exposed to the web API
Legacy PIN fallbacks in web routes — all is_pin_required() and validate_ap_pin() fallback paths removed; the controller is the sole source of truth
[1.0.2-beta.2]
Added
CPU temperature tile — sparkline graph on the System Status dashboard card using vcgencmd measure_temp; scaled 0–85°C in red
libopenblas0 added to setup script package list — resolves NumPy import failures on Pi 2 (32-bit) where the shared library was missing
Hardware documentation — separated Models table from RAM Requirements table in README; added 64‑bit vs 32‑bit image availability per model; Pi 4 promoted to Phase 1 (untested); Pi Zero 2 W marked as untested
Changed
Video transcode CPU limit default — reduced from 300 % to 200 %
Pause button — switched from Unicode characters (⏸/▶) to Material Symbols icons (pause / play_arrow) for consistent rendering
Progress bar colours — optimising images and transcoding bars now use var(--primary) (theme red), matching the scanning bar
WiFi Country Code — hint text repositioned below the input field using form-group--stack layout
Connected button — vertically centered in network list rows via display:inline-flex;align-items:center
Background processing bars — hidden when their respective feature is disabled (image optimisation off → hide optimising bar; video transcoding off → hide transcoding bar)
README — hardware section restructured with separate Supported Models and RAM Requirements tables; Pi 2 marked active (32-bit manual install); Pi 4 marked untested; Pi Zero 2 W marked untested
Fixed
Pipeline reset on sync changes — enabling/disabling local watch folders now triggers a full pipeline reset ("sync" re‑added to the config route trigger list)
Hardware docs consistency — CLAUDE.md, HARDWARE.md, ARCHITECTURE.md, GETTING_STARTED.md, INSTALLATION.md, and FEATURES.md updated to match README hardware tables (Pi 4 in Phase 1, Pi Zero 2 W untested, 32‑bit vs 64‑bit, manual install notes)
[1.0.1-beta.1]
Added
first_frame_path and last_frame_path fields on MediaItem — backend-generated video frame caches referenced directly by the presentation engine
VideoProcessor._extract_video_frames() — extracts first frame (t=0) and last frame (sseof) during Phase 2 OPTIMISE; cached as .1.frame / .2.frame JPEGs
Image optimisation worker subprocess (worker.py) — PIL operations (load, transpose, resize, save) run in an isolated process with cpulimit -l 50 (hard 50 % CPU cap) and nice -n 19 (lowest priority); OS reclaims all memory on exit
Adaptive CPU throttling — sleep between image optimisations scales with 1‑minute load average (load1 × 0.2, capped at 1.0 s); folder watcher also yields between thumbnail generations when the optimiser is busy
Boot screen progress bar — red fill bar below the spinner showing optimisation progress (1/6 → 6/6); uses draw_image with 1×1 pixel textures to avoid pi3d colour-space issues
Per‑phase progress bars in the Web UI Background Processing card — separate persistent bars for scanning, image optimisation, and video transcoding; each retains its last position when the active phase switches
Frontend→backend slideshow‑started signal — POST /api/slideshow-started defers the network monitor's AP‑fallback countdown until the first slideshow items are ready, preventing CPU/I/O contention during initial processing
/api/config/processing-status endpoint — serves per‑phase progress data
FEATURES.md — comprehensive feature list linked from README
Per‑image processing time and per‑batch memory delta logging (DEBUG level)
Raspberry Pi 5 (2GB+) official Phase 1 support — runs the same Trixie image via cage + XWayland; now the recommended platform over Pi 3
Immich network connectivity gate — syncer checks is_connected() before attempting API calls, avoiding wasted time on doomed requests when offline
Network Controller (network_controller.py) — single-owner state machine for WiFi/AP management; eliminates race conditions between network monitor thread and Flask request threads via threading.Lock
WiFi AP grace period — 5‑minute silent retry of saved WiFi before activating the captive portal; handles transient router reboots without user intervention
WiFi AP exhaustion — AP auto‑stops after 10 minutes if no user connects; never reactivates until next reboot, preventing continuous AP cycling on permanent WiFi loss
WiFi AP crash detection — controller detects when hostapd dies unexpectedly and transitions to exhausted state rather than lying about the AP being active
WiFi regulatory domain — configurable country code in setup script and Web UI; persists via cfg80211.ieee80211_regdom module parameter
WiFi power management disabled — setup script writes NetworkManager config (wifi.powersave = 2), fixing Pi 3 WiFi unreliability
Pipeline reset on config change — changes to video, image, or display settings clear all queues and re‑scan; boot layer reactivates with spinner during rebuild instead of showing a black screen
Boot layer reactivation — reactivate() method re‑shows logo + spinner on pipeline reset; reset mode skips 3 s minimum display and progress bar
Documentation overhaul — new GETTING_STARTED.md, WIFI_SETUP.md, INSTALLATION.md; README restructured with logo, showcase images, and two‑path quick‑start section
Contributing link — README Contributing section links to dev setup guide in INSTALLATION.md#path-c-development-setup-remote-via-samba
Cumulative progress tracking — image and video optimisation progress bars now show total backlog across all batches instead of resetting every 6 items
Folder watcher scanning progress — incremental scans (new files added to watch folders) now report progress to the Web UI bar, not just the initial scan
Changed
Image processing moved to subprocess — ImageProcessor.process() spawns worker.py for cache misses; cache hits remain in‑process (fast path)
Throttle formula 4× more aggressive — load1 × 0.05 → load1 × 0.2
Batch sizes reduced — optimiser flush 12→6, folder watcher enqueue 24→6; items reach the slideshow ~4× faster after startup
Boot layer fades based on slideshow_ready signal from renderer (not playlist.json on disk) and waits for active GPU texture before signalling readiness — eliminates fade‑to‑black and jump‑cut regressions
_write_progress uses per‑phase format — scanning, optimising, and transcoding each track their own total/processed independently
Web UI Background Processing card replaced with three persistent progress bars using Material Symbols icons
is_ready_to_play now requires first_frame_path and last_frame_path for video items — videos without cached frames are excluded from the slideshow
Dev setup script: removed python3-pip and python3-pygame (dead deps)
Pi 5 moved from Phase 2 to Phase 1 — ARCHITECTURE.md, CLAUDE.md, README.md, and HARDWARE.md updated; Pi 5 (2GB+) now the recommended platform
Boot progress bar auto‑hides when slideshow queue reaches 6 items, preventing repeated 100 % cycling on cached restarts
Release scripts (release.ps1, release.sh): merge strategy changed from --ff-only to --no-ff for cleaner release history
Web UI scanning label updated to "Scanning folders and generating thumbnails"
WiFi AP state machine — daemon's _network_monitor_loop now delegates to NetworkController with phase‑based transitions (MONITORING, GRACE_PERIOD, AP_ACTIVE, AP_EXHAUSTED)
Video transcode CPU limit default — increased from 50 % to 300 % (3 full cores on Pi 5/3)
VLC playback messages — changed from INFO to DEBUG log level
Immich test connection — saves server URL and API key on successful test so Fetch Albums works immediately without a separate save step
Quiet boot hint — removed "Requires a reboot to take effect" from Web UI
Fixed
AP fallback falsely activated under CPU load — is_connected() now returns True on exception (nmcli timeout) instead of False
Worker JSON corrupted by cpulimit — cpulimit prints Process NNNN detected to stdout; parser now takes the first JSON line only
Duplicate throttle line — old ×0.05 formula overwrote new ×0.2 formula
Cache‑hit AttributeError — removed _build_item call, inlined MediaItem construction with _get_cached_dimensions()
Boot screen jump cut — slideshow_ready now requires non‑None active GPU texture before signalling, preventing fade timer expiry during sync texture load
Slideshow‑started sent on empty queue — notification only fires when items are present; hot‑reload path also signals if initial load was empty
Progress bar jumping backward — uses batch size (6) as total instead of shrinking total_remaining
Progress bar disappearing — retains last‑known percentage when phase switches away from optimising_images
Boot layer duplicate docstring — removed dead duplicate of _read_progress_pct that shadowed the real implementation
WiFi captive portal not appearing on first boot — start_ap_mode() return value now checked; ap_was_active only set on success; wlan0 readiness polled (up to 30 s) before starting AP; hostapd no longer auto‑starts at boot
AP fallback retry deadlock — retry guard now checks is_ap_mode_active() as source of truth instead of a stale ap_was_active boolean
PIN validation race — PIN validated against cleared state (passed any input); now owned by NetworkController with threading.Lock; never passes when no PIN is active
Transparent PNG rendering — RGBA/PA images composited onto black background before RGB conversion; fixes white artifacts in transparent areas for both thumbnails and optimised images
Display power button state — Web UI button now reads actual display state from daemon via health endpoint instead of using a local toggle guess
Media library CPU spike — removed on‑the‑fly thumbnail generation from /api/media/list; relies on FolderWatcher‑generated thumbnails only
Log viewer follow mode — auto‑follow now jumps to last page when new entries arrive; scroll target corrected to #log-viewer container
On‑screen message spacing — Y‑position now accumulated from actual heights of preceding messages instead of index × current_height, eliminating gaps and overlaps between differently‑sized popups
Removed
All ffmpeg/ffprobe calls from PresentationEngine (engine.py): _extract_frame_array_cpu, _get_or_create_video_frame, _video_frame_cache_path, _video_frame_is_cached
contextlib import from frontend (no longer needed)
_count_playlist_items(), _PLAYLIST_PATH, and json import from boot layer
_resize_to_screen, _extract_exif, _build_item from ImageProcessor
ImageOps, UnidentifiedImageError imports from image.py