# 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)
