Commit Graph
150 Commits
Author SHA1 Message Date
maziggy 079e9fc2cc - Camera popup window:
- Auto-resize to fit video resolution on first open
  - Persist window size and position to localStorage
  - Restore saved window state for subsequent opens
2026-01-01 12:29:25 +01:00
maziggy d3686a1b60 - Fixed maintenance interval display on status page 2026-01-01 12:21:12 +01:00
maziggy ccdb084f23 - Refactored printer card's AMS section for better experience 2026-01-01 12:08:55 +01:00
maziggy 01bc211616 - Slicer Protocol:
- Add utils/slicer.ts with OS detection for protocol handler
  - Windows: bambustudio://, macOS/Linux: bambustudioopen://
  - Update all usages in ArchivesPage.tsx and ModelViewerModal.tsx
2026-01-01 07:52:47 +01:00
maziggy 2457577f54 - Added frontend/backend tests for new theme functions
- Fixed some bugs, where settings were not backed up or restored
- Docker: Fixed mounting of database for persistence
2025-12-31 17:07:15 +01:00
maziggy bca1a80172 * Add customizable theme system with style, background, and accent options
Implement comprehensive theme customization with independent settings for
  dark and light modes:

  - Style layer: Classic (clean shadows), Glow (accent-colored glow effects),
    Vibrant (dramatic deep shadows)
  - Background layer: Neutral, Warm, Cool (light mode); plus OLED, Slate,
    Forest (dark mode only)
  - Accent colors: Green, Teal, Blue, Orange, Purple, Red

  All combinations work independently (e.g., Glow + Forest + Teal). Settings
  sync across devices via database and show toast confirmations on change.

  Backend:
  - Add 6 new settings fields (dark_style, dark_background, dark_accent,
    light_style, light_background, light_accent)
  - Add integration test for theme settings API

  Frontend:
  - Refactor index.css with 3-layer CSS variable system
  - Update ThemeContext for dual-mode theme management
  - Add Appearance section to Settings page with 6 dropdowns
  - Update components for new ThemeContext API
2025-12-31 15:44:52 +01:00
maziggy 219d26b8ab - Fixed bug in printer's hour counter 2025-12-31 12:01:17 +01:00
maziggy 16c21cf1fa - Added confirmation modal to sidebar's quick power switch 2025-12-31 11:40:14 +01:00
maziggy 9f665a9382 Housekeeping 2025-12-31 11:36:53 +01:00
maziggy 0291f09649 - Fixed bug where add smart plug modal doesn't scroll on lower resolution screens (https://github.com/maziggy/bambuddy/issues/34) 2025-12-30 12:22:22 +01:00
maziggy 88487f8f46 Bottom sidebar layout fix 2025-12-30 09:28:32 +01:00
maziggy a322b8fad4 Minor bugfixes in queueing module 2025-12-29 16:00:49 +01:00
maziggy 88e84ca45a - Add Virtual Printer feature - emulate Bambu printer on network
- Virtual printer appears in Bambu Studio/Orca Slicer via SSDP discovery
  - Secure TLS/MQTT communication with auto-generated certificates
  - Queue mode (pending uploads) or auto-start mode
  - Configurable access code for authentication
  - Docker support with network_mode: host and certificate persistence
  - Fix backup/restore for virtual printer settings (auto-save no longer overwrites)
2025-12-29 15:52:48 +01:00
maziggy 3cba1c0989 - Fix AMS filament matching in reprint modal
- Add fuzzy color matching for filament comparison
    Colors within RGB threshold of 40 are considered a match, fixing
    cases where 3MF and printer report slightly different hex values
    for the same filament color (e.g., beige #F5F5DC vs #FCECD6)

  - Add POST /printers/{id}/refresh-status endpoint
    Sends MQTT pushall command to request fresh AMS data from printer

  - Improve Re-read button in filament check section
    - Now triggers backend refresh before refetching status
    - More prominent styling with text label and border
    - 500ms delay after pushall to allow printer to respond

  - Add AMS slot labels to matched filament display
    Shows location like "PLA (AMS-B Slot 3)" instead of just "PLA"

  - Add color hex tooltips on filament color circles
2025-12-29 10:03:51 +01:00
maziggy a008be96ee - All archive page filters are now persitent 2025-12-29 09:16:28 +01:00
maziggy e97d697d90 - Archive card: fixed a cache bug, where wrong cover image was shown 2025-12-29 09:05:10 +01:00
maziggy 0bcedd2c16 - Add Tasmota device discovery and switchbar quick access
- Features:
    - Tasmota device discovery: Auto-detect network and scan for Tasmota devices
      in Add Smart Plug modal. Supports devices with/without authentication.
    - Switchbar: Quick access widget in sidebar footer for controlling smart plugs
      from anywhere in the app. Shows real-time status and power consumption.
2025-12-28 15:02:42 +01:00
maziggy b3ef6ccb32 - Add AMS filament preview for reprints and file type badges
- AMS Filament Preview:
    - Add GET /archives/{id}/filament-requirements endpoint to extract
      filament data from archived 3MF slice_info.config
    - Show filament comparison in reprint modal when printer is selected
    - Compare both filament type AND color with visual indicators:
      - Green checkmark: Type and color match
      - Yellow warning: Same type, different color
      - Orange warning: Different type or empty slot
    - Handle AMS-HT units (id >= 128) and virtual tray (slot 254)
    - Normalize color formats between API (RRGGBBAA) and 3MF (#RRGGBB)

- File Type Badge:
  - Archive cards show GCODE (green) or SOURCE (orange) badge
  - Helps users identify which files have print data vs source-only
  - Badge appears next to printer name in card content area

- Camera Stream Stability:
  - Increase ffmpeg read timeout from 10s to 30s
  - Add ffmpeg RTSP options: -timeout, -buffer_size, -max_delay
  - Add frontend auto-reconnection with exponential backoff (2s-30s)
  - Show reconnection UI with countdown and attempt counter
2025-12-28 13:50:29 +01:00
maziggy 530a7a4608 - Fix camera stream stopping after a few minutes
- Backend:
    - Increase ffmpeg stdout read timeout from 10s to 30s
    - Add ffmpeg RTSP stability options: -timeout, -buffer_size, -max_delay
  - Frontend:
    - Add auto-reconnection with exponential backoff (2s→30s max)
    - Show reconnection UI with countdown and attempt counter
    - Maximum 5 reconnection attempts before showing error
    - "Reconnect now" button to skip countdown
    - Reset reconnection state on manual refresh or mode switch
2025-12-28 13:16:14 +01:00
maziggy b1be35ba8c - Add timelapse editor with trim, speed adjustment, and music overlay
- Add TimelapseEditorModal component with visual timeline scrubber
  - Add timelapse processing endpoints (info, thumbnails, process)
  - Add TimelapseProcessor service using FFmpeg for video processing
  - Support trim (start/end), speed (0.25x-4x), and audio overlay
  - Add Edit button to TimelapseViewer
  - Fix browser caching with timestamp query parameter
  - Change default timelapse playback speed from 2x to 1x
2025-12-28 13:06:30 +01:00
maziggy db5cb86d3a - Project page enhancements and bug fixes
- Add filament color swatches to project cards showing colors from assigned archives
  - Add "Hide done" toggle to filter completed BOM items
  - Include projects in backup/restore (with BOM items and attachments)
  - Add file type validation for project attachments
  - Enhance project card design with gradients, shadows, and glow effects
  - Improve layout spacing on project list and detail pages
  - Replace browser confirm dialogs with styled confirmation modals
  - Fix attachment uploads not persisting (SQLAlchemy JSON column mutation)
2025-12-28 12:04:33 +01:00
maziggy 8acc91d3cd - Add subnet scanning for printer discovery in Docker environments
- Detect Docker environment automatically via /.dockerenv and cgroup
  - Show subnet input field in Add Printer dialog when running in Docker
  - Scan IP range for Bambu printer ports (8883 MQTT, 990 FTPS)
  - Query SSDP to get printer name/serial/model from discovered IPs
  - Map raw SSDP model codes to friendly names (BL-P001→X1C, O1D→H2D)
2025-12-28 09:40:47 +01:00
maziggy e9193276a6 Summary of Fixes
1. "Unknown Status (255)" bug

  File: backend/app/services/printer_manager.py:291-293

  Problem: A1/P1 printers send stg_cur=255 when idle (unlike X1 which uses -1). The code treated 255 as a valid stage number and returned "Unknown stage (255)".

  Fix: Changed the condition from if state.stg_cur >= 0: to if 0 <= state.stg_cur < 255: so that 255 is now treated as "no calibration stage" (same as -1).

  2. AMS humidity/temp displaced on mobile

  File: frontend/src/pages/PrintersPage.tsx:1164-1212

  Problem: The AMS row used a rigid horizontal flex layout that didn't adapt to narrow mobile screens, causing humidity/temperature indicators to be displaced.

  Fix:
  - Added flex-wrap to allow wrapping on very narrow screens
  - Added ml-auto to humidity/temp section to keep it right-aligned
  - Added max-w-[60px] sm:max-w-none to truncate long filament names on mobile
  - Changed gap from fixed gap-3 to responsive gap-2 sm:gap-3

  Tests Added

  4 new unit tests for get_derived_status_name:
  - test_stg_cur_255_returns_none - Verifies A1/P1 idle state
  - test_stg_cur_negative_one_returns_none_when_idle - Verifies X1 idle state
  - test_valid_stage_returns_name - Verifies valid stages work
  - test_stg_cur_zero_returns_printing - Verifies printing state

  All 382 tests pass.
2025-12-27 15:53:37 +01:00
maziggy 929c4c8cd6 - Fix total print hours calculation in set_total_hours to include all
prints (not just completed), matching get_printer_total_hours behavior
  - Add option to keep or delete archives when deleting a printer
  - Custom maintenance types no longer auto-assign to all printers
  - Add UI to manually assign/remove custom maintenance types per printer
  - Add backend endpoints for assigning types to printers and removing items
  - Exclude static/assets from large file pre-commit check
2025-12-23 09:00:36 +01:00
maziggy ef68804fa9 - Added printer auto discovery to add printer modal 2025-12-21 11:23:20 +01:00
maziggy 0da5de0f46 - Printer cards now shows heater states 2025-12-21 10:48:50 +01:00
maziggy 0200f69ba3 - Show detailed printer status stages on printer cards
- Add getStatusDisplay() helper in frontend to format status nicely
  - Add get_derived_status_name() in backend to compute status from:
    - stg_cur calibration stages (bed leveling, vibration comp, etc.)
    - Temperature data when stg_cur unavailable (heating heatbed/nozzle)
  - Include stg_cur and stg_cur_name in WebSocket broadcasts
  - Update broadcast status key to detect heating phase changes

  Printer cards now show: "Heating heatbed", "Heating nozzle", "Preparing",
  "Auto bed leveling", "Cleaning nozzle tip", and 60+ other stage names
  instead of just "Printing" or "Idle".
2025-12-21 10:24:26 +01:00
maziggy 9f0debc5a7 Fix printer status "timelapse" effect after print completion
When navigating to the printer page after a print completed, status
metrics would animate slowly from mid-print values to final state.

Root cause: The message queue processed messages one at a time with 16ms
delays. If 50+ status updates queued during a print, they took 800ms+ to
process, causing multiple throttle windows and the visual "timelapse".

Solution: Handle printer_status messages directly without queueing.
They're already throttled (100ms) so don't need queue rate limiting.
Other message types still use the queue.
2025-12-17 15:03:27 +01:00
maziggyandClaude Opus 4.5 a7319f0e70 Fix browser freeze on print completion with camera stream open
Root cause: When a print completed with the camera stream popup open,
spawning a second ffmpeg process for finish photo capture caused a
conflict that froze the browser tab and video window.

Solution: Buffer the last frame from active camera streams. When
capturing finish photo, use the buffered frame if a stream is active
instead of spawning a new ffmpeg process.

Backend changes:
- camera.py: Added _last_frames buffer and get_buffered_frame() helper
- main.py: Photo capture uses buffered frame when stream is active
- main.py: Moved slow operations to background tasks (energy calc,
  photo capture, smart plug, notifications, maintenance)
- archive.py: Fixed sync file write blocking event loop (asyncio.to_thread)
- printers.py: Added debug endpoint to simulate print completion

Frontend changes:
- useWebSocket.ts: Throttled printer status updates (100ms) to prevent
  UI overload from rapid WebSocket messages
- useWebSocket.ts: Debounced archive invalidations (3s) to prevent
  cascade of re-renders on print completion
- useWebSocket.test.ts: Updated tests for throttled/debounced handlers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 18:45:03 +01:00
maziggy e248b41fad Fixed bug where layer slider in gcode viewer was broken 2025-12-16 12:35:53 +01:00
maziggy c8b9a8e3d6 Summary of Changes
Backend (backend/app/api/routes/archives.py):
  - Added filament_colors extraction from slice_info.config in 3MF files
  - Maps filament IDs (1-based) to tool numbers (0-based)

  Frontend (frontend/src/components/GcodeViewer.tsx):
  1. Multi-color support: Pass extrusionColor as array of CSS color strings for multi-color prints
  2. Bambu T-command filtering: Filter out special Bambu commands (T255, T1000, T1001, T65535, T65279) that corrupt tool state
  3. Initial tool: Prepend T0 to ensure initial tool is set correctly
  4. Disable gradient: Set disableGradient: true for multi-color to preserve actual filament colors (the gradient was turning black into gray/white)
  5. Disable topLayerColor: Don't set topLayerColor for multi-color (it overrides per-tool colors)

  Key fixes for Bambu G-code compatibility:
  - Bambu uses special T commands (T1000, T65535, etc.) that aren't real tool changes
  - The gcode-preview library's brightness gradient was modifying colors based on layer index

  Commit message suggestion:
  Add multi-color filament support to G-code viewer

  - Extract filament colors from 3MF slice_info.config
  - Pass color array to gcode-preview for tool-based coloring
  - Filter Bambu special T commands (T1000, T65535, etc.)
  - Disable gradient to preserve actual filament colors
2025-12-16 12:30:58 +01:00
maziggy 5cd366ceac Fix browser tab crash on print completion (debounced invalidations)
When a print completes, multiple WebSocket messages are sent rapidly
  (print_complete, archive_updated, etc.). Each was immediately invalidating
  the archives query, causing a cascade of rapid re-renders that crashed
  the browser tab.

  Added debounced query invalidation to useWebSocket.ts:
  - Multiple invalidation requests within 100ms are coalesced into one
  - Uses requestAnimationFrame to avoid blocking the main thread
  - Properly cleans up timeouts on unmount
2025-12-16 10:26:30 +01:00
maziggy 99b504079b Fixed bug in archive card context menu 2025-12-16 08:26:02 +01:00
maziggy 0bb76024fd Fix printer card flicker when disconnecting
- Cache connected state in ref to prevent flicker when status
    briefly becomes undefined during query refetches
  - Use cached value for shouldHide calculation instead of raw status
2025-12-15 14:03:26 +01:00
maziggy f22acedc7d Update HMS error handling with comprehensive database and unknown filtering
- Replace manual error database with 853 codes from ha-bambulab
    (Source: https://github.com/greghesp/ha-bambulab)
  - Filter out unknown HMS error codes from display
    (Printers sometimes send duplicate/undocumented codes for same error)
  - Update HMS badge count to only show known errors
  - Export filterKnownHMSErrors helper for reuse across components
2025-12-15 13:50:13 +01:00
maziggyandClaude Opus 4.5 3a8cdba1e3 Improve HMS error descriptions and wiki links
- Add ~80 HMS error code descriptions covering:
  - 0300 series: temperature, fan, motor, force sensor, build plate errors
  - 0500 series: storage, network, module communication, camera errors
  - 0700 series: AMS communication, filament, hotend matching errors
  - 0C00 series: toolhead camera, lidar, AI detection errors
  - 1200 series: external spool errors
- Fix 0500_0400_0002_0043 description (toolhead camera dirty, not file system)
- Always link to HMS home page (wiki.bambulab.com/en/hms/home) instead of
  specific error pages that may not exist

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 09:46:19 +01:00
maziggy adcefd3a5b Issue 1: Aborted prints showing "Unknown" in failure analysis
- The failure analysis service was only querying status == "failed"
  - Changed all queries to include both failed AND aborted: status.in_(["failed", "aborted"])
  - File: backend/app/services/failure_analysis.py

  Issue 2: Cannot clear failed flag in edit mode
  - Added status field to ArchiveUpdate schema (backend)
  - Added status to API client type (frontend)
  - Added status dropdown to EditArchiveModal with options: Completed, Failed, Cancelled, Printing
  - When status changes to "completed", failure_reason is automatically cleared
  - File changes:
    - backend/app/schemas/archive.py - added status field
    - frontend/src/api/client.ts - added status to updateArchive type
    - frontend/src/components/EditArchiveModal.tsx - added status dropdown
2025-12-15 08:50:17 +01:00
maziggyandClaude Opus 4.5 dc92816515 Fix i18next warning in frontend tests
- Initialize i18n in test setup to suppress 'NO_I18NEXT_INSTANCE' warning
- Update AMSHistoryModal tests to use actual translated text

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 09:54:36 +01:00
maziggyandClaude Opus 4.5 6cd9cb7033 Fixed docker build warnings
- Added DEBIAN_FRONTEND=noninteractive to suppress debconf warnings
- Added --root-user-action=ignore to pip install
- Added ffmpeg to Docker image
- Increased chunkSizeWarningLimit to 3000 for Vite build

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 09:36:19 +01:00
maziggyandClaude Opus 4.5 ca2bd6bf57 Fix timelapse auto-download and browser tab crash on print completion
1. Timelapse auto-download rewrite (main.py)
   - New retry mechanism with short delays (5s, 10s, 20s)
   - Grabs most recent file directly without mtime comparison
   - Works with printers that have wrong clock (LAN-only mode)
   - Checks multiple paths: /timelapse, /timelapse/video, /record, /recording

2. Browser tab crash fix (PrintersPage.tsx)
   - Throttled query cache subscription using requestAnimationFrame
   - Prevents rapid re-render cascade on print completion events

3. WebSocket optimization (main.py)
   - Removed large raw_data field from print_complete message
   - Only sends necessary fields: status, filename, subtask_name, timelapse_was_active

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 08:46:38 +01:00
maziggy e85e5fa33e Backend changes:
1. bambu_mqtt.py: Added hms_errors to the completion callback data, which includes the HMS error codes when a print fails
  2. archive.py: Updated update_archive_status() to accept an optional failure_reason parameter
  3. main.py: Added auto-detection of failure reasons:
    - status == "aborted" → failure_reason = "User cancelled"
    - status == "failed" with HMS errors → maps module codes to reasons:
        - Module 0x07 (Filament) → "Filament runout"
      - Module 0x0C (Motion Controller) → "Layer shift"
      - Module 0x05 (Nozzle) → "Clogged nozzle"

  Frontend changes:

  1. ArchivesPage.tsx:
    - Badge now shows "cancelled" for aborted prints, "failed" for failed prints
    - "Failed Prints" collection and "Hide Failed" filter now include both failed and aborted statuses
  2. EditArchiveModal.tsx: Failure reason field now shows for both failed and aborted prints

  New tests added:
  - test_hms_errors_included_in_failed_completion_callback
  - test_aborted_status_when_cancelled

  The HMS error module mapping is basic and can be expanded as you observe more failure types. The actual Bambu HMS error codes are documented in their wiki - we can add
  more mappings as needed.
2025-12-13 10:13:54 +01:00
maziggy f5cec45dbf 1. Timelapse viewer default speed changed to 2x
- frontend/src/components/TimelapseViewer.tsx:17 - Changed from 0.5 to 2

  2. Archive page: hide failed prints filter with persistence
  - Added hideFailed state initialized from localStorage.getItem('archiveHideFailed')
  - Added localStorage persistence via useEffect
  - Added "Hide Failed" toggle button (red styling when active) next to the Favorites button
  - Filter is automatically bypassed when viewing the "Failed Prints" collection
  - "Reset" button also clears this filter

  The filter state persists across page refreshes and browser sessions via localStorage.
2025-12-13 10:04:15 +01:00
maziggy 9ba4ef6355 Fix docker build 2025-12-12 16:48:12 +01:00
maziggy b6b83a6360 Added a persistent loading toast with spinner for backup exports that include print archives:
Changes to ToastContext.tsx:
  - Added 'loading' toast type with a spinning Loader2 icon
  - Added showPersistentToast(id, message, type) function for toasts that don't auto-dismiss
  - Exposed dismissToast(id) function to allow programmatic dismissal
  - Added green-themed styling for loading toasts

  Changes to SettingsPage.tsx:
  - When exporting a backup that includes archives, shows a persistent "Preparing backup..." toast with spinner
  - The toast is automatically dismissed when the download starts or if an error occurs
  - For non-archive backups (which are fast), no loading toast is shown

  The user will now see clear feedback when creating a backup with print archives - a toast with a spinner appears immediately after clicking export and stays visible
  until the download dialog appears.
2025-12-12 16:12:30 +01:00
maziggy a14dcbc034 Frontend WebSocket Tests Summary
Expanded the useWebSocket.test.ts from minimal coverage to 20 comprehensive tests covering:

  WebSocket Mock Tests (6 tests)

  - Creates WebSocket with correct URL
  - Starts in CONNECTING state
  - Transitions to OPEN state
  - Can receive messages
  - Can close connection
  - Tracks all instances

  Hook Connection Tests (2 tests)

  - Connects to WebSocket on mount
  - Reports connected state when WebSocket opens

  Message Handling Tests (9 tests)

  - Updates printer status in query cache on printer_status message
  - Preserves wifi_signal when new value is null
  - Invalidates archives on print_complete message
  - Invalidates archives on archive_created message
  - Invalidates archives on archive_updated message (new handler for timelapse auto-assignment)
  - Ignores pong messages without error
  - Handles malformed JSON gracefully
  - Handles unknown message types gracefully

  sendMessage Tests (2 tests)

  - Sends JSON message when connected
  - Does not send when disconnected

  Reconnection Tests (2 tests)

  - Reconnects after connection closes
  - Cleans up on unmount

  Key Fixes

  - Fixed MSW (Mock Service Worker) conflict by:
    a. Adding addEventListener/removeEventListener to MockWebSocket class
    b. Updating MSW setup to bypass WebSocket requests
    c. Properly managing WebSocket mock lifecycle in each test

  Test Results:
  - Frontend: 137 tests passed (9 test files)
  - Backend: 346 tests passed
2025-12-12 15:52:35 +01:00
maziggy 3a0890145b Fix unused variable in test file causing Docker build failure 2025-12-12 14:48:41 +01:00
maziggy f7cde95bbb Tests Added Summary:
Backend Tests (New Files)

  1. backend/tests/integration/test_camera_api.py - 13 tests
    - Camera stop endpoint (GET and POST methods)
    - Active stream termination
    - Printer-specific stream stopping
    - Camera test endpoint
    - Camera snapshot endpoint
    - Camera stream validation
  2. backend/tests/integration/test_system_api.py - 16 tests
    - System info endpoint structure
    - App, database, storage, memory, CPU sections
    - Helper functions (format_bytes, format_uptime)
    - Archive statistics
    - Printer connection info
  3. backend/tests/unit/services/test_telemetry.py - 16 tests
    - Installation ID creation/retrieval
    - Telemetry enabled/disabled checks
    - Telemetry URL configuration
    - Heartbeat sending and rate limiting
    - Exception handling
  4. backend/tests/unit/services/test_notification_service.py (updated) - Added TestNotificationVariableFallbacks class with 9 tests
    - Duration formatting with None values
    - Print complete fallback values
    - Estimated time and remaining time fallbacks
    - Filename fallback to "Unknown"

  Frontend Tests (New Files)

  5. frontend/src/__tests__/pages/SystemInfoPage.test.tsx - 14 tests
    - Loading and error states
    - Application, database, storage, memory, CPU sections
    - Connected printers display
    - Disk usage warning/danger colors
    - Refresh button
  6. frontend/src/__tests__/components/AMSHistoryModal.test.tsx - 21 tests
    - Modal open/close behavior
    - Humidity/temperature mode switching
    - Time range selection
    - Statistics cards (current, avg, min, max)
    - Loading and error states
    - Trend calculation
  7. frontend/src/__tests__/components/NotificationProviderCard.test.tsx (updated) - Added NotificationProviderCard AMS toggles describe block with 11 tests
    - AMS humidity/temperature notification toggles
    - AMS-HT humidity/temperature notification toggles
    - Independence of AMS and AMS-HT toggles
    - Default values

  Total New Tests: ~100 tests covering:
  - Camera API endpoints
  - System info API
  - Anonymous telemetry service
  - Notification variable fallbacks
  - System Info page UI
  - AMS History modal UI
  - AMS/AMS-HT notification toggle settings

  Tests Added - Complete Summary:

  Backend Tests (4 new files, 1 updated)

  | File                                                       | Tests | Description                                                     |
  |------------------------------------------------------------|-------|-----------------------------------------------------------------|
  | tests/integration/test_camera_api.py                       | 13    | Camera stop (GET/POST), snapshot, stream, test endpoints        |
  | tests/integration/test_system_api.py                       | 16    | System info API, helper functions (format_bytes, format_uptime) |
  | tests/unit/services/test_telemetry.py                      | 16    | Installation ID, telemetry toggle, heartbeat sending            |
  | tests/unit/services/test_notification_service.py (updated) | +10   | Variable fallbacks for duration, filename, estimated_time       |

  Frontend Tests (2 new files, 1 updated)

  | File                                                                 | Tests | Description                                           |
  |----------------------------------------------------------------------|-------|-------------------------------------------------------|
  | src/__tests__/pages/SystemInfoPage.test.tsx                          | 14    | System info page UI, sections, loading/error states   |
  | src/__tests__/components/AMSHistoryModal.test.tsx                    | 21    | AMS history modal, mode switching, time ranges, stats |
  | src/__tests__/components/NotificationProviderCard.test.tsx (updated) | +11   | AMS/AMS-HT notification toggle independence           |

  Total: 101 new tests
  - Backend: 335 tests total (all passing)
  - Frontend: 124 tests total (all passing)
2025-12-12 11:15:59 +01:00
maziggy 1a4f146dd0 Notifications:
- Separate AMS and AMS-HT notification switches (one per device type)
  - Fix notification variables not showing (duration, filament, estimated_time)
  - Add fallback values for empty notification variables ("Unknown" instead of blank)

  Settings:
  - Fix API keys badge count only showing after visiting tab
  - Move External Links card to third column above Updates
  - Add Release Notes modal for viewing full notes before updating

  Statistics:
  - Fix filament usage trends not showing (wrong API parameters)
  - Move dashboard controls (Hidden, Reset Layout) to header row
  - Remove duplicate Reset Layout button

  Camera:
  - Fix ffmpeg processes not killed when closing webcam window
  - Add /camera/stop endpoint with POST support for sendBeacon
  - Track active streams and proper cleanup on disconnect
2025-12-12 09:18:59 +01:00
maziggy 7048bafff3 Added anonymous stats 2025-12-11 13:35:59 +01:00
maziggy 77a66ea867 Added system info page 2025-12-11 12:45:38 +01:00