# Idle Consumption Measurement Operation

Măsurarea consumului rezidual (standby) al unui dispozitiv încărcat la maxim.

## Responsabilități

- Determinarea consumului inactiv real al unui dispozitiv (după 100% + top-up)
- Detectare automată a stării "idle" (power stabilizat)
- Selectare manuală a intervalului idle din grafic
- Salvare profil idle per device
- Utilizare pentru terminare automată sesiune + bilanț energetic

## Context

### Problema

După ce dispozitivul raportează **100% battery**, încărcătorul nu se opreşte imediat:

```
Timeline:
00:00 — Device reaches 100%
00:00-00:15 — Top-up phase (unele devices încarcă semnificativ)
               Current: 1.5A → 0.8A → 0.3A → 0.05A
00:15 — Top-up terminat, device intra în idle
00:15-∞ — Idle phase (consum rezidual constant)
         Current: 0.05A constant
         Power: V × 0.05A ≈ 0.25W
```

**Necesar:**
- Distinge top-up phase de idle phase
- Măsoară idle power real
- Folosește-o pentru bilanț energetic final

### Consumul idle = baseline pentru bilanț

```
Total energy from charger:      50 Wh (measured)
Time charging:                  2 hours
Idle power:                      0.25 W
Energy to idle consumption:     0.25W × 2h = 0.5 Wh

Real energy to device battery: 50 - 0.5 = 49.5 Wh

⟹ Capacity learning mai precis
⟹ Bilanț charger vs device vs loss
```

## Invarianţi

- **MUST**: Device la 100% battery la start
- **MUST**: Idle power < 5W (nu active usage)
- **MUST**: Idle power > 0.01W (detectabil)
- **MUST**: Idle interval ≥ 30 secunde (stabilitate)
- **MUST**: Idle power salvat per (device, charger type)
- **SHOULD**: Idle consumul constant (variance < 10%)
- **MAY**: Idle profile salvat in Core Data

## Lifecycle

### 1. Setup & initialization

Utilizatorul:
1. Încarcă device la maxim (100%)
2. Lasă conectat la charger după 100%
3. Deschide app → "Measure idle consumption"
4. Selectează dispozitiv + charger type
5. Pornește măsurare

```swift
struct IdleConsumptionSession {
    let id: UUID
    let deviceID: UUID
    let chargerType: ChargerType?
    let startTime: Date
    let measuredAt100Percent: Bool  // MUST: true
    let sessionID: UUID
}
```

**Precondition:**
- Device must report 100% battery
- Device connected to charger
- Meter recording power

### 2. Wait for top-up completion

App observă power trend:

```swift
func detectIdlePhase(measurements: [Measurement]) -> Int? {
    // Last 60 measurements (1 minute)
    let recent = measurements.suffix(60)
    
    // Calculate power variance in recent window
    let powers = recent.map { $0.power }
    let mean = powers.reduce(0, +) / Double(powers.count)
    let variance = powers.map { pow($0 - mean, 2) }.reduce(0, +) / Double(powers.count)
    let stdDev = sqrt(variance)
    
    // Idle criteria:
    // 1. Power < 5W (no active charging)
    // 2. Variance < 10% of mean (stable)
    // 3. Duration > 30s at this level
    
    if mean < 5.0 && stdDev < (mean * 0.1) {
        return measurements.count - 60  // Index of phase start
    }
    return nil
}
```

**Detection criteria:**
- Power < 5W (no active charging)
- StdDev < 10% of mean (stable, not fluctuating)
- Duration > 30 seconds (confirm stability)

**Timeline:**
```
10:00 — User starts measurement
10:15 — Top-up ends, power drops to 0.25W
        App auto-detects idle phase
10:15-10:45 — User reviews, confirms
```

**MUST**: Detect automatically when possible
**SHOULD**: Notify user "Idle phase detected @ 10:15"

### 3. User review & selection

User vede graficul cu highlight pentru idle phase detectată:

```
Power (W)
20 ├───────────────────┐
   │ Top-up phase      │
10 ├───────────┐       │
   │           │       │
 5 ├───────────┤       │
   │  ↓ Idle detected   │
 0.5 ├───────────────┐ ◄─ Auto-selected interval
   │                │
   └────┬──────────┬──┴───────────→ time
        10:15      10:45
    (start idle)   (user confirmed)
```

**User actions:**
- Tap to confirm auto-selected interval
- Drag to refine start position
- Drag to refine end position
- Tap "Save idle profile"

**MUST**: Allow manual override
**SHOULD**: Offer auto-selection if confidence high

### 4. Compute idle power

```swift
func computeIdlePower(
    measurements: [Measurement],
    from startTime: Date,
    to endTime: Date
) -> IdleConsumptionProfile {
    let filtered = measurements.filter { m in
        m.timestamp >= startTime && m.timestamp <= endTime
    }
    
    guard !filtered.isEmpty else { return nil }
    
    let powers = filtered.map { $0.power }
    let currents = filtered.map { $0.current }
    let voltages = filtered.map { $0.voltage }
    
    let avgPower = powers.reduce(0, +) / Double(powers.count)
    let stdDev = sqrt(
        powers.map { pow($0 - avgPower, 2) }.reduce(0, +) / Double(powers.count)
    )
    
    // Energy over idle period
    let duration = endTime.timeIntervalSince(startTime)
    let energy = avgPower * duration / 3600  // Wh
    
    return IdleConsumptionProfile(
        deviceID: device.id,
        chargerID: charger?.id,
        idlePower: avgPower,  // W
        stdDev: stdDev,
        energy: energy,
        sampleCount: filtered.count,
        measuredAt: Date.now,
        interval: (startTime, endTime)
    )
}
```

### 5. Save idle profile

**Salvează:**
```swift
struct IdleConsumptionProfile {
    let id: UUID
    let deviceID: UUID
    let chargerID: UUID?  // Optional, may be unknown charger
    let idlePower: Double  // W, avg
    let stdDev: Double     // W, variance
    let energy: Double     // Wh, cumulative idle energy
    let sampleCount: Int
    let measuredAt: Date
    let interval: (start: Date, end: Date)
    let notes: String?
}
```

**NU salvează:** Raw measurements

**Storage:** Core Data `DeviceIdleProfile` table

**MUST**: Salvare explicită (user confirm)
**SHOULD**: Allow update (measure again if suspicious)

## API Public

### Measurement

```swift
// Start idle measurement session
func startIdleConsumptionSession(
    for device: ChargedDevice,
    charger: ChargerType?
) -> IdleConsumptionSession

// Detect idle phase automatically
func detectIdlePhase(
    in measurements: [Measurement]
) -> (startIndex: Int, confidence: Double)?

// Compute idle profile for interval
func computeIdlePower(
    measurements: [Measurement],
    from: Date,
    to: Date
) -> IdleConsumptionProfile?

// Save profile
func saveIdleProfile(_ profile: IdleConsumptionProfile) -> Bool
```

### Query & usage

```swift
// Get idle profile for device
func loadIdleProfile(for device: ChargedDevice) -> IdleConsumptionProfile?

// Get all profiles for device (multiple charger types)
func loadIdleProfiles(for device: ChargedDevice) 
    -> [IdleConsumptionProfile]

// Get idle power estimate for device+charger combo
func getIdlePower(
    device: ChargedDevice,
    charger: ChargerType?
) -> Double?

// Update profile after new measurement
func updateIdleProfile(_ profile: IdleConsumptionProfile) -> Bool

// Delete old profile
func deleteIdleProfile(_ id: UUID) -> Bool
```

## Comportamente critice

### Top-up phase confusion

```
Timeline misreading:
User thinks 10:10 = idle start
But device still trickling charge @ 0.2A
Power = 1.0W (not idle yet!)
⟹ False idle profile
```

**MUST**: Require variance < 10% (not just low power)
**SHOULD**: Alert user if variance high
**MAY**: Auto-suggest earlier/later boundary

### Multiple charger types

```
Device A measured with:
- Charger X: idle = 0.25W
- Charger Y: idle = 0.30W (different charger)

⟹ Store separate profiles
⟹ User selects charger type at measurement time
```

**MUST**: Key by (device, charger type)
**SHOULD**: Allow null charger (unknown)
**SHOULD**: Group by device in UI

### Device still charging (false idle)

```
User hits "save" while device still @ 100%
But charger delivering 0.5A (top-up)
Power = 2.5W
⟹ This is NOT idle, it's slow charge!
```

**MUST**: Warn if battery % still 100% during interval
**SHOULD**: Reject if power > 3W (too high for idle)
**MAY**: Ask "Are you sure this is idle?"

### Idle profile very high (> 1W)

```
Device idle measurement: 1.5W
(Typical idle: 0.1-0.5W)
⟹ Something unusual:
   - Screen still on?
   - Background app running?
   - Device malfunctioning?
```

**SHOULD**: Warn "Idle power seems high"
**SHOULD**: Suggest retry with device fully idle
**MAY**: Store with `dataQuality: "warning"`

## Use cases

### 1. Auto-terminate charging session

```
Charging session started:
  Start battery: 20%
  Start energy: 0 Wh

During charging:
  Monitor power trend
  When power drops to idle level...
  
Idle detection trigger:
  Power < idle_threshold (e.g., 0.3W)
  Duration > 2 minutes stable
  Battery = 100%
  
⟹ Auto-terminate session
⟹ Calculate final energy
```

**Pseudocode:**
```swift
func shouldAutoTerminateCharging(
    currentPower: Double,
    currentBattery: Double,
    duration: TimeInterval
) -> Bool {
    let idleProfile = loadIdleProfile(for: device)
    let idleThreshold = (idleProfile?.idlePower ?? 0.5) * 1.5  // 50% margin
    
    return currentPower < idleThreshold &&
           currentBattery >= 99.0 &&
           duration > 120  // 2 min stable
}
```

### 2. Bilanț energetic final

```
Charging session:
  Meter energy:      50.0 Wh
  Duration:          2 hours
  Idle power:        0.25 W
  Idle energy:       0.25W × 2h = 0.5 Wh
  
Charger idle:        0.10 W
Charger idle energy: 0.10W × 2h = 0.2 Wh

Energy to device:    50.0 - 0.5 - 0.2 = 49.3 Wh
```

**Accuracy improvement**: +1-2% fidelity

### 3. Anomaly detection

```
Device A (iPhone):
  Normal idle: 0.15W
  Today's measurement: 0.45W  (3× higher!)
  
⟹ Alert user:
   "Device drawing 3× idle power"
   "Battery drain issue?"
   "Check background apps"
```

## Statistici generate

| Metric | Unit | Descriere |
|---|---|---|
| Idle Power | W | Average power during idle |
| StdDev | W | Variance (should be low) |
| Idle Energy | Wh | Total over measurement period |
| Sample Count | # | Measurements in interval |
| Confidence | % | Stability metric |
| Data Quality | flag | "good" / "warning" / "poor" |

### Confidence calculation

```swift
func calculateConfidence(stdDev: Double, mean: Double) -> Double {
    let relativeStdDev = stdDev / mean
    
    if relativeStdDev < 0.05 {
        return 0.95  // Excellent
    } else if relativeStdDev < 0.10 {
        return 0.85  // Good
    } else if relativeStdDev < 0.20 {
        return 0.70  // Fair
    } else {
        return 0.50  // Poor (high variance)
    }
}
```

## Testare

### Unit tests

```swift
test_detectIdlePhase_ValidData()
test_detectIdlePhase_HighVariance_NotDetected()
test_detectIdlePhase_HighPower_NotDetected()
test_detectIdlePhase_MinimumDuration()
test_computeIdlePower_ValidInterval()
test_computeIdlePower_EmptyInterval()
test_computeIdlePower_HighVariance_Warning()
test_saveIdleProfile_Persists()
test_loadIdleProfile_ByDevice()
test_multipleChargerTypes_SeparateProfiles()
test_shouldAutoTerminateCharging_WithIdleProfile()
test_energyBilantz_SubtractsIdleEnergy()
test_confidenceCalculation_RelativeVariance()
```

### Integration tests

- [ ] Measure idle: device 100%, charger connected
- [ ] Auto-detect idle phase when variance drops
- [ ] User overrides auto-selection with manual interval
- [ ] Save idle profile → reload → same values
- [ ] Multiple profiles per device (different chargers)
- [ ] Auto-terminate charging when idle detected
- [ ] Energy bilanț matches meter - idle correction
- [ ] Anomaly detection for 3× idle power

## Edge cases

### Very low idle (< 0.01W)

```
Device truly idle: 0.008W
Below detection threshold
⟹ Impossible to measure accurately
```

**SHOULD**: "Idle power too low to measure"
**MAY**: Skip measurement

### Device powered off during measurement

```
User powers off device mid-measurement
Power drops to 0W
⟹ Not idle, device is off
```

**MUST**: Detect power drop to 0
**SHOULD**: Discard measurement
**MAY**: Suggest "Check device is still on"

### Charger unplugged mid-measurement

```
User unplugs charger
Power drops immediately
⟹ Not valid idle state
```

**SHOULD**: Detect sudden power drop
**SHOULD**: Alert user "Charger disconnected?"

### Very long idle measurement

```
User measures 24 hours of idle
Device behavior may change:
- OS updates background activity
- Bluetooth periodic sync
- GPS location update
⟹ Not constant idle
```

**SHOULD**: Warn if > 2 hours
**MAY**: Suggest shorter interval (30 min typical)

## Storage & syncing

### What IS saved

```json
{
  "id": "UUID",
  "deviceID": "UUID",
  "chargerID": "UUID?",
  "idlePower": 0.25,
  "stdDev": 0.02,
  "energy": 0.5,
  "sampleCount": 3600,
  "measuredAt": "2026-05-23T10:45:00Z",
  "confidence": 0.85,
  "dataQuality": "good"
}
```

### What is NOT saved

```
❌ individual measurements
❌ raw power samples
❌ voltage/current timeseries
❌ interval timestamps (only duration)
```

**Reason**: Storage efficiency

### CloudKit sync

- **SHOULD**: Sync idle profiles to iCloud
- **REASON**: Use on other devices (same charger type)
- **Example**: iPhone idle measured on device A, used on device B

## Dependenţe

- [Charging Monitoring](./ChargingMonitoring.md): session context
- [Consumption Measurement](./ConsumptionMeasurement.md): power analysis
- Core Data: `DeviceIdleProfile` entity
- UI: Graph with selection + auto-detection highlight

## Related features

### Auto-terminate charging session

```swift
func shouldAutoTerminate(
    session: ChargeSession,
    currentPower: Double,
    battery: Double
) -> Bool {
    guard battery >= 99.0 else { return false }
    
    let idle = loadIdleProfile(for: session.device)
    let threshold = (idle?.idlePower ?? 0.5) * 1.5
    
    return currentPower < threshold
}
```

### Energy balance correction

```swift
func correctedDeviceEnergy(
    total: Double,
    idleProfile: IdleConsumptionProfile,
    duration: TimeInterval
) -> Double {
    let idleEnergy = idleProfile.idlePower * duration / 3600
    return total - idleEnergy
}
```

## Notes

- **Measurement window**: Tipic 30 min — 2 ore
- **Update frequency**: Every few weeks (device behavior stable)
- **Per charger**: Different chargers may have different idle power
- **Privacy**: No raw data, just aggregates
- **Related**: [Charge Session Integrity](../Charge%20Session%20Integrity%20and%20Conflict%20Healing.md)
