|
Bogdan Timofte
authored
2 weeks ago
|
1
|
# Consumption Measurement Operation
|
|
|
2
|
|
|
|
3
|
Măsurarea consumului pe o perioadă arbitrară, cu statistici şi predicţii bazate pe interval selectat.
|
|
|
4
|
|
|
|
5
|
## Responsabilități
|
|
|
6
|
|
|
|
7
|
- Măsurare consum liber (orice interval, nu doar sesiuni complete)
|
|
|
8
|
- Afişare statistici pe interval selectat
|
|
|
9
|
- Generare predicţii (extrapolări pe bază de trend)
|
|
|
10
|
- Tăiere curbei ("tail trimming") pentru a selecta porţiunea relevantă
|
|
|
11
|
- Salvează statisticile, NU datele măsurătorilor brute
|
|
|
12
|
|
|
|
13
|
## Diferenţa faţă de Charging Monitoring
|
|
|
14
|
|
|
|
15
|
| Aspect | Charging Monitoring | Consumption Measurement |
|
|
|
16
|
|---|---|---|
|
|
|
17
|
| Interval | Fixed (start → end charge) | Arbitrary (user-selected) |
|
|
|
18
|
| Storage | Sesiune completă + measurements | Statistics only |
|
|
|
19
|
| UI | Timeline sesiune | Graph with selection |
|
|
|
20
|
| Predicţii | Capacity learning | Energy extrapolation |
|
|
|
21
|
|
|
|
22
|
## Concepte
|
|
|
23
|
|
|
|
24
|
### Consumption = Energy over time
|
|
|
25
|
|
|
|
26
|
```
|
|
|
27
|
Energy (Wh) = ∫ P(t) dt
|
|
|
28
|
= ∑ (V × A × Δt)
|
|
|
29
|
|
|
|
30
|
Power (W) = V × A (instantaneous)
|
|
|
31
|
Average Power (W) = Total Energy / Duration
|
|
|
32
|
```
|
|
|
33
|
|
|
|
34
|
## Invarianţi
|
|
|
35
|
|
|
|
36
|
- **MUST**: `startTime < endTime` (interval valid)
|
|
|
37
|
- **MUST**: Interval ≥ 10 secunde (minim useful)
|
|
|
38
|
- **MUST**: Interval ≤ 30 zile (limit practic pentru extrapolări)
|
|
|
39
|
- **MUST**: Statisticile trebuie salvate, măsurătorile brute se discard
|
|
|
40
|
- **MUST**: Fără măsurători individuale în persistență, doar agregări
|
|
|
41
|
- **SHOULD**: Interval tipic: 1 minut — 24 ore
|
|
|
42
|
- **MAY**: Predicţii valide doar pe ±1 interval (ex: dacă measured 1h, predict next 1h)
|
|
|
43
|
|
|
|
44
|
## Lifecycle
|
|
|
45
|
|
|
|
46
|
### 1. Selection interval (user interaction)
|
|
|
47
|
|
|
|
48
|
Utilizatorul deschide graficul şi selectează interval:
|
|
|
49
|
|
|
|
50
|
```
|
|
|
51
|
User taps start position în grafic
|
|
|
52
|
User drags to end position
|
|
|
53
|
System highlights selected region
|
|
|
54
|
User taps "Measure consumption"
|
|
|
55
|
```
|
|
|
56
|
|
|
|
57
|
**Rezultat:**
|
|
|
58
|
```swift
|
|
|
59
|
struct ConsumptionSelection {
|
|
|
60
|
let startTime: Date
|
|
|
61
|
let endTime: Date
|
|
|
62
|
let meter: Meter
|
|
|
63
|
let device: ChargedDevice? // Optional, may be unknown
|
|
|
64
|
}
|
|
|
65
|
```
|
|
|
66
|
|
|
|
67
|
**MUST:** Times trebuie din măsurători existente (bounded)
|
|
|
68
|
|
|
|
69
|
### 2. Compute statistics
|
|
|
70
|
|
|
|
71
|
```swift
|
|
|
72
|
func computeConsumptionStats(
|
|
|
73
|
measurements: [Measurement],
|
|
|
74
|
from startTime: Date,
|
|
|
75
|
to endTime: Date
|
|
|
76
|
) -> ConsumptionStats {
|
|
|
77
|
// 1. Filter measurements în interval
|
|
|
78
|
let filtered = measurements.filter { m in
|
|
|
79
|
m.timestamp >= startTime && m.timestamp <= endTime
|
|
|
80
|
}
|
|
|
81
|
|
|
|
82
|
guard !filtered.isEmpty else {
|
|
|
83
|
return .empty
|
|
|
84
|
}
|
|
|
85
|
|
|
|
86
|
// 2. Calculate energy
|
|
|
87
|
let energy = calculateEnergy(filtered)
|
|
|
88
|
|
|
|
89
|
// 3. Calculate time-based metrics
|
|
|
90
|
let duration = endTime.timeIntervalSince(startTime)
|
|
|
91
|
let averagePower = energy * 3600 / duration // Convert Wh to W
|
|
|
92
|
|
|
|
93
|
// 4. Power statistics
|
|
|
94
|
let powers = filtered.map { $0.power }
|
|
|
95
|
let peakPower = powers.max() ?? 0
|
|
|
96
|
let minPower = powers.min() ?? 0
|
|
|
97
|
let stdDev = calculateStdDev(powers)
|
|
|
98
|
|
|
|
99
|
// 5. Voltage/Current stats
|
|
|
100
|
let voltages = filtered.map { $0.voltage }
|
|
|
101
|
let currents = filtered.map { $0.current }
|
|
|
102
|
|
|
|
103
|
return ConsumptionStats(
|
|
|
104
|
energy: energy,
|
|
|
105
|
duration: duration,
|
|
|
106
|
averagePower: averagePower,
|
|
|
107
|
peakPower: peakPower,
|
|
|
108
|
minPower: minPower,
|
|
|
109
|
stdDevPower: stdDev,
|
|
|
110
|
sampleCount: filtered.count,
|
|
|
111
|
startVoltage: filtered.first?.voltage,
|
|
|
112
|
endVoltage: filtered.last?.voltage,
|
|
|
113
|
averageVoltage: voltages.average(),
|
|
|
114
|
averageCurrent: currents.average()
|
|
|
115
|
)
|
|
|
116
|
}
|
|
|
117
|
```
|
|
|
118
|
|
|
|
119
|
### 3. Generate predictions
|
|
|
120
|
|
|
|
121
|
```swift
|
|
|
122
|
func predictFutureConsumption(
|
|
|
123
|
stats: ConsumptionStats,
|
|
|
124
|
duration: TimeInterval
|
|
|
125
|
) -> ConsumptionPrediction {
|
|
|
126
|
// Linear extrapolation
|
|
|
127
|
let predictedEnergy = stats.averagePower * duration / 3600
|
|
|
128
|
|
|
|
129
|
// Confidence bounds (±20% for stability)
|
|
|
130
|
let confidence = 0.8
|
|
|
131
|
let lowerBound = predictedEnergy * (1 - (1 - confidence))
|
|
|
132
|
let upperBound = predictedEnergy * (1 + (1 - confidence))
|
|
|
133
|
|
|
|
134
|
return ConsumptionPrediction(
|
|
|
135
|
energy: predictedEnergy,
|
|
|
136
|
lowerBound: lowerBound,
|
|
|
137
|
upperBound: upperBound,
|
|
|
138
|
confidence: confidence,
|
|
|
139
|
method: "linear"
|
|
|
140
|
)
|
|
|
141
|
}
|
|
|
142
|
```
|
|
|
143
|
|
|
|
144
|
**Metode:**
|
|
|
145
|
- **Linear**: `E_predicted = P_avg × t` (simple, stable)
|
|
|
146
|
- **Polynomial**: `E_predicted = a + b×t + c×t²` (for trends)
|
|
|
147
|
- **Exponential**: (rarely used, for battery degradation)
|
|
|
148
|
|
|
|
149
|
**MUST:** Confidence ≤ 1.0 (0-100%)
|
|
|
150
|
**SHOULD:** Linear enough for 1 interval ahead
|
|
|
151
|
**MAY:** Polynomial dacă > 2 intervals
|
|
|
152
|
|
|
|
153
|
### 4. Tail trimming (user action)
|
|
|
154
|
|
|
|
155
|
Utilizatorul poate "tăia coada" pentru a refina selecţia:
|
|
|
156
|
|
|
|
157
|
```
|
|
|
158
|
Initial selection: 00:00 - 10:00 (10h)
|
|
|
159
|
User drags start: 00:30 - 10:00 (9.5h)
|
|
|
160
|
User drags end: 00:30 - 09:30 (9h)
|
|
|
161
|
Result: statistics recalculate pe [00:30, 09:30]
|
|
|
162
|
```
|
|
|
163
|
|
|
|
164
|
**Interactiv:**
|
|
|
165
|
- Slider @ start time
|
|
|
166
|
- Slider @ end time
|
|
|
167
|
- Real-time stats update
|
|
|
168
|
|
|
|
169
|
**MUST:** Recalculate stats on every trim
|
|
|
170
|
**SHOULD:** Debounce updates (100ms)
|
|
|
171
|
**MUST:** Store final selection, not intermediate states
|
|
|
172
|
|
|
|
173
|
### 5. Save consumption record
|
|
|
174
|
|
|
|
175
|
**NU salvează:** Măsurătorile individuale
|
|
|
176
|
|
|
|
177
|
**SALVEAZĂ:**
|
|
|
178
|
```swift
|
|
|
179
|
struct ConsumptionRecord {
|
|
|
180
|
let id: UUID
|
|
|
181
|
let meterID: UUID
|
|
|
182
|
let deviceID: UUID?
|
|
|
183
|
let startTime: Date
|
|
|
184
|
let endTime: Date
|
|
|
185
|
|
|
|
186
|
// Statistics (aggregated)
|
|
|
187
|
let totalEnergy: Double // Wh
|
|
|
188
|
let averagePower: Double // W
|
|
|
189
|
let peakPower: Double
|
|
|
190
|
let minPower: Double
|
|
|
191
|
let stdDevPower: Double
|
|
|
192
|
let sampleCount: Int
|
|
|
193
|
|
|
|
194
|
// Metadata
|
|
|
195
|
let createdAt: Date
|
|
|
196
|
let notes: String? // User annotation
|
|
|
197
|
}
|
|
|
198
|
```
|
|
|
199
|
|
|
|
200
|
**NOT saved:**
|
|
|
201
|
- ❌ Individual measurements (voltage, current per sample)
|
|
|
202
|
- ❌ Timestamps of each sample
|
|
|
203
|
- ❌ Raw power values
|
|
|
204
|
|
|
|
205
|
**REASON:** Storage efficiency + privacy
|
|
|
206
|
|
|
|
207
|
## API Public
|
|
|
208
|
|
|
|
209
|
### Selection & Stats
|
|
|
210
|
|
|
|
211
|
```swift
|
|
|
212
|
// User selects interval from graph
|
|
|
213
|
func selectConsumptionInterval(
|
|
|
214
|
start: Date,
|
|
|
215
|
end: Date,
|
|
|
216
|
meter: Meter
|
|
|
217
|
) -> ConsumptionStats?
|
|
|
218
|
|
|
|
219
|
// Refine selection via tail trimming
|
|
|
220
|
func trimConsumptionInterval(
|
|
|
221
|
from newStart: Date,
|
|
|
222
|
to newEnd: Date
|
|
|
223
|
) -> ConsumptionStats?
|
|
|
224
|
|
|
|
225
|
// Get current statistics
|
|
|
226
|
func currentStats() -> ConsumptionStats?
|
|
|
227
|
```
|
|
|
228
|
|
|
|
229
|
### Predictions
|
|
|
230
|
|
|
|
231
|
```swift
|
|
|
232
|
// Predict next interval
|
|
|
233
|
func predictConsumption(
|
|
|
234
|
for duration: TimeInterval
|
|
|
235
|
) -> ConsumptionPrediction?
|
|
|
236
|
|
|
|
237
|
// Extrapolate to full day/week
|
|
|
238
|
func extrapolateToDay() -> ConsumptionPrediction?
|
|
|
239
|
func extrapolateToWeek() -> ConsumptionPrediction?
|
|
|
240
|
```
|
|
|
241
|
|
|
|
242
|
### Persistence
|
|
|
243
|
|
|
|
244
|
```swift
|
|
|
245
|
// Save aggregated stats only
|
|
|
246
|
func saveConsumptionRecord(_ record: ConsumptionRecord) -> Bool
|
|
|
247
|
|
|
|
248
|
// Load history
|
|
|
249
|
func loadConsumptionRecords(for meter: Meter) -> [ConsumptionRecord]
|
|
|
250
|
|
|
|
251
|
// Discard raw data (keep stats)
|
|
|
252
|
func discardMeasurements(before: Date) -> Int
|
|
|
253
|
```
|
|
|
254
|
|
|
|
255
|
## Comportamente critice
|
|
|
256
|
|
|
|
257
|
### Interval validation
|
|
|
258
|
|
|
|
259
|
```
|
|
|
260
|
User selects: 10:00 AM - 10:00 AM (zero duration)
|
|
|
261
|
⟹ Error: "Minimum 10 seconds"
|
|
|
262
|
|
|
|
263
|
User selects: 01-May - 01-June (31 days)
|
|
|
264
|
⟹ Warning: "Extrapolation unreliable >7 days"
|
|
|
265
|
```
|
|
|
266
|
|
|
|
267
|
**MUST:** duration ≥ 10s
|
|
|
268
|
**SHOULD:** Warn if duration > 7 days
|
|
|
269
|
**SHOULD:** Cap predictions to ±1 interval
|
|
|
270
|
|
|
|
271
|
### No tail = invalid
|
|
|
272
|
|
|
|
273
|
```
|
|
|
274
|
Selection: 10:00-11:00
|
|
|
275
|
User never presses "Save"
|
|
|
276
|
App terminates
|
|
|
277
|
⟹ Selection lost (no persistence of in-progress)
|
|
|
278
|
```
|
|
|
279
|
|
|
|
280
|
**MUST:** NU salvează interim selections
|
|
|
281
|
**SHOULD:** Keep in-memory during session
|
|
|
282
|
**MUST:** Explicit save() required
|
|
|
283
|
|
|
|
284
|
### Power variability
|
|
|
285
|
|
|
|
286
|
```
|
|
|
287
|
Measuring USB charger:
|
|
|
288
|
Min power: 0.1W (idle)
|
|
|
289
|
Max power: 18W (fast charge)
|
|
|
290
|
StdDev: high (~5W)
|
|
|
291
|
|
|
|
292
|
Prediction: linear avg = 8W
|
|
|
293
|
Confidence: 0.7 (high variance)
|
|
|
294
|
```
|
|
|
295
|
|
|
|
296
|
**SHOULD:** Calculate stdDev
|
|
|
297
|
**SHOULD:** Reflect confidence (lower if high variance)
|
|
|
298
|
**MAY:** Suggest "Power unstable, prediction unreliable"
|
|
|
299
|
|
|
|
300
|
### Measurement gaps
|
|
|
301
|
|
|
|
302
|
```
|
|
|
303
|
Recording: 10:00-11:00
|
|
|
304
|
Meter disconnect: 10:30-10:45
|
|
|
305
|
Data available: 10:00-10:30 (30min) + 10:45-11:00 (15min)
|
|
|
306
|
User selects: 10:00-11:00 (but only 45min data)
|
|
|
307
|
|
|
|
308
|
Stats: calculate on available data
|
|
|
309
|
Warning: "15 minutes missing"
|
|
|
310
|
```
|
|
|
311
|
|
|
|
312
|
**MUST:** Alert user to gaps
|
|
|
313
|
**SHOULD:** Calculate on available data only
|
|
|
314
|
**MAY:** Adjust confidence (lower if gaps > 10%)
|
|
|
315
|
|
|
|
316
|
## Statistici generate
|
|
|
317
|
|
|
|
318
|
### Basic
|
|
|
319
|
|
|
|
320
|
| Metric | Unit | Formula |
|
|
|
321
|
|---|---|---|
|
|
|
322
|
| Total Energy | Wh | ∑(V × A × Δt) |
|
|
|
323
|
| Duration | seconds | endTime - startTime |
|
|
|
324
|
| Average Power | W | Energy × 3600 / Duration |
|
|
|
325
|
| Peak Power | W | max(P(t)) |
|
|
|
326
|
| Min Power | W | min(P(t)) |
|
|
|
327
|
|
|
|
328
|
### Advanced
|
|
|
329
|
|
|
|
330
|
| Metric | Unit | Descriere |
|
|
|
331
|
|---|---|---|
|
|
|
332
|
| StdDev Power | W | Variabilitate în putere |
|
|
|
333
|
| Avg Voltage | V | Media tensiune |
|
|
|
334
|
| Avg Current | A | Media curent |
|
|
|
335
|
| Sample Count | # | Measurement count |
|
|
|
336
|
| Confidence | % | Reliability predicţie |
|
|
|
337
|
|
|
|
338
|
## Predicţii
|
|
|
339
|
|
|
|
340
|
### Linear extrapolation
|
|
|
341
|
|
|
|
342
|
```
|
|
|
343
|
Measured: P_avg = 10W over 1 hour
|
|
|
344
|
Predict next 1h: E = 10W × 1h = 10Wh
|
|
|
345
|
Bounds: ±(1-confidence)% = ±20% = [8Wh, 12Wh]
|
|
|
346
|
```
|
|
|
347
|
|
|
|
348
|
### Time scaling
|
|
|
349
|
|
|
|
350
|
```
|
|
|
351
|
Measured: 5W average over 10 minutes
|
|
|
352
|
Predict over 1 hour: 5W × 6 = 30Wh
|
|
|
353
|
Predict over 24h: 5W × 144 = 720Wh
|
|
|
354
|
|
|
|
355
|
But confidence decreases with time:
|
|
|
356
|
- 1×interval: 80% confidence
|
|
|
357
|
- 2×interval: 60% confidence
|
|
|
358
|
- 5×interval: 40% confidence
|
|
|
359
|
- 10+×interval: 20% confidence
|
|
|
360
|
```
|
|
|
361
|
|
|
|
362
|
**MUST:** Confidence degrades dengan scaling
|
|
|
363
|
**SHOULD:** Warn if confidence < 50%
|
|
|
364
|
|
|
|
365
|
## Testare
|
|
|
366
|
|
|
|
367
|
### Unit tests
|
|
|
368
|
|
|
|
369
|
```swift
|
|
|
370
|
test_selectConsumptionInterval_ValidDates()
|
|
|
371
|
test_selectConsumptionInterval_MinimumDuration()
|
|
|
372
|
test_trimConsumptionInterval_UpdatesStats()
|
|
|
373
|
test_computeConsumptionStats_ValidData()
|
|
|
374
|
test_computeConsumptionStats_EmptyRange()
|
|
|
375
|
test_calculateAveragePower()
|
|
|
376
|
test_predictConsumption_Linear()
|
|
|
377
|
test_predictConsumption_ConfidenceDegrades()
|
|
|
378
|
test_extrapolateToDay_WithinBounds()
|
|
|
379
|
test_recordDoesNotStoreMeasurements()
|
|
|
380
|
test_discardMeasurements_KeepsStats()
|
|
|
381
|
```
|
|
|
382
|
|
|
|
383
|
### Integration tests
|
|
|
384
|
|
|
|
385
|
- [ ] Select 1h interval → get stats
|
|
|
386
|
- [ ] Trim tail → stats recalculate
|
|
|
387
|
- [ ] Predict next 1h → confidence 80%
|
|
|
388
|
- [ ] Predict next 24h → confidence drops to 40%
|
|
|
389
|
- [ ] Save record → measurements discarded, stats kept
|
|
|
390
|
- [ ] Load record → only stats available
|
|
|
391
|
- [ ] Measurement gap detected → warning shown
|
|
|
392
|
- [ ] High power variance → confidence reduced
|
|
|
393
|
|
|
|
394
|
## Edge cases
|
|
|
395
|
|
|
|
396
|
### Very short interval
|
|
|
397
|
|
|
|
398
|
```
|
|
|
399
|
User selects: 10:00:00 - 10:00:05 (5 seconds)
|
|
|
400
|
⟹ Error: below 10s minimum
|
|
|
401
|
```
|
|
|
402
|
|
|
|
403
|
**MUST:** Reject
|
|
|
404
|
|
|
|
405
|
### Very long interval
|
|
|
406
|
|
|
|
407
|
```
|
|
|
408
|
User selects: 01-Jan - 31-Dec (365 days)
|
|
|
409
|
⟹ Warning: "Extrapolation unreliable beyond 7 days"
|
|
|
410
|
Confidence: 20%
|
|
|
411
|
```
|
|
|
412
|
|
|
|
413
|
**SHOULD:** Accept but warn
|
|
|
414
|
**SHOULD:** Reduce confidence
|
|
|
415
|
|
|
|
416
|
### Single measurement
|
|
|
417
|
|
|
|
418
|
```
|
|
|
419
|
Interval: 10:00:00 - 10:00:01
|
|
|
420
|
Data points: 1 (only start)
|
|
|
421
|
Energy: 0 (insufficient interval)
|
|
|
422
|
⟹ Error: "Need minimum 10 measurements"
|
|
|
423
|
```
|
|
|
424
|
|
|
|
425
|
**MUST:** Require ≥ 10 samples
|
|
|
426
|
|
|
|
427
|
### No voltage/current data
|
|
|
428
|
|
|
|
429
|
```
|
|
|
430
|
Meter type: Unknown or not reporting
|
|
|
431
|
Power available: yes
|
|
|
432
|
Voltage/Current: null
|
|
|
433
|
|
|
|
434
|
Stats: calculate energy from power only
|
|
|
435
|
Avg Voltage: null
|
|
|
436
|
Avg Current: null
|
|
|
437
|
```
|
|
|
438
|
|
|
|
439
|
**SHOULD:** Handle gracefully
|
|
|
440
|
**MAY:** Show "Limited data available"
|
|
|
441
|
|
|
|
442
|
## Storage model
|
|
|
443
|
|
|
|
444
|
### What IS saved
|
|
|
445
|
|
|
|
446
|
```json
|
|
|
447
|
{
|
|
|
448
|
"id": "UUID",
|
|
|
449
|
"meterID": "UUID",
|
|
|
450
|
"startTime": "2026-05-23T10:00:00Z",
|
|
|
451
|
"endTime": "2026-05-23T11:00:00Z",
|
|
|
452
|
"totalEnergy": 12.5,
|
|
|
453
|
"averagePower": 12.5,
|
|
|
454
|
"peakPower": 18.0,
|
|
|
455
|
"minPower": 1.2,
|
|
|
456
|
"stdDevPower": 4.5,
|
|
|
457
|
"sampleCount": 3600,
|
|
|
458
|
"createdAt": "2026-05-23T11:02:00Z"
|
|
|
459
|
}
|
|
|
460
|
```
|
|
|
461
|
|
|
|
462
|
### What is NOT saved
|
|
|
463
|
|
|
|
464
|
```
|
|
|
465
|
❌ individual measurements
|
|
|
466
|
❌ voltage samples
|
|
|
467
|
❌ current samples
|
|
|
468
|
❌ power curve
|
|
|
469
|
❌ battery levels
|
|
|
470
|
❌ meter metadata
|
|
|
471
|
```
|
|
|
472
|
|
|
|
473
|
## Dependenţe
|
|
|
474
|
|
|
|
475
|
- [Charging Monitoring](./ChargingMonitoring.md): access to meter data
|
|
|
476
|
- [Charge Curve Storage](./ChargeCurveStorage.md): query full measurements
|
|
|
477
|
- Core Data: ConsumptionRecord entity
|
|
|
478
|
- UI: Graph view with interval selection
|
|
|
479
|
|
|
|
480
|
## UI Components
|
|
|
481
|
|
|
|
482
|
### Graph with selection
|
|
|
483
|
|
|
|
484
|
```
|
|
|
485
|
┌─────────────────────────────────┐
|
|
|
486
|
│ Power (W) │
|
|
|
487
|
│ 20 ├──────┐ │
|
|
|
488
|
│ │ ████ │ ← user selection │
|
|
|
489
|
│ 10 ├──████└────┐ │
|
|
|
490
|
│ │ │ │
|
|
|
491
|
│ 0 └──────────┴───────────────┘
|
|
|
492
|
│ 10:00 10:30 11:00
|
|
|
493
|
│ ↑start ↑end
|
|
|
494
|
│ (draggable) (draggable)
|
|
|
495
|
└─────────────────────────────────┘
|
|
|
496
|
```
|
|
|
497
|
|
|
|
498
|
### Stats display
|
|
|
499
|
|
|
|
500
|
```
|
|
|
501
|
Selected Interval: 10:00 — 10:30 (30 min)
|
|
|
502
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
503
|
Energy: 6.3 Wh
|
|
|
504
|
Average Power: 12.6 W
|
|
|
505
|
Peak Power: 18.0 W
|
|
|
506
|
Min Power: 1.2 W
|
|
|
507
|
Variance: ±4.5 W
|
|
|
508
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
509
|
Prediction (next 30 min):
|
|
|
510
|
Energy: 6.3 Wh (confidence: 80%)
|
|
|
511
|
Range: 5.0 — 7.6 Wh
|
|
|
512
|
```
|
|
|
513
|
|
|
|
514
|
## Notes
|
|
|
515
|
|
|
|
516
|
- **Not a charge session**: No device pairing, no capacity learning
|
|
|
517
|
- **Independent operation**: Coexists with ChargeInsights
|
|
|
518
|
- **Low storage footprint**: Only aggregates, not raw curves
|
|
|
519
|
- **Privacy-friendly**: Discards detailed measurements
|
|
|
520
|
- **Real-time**: Updates on interval selection/trimming
|
|
|
521
|
- Related: [Charging Monitoring](./ChargingMonitoring.md) for full sessions
|