# Power Management Policy **Date**: 2025-11-26 **Status**: ⚠️ **PRIORITY 1 - IMPLEMENT BEFORE PI TESTING** **Priority**: CRITICAL - Must be implemented before hardware deployment --- ## Overview Dangerous Pi's power management policy determines when power-intensive operations are allowed based on hardware detection and battery status. --- ## Core Principle **If UPS hardware is not detected, assume AC line power is available.** This means: - No battery-level restrictions on operations - Power-intensive tasks are allowed - Only constrained by Raspberry Pi's power delivery capability - User takes responsibility for power stability --- ## Power States ### State 1: UPS Hardware Detected + AC Power ```python { "ups_available": True, "power_source": "AC", "battery_percentage": 100, "restrictions": None } ``` **Allowed Operations**: ALL - Firmware flashing (bootloader + fullimage) - Intensive PM3 operations - System updates - Multiple simultaneous PM3 devices --- ### State 2: UPS Hardware Detected + Battery Power ```python { "ups_available": True, "power_source": "Battery", "battery_percentage": 85, # Example "restrictions": "See battery level policies below" } ``` **Battery Level Policies**: | Battery % | Allowed Operations | Restrictions | |-----------|-------------------|--------------| | 80-100% | ALL | Bootloader flashing allowed with warning | | 50-79% | Most operations | Bootloader flashing blocked, fullimage allowed | | 20-49% | Standard ops | No firmware flashing, PM3 commands OK | | 10-19% | Critical mode | Read-only operations, no writes | | 0-9% | Emergency | Initiate safe shutdown | --- ### State 3: UPS Hardware NOT Detected (Most Common) ```python { "ups_available": False, "power_source": "Assumed AC", "battery_percentage": None, "restrictions": None } ``` **Allowed Operations**: ALL - **Assumption**: User is on stable AC power or external battery bank - **Rationale**: If user doesn't have UPS HAT, we can't monitor battery anyway - **Constraints**: Only limited by Pi Zero 2 W power delivery (~5V 2.5A typical) - **User Responsibility**: Ensure stable power for firmware flashing **Warning Display**: Show header widget warning that UPS is not detected (informational, dismissible) --- ## Implementation ### UPS Manager Detection ```python # app/backend/managers/ups_manager.py class UPSManager: def get_power_restrictions(self) -> Dict[str, Any]: """Get current power restrictions based on hardware state. Returns: Dict with restriction info """ # UPS not available = assume AC power if not self._status.is_available: return { "restricted": False, "reason": None, "power_source": "assumed_ac", "ups_available": False, "allow_firmware_flash": True, "allow_bootloader_flash": True, "allow_intensive_operations": True, "message": "UPS not detected. Assuming stable AC power. Ensure power stability before firmware operations." } # UPS available - check battery state if self._status.power_source == PowerSource.AC: return { "restricted": False, "reason": None, "power_source": "ac", "ups_available": True, "battery_percentage": self._status.battery_percentage, "allow_firmware_flash": True, "allow_bootloader_flash": True, "allow_intensive_operations": True } # On battery power - apply restrictions battery_pct = self._status.battery_percentage if battery_pct >= 80: return { "restricted": False, "reason": None, "power_source": "battery", "ups_available": True, "battery_percentage": battery_pct, "allow_firmware_flash": True, "allow_bootloader_flash": True, # With warning "allow_intensive_operations": True, "warning": "On battery power. Bootloader flashing not recommended." } elif battery_pct >= 50: return { "restricted": True, "reason": "Battery level below 80%", "power_source": "battery", "ups_available": True, "battery_percentage": battery_pct, "allow_firmware_flash": True, # Fullimage only "allow_bootloader_flash": False, "allow_intensive_operations": True, "message": "Bootloader flashing disabled. Battery too low." } elif battery_pct >= 20: return { "restricted": True, "reason": "Battery level below 50%", "power_source": "battery", "ups_available": True, "battery_percentage": battery_pct, "allow_firmware_flash": False, "allow_bootloader_flash": False, "allow_intensive_operations": True, "message": "Firmware flashing disabled. Battery too low." } elif battery_pct >= 10: return { "restricted": True, "reason": "Battery critically low", "power_source": "battery", "ups_available": True, "battery_percentage": battery_pct, "allow_firmware_flash": False, "allow_bootloader_flash": False, "allow_intensive_operations": False, "message": "Critical battery. Read-only mode active." } else: return { "restricted": True, "reason": "Battery emergency level", "power_source": "battery", "ups_available": True, "battery_percentage": battery_pct, "allow_firmware_flash": False, "allow_bootloader_flash": False, "allow_intensive_operations": False, "message": "Emergency battery level. Shutting down soon.", "shutdown_imminent": True } ``` ### Firmware Flash Service ```python # app/backend/services/firmware_service.py (future implementation) class FirmwareService: def __init__(self, ups_manager: UPSManager): self._ups_manager = ups_manager async def can_flash_firmware(self, include_bootloader: bool = False) -> Dict[str, Any]: """Check if firmware flashing is allowed. Args: include_bootloader: Whether bootloader flash is requested Returns: Dict with allowed status and reason """ restrictions = self._ups_manager.get_power_restrictions() if include_bootloader: if not restrictions["allow_bootloader_flash"]: return { "allowed": False, "reason": restrictions.get("message", "Bootloader flashing not allowed"), "power_source": restrictions["power_source"], "battery_percentage": restrictions.get("battery_percentage") } if not restrictions["allow_firmware_flash"]: return { "allowed": False, "reason": restrictions.get("message", "Firmware flashing not allowed"), "power_source": restrictions["power_source"], "battery_percentage": restrictions.get("battery_percentage") } # Allowed - return info return { "allowed": True, "power_source": restrictions["power_source"], "warning": restrictions.get("warning"), # May be None "battery_percentage": restrictions.get("battery_percentage") } ``` ### API Endpoint ```python # app/backend/api/system.py @router.get("/power/restrictions") async def get_power_restrictions(): """Get current power restrictions. Returns: Power restriction information """ try: ups_manager = get_ups_manager() restrictions = ups_manager.get_power_restrictions() return { "success": True, **restrictions } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) ``` --- ## Frontend Integration ### Firmware Flash UI ```typescript // Before starting firmware flash const checkPowerRestrictions = async (includeBootloader: boolean) => { const response = await fetch("/api/system/power/restrictions"); const data = await response.json(); // UPS not available - show warning but allow if (!data.ups_available) { return confirm( "⚠️ UPS hardware not detected.\n\n" + "Ensure you have stable AC power before flashing firmware.\n" + "Power loss during flashing can brick your Proxmark3.\n\n" + "Continue with firmware flash?" ); } // On battery - check restrictions if (includeBootloader && !data.allow_bootloader_flash) { alert( `❌ Bootloader flashing blocked\n\n` + `${data.message}\n` + `Battery: ${data.battery_percentage}% (minimum 80% required)` ); return false; } if (!data.allow_firmware_flash) { alert( `❌ Firmware flashing blocked\n\n` + `${data.message}\n` + `Battery: ${data.battery_percentage}% (minimum 50% required)` ); return false; } // Allowed with warning if (data.warning) { return confirm(`⚠️ ${data.warning}\n\nContinue anyway?`); } return true; }; ``` --- ## Hardware Assumptions ### Raspberry Pi Zero 2 W Power Limits - **Max Current Draw**: ~2.5A @ 5V typical - **Single PM3**: ~500mA typical, ~800mA peak - **Multiple PM3s**: May exceed Pi's USB current limit - **Recommendation**: Use powered USB hub for 3+ devices ### UPS HAT Detection ```python # UPS detection via I2C # If I2C device not present at address 0x36 (typical MAX17040): # - smbus2 import fails → UPS unavailable # - I2C read fails → UPS unavailable # - Battery register reads 0 → UPS unavailable # Any of above = assume AC power ``` --- ## User Communication ### Dashboard Widget (UPS Not Detected) ``` ⚠️ UPS hardware not detected. Battery monitoring unavailable. Assuming stable AC power for all operations. [Learn More] [Dismiss] ``` ### Firmware Flash Dialog (No UPS) ``` ⚠️ Power Stability Required UPS hardware not detected. Ensure you have stable AC power. Firmware flashing can take 30-60 seconds. Power loss during this time can brick your Proxmark3 device. ✓ I have stable AC power connected [Cancel] [Continue Anyway] ``` ### Firmware Flash Dialog (Low Battery) ``` ❌ Battery Too Low for Bootloader Flash Current battery: 45% Minimum required: 80% Bootloader flashing requires high power stability. Please connect to AC power or wait for battery to charge above 80%. Fullimage flashing is still available (requires 50% minimum). [Cancel] [Flash Fullimage Only] ``` --- ## Testing Scenarios ### Test 1: No UPS Hardware 1. Boot system without UPS HAT 2. Navigate to firmware flash page 3. Verify: No restrictions, warning shown 4. Verify: Flash proceeds with user confirmation ### Test 2: UPS on AC Power 1. Boot with UPS HAT on AC 2. Navigate to firmware flash page 3. Verify: No restrictions, no warnings 4. Verify: Flash proceeds immediately ### Test 3: UPS on Battery (90%) 1. Disconnect AC power (battery 90%) 2. Navigate to firmware flash page 3. Verify: Bootloader flash allowed with warning 4. Verify: User must confirm warning ### Test 4: UPS on Battery (60%) 1. Discharge to 60% 2. Navigate to firmware flash page 3. Verify: Bootloader flash blocked 4. Verify: Fullimage flash allowed ### Test 5: UPS on Battery (30%) 1. Discharge to 30% 2. Navigate to firmware flash page 3. Verify: All firmware flashing blocked 4. Verify: PM3 commands still work --- ## Migration Notes ### Existing Code Current firmware flash logic (if exists) may have hardcoded battery checks. Update to use `get_power_restrictions()` method. ### Future Implementation When implementing Sprint 3 (Firmware Management): 1. Implement `FirmwareService` with power checks 2. Add `/api/system/power/restrictions` endpoint 3. Update frontend firmware flash UI 4. Add user confirmation dialogs 5. Add power stability warnings --- ## Security & Safety 1. **User Consent**: Always require explicit confirmation for risky operations 2. **Clear Warnings**: Explain consequences of power loss 3. **Conservative Defaults**: Err on side of safety when battery detected 4. **No Silent Failures**: Always inform user why operation was blocked --- ## Sleep Mode - Not Applicable Dangerous Pi does not implement sleep/standby mode. The device operates in two states: 1. **Active** - WiFi enabled, all features available 2. **Shutdown** - Device powered off **Why no sleep mode?** - WiFi must remain active for remote access (primary use case) - Sleep without WiFi makes device inaccessible remotely - BLE-only wake would require dedicated mobile app - Physical button wake defeats portable/remote use case - Middle "sleep" state = worst of both worlds (draws power but inaccessible) **Alternative for scheduled operation**: PiSugar users can use RTC scheduled wake-up for "sleep overnight, wake at 8am" scenarios: 1. Set wake alarm via PiSugar web interface or native I2C driver 2. Shutdown device: `sudo shutdown -h now` 3. Device wakes automatically at scheduled time This is more power-efficient than any "sleep" mode would be. --- **Status**: Design Complete **Priority**: Implement before Sprint 3 (Firmware Management) **Dependencies**: UPS Manager (✅ exists), Firmware Service (⏳ future)