Files
MicroPythonOS/internal_filesystem/lib/mpos/camera_manager.py
T
Thomas Farstrike 30b3764710 Harmonize frameworks
All frameworks now follow the same singleton class pattern with class methods:

AudioFlinger (already had this pattern)
DownloadManager (refactored)
ConnectivityManager (refactored)
CameraManager (refactored)
SensorManager (refactored)
Pattern Structure:

class FrameworkName:
    _initialized = False
    _instance_data = {}

    @classmethod
    def init(cls, *args, **kwargs):
        """Initialize the framework"""
        cls._initialized = True
        # initialization logic

    @classmethod
    def is_available(cls):
        """Check if framework is available"""
        return cls._initialized

    @classmethod
    def method_name(cls, *args):
        """Framework methods as class methods"""
        # implementation

2. Standardized Imports in __init__.py
All frameworks are now imported consistently as classes:

from .content.package_manager import PackageManager
from .config import SharedPreferences
from .net.connectivity_manager import ConnectivityManager
from .net.wifi_service import WifiService
from .audio.audioflinger import AudioFlinger
from .net.download_manager import DownloadManager
from .task_manager import TaskManager
from .camera_manager import CameraManager
from .sensor_manager import SensorManager
3. Updated Board Initialization Files
Fixed imports in all board files to use the new class-based pattern:

linux.py
fri3d_2024.py
fri3d_2026.py
waveshare_esp32_s3_touch_lcd_2.py
4. Updated UI Components
Fixed topmenu.py to import SensorManager as a class instead of a module.

5. Benefits of This Harmonization
 Consistency: All frameworks follow the same pattern - no more mixing of module imports and class imports  Simplicity: Single, clear way to use frameworks - always as classes with class methods  Functionality: All frameworks work identically - init(), is_available(), and other methods are consistent  Maintainability: New developers see one pattern to follow across all frameworks  No Breaking Changes: Apps continue to work without modification (Quasi apps, Lightning Piggy, etc.)

6. Testing
All tests pass successfully, confirming:

Framework initialization works correctly
Board hardware detection functions properly
UI components render without errors
No regressions in existing functionality
The harmonization is complete and production-ready. All frameworks now provide a unified, predictable interface that's easy to understand and extend.
2026-01-23 15:31:47 +01:00

214 lines
6.4 KiB
Python

"""Android-inspired CameraManager for MicroPythonOS.
Provides unified access to camera devices (back-facing, front-facing, external).
Follows singleton pattern with class method delegation.
Example usage:
from mpos import CameraManager
# In board init file:
CameraManager.add_camera(CameraManager.Camera(
lens_facing=CameraManager.CameraCharacteristics.LENS_FACING_BACK,
name="OV5640",
vendor="OmniVision"
))
# In app:
cam_list = CameraManager.get_cameras()
if len(cam_list) > 0:
print("we have a camera!")
MIT License
Copyright (c) 2024 MicroPythonOS contributors
"""
# Camera lens facing constants (matching Android Camera2 API)
class CameraCharacteristics:
"""Camera characteristics and constants."""
LENS_FACING_BACK = 0 # Back-facing camera (primary)
LENS_FACING_FRONT = 1 # Front-facing camera (selfie)
LENS_FACING_EXTERNAL = 2 # External USB camera
class Camera:
"""Camera metadata (lightweight data class, Android-inspired).
Represents a camera device with its characteristics.
"""
def __init__(self, lens_facing, name=None, vendor=None, version=None):
"""Initialize camera metadata.
Args:
lens_facing: Camera orientation (LENS_FACING_BACK, LENS_FACING_FRONT, etc.)
name: Human-readable camera name (e.g., "OV5640", "Front Camera")
vendor: Camera vendor/manufacturer (e.g., "OmniVision")
version: Driver version (default 1)
"""
self.lens_facing = lens_facing
self.name = name or "Camera"
self.vendor = vendor or "Unknown"
self.version = version or 1
def __repr__(self):
facing_names = {
CameraCharacteristics.LENS_FACING_BACK: "BACK",
CameraCharacteristics.LENS_FACING_FRONT: "FRONT",
CameraCharacteristics.LENS_FACING_EXTERNAL: "EXTERNAL"
}
facing_str = facing_names.get(self.lens_facing, f"UNKNOWN({self.lens_facing})")
return f"Camera({self.name}, facing={facing_str})"
class CameraManager:
"""
Centralized camera device management service.
Implements singleton pattern for unified camera access.
Usage:
from mpos import CameraManager
# Register a camera
CameraManager.add_camera(CameraManager.Camera(
lens_facing=CameraManager.CameraCharacteristics.LENS_FACING_BACK,
name="OV5640"
))
# Get all cameras
cameras = CameraManager.get_cameras()
"""
# Expose inner classes as class attributes
Camera = Camera
CameraCharacteristics = CameraCharacteristics
_instance = None
_cameras = [] # Class-level camera list for singleton
def __init__(self):
"""Initialize CameraManager singleton instance."""
if CameraManager._instance:
return
CameraManager._instance = self
self._initialized = False
self.init()
@classmethod
def get(cls):
"""Get or create the singleton instance."""
if cls._instance is None:
cls._instance = cls()
return cls._instance
def init(self):
"""Initialize CameraManager.
Returns:
bool: True if initialized successfully
"""
self._initialized = True
return True
def is_available(self):
"""Check if CameraManager is initialized.
Returns:
bool: True if CameraManager is initialized
"""
return self._initialized
def add_camera(self, camera):
"""Register a camera device.
Args:
camera: Camera object to register
Returns:
bool: True if camera added successfully
"""
if not isinstance(camera, Camera):
print(f"[CameraManager] Error: add_camera() requires Camera object, got {type(camera)}")
return False
# Check if camera with same facing already exists
for existing in CameraManager._cameras:
if existing.lens_facing == camera.lens_facing:
print(f"[CameraManager] Warning: Camera with facing {camera.lens_facing} already registered")
# Still add it (allow multiple cameras with same facing)
CameraManager._cameras.append(camera)
print(f"[CameraManager] Registered camera: {camera}")
return True
def get_cameras(self):
"""Get list of all registered cameras.
Returns:
list: List of Camera objects (copy of internal list)
"""
return CameraManager._cameras.copy() if CameraManager._cameras else []
def get_camera_by_facing(self, lens_facing):
"""Get first camera with specified lens facing.
Args:
lens_facing: Camera orientation (LENS_FACING_BACK, LENS_FACING_FRONT, etc.)
Returns:
Camera object or None if not found
"""
for camera in CameraManager._cameras:
if camera.lens_facing == lens_facing:
return camera
return None
def has_camera(self):
"""Check if any camera is registered.
Returns:
bool: True if at least one camera available
"""
return len(CameraManager._cameras) > 0
def get_camera_count(self):
"""Get number of registered cameras.
Returns:
int: Number of cameras
"""
return len(CameraManager._cameras)
# ============================================================================
# Class method delegation (at module level)
# ============================================================================
_original_methods = {}
_methods_to_delegate = [
'init', 'is_available', 'add_camera', 'get_cameras',
'get_camera_by_facing', 'has_camera', 'get_camera_count'
]
for method_name in _methods_to_delegate:
_original_methods[method_name] = getattr(CameraManager, method_name)
def _make_class_method(method_name):
"""Create a class method that delegates to the singleton instance."""
original_method = _original_methods[method_name]
@classmethod
def class_method(cls, *args, **kwargs):
instance = cls.get()
return original_method(instance, *args, **kwargs)
return class_method
for method_name in _methods_to_delegate:
setattr(CameraManager, method_name, _make_class_method(method_name))
# Initialize on module load
CameraManager.init()