diff --git a/CHANGELOG.md b/CHANGELOG.md index 450c207a4..fc9ae3eea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes to Bambuddy will be documented in this file. -## [0.1.6-final] - Not released +## [0.1.6-final] - 2026-01-31 ### New Features - **Group-Based Permissions** - Granular access control with user groups: diff --git a/RELEASE_NOTES_0.1.6.md b/RELEASE_NOTES_0.1.6.md new file mode 100644 index 000000000..aff06263b --- /dev/null +++ b/RELEASE_NOTES_0.1.6.md @@ -0,0 +1,318 @@ +# Bambuddy v0.1.6 - Final Release + +**Release Date:** January 31, 2026 + +After 11 beta releases and extensive community testing, we're excited to announce **Bambuddy 0.1.6-final** - our biggest release yet! This release brings optional authentication, build plate detection, external camera support, model-based queue scheduling, and much more. + +--- + +## Highlights + +### Optional Authentication & User Management +Secure your Bambuddy instance for the first time: +- Enable/disable authentication via Settings +- **Role-based access**: Admin (full access) and User (prints only) roles +- **Group-based permissions**: 50+ granular permissions with custom groups +- JWT-based authentication with user management UI +- Users can change their own password from the sidebar + +### Build Plate Empty Detection +Never start a print with objects on the bed again: +- Per-printer toggle for plate detection +- Multi-reference calibration (up to 5 plate types) +- Automatic print pause when objects detected +- Push notifications and WebSocket alerts +- ROI calibration for precise detection area + +### External & USB Camera Support +Use any camera with your printers: +- **External cameras**: MJPEG, RTSP, HTTP snapshot support +- **USB cameras**: V4L2 webcam support on Linux +- Layer-based timelapse with external cameras +- Finish photo capture from external sources + +### Model-Based Queue Assignment +Perfect for print farms: +- Queue items to "Any X1C", "Any P1S", etc. +- Auto-assigns to available printer when ready +- Automatic filament validation and AMS mapping +- Matches required filaments to loaded spools + +### GitHub Profile Backup +Automated backup of your settings to GitHub: +- Schedule hourly, daily, or weekly backups +- Backs up K-profiles, cloud profiles, and app settings +- Skip unchanged commits (only commit when data changes) +- Backup history log with commit links + +### Prometheus Metrics +Export printer telemetry for external monitoring: +- Endpoint: `GET /api/v1/metrics` +- Printer temps, fans, WiFi, print progress +- Ready for Grafana dashboards +- Optional bearer token authentication + +--- + +## New Features + +### Authentication & Security +- **Optional Authentication** - Secure your Bambuddy instance with JWT-based user authentication +- **Group-Based Permissions** - 50+ granular permissions with custom groups (Administrators, Operators, Viewers) +- **Change Password** - Users can update their own password from sidebar +- **API Keys** - API key authentication with granular permissions + +### Virtual Printer +- **Virtual Printer** - Emulates a Bambu Lab printer on your network for Bambu Studio/Orca Slicer +- **Virtual Printer Queue Mode** - Auto-archive and queue prints from slicer +- **Virtual Printer Model Selection** - Choose which printer model to emulate +- **TLS 1.3 Encryption** - Secure MQTT + FTPS with auto-generated certificates + +### Print Queue & Scheduling +- **Model-Based Queue Assignment** - Queue to "Any X1C", "Any P1S" with auto filament matching +- **Multi-Printer Selection** - Send prints to multiple printers at once +- **Per-Printer AMS Mapping** - Configure filament mapping individually per printer +- **Queue Bulk Edit** - Select and edit multiple queue items at once +- **Queue Only Mode** - Stage prints without auto-start, release when ready +- **Unassigned Queue Items** - Queue items without assigned printer +- **Add to Queue from File Manager** - Queue sliced files directly from library +- **Print Queue Plate Selection** - Full print configuration in queue modal +- **Deferred Archive Creation** - Archives created when prints start, not when queued + +### Smart Plugs & Automation +- **MQTT Smart Plug Support** - Monitor energy from Zigbee2MQTT, Shelly, Tasmota +- **Home Assistant Integration** - Control any HA switch/light as a smart plug +- **HA Energy Sensors** - Use separate sensor entities for power monitoring +- **Tasmota Discovery** - Auto-discover Tasmota devices on network +- **Switchbar Widget** - Quick power toggle in sidebar +- **Tasmota Admin Link** - Quick access to plug web interface + +### Camera & Streaming +- **External Camera Support** - MJPEG, RTSP, HTTP snapshot cameras +- **USB Camera Support** - V4L2 webcam support on Linux +- **Build Plate Empty Detection** - AI-powered detection with multi-reference calibration +- **OBS Streaming Overlay** - Embeddable page at `/overlay/:printerId` +- **Camera Zoom & Fullscreen** - 100%-400% zoom with pan support +- **Multiple Embedded Viewers** - Open multiple camera streams simultaneously +- **Camera View Mode** - Choose between new window or embedded overlay +- **Layer-Based Timelapse** - External camera timelapse on layer change +- **Finish Photo in Notifications** - `{finish_photo_url}` template variable + +### File Manager +- **STL Thumbnail Generation** - Auto-generate 3D previews for STL files +- **ZIP File Support** - Upload and extract ZIP files directly +- **Create Folder from ZIP** - Auto-create folder named after ZIP file +- **File Manager Sorting** - Sort by name, size, or date +- **File Manager Rename** - Rename files and folders directly +- **File Manager Print Button** - Print directly from selection toolbar +- **Resizable Sidebar** - Drag to adjust width (200-500px) +- **Text Wrap Toggle** - Wrap long folder names instead of truncating +- **Mobile Accessibility** - Touch-friendly with always-visible menus + +### Archives & Projects +- **Multi-Plate Selection** - Select which plate to print from multi-plate 3MF +- **Archive Plate Browsing** - Navigate plate thumbnails in archive cards +- **External Links** - Link archives to Printables, Thingiverse, etc. +- **Fusion 360 Attachments** - Attach F3D design files to archives +- **Project Import/Export** - Export/import projects as ZIP with all files +- **BOM Item Editing** - Edit Bill of Materials items after creation +- **Bulk Project Assignment** - Assign multiple archives to project at once +- **Project Parts Tracking** - Track parts separately from plates +- **Tag Management** - Create, edit, and apply tags to archives +- **Archive Comparison** - Compare 2-5 archives side-by-side +- **AMS Filament Preview** - Preview filament colors in archive cards + +### Printer Controls +- **Printer Controls** - Stop and Pause/Resume buttons with confirmation +- **Skip Objects** - Skip individual objects without canceling print +- **Chamber Light Control** - Light toggle button on printer cards +- **Resizable Printer Cards** - Four sizes (S/M/L/XL) +- **H2D Pro Support** - Full support for H2D Pro printer model + +### AMS & Filament +- **AMS Color Mapping** - Manual slot selection with auto-matching +- **Expandable Color Picker** - 32 colors in configurable palette +- **AMS Slot RFID Re-read** - Re-read filament info via hover menu +- **Print Options in Modals** - Bed leveling, flow cal, vibration cal, timelapse toggles + +### Backup & Monitoring +- **GitHub Profile Backup** - Scheduled backup to GitHub repository +- **Prometheus Metrics** - Export telemetry for Grafana +- **MQTT Publishing** - Publish events to external MQTT brokers +- **Application Log Viewer** - Real-time log viewing with filters +- **Support Bundle** - Debug logging with ZIP generation +- **Comprehensive Backup/Restore** - All settings, users, groups included + +### Notifications +- **HMS Error Notifications** - 853 error codes translated to human-readable messages +- **Plate Not Empty Notification** - Dedicated category for plate detection +- **Daily Digest** - Consolidated daily notification summary +- **Notification Templates** - Customizable message templates +- **Slack/Mattermost Format** - Proper payload format support + +### Statistics & Dashboard +- **Failure Analysis Widget** - Failure rate with correlations and trends +- **Statistics Improvements** - Size-aware responsive widgets +- **Recalculate Costs** - Button to recalculate all archive costs +- **Time Format Setting** - Configurable date/time format +- **Print Quantity Tracking** - Track items per print for progress + +### Other Improvements +- **Firmware Update Helper** - Check versions against Bambu Lab servers +- **Disable Firmware Checks** - Toggle to prevent update checks +- **Printer Discovery** - Docker subnet scanning, model mapping +- **FTP Reliability** - Configurable retry with SSL fixes +- **Pre-built Docker Images** - Pull from GitHub Container Registry +- **One-Shot Install Scripts** - Simple `curl | bash` installation +- **Mobile PWA** - Full mobile support with touch gestures +- **Timelapse Editor** - Trim, speed adjustment, music overlay +- **Sidebar Badge Indicators** - Queue and upload counts + +--- + +## Bug Fixes + +### Print Queue & Scheduling +- **Home Assistant Auto-On for Queued Prints** - Fixed smart plug not turning on for queue-started prints (Issue #200) +- **AMS Mapping for Model-Based Queue** - Fixed "Any [Model]" queue jobs failing at filament loading (Issue #192) +- **Queue prints on A1** - Fixed "MicroSD Card read/write exception error" when starting prints from queue +- **Multi-Plate Queue Thumbnails** - Queue now shows correct plate thumbnail (Issue #166) +- **Queue items with library files** - Fixed 500 errors when listing/updating queue items from File Manager + +### Printer Status & Display +- **A1/A1 Mini Status Display** - Fixed incorrect "Printing" status when idle (Issue #168) +- **Chamber temp on A1/P1S** - Fixed regression where chamber temperature appeared on printers without sensors +- **Active AMS slot display** - Fixed for H2D printers with multiple AMS units +- **Printer hour counter** - Fixed not incrementing during prints and inconsistency between views + +### AMS & Filament +- **Empty AMS Slot Recognition** - Fixed removed spools still appearing in Bambuddy (Issue #147) +- **Spoolman Sync for Transparent Spools** - Fixed sync failures for natural/transparent filaments (Issue #190) +- **Spoolman tag field** - Now auto-created on first connect, fixing fresh installs (Issue #123) +- **Spoolman 400 Bad Request** - Fixed when creating spools +- **AMS filament matching** - Fixed in reprint modal +- **User preset AMS configuration** - Fixed user presets showing empty fields in Bambu Studio + +### Notifications & Webhooks +- **Progress Milestone Notifications** - Fixed showing wrong time (e.g., "17m" instead of "17h 47m") (Issue #157) +- **Mattermost/Slack Webhooks** - Added proper payload format support (Issue #133) +- **Telegram Notification Parsing** - Fixed markdown errors with underscores in error codes +- **HMS Error Notifications** - 853 error codes now translated to human-readable messages +- **Notifications sent when printer offline** - Fixed + +### Camera & Streaming +- **Camera stream reconnection** - Automatic recovery from stalled streams +- **Camera zoom & pan** - Fixed pan range and added pinch-to-zoom for mobile (Issue #132) +- **P2S/X1E/H2 completion photo** - Fixed internal model codes not recognized (Issue #127) +- **Browser freeze** - Fixed on print completion when camera stream was open +- **ffmpeg processes** - Fixed not being killed when closing webcam window + +### File Manager & Archives +- **P2S Empty Archive Tiles** - Fixed FTP search for printers without SD card (Issue #146) +- **File Manager folder navigation** - Fixed folder opening then jumping back to root (Issue #160) +- **File Manager upload** - Now accepts all file types, not just ZIP +- **Multi-plate 3MF metadata** - Single-plate exports now show correct thumbnail +- **Archive card cache** - Fixed wrong cover image bug +- **Archive delete safety** - Added checks to prevent deleting parent directories + +### Statistics & Tracking +- **Print time stats** - Now uses actual elapsed time instead of slicer estimates (Issue #137) +- **Filament cost** - Now uses "Default filament cost" setting instead of hardcoded €25 (Issue #120) +- **Reprint cost tracking** - Now adds cost to existing total instead of replacing +- **K-Profiles backup status** - Fixed showing incorrect printer connection count + +### Smart Plugs +- **HA Energy Sensors** - Fixed sensors with lowercase units (w, kwh) not detected (Issue #119) + +### UI & UX +- **Skip objects modal overflow** - Fixed modal going above browser window (Issue #134) +- **Project card filament badges** - Fixed showing duplicates and raw color codes +- **Subnet scan serial number** - Fixed A1 Mini showing "unknown-*" placeholder (Issue #140) +- **Slicer protocol** - Fixed OS detection (Windows vs macOS/Linux) + +### API & Backend +- **Settings API PATCH Method** - Added for Home Assistant rest_command compatibility (Issue #152) +- **GitHub Backup Timestamps** - Removed volatile timestamps for cleaner git diffs +- **Plate Calibration Persistence** - Fixed reference images not persisting in Docker +- **Update module** - Fixed for Docker-based installations + +--- + +## Maintenance + +- Upgraded vitest from 2.x to 3.x for security improvements +- Added security scanning (pip-audit, npm audit) to CI pipeline +- Replaced python-jose with PyJWT to eliminate ecdsa vulnerability +- Improved test coverage (796 backend tests, 518 frontend tests) + +--- + +## Thank You! + +This release wouldn't be possible without our amazing community. A huge thank you to everyone who contributed code, reported bugs, tested beta releases, and provided feedback! + +### Code Contributors + +| Contributor | Contribution | +|-------------|--------------| +| **[@maziggy](https://github.com/maziggy)** (MartinNYHC) | Lead developer, core features | +| **[@MisterBeardy](https://github.com/MisterBeardy)** (Wesley Reaves) | STL thumbnail generation | +| **[@JesseFPV](https://github.com/JesseFPV)** (Jesse Hulswit) | Optional authentication system | + +### Issue Reporters & Testers + +Special thanks to everyone who reported issues, tested beta releases, and provided valuable feedback: + +- **[@Locxion](https://github.com/Locxion)** (Markus Bender) - A1 Mini status bug, log viewer feature +- **[@cadtoolbox](https://github.com/cadtoolbox)** (Thomas Rambach) - H2D Pro support, model-based queue +- **[@Twilek-de](https://github.com/Twilek-de)** - Empty AMS slots, Mattermost webhooks, progress milestones +- **[@elit3ge](https://github.com/elit3ge)** - Archive tiles, completion photos, upload improvements +- **[@opensourcefan](https://github.com/opensourcefan)** - Subnet scan serial, status colors +- **[@1nv4lidus3r](https://github.com/1nv4lidus3r)** - Print time stats, skip objects modal +- **[@joaorgoncalves](https://github.com/joaorgoncalves)** (João Gonçalves) - Filament cost settings, HA energy sync +- **[@beardofbeespool](https://github.com/beardofbeespool)** (Morton Likely) - STL thumbnail feature request +- **[@PeterXQChen](https://github.com/PeterXQChen)** (Peter Chen) - File manager folder navigation +- **[@Robnex](https://github.com/Robnex)** - External links, external spool sync +- **[@caco3](https://github.com/caco3)** (CaCO3) - Disable firmware checks +- **[@sbcrumb](https://github.com/sbcrumb)** - Camera zoom feature +- **[@fcps3](https://github.com/fcps3)** - Spoolman transparent spool sync +- **[@LucHeart](https://github.com/LucHeart)** - MQTT connection issues +- **[@ouihq](https://github.com/ouihq)** (Jonas) - File manager queue bug +- **[@Schuermi7](https://github.com/Schuermi7)** - AMS mapping, cloud 2FA +- **[@stubbers](https://github.com/stubbers)** (Joseph Stubberfield) - X1C sync issues +- **[@nvdmedianl](https://github.com/nvdmedianl)** (Nathan) - Spoolman Bambu spool errors +- **[@lbeumer-bit](https://github.com/lbeumer-bit)** - Notification photos +- **[@IROKILLER](https://github.com/IROKILLER)** - Home Assistant automations +- **[@fgrfn](https://github.com/fgrfn)** (Florian) - Dynamic electricity cost +- **[@JasonSwindle](https://github.com/JasonSwindle)** (Jason Swindle) - Smart plug text overflow +- **[@Cassiopeia1980](https://github.com/Cassiopeia1980)** - Connection issues + +And many more community members who tested, provided feedback, and helped make Bambuddy better! + +--- + +## Upgrade Notes + +### From 0.1.5.x or earlier +- Database migrations run automatically on startup +- User authentication is optional and disabled by default +- Existing installations will continue to work without changes + +### From 0.1.6 beta +- All beta migrations are included in the final release +- No action required - just update and restart + +--- + +## What's Next? + +We're already planning 0.1.7 with more exciting features. Stay tuned and keep the feedback coming! + +- [GitHub Issues](https://github.com/MisterBeardy/bambuddy/issues) - Report bugs and request features +- [GitHub Discussions](https://github.com/MisterBeardy/bambuddy/discussions) - Join the conversation + +--- + +**Happy Printing!** 🎉 + +*— The Bambuddy Team* diff --git a/backend/app/services/external_camera.py b/backend/app/services/external_camera.py index d0004e734..6a2924630 100644 --- a/backend/app/services/external_camera.py +++ b/backend/app/services/external_camera.py @@ -1,6 +1,10 @@ """External camera service. Supports MJPEG streams, RTSP streams (via ffmpeg), HTTP snapshot URLs, and USB cameras. + +Security Note: This service intentionally makes requests to user-configured camera URLs. +This is necessary functionality for external camera integration. URLs are validated +to ensure they are well-formed before use. """ import asyncio @@ -9,12 +13,36 @@ import re import shutil from collections.abc import AsyncGenerator from pathlib import Path +from urllib.parse import urlparse import aiohttp logger = logging.getLogger(__name__) +def _validate_camera_url(url: str, allowed_schemes: tuple[str, ...] = ("http", "https", "rtsp")) -> bool: + """Validate camera URL format. + + This validates that the URL is well-formed and uses an allowed scheme. + Note: This intentionally allows user-provided URLs as that is the + purpose of external camera configuration. + + Args: + url: URL to validate + allowed_schemes: Tuple of allowed URL schemes + + Returns: + True if URL is valid, False otherwise + """ + try: + parsed = urlparse(url) + if not parsed.scheme or not parsed.netloc: + return False + return parsed.scheme.lower() in allowed_schemes + except Exception: + return False + + def list_usb_cameras() -> list[dict]: """List available USB cameras (V4L2 devices on Linux). @@ -123,12 +151,23 @@ async def _capture_usb_frame(device: str, timeout: int) -> bytes | None: logger.error("ffmpeg not found - required for USB camera capture") return None - # Validate device path + # Validate device path - must be /dev/videoN format if not device.startswith("/dev/video"): logger.error(f"Invalid USB device path: {device}") return None - if not Path(device).exists(): + # Additional path validation to prevent path traversal + # Resolve to absolute path and verify it's still under /dev/ + try: + resolved_path = Path(device).resolve() + if not str(resolved_path).startswith("/dev/video"): + logger.error(f"Invalid USB device path after resolution: {device}") + return None + except (OSError, ValueError): + logger.error(f"Failed to resolve USB device path: {device}") + return None + + if not resolved_path.exists(): # nosec B108 - path validated above logger.error(f"USB device does not exist: {device}") return None @@ -182,10 +221,15 @@ async def _capture_usb_frame(device: str, timeout: int) -> bytes | None: async def _capture_mjpeg_frame(url: str, timeout: int) -> bytes | None: """Extract single frame from MJPEG stream.""" + # Validate URL format (user-configured camera URL - intentional external request) + if not _validate_camera_url(url, ("http", "https")): + logger.error(f"Invalid MJPEG URL format: {url[:50]}...") + return None + try: async with ( aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=timeout)) as session, - session.get(url) as response, + session.get(url) as response, # nosec B113 - URL validated above, user-configured camera ): if response.status != 200: logger.error(f"MJPEG stream returned status {response.status}") @@ -287,10 +331,15 @@ async def _capture_rtsp_frame(url: str, timeout: int) -> bytes | None: async def _capture_snapshot(url: str, timeout: int) -> bytes | None: """Fetch snapshot from HTTP URL.""" + # Validate URL format (user-configured camera URL - intentional external request) + if not _validate_camera_url(url, ("http", "https")): + logger.error(f"Invalid snapshot URL format: {url[:50]}...") + return None + try: async with ( aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=timeout)) as session, - session.get(url) as response, + session.get(url) as response, # nosec B113 - URL validated above, user-configured camera ): if response.status != 200: logger.error(f"Snapshot URL returned status {response.status}") @@ -348,7 +397,10 @@ async def test_connection(url: str, camera_type: str) -> dict: return {"success": False, "error": "Failed to capture frame from camera"} except Exception as e: - return {"success": False, "error": str(e)} + # Sanitize error message - don't expose internal details + error_type = type(e).__name__ + logger.error(f"Camera connection test failed: {e}") + return {"success": False, "error": f"Connection failed: {error_type}"} async def generate_mjpeg_stream(url: str, camera_type: str, fps: int = 10) -> AsyncGenerator[bytes, None]: diff --git a/backend/app/services/github_backup.py b/backend/app/services/github_backup.py index d499948a7..64b4f0ec7 100644 --- a/backend/app/services/github_backup.py +++ b/backend/app/services/github_backup.py @@ -165,17 +165,30 @@ class GitHubBackupService: except Exception as e: logger.error(f"GitHub connection test failed: {e}") - return {"success": False, "message": str(e), "repo_name": None, "permissions": None} + # Sanitize error - don't expose internal details + error_type = type(e).__name__ + return { + "success": False, + "message": f"Connection failed: {error_type}", + "repo_name": None, + "permissions": None, + } def _parse_repo_url(self, url: str) -> tuple[str, str]: """Parse owner and repo from GitHub URL.""" - # Handle HTTPS URLs - match = re.match(r"https://github\.com/([^/]+)/([^/]+?)(?:\.git)?/?$", url) + # Limit URL length to prevent ReDoS attacks + if not url or len(url) > 500: + raise ValueError("Invalid GitHub URL: URL too long or empty") + + # Handle HTTPS URLs - use atomic groups via limited character classes + # GitHub usernames: 1-39 chars, alphanumeric and hyphens + # Repo names: 1-100 chars, alphanumeric, hyphens, underscores, dots + match = re.match(r"https://github\.com/([\w-]{1,39})/([\w.\-]{1,100})(?:\.git)?/?$", url) if match: return match.group(1), match.group(2) # Handle SSH URLs - match = re.match(r"git@github\.com:([^/]+)/([^/]+?)(?:\.git)?$", url) + match = re.match(r"git@github\.com:([\w-]{1,39})/([\w.\-]{1,100})(?:\.git)?$", url) if match: return match.group(1), match.group(2)