🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
14 KiB
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
{
"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
{
"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)
{
"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
# 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
# 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
# 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
// 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
# 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
- Boot system without UPS HAT
- Navigate to firmware flash page
- Verify: No restrictions, warning shown
- Verify: Flash proceeds with user confirmation
Test 2: UPS on AC Power
- Boot with UPS HAT on AC
- Navigate to firmware flash page
- Verify: No restrictions, no warnings
- Verify: Flash proceeds immediately
Test 3: UPS on Battery (90%)
- Disconnect AC power (battery 90%)
- Navigate to firmware flash page
- Verify: Bootloader flash allowed with warning
- Verify: User must confirm warning
Test 4: UPS on Battery (60%)
- Discharge to 60%
- Navigate to firmware flash page
- Verify: Bootloader flash blocked
- Verify: Fullimage flash allowed
Test 5: UPS on Battery (30%)
- Discharge to 30%
- Navigate to firmware flash page
- Verify: All firmware flashing blocked
- 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):
- Implement
FirmwareServicewith power checks - Add
/api/system/power/restrictionsendpoint - Update frontend firmware flash UI
- Add user confirmation dialogs
- Add power stability warnings
Security & Safety
- User Consent: Always require explicit confirmation for risky operations
- Clear Warnings: Explain consequences of power loss
- Conservative Defaults: Err on side of safety when battery detected
- 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:
- Active - WiFi enabled, all features available
- 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:
- Set wake alarm via PiSugar web interface or native I2C driver
- Shutdown device:
sudo shutdown -h now - 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)