Files
pi-pm3/POWER_MANAGEMENT_POLICY.md
michael 4f35df1781 Initial commit - Phase 3/4
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-06 13:46:22 -08:00

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

  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)