pvanand commited on
Commit
cee8b9e
Β·
verified Β·
1 Parent(s): 29d6521

Upload 18 files

Browse files
data.json ADDED
@@ -0,0 +1,954 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "description": "Lookup data for the Resource Calculator application, structured for Python integration. All values are per day unless otherwise specified.",
3
+ "inputs": {
4
+ "title": "Trip Configuration",
5
+ "params": {
6
+ "user_type": {
7
+ "label": "User Profile",
8
+ "value": "Glamper",
9
+ "options": [
10
+ "Glamper",
11
+ "Typical",
12
+ "Expert"
13
+ ],
14
+ "description": "Defines lifestyle and resource consumption habits."
15
+ },
16
+ "num_people": {
17
+ "label": "Number of People",
18
+ "value": 1,
19
+ "options": [
20
+ 1,
21
+ 2,
22
+ 3,
23
+ 4
24
+ ],
25
+ "description": "Total number of occupants."
26
+ },
27
+ "trip_duration_days": {
28
+ "label": "Trip Duration",
29
+ "value": 4,
30
+ "unit": "days",
31
+ "description": "Total length of the trip in days."
32
+ },
33
+ "hvac_runtime_hrs": {
34
+ "label": "HVAC Runtime",
35
+ "value": 12,
36
+ "unit": "hrs/day",
37
+ "description": "Daily hours of HVAC operation for climate control and water generation."
38
+ },
39
+ "relocation_count": {
40
+ "label": "Relocation Count",
41
+ "value": 2,
42
+ "description": "Number of times the trailer will be moved to a new campsite during the trip."
43
+ },
44
+ "temperature": {
45
+ "label": "Ambient Temperature",
46
+ "value": "Cold",
47
+ "options": [
48
+ "Hot",
49
+ "Temperate",
50
+ "Cold"
51
+ ],
52
+ "description": "Represents seasonal conditions affecting HVAC and solar generation."
53
+ },
54
+ "humidity": {
55
+ "label": "Ambient Humidity",
56
+ "value": "Dry",
57
+ "options": [
58
+ "Humid",
59
+ "Comfortable",
60
+ "Dry"
61
+ ],
62
+ "description": "Affects water generation from HVAC and solar panel efficiency."
63
+ },
64
+ "sunlight": {
65
+ "label": "Sunlight Exposure",
66
+ "value": "Lo- Shady",
67
+ "options": [
68
+ "Hi- Sunny",
69
+ "Mid- Cloudy",
70
+ "Lo- Shady"
71
+ ],
72
+ "description": "Determines the effectiveness of solar power generation."
73
+ }
74
+ }
75
+ },
76
+ "trailer_specs": {
77
+ "title": "Trailer Specifications",
78
+ "specs": {
79
+ "battery_capacity_kwh": {
80
+ "label": "Battery Capacity",
81
+ "value": 100,
82
+ "unit": "kWh"
83
+ },
84
+ "solar_capacity_kw": {
85
+ "label": "Solar Capacity",
86
+ "value": 5.25,
87
+ "unit": "kW"
88
+ },
89
+ "num_solar_panels": {
90
+ "label": "Number of Solar Panels",
91
+ "value": 35,
92
+ "unit": ""
93
+ },
94
+ "freshwater_capacity_gal": {
95
+ "label": "Freshwater Capacity",
96
+ "value": 100,
97
+ "unit": "gal"
98
+ },
99
+ "blackwater_capacity_gal": {
100
+ "label": "Blackwater Capacity",
101
+ "value": 45,
102
+ "unit": "gal"
103
+ },
104
+ "greywater_capacity_gal": {
105
+ "label": "Greywater Capacity",
106
+ "value": 50,
107
+ "unit": "gal"
108
+ }
109
+ }
110
+ },
111
+ "lookups": {
112
+ "user_profiles": {
113
+ "description": "Usage multipliers based on user profile. From 'User Matrix' sheet.",
114
+ "profiles": {
115
+ "Glamper": {
116
+ "time_based": {
117
+ "mins_per_day": {
118
+ "living_audio": 240,
119
+ "living_tv": 120,
120
+ "living_lighting": 450
121
+ },
122
+ "hrs_per_day": {
123
+ "living_smart_glass": 10,
124
+ "living_electronics": 24,
125
+ "living_party_lighting": 6,
126
+ "living_tire_sensors": 24,
127
+ "living_sensors_controllers": 24,
128
+ "living_security_cameras": 24,
129
+ "living_ext_camp_lights": 4,
130
+ "living_ext_porch_lights": 4,
131
+ "living_smart_tint": 12
132
+ },
133
+ "mins_per_meal": {
134
+ "cooking_stove": 20,
135
+ "cooking_microwave": 5,
136
+ "cooking_garbage_disposal": 1,
137
+ "cooking_consumer_electronics": 8,
138
+ "cooking_water_heater": 4,
139
+ "cooking_pump": 5.585,
140
+ "cooking_range_hood": 22
141
+ },
142
+ "mins_per_cycle": {
143
+ "toilet_pump": 1.13,
144
+ "toilet_vent_fan": 5,
145
+ "toilet_water_heater": 0.33,
146
+ "shower_duration": 10,
147
+ "shower_vent_fan": 20,
148
+ "shower_electronics": 10,
149
+ "shower_water_pump": 10,
150
+ "laundry_water_heater": 30,
151
+ "laundry_water_pump": 30
152
+ }
153
+ },
154
+ "count_based": {
155
+ "cycles_per_day": {
156
+ "living_smart_key": 8
157
+ },
158
+ "meals_per_day": {
159
+ "cooking": 3
160
+ },
161
+ "cycles_per_meal": {
162
+ "cooking_dishwasher": 1
163
+ },
164
+ "cycles_per_day_per_person": {
165
+ "toilet": 7,
166
+ "shower": 1.5,
167
+ "laundry": 0.3333333333
168
+ },
169
+ "burners_per_meal": {
170
+ "cooking_stove": 2
171
+ }
172
+ },
173
+ "volume_based": {
174
+ "gal_per_day": {
175
+ "living_cleaning": 0.75,
176
+ "living_drinking_water": 0.5,
177
+ "living_ice_maker_water": 0.29
178
+ },
179
+ "gal_per_meal": {
180
+ "cooking_dishwasher_water": 3.17,
181
+ "cooking_kitchen_faucet": 2
182
+ },
183
+ "gal_per_cycle": {
184
+ "toilet_sink_water": 0.495,
185
+ "toilet_gravity_flush": 0.4,
186
+ "shower_water": 20
187
+ }
188
+ }
189
+ },
190
+ "Typical": {
191
+ "time_based": {
192
+ "mins_per_day": {
193
+ "living_audio": 120,
194
+ "living_tv": 60,
195
+ "living_lighting": 300
196
+ },
197
+ "hrs_per_day": {
198
+ "living_smart_glass": 6,
199
+ "living_electronics": 16,
200
+ "living_party_lighting": 2.5,
201
+ "living_tire_sensors": 24,
202
+ "living_sensors_controllers": 24,
203
+ "living_security_cameras": 24,
204
+ "living_ext_camp_lights": 2,
205
+ "living_ext_porch_lights": 2,
206
+ "living_smart_tint": 10
207
+ },
208
+ "mins_per_meal": {
209
+ "cooking_stove": 15,
210
+ "cooking_microwave": 3.5,
211
+ "cooking_garbage_disposal": 0.5,
212
+ "cooking_consumer_electronics": 5,
213
+ "cooking_water_heater": 3,
214
+ "cooking_pump": 3.625,
215
+ "cooking_range_hood": 17
216
+ },
217
+ "mins_per_cycle": {
218
+ "toilet_pump": 1.05,
219
+ "toilet_vent_fan": 4,
220
+ "toilet_water_heater": 0.25,
221
+ "shower_duration": 6,
222
+ "shower_vent_fan": 15,
223
+ "shower_electronics": 5,
224
+ "shower_water_pump": 6,
225
+ "laundry_water_heater": 30,
226
+ "laundry_water_pump": 30
227
+ }
228
+ },
229
+ "count_based": {
230
+ "cycles_per_day": {
231
+ "living_smart_key": 4
232
+ },
233
+ "meals_per_day": {
234
+ "cooking": 2
235
+ },
236
+ "cycles_per_meal": {
237
+ "cooking_dishwasher": 0.5
238
+ },
239
+ "cycles_per_day_per_person": {
240
+ "toilet": 5,
241
+ "shower": 1,
242
+ "laundry": 0.2
243
+ },
244
+ "burners_per_meal": {
245
+ "cooking_stove": 1
246
+ }
247
+ },
248
+ "volume_based": {
249
+ "gal_per_day": {
250
+ "living_cleaning": 0.625,
251
+ "living_drinking_water": 0.75,
252
+ "living_ice_maker_water": 0.29
253
+ },
254
+ "gal_per_meal": {
255
+ "cooking_dishwasher_water": 1.25,
256
+ "cooking_kitchen_faucet": 1.5
257
+ },
258
+ "gal_per_cycle": {
259
+ "toilet_sink_water": 0.375,
260
+ "toilet_gravity_flush": 0.4,
261
+ "shower_water": 12
262
+ }
263
+ }
264
+ },
265
+ "Expert": {
266
+ "time_based": {
267
+ "mins_per_day": {
268
+ "living_audio": 10,
269
+ "living_tv": 30,
270
+ "living_lighting": 240
271
+ },
272
+ "hrs_per_day": {
273
+ "living_smart_glass": 3,
274
+ "living_electronics": 10,
275
+ "living_party_lighting": 1,
276
+ "living_tire_sensors": 24,
277
+ "living_sensors_controllers": 24,
278
+ "living_security_cameras": 24,
279
+ "living_ext_camp_lights": 1,
280
+ "living_ext_porch_lights": 1,
281
+ "living_smart_tint": 8
282
+ },
283
+ "mins_per_meal": {
284
+ "cooking_stove": 10,
285
+ "cooking_microwave": 2,
286
+ "cooking_garbage_disposal": 0.1666666667,
287
+ "cooking_consumer_electronics": 3,
288
+ "cooking_water_heater": 1,
289
+ "cooking_pump": 1.4125,
290
+ "cooking_range_hood": 12
291
+ },
292
+ "mins_per_cycle": {
293
+ "toilet_pump": 0.967,
294
+ "toilet_vent_fan": 2.5,
295
+ "toilet_water_heater": 0.167,
296
+ "shower_duration": 3,
297
+ "shower_vent_fan": 10,
298
+ "shower_electronics": 2,
299
+ "shower_water_pump": 3,
300
+ "laundry_water_heater": 30,
301
+ "laundry_water_pump": 30
302
+ }
303
+ },
304
+ "count_based": {
305
+ "cycles_per_day": {
306
+ "living_smart_key": 2
307
+ },
308
+ "meals_per_day": {
309
+ "cooking": 1
310
+ },
311
+ "cycles_per_meal": {
312
+ "cooking_dishwasher": 0.33
313
+ },
314
+ "cycles_per_day_per_person": {
315
+ "toilet": 3,
316
+ "shower": 0.5,
317
+ "laundry": 0.1428571429
318
+ },
319
+ "burners_per_meal": {
320
+ "cooking_stove": 1
321
+ }
322
+ },
323
+ "volume_based": {
324
+ "gal_per_day": {
325
+ "living_cleaning": 0.5,
326
+ "living_drinking_water": 1,
327
+ "living_ice_maker_water": 0.29
328
+ },
329
+ "gal_per_meal": {
330
+ "cooking_dishwasher_water": 0.825,
331
+ "cooking_kitchen_faucet": 0.5
332
+ },
333
+ "gal_per_cycle": {
334
+ "toilet_sink_water": 0.2505,
335
+ "toilet_gravity_flush": 0.4,
336
+ "shower_water": 6
337
+ }
338
+ }
339
+ }
340
+ }
341
+ },
342
+ "components": {
343
+ "schema": [
344
+ "name",
345
+ "voltage_v",
346
+ "avg_amps",
347
+ "water_gal_per_cycle",
348
+ "idle_amps"
349
+ ],
350
+ "unit": [
351
+ "",
352
+ "V",
353
+ "A",
354
+ "gal/cycle",
355
+ "A"
356
+ ],
357
+ "living": [
358
+ [
359
+ "Audio Amplifier",
360
+ 12,
361
+ 6,
362
+ 0,
363
+ 1
364
+ ],
365
+ [
366
+ "Television",
367
+ 120,
368
+ 0.7,
369
+ 0,
370
+ 0
371
+ ],
372
+ [
373
+ "Surround Sound",
374
+ 120,
375
+ 0.625,
376
+ 0,
377
+ 0.1317
378
+ ],
379
+ [
380
+ "Refrigerator",
381
+ 120,
382
+ 0.5137,
383
+ 0.2857,
384
+ 0.5137
385
+ ],
386
+ [
387
+ "Satellite Internet PSU",
388
+ 12,
389
+ 2.5,
390
+ 0,
391
+ 0
392
+ ],
393
+ [
394
+ "Lighting",
395
+ 12,
396
+ 12,
397
+ 0,
398
+ 0
399
+ ],
400
+ [
401
+ "Tire Pressure Sensors",
402
+ 24,
403
+ 0.4,
404
+ 0,
405
+ 0
406
+ ],
407
+ [
408
+ "Misc. Sensors & Controllers",
409
+ 12,
410
+ 0,
411
+ 0,
412
+ 5
413
+ ],
414
+ [
415
+ "Security Cameras",
416
+ 12,
417
+ 2,
418
+ 0,
419
+ 0
420
+ ],
421
+ [
422
+ "Consumer Electronics",
423
+ 120,
424
+ 0.5,
425
+ 0,
426
+ 0
427
+ ],
428
+ [
429
+ "Misc. Cleaning",
430
+ 0,
431
+ 0,
432
+ 0.625,
433
+ 0
434
+ ],
435
+ [
436
+ "Drinking Water Tap",
437
+ 0,
438
+ 0,
439
+ 0.75,
440
+ 0
441
+ ],
442
+ [
443
+ "Solar Glycol Pump",
444
+ 12,
445
+ 0.7,
446
+ 0,
447
+ 0
448
+ ],
449
+ [
450
+ "Water Pump",
451
+ 12,
452
+ 8.5,
453
+ 0,
454
+ 0
455
+ ],
456
+ [
457
+ "UV Water System",
458
+ 12,
459
+ 1.8,
460
+ 0,
461
+ 0
462
+ ],
463
+ [
464
+ "Tablet UI",
465
+ 12,
466
+ 0.4167,
467
+ 0,
468
+ 0
469
+ ],
470
+ [
471
+ "SmartTint DC PSU",
472
+ 12,
473
+ 2.5,
474
+ 0,
475
+ 0
476
+ ],
477
+ [
478
+ "Exterior Camp Lights",
479
+ 12,
480
+ 0.054,
481
+ 0,
482
+ 0
483
+ ],
484
+ [
485
+ "Exterior Porch Lighting",
486
+ 12,
487
+ 0.14,
488
+ 0,
489
+ 0
490
+ ],
491
+ [
492
+ "Storage Lights",
493
+ 12,
494
+ 0.325,
495
+ 0,
496
+ 0
497
+ ],
498
+ [
499
+ "LAN Switch PSU",
500
+ 120,
501
+ 0.25,
502
+ 0,
503
+ 0
504
+ ],
505
+ [
506
+ "Secure Gateway",
507
+ 12,
508
+ 0.8333,
509
+ 0,
510
+ 0.416
511
+ ],
512
+ [
513
+ "Telematics T-Box",
514
+ 12,
515
+ 1.25,
516
+ 0,
517
+ 0.25
518
+ ],
519
+ [
520
+ "4G LTE Antenna",
521
+ 12,
522
+ 0,
523
+ 0,
524
+ 0.75
525
+ ],
526
+ [
527
+ "Air Purifier",
528
+ 120,
529
+ 0.375,
530
+ 0,
531
+ 0
532
+ ],
533
+ [
534
+ "AI Compute",
535
+ 12,
536
+ 20.8333,
537
+ 0,
538
+ 1.25
539
+ ],
540
+ [
541
+ "Party Lighting",
542
+ 12,
543
+ 18.3333,
544
+ 0,
545
+ 0
546
+ ],
547
+ [
548
+ "Smart Glass Display",
549
+ 120,
550
+ 0.7167,
551
+ 0,
552
+ 0.8333
553
+ ],
554
+ [
555
+ "Smart Key Lighting",
556
+ 12,
557
+ 8,
558
+ 0,
559
+ 0.004
560
+ ],
561
+ [
562
+ "Home Assistant Server",
563
+ 12,
564
+ 12.5,
565
+ 0,
566
+ 0.004
567
+ ],
568
+ [
569
+ "HVAC Controller",
570
+ 12,
571
+ 7.855,
572
+ 0,
573
+ 0.01
574
+ ],
575
+ [
576
+ "Obstruction Sensors",
577
+ 12,
578
+ 1,
579
+ 0,
580
+ 0.01
581
+ ]
582
+ ],
583
+ "cooking": [
584
+ [
585
+ "Stove",
586
+ 240,
587
+ 12.5,
588
+ 0,
589
+ 0
590
+ ],
591
+ [
592
+ "Microwave",
593
+ 120,
594
+ 8.3333,
595
+ 0,
596
+ 0
597
+ ],
598
+ [
599
+ "Dishwasher",
600
+ 120,
601
+ 5.35,
602
+ 1.25,
603
+ 0
604
+ ],
605
+ [
606
+ "Garbage Disposal",
607
+ 120,
608
+ 5.6,
609
+ 0,
610
+ 0
611
+ ],
612
+ [
613
+ "Range Hood",
614
+ 120,
615
+ 3.52,
616
+ 0,
617
+ 0
618
+ ],
619
+ [
620
+ "Consumer Electronics",
621
+ 120,
622
+ 5,
623
+ 0,
624
+ 0
625
+ ],
626
+ [
627
+ "Tankless Water Heater",
628
+ 240,
629
+ 33.4,
630
+ 0,
631
+ 0
632
+ ],
633
+ [
634
+ "Water Pump",
635
+ 12,
636
+ 8.5,
637
+ 0,
638
+ 0
639
+ ],
640
+ [
641
+ "Kitchen Faucet",
642
+ 0,
643
+ 0,
644
+ 1.5,
645
+ 0
646
+ ]
647
+ ],
648
+ "toilet": [
649
+ [
650
+ "Grey Water Pump",
651
+ 12,
652
+ 2.4,
653
+ 0,
654
+ 0
655
+ ],
656
+ [
657
+ "Bathroom Vent Fan",
658
+ 12,
659
+ 1.9,
660
+ 0,
661
+ 0
662
+ ],
663
+ [
664
+ "Macerating Pump",
665
+ 12,
666
+ 17,
667
+ 0,
668
+ 0
669
+ ],
670
+ [
671
+ "Tankless Water Heater",
672
+ 240,
673
+ 33.4,
674
+ 0,
675
+ 0
676
+ ],
677
+ [
678
+ "Bathroom Sink",
679
+ 12,
680
+ 1.9,
681
+ 0.375,
682
+ 0
683
+ ]
684
+ ],
685
+ "shower": [
686
+ [
687
+ "Electric Shower",
688
+ 240,
689
+ 33,
690
+ 0,
691
+ 0
692
+ ],
693
+ [
694
+ "Bathroom Vent Fan",
695
+ 12,
696
+ 1.9,
697
+ 0,
698
+ 0
699
+ ],
700
+ [
701
+ "Consumer Electronics",
702
+ 120,
703
+ 5,
704
+ 0,
705
+ 0
706
+ ],
707
+ [
708
+ "Water Pump",
709
+ 12,
710
+ 8.5,
711
+ 0,
712
+ 0
713
+ ]
714
+ ],
715
+ "laundry": [
716
+ [
717
+ "Washer/Dryer Combo",
718
+ 120,
719
+ 15,
720
+ 20.6,
721
+ 0
722
+ ],
723
+ [
724
+ "Tankless Water Heater",
725
+ 240,
726
+ 33.4,
727
+ 0,
728
+ 0
729
+ ],
730
+ [
731
+ "Water Pump",
732
+ 12,
733
+ 8.5,
734
+ 0,
735
+ 0
736
+ ]
737
+ ],
738
+ "actuation": [
739
+ [
740
+ "Side Room Floor",
741
+ 24,
742
+ 1.4,
743
+ 0,
744
+ 0
745
+ ],
746
+ [
747
+ "Side Room Wall",
748
+ 24,
749
+ 1.1,
750
+ 0,
751
+ 0
752
+ ],
753
+ [
754
+ "Front Room",
755
+ 24,
756
+ 0.427,
757
+ 0,
758
+ 0
759
+ ],
760
+ [
761
+ "Rear Room",
762
+ 24,
763
+ 0.61,
764
+ 0,
765
+ 0
766
+ ],
767
+ [
768
+ "Chassis Leveling Jacks",
769
+ 24,
770
+ 9.3,
771
+ 0,
772
+ 0
773
+ ],
774
+ [
775
+ "Room Support Jacks",
776
+ 12,
777
+ 15,
778
+ 0,
779
+ 0
780
+ ],
781
+ [
782
+ "Gearage",
783
+ 24,
784
+ 0.13,
785
+ 0,
786
+ 0
787
+ ],
788
+ [
789
+ "Awning",
790
+ 12,
791
+ 10,
792
+ 0,
793
+ 0
794
+ ],
795
+ [
796
+ "Furniture Automation",
797
+ 120,
798
+ 10,
799
+ 0,
800
+ 0
801
+ ],
802
+ [
803
+ "TV Rise",
804
+ 120,
805
+ 3.5,
806
+ 0,
807
+ 0
808
+ ],
809
+ [
810
+ "Electric Tongue Jack",
811
+ 12,
812
+ 30,
813
+ 0,
814
+ 0
815
+ ],
816
+ [
817
+ "Motor Controllers",
818
+ 12,
819
+ 1.25,
820
+ 0,
821
+ 0
822
+ ]
823
+ ],
824
+ "dumping": [
825
+ [
826
+ "Electric Waste Valves",
827
+ 12,
828
+ 2.5,
829
+ 0,
830
+ 0
831
+ ]
832
+ ],
833
+ "hvac": [
834
+ [
835
+ "Mini-Split HVAC (Hot/Cold)",
836
+ 240,
837
+ 9.205,
838
+ 0,
839
+ 0
840
+ ],
841
+ [
842
+ "Mini-Split HVAC (Temperate)",
843
+ 240,
844
+ 4.082,
845
+ 0,
846
+ 0
847
+ ]
848
+ ]
849
+ },
850
+ "hvac_energy_wh_day": {
851
+ "Hot": 6923.75,
852
+ "Temperate": 744.68,
853
+ "Cold": 15615.35
854
+ },
855
+ "hvac_water_gen_gph": {
856
+ "day": {
857
+ "Hot": {
858
+ "Humid": 0,
859
+ "Comfortable": 0,
860
+ "Dry": 0
861
+ },
862
+ "Temperate": {
863
+ "Humid": 0,
864
+ "Comfortable": 0,
865
+ "Dry": 0
866
+ },
867
+ "Cold": {
868
+ "Humid": 0,
869
+ "Comfortable": 0,
870
+ "Dry": 0
871
+ }
872
+ },
873
+ "night": {
874
+ "Hot": {
875
+ "Humid": 0,
876
+ "Comfortable": 0,
877
+ "Dry": 0
878
+ },
879
+ "Temperate": {
880
+ "Humid": 0,
881
+ "Comfortable": 0,
882
+ "Dry": 0
883
+ },
884
+ "Cold": {
885
+ "Humid": 0,
886
+ "Comfortable": 0,
887
+ "Dry": 0
888
+ }
889
+ }
890
+ },
891
+ "waste_production": {
892
+ "description": "Values are per day for Living, and per cycle for others.",
893
+ "unit": "gal",
894
+ "living_cleaning": {
895
+ "grey": 0.625,
896
+ "black": 0
897
+ },
898
+ "cooking_dishwasher": {
899
+ "grey": 1.25,
900
+ "black": 0
901
+ },
902
+ "cooking_sink": {
903
+ "grey": 0,
904
+ "black": 1.125
905
+ },
906
+ "shower": {
907
+ "grey": 12,
908
+ "black": 0
909
+ },
910
+ "laundry": {
911
+ "grey": 4.12,
912
+ "black": 0
913
+ },
914
+ "toilet_handwashing": {
915
+ "grey": 0.375,
916
+ "black": 0
917
+ },
918
+ "toilet_flush": {
919
+ "grey": -0.4,
920
+ "black": 0.4
921
+ }
922
+ },
923
+ "solar": {
924
+ "system_loss_factor": 0.9,
925
+ "tilt_factor": {
926
+ "Hot": 0.8,
927
+ "Temperate": 0.8,
928
+ "Cold": 0.75
929
+ },
930
+ "sunlight_factor": {
931
+ "Hi- Sunny": 1.0,
932
+ "Mid- Cloudy": 0.5,
933
+ "Lo- Shady": 0.25
934
+ },
935
+ "insolation_wh_m2_day": {
936
+ "Hot": {
937
+ "Dry": 10022,
938
+ "Comfortable": 9514,
939
+ "Humid": 7526
940
+ },
941
+ "Temperate": {
942
+ "Dry": 7231,
943
+ "Comfortable": 6821,
944
+ "Humid": 5340
945
+ },
946
+ "Cold": {
947
+ "Dry": 3758,
948
+ "Comfortable": 3532,
949
+ "Humid": 2604
950
+ }
951
+ }
952
+ }
953
+ }
954
+ }
documentation_docx/mock_data_documentation.docx ADDED
Binary file (23.2 kB). View file
 
documentation_docx/stability_index_spec.docx ADDED
Binary file (20.9 kB). View file
 
generate_mock_data.py ADDED
@@ -0,0 +1,550 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ EV Camper Mock Data Generator
3
+ ==============================
4
+ Generates realistic power and water CSV exports following the discussed schema,
5
+ derived from data.json lookup tables.
6
+
7
+ Column naming convention:
8
+ *_kW instantaneous power (flow files: 1SEC, 1MIN, 15MIN)
9
+ *_kWh accumulated energy (energy files: 1H, 1DAY)
10
+ *_V voltage (instantaneous or averaged)
11
+ *_Ah amp-hours (battery state snapshot)
12
+ *_Pct percentage (battery / tank level)
13
+ *_Lpm litres per minute (water flow files: 1MIN, 15MIN)
14
+ *_L litres (water energy files: 1H, 1DAY, and tank level snapshots)
15
+
16
+ Output ZIP contains:
17
+ power/ -> 1SEC.csv, 1MIN.csv, 15MIN.csv, 1H.csv, 1DAY.csv
18
+ water/ -> 1MIN.csv, 15MIN.csv, 1H.csv, 1DAY.csv
19
+
20
+ Usage:
21
+ python generate_mock_data.py [--config data.json] [--out output] [--seed 42]
22
+ python generate_mock_data.py --user Glamper --people 2 --days 5 --temp Hot
23
+ """
24
+
25
+ import json
26
+ import csv
27
+ import os
28
+ import math
29
+ import random
30
+ import zipfile
31
+ import argparse
32
+ from datetime import datetime, timedelta
33
+ from pathlib import Path
34
+
35
+
36
+ # ---------------------------------------------------------------------------
37
+ # CONSTANTS
38
+ # ---------------------------------------------------------------------------
39
+
40
+ GAL_TO_LITRES = 3.78541
41
+ MINS_PER_DAY = 1440
42
+ SOLAR_PANEL_AREA_M2 = 1.8 # ~330W panel footprint
43
+ BATTERY_NOMINAL_V = 48.0 # for kWh <-> Ah conversion
44
+
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # DATA LOADER
48
+ # ---------------------------------------------------------------------------
49
+
50
+ def load_data(path: str) -> dict:
51
+ with open(path) as f:
52
+ return json.load(f)
53
+
54
+
55
+ # ---------------------------------------------------------------------------
56
+ # DAILY BUDGET CALCULATORS
57
+ # ---------------------------------------------------------------------------
58
+
59
+ def calc_power_budget(data: dict, user: str, people: int,
60
+ temp: str, hvac_hrs: float) -> dict:
61
+ """
62
+ Returns expected kWh per day broken down by circuit + solar generation.
63
+ Derived from component voltage x amps tables and user profile runtimes.
64
+ """
65
+ p = data["lookups"]["user_profiles"]["profiles"][user]
66
+ tb = p["time_based"]
67
+ cb = p["count_based"]
68
+
69
+ def watts(v, a): return v * a
70
+
71
+ # HVAC
72
+ hvac_kwh = data["lookups"]["hvac_energy_wh_day"][temp] / 1000.0
73
+
74
+ # Lighting (12V 12A)
75
+ lighting_kwh = watts(12, 12) * (tb["mins_per_day"].get("living_lighting", 300) / 60) / 1000
76
+
77
+ # Devices: always-on sensors/compute + active electronics
78
+ devices_kwh = (
79
+ 5 * 12 * 24 # sensor idle
80
+ + 1.25 * 12 * 24 # compute idle
81
+ + 0.5 * 120 * tb["hrs_per_day"].get("living_electronics", 16)
82
+ ) / 1000
83
+
84
+ # Fridge (24h)
85
+ fridge_kwh = watts(120, 0.5137) * 24 / 1000
86
+
87
+ # Water pump
88
+ meals = cb["meals_per_day"]["cooking"]
89
+ shower_cyc = cb["cycles_per_day_per_person"]["shower"] * people
90
+ toilet_cyc = cb["cycles_per_day_per_person"]["toilet"] * people
91
+ pump_mins = (tb["mins_per_meal"].get("cooking_pump", 3.625) * meals
92
+ + tb["mins_per_cycle"].get("shower_water_pump", 6) * shower_cyc
93
+ + 1.0 * toilet_cyc)
94
+ water_pump_kwh = watts(12, 8.5) * (pump_mins / 60) / 1000
95
+
96
+ # Cooking (stove + microwave + water heater)
97
+ cooking_kwh = (
98
+ watts(240, 12.5) * (tb["mins_per_meal"].get("cooking_stove", 15) * meals / 60)
99
+ + watts(120, 8.333) * (tb["mins_per_meal"].get("cooking_microwave", 3.5) * meals / 60)
100
+ + watts(240, 33.4) * ((tb["mins_per_meal"].get("cooking_water_heater", 3) * meals
101
+ + tb["mins_per_cycle"].get("shower_duration", 6) * shower_cyc) / 60)
102
+ ) / 1000
103
+
104
+ # Inverter load (TV as representative AC load)
105
+ inverter_kwh = watts(120, 0.7) * (tb["mins_per_day"].get("living_tv", 60) / 60) / 1000
106
+
107
+ # Solar generation
108
+ sol = data["lookups"]["solar"]
109
+ humidity = data["inputs"]["params"]["humidity"]["value"]
110
+ sunlight = data["inputs"]["params"]["sunlight"]["value"]
111
+ insolation = sol["insolation_wh_m2_day"][temp][humidity]
112
+ num_panels = (data["trailer_specs"]["specs"]["num_solar_panels"]["value"]
113
+ if "num_solar_panels" in data["trailer_specs"]["specs"] else 35)
114
+ solar_kwh = (insolation * num_panels * SOLAR_PANEL_AREA_M2
115
+ * sol["system_loss_factor"]
116
+ * sol["tilt_factor"][temp]
117
+ * sol["sunlight_factor"][sunlight]) / 1_000
118
+ solar_kwh = min(solar_kwh,
119
+ data["trailer_specs"]["specs"]["solar_capacity_kw"]["value"] * 6)
120
+
121
+ return {
122
+ "solar_kwh": round(solar_kwh, 3),
123
+ "hvac_kwh": round(hvac_kwh, 3),
124
+ "lighting_kwh": round(lighting_kwh, 3),
125
+ "devices_kwh": round(devices_kwh, 3),
126
+ "fridge_kwh": round(fridge_kwh, 3),
127
+ "water_pump_kwh": round(water_pump_kwh, 3),
128
+ "cooking_kwh": round(cooking_kwh, 3),
129
+ "inverter_kwh": round(inverter_kwh, 3),
130
+ }
131
+
132
+
133
+ def calc_water_budget(data: dict, user: str, people: int) -> dict:
134
+ """Returns expected litres per day per circuit, converted from profile gallon tables."""
135
+ p = data["lookups"]["user_profiles"]["profiles"][user]
136
+ vb = p["volume_based"]
137
+ cb = p["count_based"]
138
+
139
+ meals = cb["meals_per_day"]["cooking"]
140
+ shower_cyc = cb["cycles_per_day_per_person"]["shower"] * people
141
+ toilet_cyc = cb["cycles_per_day_per_person"]["toilet"] * people
142
+
143
+ def g2l(g): return round(g * GAL_TO_LITRES, 3)
144
+
145
+ return {
146
+ "shower_L": g2l(vb["gal_per_cycle"]["shower_water"] * shower_cyc),
147
+ "toilet_L": g2l((vb["gal_per_cycle"]["toilet_sink_water"]
148
+ + vb["gal_per_cycle"]["toilet_gravity_flush"]) * toilet_cyc),
149
+ "kitchen_L": g2l((vb["gal_per_meal"].get("cooking_kitchen_faucet", 1.5)
150
+ + vb["gal_per_meal"].get("cooking_dishwasher_water", 1.25)) * meals
151
+ + vb["gal_per_day"].get("living_cleaning", 0.625)
152
+ + vb["gal_per_day"].get("living_drinking_water", 0.75) * people),
153
+ }
154
+
155
+
156
+ # ---------------------------------------------------------------------------
157
+ # TIME-OF-DAY SHAPE FUNCTIONS
158
+ # ---------------------------------------------------------------------------
159
+
160
+ def solar_curve(hour: float) -> float:
161
+ if hour < 6 or hour > 18:
162
+ return 0.0
163
+ return max(0.0, math.sin(math.pi * (hour - 6) / 12))
164
+
165
+ def activity_curve(hour: float) -> float:
166
+ return max(0.01,
167
+ math.exp(-0.5 * ((hour - 8.0) / 1.2) ** 2)
168
+ + math.exp(-0.5 * ((hour - 19.5) / 1.5) ** 2) * 0.8)
169
+
170
+ def hvac_curve(hour: float, temp: str) -> float:
171
+ if temp == "Hot":
172
+ return (0.4 + 0.6 * math.sin(math.pi * max(0, hour - 9) / 12)
173
+ if 9 <= hour <= 21 else 0.2)
174
+ if temp == "Cold":
175
+ return 0.7 + 0.3 * (1 - solar_curve(hour))
176
+ return 0.4 + 0.1 * math.sin(math.pi * hour / 24)
177
+
178
+ def water_event_curve(hour: float) -> float:
179
+ return max(0.0,
180
+ math.exp(-0.5 * ((hour - 7.5) / 1.0) ** 2)
181
+ + math.exp(-0.5 * ((hour - 19.0) / 1.0) ** 2) * 0.6)
182
+
183
+ def jitter(rng: random.Random, scale: float = 0.05) -> float:
184
+ return 1.0 + rng.gauss(0, scale)
185
+
186
+
187
+ # ---------------------------------------------------------------------------
188
+ # MINUTE-LEVEL SERIES BUILDERS
189
+ # ---------------------------------------------------------------------------
190
+
191
+ def build_power_minutes(budget: dict, temp: str, battery_cap_kwh: float,
192
+ start: datetime, num_days: int,
193
+ rng: random.Random) -> list[dict]:
194
+ """
195
+ 1-minute power rows.
196
+ Flow columns: *_kW | State columns: *_Ah, *_Pct, *_V
197
+ """
198
+ solar_cap_kw = budget["solar_kwh"] / 6.0
199
+ hvac_mean = budget["hvac_kwh"] / 24
200
+ lighting_mean = budget["lighting_kwh"] / (300 / 60)
201
+ devices_mean = budget["devices_kwh"] / 24
202
+ fridge_mean = budget["fridge_kwh"] / 24
203
+ pump_mean = budget["water_pump_kwh"] / 2
204
+ cooking_mean = budget["cooking_kwh"] / 1.5
205
+ inverter_mean = budget["inverter_kwh"] / max(budget["inverter_kwh"] / (0.7 * 120 / 1000), 0.1)
206
+
207
+ battery_kwh = battery_cap_kwh * 0.80
208
+ rows = []
209
+
210
+ for m in range(num_days * MINS_PER_DAY):
211
+ ts = start + timedelta(minutes=m)
212
+ hour = ts.hour + ts.minute / 60.0
213
+
214
+ solar_kw = round(max(0, solar_cap_kw * solar_curve(hour) * rng.uniform(0.85, 1.05)), 4)
215
+ ac = activity_curve(hour)
216
+
217
+ hvac_kw = round(max(0, hvac_mean * hvac_curve(hour, temp) * jitter(rng, 0.08)), 4)
218
+ lighting_kw = round((max(0, lighting_mean * ac * jitter(rng, 0.05)) if 6 <= hour <= 23 else 0.002), 4)
219
+ devices_kw = round(max(0, devices_mean * jitter(rng, 0.04)), 4)
220
+ fridge_kw = round(max(0, fridge_mean * (0.7 + 0.6 * rng.random()) * jitter(rng, 0.03)), 4)
221
+ pump_kw = round(max(0, pump_mean * water_event_curve(hour) * jitter(rng, 0.15)), 4)
222
+ cooking_kw = round(max(0, cooking_mean * ac * jitter(rng, 0.20)) if ac > 0.3 else 0.0, 4)
223
+ inverter_kw = round(max(0, inverter_mean * ac * jitter(rng, 0.10)), 4)
224
+
225
+ total_load = hvac_kw + lighting_kw + devices_kw + fridge_kw + pump_kw + cooking_kw + inverter_kw
226
+ net = solar_kw - total_load
227
+ shore_kw = 0.0
228
+
229
+ if net < 0 and battery_kwh < abs(net) / 60 * 0.95:
230
+ shore_kw = round(abs(net) * 1.05, 4)
231
+ net = 0.0
232
+
233
+ battery_flow_kw = round(net, 4)
234
+ battery_kwh = max(0, min(battery_cap_kwh, battery_kwh + battery_flow_kw / 60))
235
+
236
+ unmetered_kw = round(max(0,
237
+ solar_kw + shore_kw
238
+ + (abs(battery_flow_kw) if battery_flow_kw < 0 else 0)
239
+ - total_load
240
+ - (battery_flow_kw if battery_flow_kw > 0 else 0)
241
+ ), 4)
242
+
243
+ rows.append({
244
+ "Time": ts.strftime("%Y-%m-%dT%H:%M:%SZ"),
245
+ "Solar_Flow_kW": solar_kw,
246
+ "Shore_Flow_kW": shore_kw,
247
+ "Battery_Flow_kW": battery_flow_kw,
248
+ "HVAC_Flow_kW": hvac_kw,
249
+ "Lighting_Flow_kW": lighting_kw,
250
+ "Devices_Flow_kW": devices_kw,
251
+ "Fridge_Flow_kW": fridge_kw,
252
+ "WaterPump_Flow_kW": pump_kw,
253
+ "Cooking_Flow_kW": cooking_kw,
254
+ "Inverter_Flow_kW": inverter_kw,
255
+ "Unmetered_Flow_kW": unmetered_kw,
256
+ "Battery_Level_Ah": round(battery_kwh * 1000 / BATTERY_NOMINAL_V, 1),
257
+ "Battery_Level_Pct": round(battery_kwh / battery_cap_kwh * 100, 2),
258
+ "Solar_Voltage_V": round(rng.uniform(36, 52) if solar_kw > 0 else 0.0, 1),
259
+ "Battery_Voltage_V": round(46 + (battery_kwh / battery_cap_kwh) * 6 + rng.gauss(0, 0.2), 2),
260
+ })
261
+
262
+ return rows
263
+
264
+
265
+ def build_water_minutes(budget: dict, fresh_cap_L: float, grey_cap_L: float,
266
+ black_cap_L: float,
267
+ start: datetime, num_days: int,
268
+ rng: random.Random) -> list[dict]:
269
+ """
270
+ 1-minute water rows.
271
+ Flow columns: *_Lpm | State columns: *_L
272
+
273
+ Black tank level is derived entirely from Toilet_Flow_Lpm:
274
+ 100% of toilet flush volume enters the black tank.
275
+ Grey tank receives shower + kitchen waste only (90% of flow, 10% evaporation/splash).
276
+ """
277
+ shower_rate = budget["shower_L"] / MINS_PER_DAY
278
+ toilet_rate = budget["toilet_L"] / MINS_PER_DAY
279
+ kitchen_rate = budget["kitchen_L"] / MINS_PER_DAY
280
+
281
+ fresh_L = fresh_cap_L * 0.95
282
+ grey_L = 0.0
283
+ black_L = 0.0 # starts empty; fills from toilet flow
284
+ rows = []
285
+
286
+ for m in range(num_days * MINS_PER_DAY):
287
+ ts = start + timedelta(minutes=m)
288
+ hour = ts.hour + ts.minute / 60.0
289
+ wc = water_event_curve(hour)
290
+
291
+ # Tank-fill event at 07:00 on day 1 only
292
+ inlet_Lpm = round(rng.uniform(8, 12), 3) if m == 420 else 0.0
293
+
294
+ shower_Lpm = round(max(0, shower_rate * wc * 2.5 * jitter(rng, 0.15)), 4)
295
+ kitchen_Lpm = round(max(0, kitchen_rate * wc * 2.0 * jitter(rng, 0.10)), 4)
296
+ toilet_Lpm = round(max(0, toilet_rate * wc * 2.0 * jitter(rng, 0.20)), 4)
297
+ pump_Lpm = shower_Lpm + kitchen_Lpm + toilet_Lpm
298
+
299
+ if fresh_L < pump_Lpm:
300
+ pump_Lpm = max(0, fresh_L)
301
+ shower_Lpm = round(pump_Lpm * 0.60, 4)
302
+ kitchen_Lpm = round(pump_Lpm * 0.25, 4)
303
+ toilet_Lpm = round(pump_Lpm * 0.15, 4)
304
+
305
+ unmetered_Lpm = round(max(0, pump_Lpm - shower_Lpm - kitchen_Lpm - toilet_Lpm), 4)
306
+
307
+ # Update tank levels
308
+ fresh_L = max(0, min(fresh_cap_L, fresh_L + inlet_Lpm - pump_Lpm))
309
+ grey_L = min(grey_cap_L, grey_L + (shower_Lpm + kitchen_Lpm) * 0.9)
310
+ black_L = min(black_cap_L, black_L + toilet_Lpm) # 100% of toilet β†’ black tank
311
+
312
+ rows.append({
313
+ "Time": ts.strftime("%Y-%m-%dT%H:%M:%SZ"),
314
+ "Inlet_Flow_Lpm": inlet_Lpm,
315
+ "Pump_Flow_Lpm": round(pump_Lpm, 4),
316
+ "Shower_Flow_Lpm": shower_Lpm,
317
+ "Kitchen_Flow_Lpm": kitchen_Lpm,
318
+ "Toilet_Flow_Lpm": toilet_Lpm,
319
+ "Unmetered_Flow_Lpm": unmetered_Lpm,
320
+ "FreshTank_Level_L": round(fresh_L, 2),
321
+ "GreyTank_Level_L": round(grey_L, 2),
322
+ "BlackTank_Level_L": round(black_L, 2),
323
+ })
324
+
325
+ return rows
326
+
327
+
328
+ # ---------------------------------------------------------------------------
329
+ # RESAMPLING
330
+ # ---------------------------------------------------------------------------
331
+
332
+ def resample_power(rows: list[dict], interval_mins: int, mode: str = "mean") -> list[dict]:
333
+ """
334
+ mode='mean' β†’ kW (15MIN flow file)
335
+ mode='sum' β†’ kWh (1H, 1DAY energy files) [kW Γ— 1 min / 60 = kWh]
336
+ """
337
+ CIRCUITS = ["HVAC", "Lighting", "Devices", "Fridge",
338
+ "WaterPump", "Cooking", "Inverter", "Unmetered"]
339
+ out = []
340
+
341
+ for i in range(0, len(rows), interval_mins):
342
+ bucket = rows[i: i + interval_mins]
343
+ if not bucket:
344
+ continue
345
+ first, last = bucket[0], bucket[-1]
346
+
347
+ def mean(col):
348
+ return round(sum(r[col] for r in bucket) / len(bucket), 4)
349
+
350
+ def to_kwh(col):
351
+ return round(sum(r[col] for r in bucket) / 60, 6)
352
+
353
+ def avg_v(col):
354
+ return round(sum(r[col] for r in bucket) / len(bucket), 2)
355
+
356
+ row = {"Time": first["Time"]}
357
+
358
+ if mode == "mean":
359
+ row["Solar_Flow_kW"] = mean("Solar_Flow_kW")
360
+ row["Shore_Flow_kW"] = mean("Shore_Flow_kW")
361
+ row["Battery_Flow_kW"] = mean("Battery_Flow_kW")
362
+ for c in CIRCUITS:
363
+ row[f"{c}_Flow_kW"] = mean(f"{c}_Flow_kW")
364
+ row["Battery_Level_Ah"] = last["Battery_Level_Ah"]
365
+ row["Battery_Level_Pct"] = last["Battery_Level_Pct"]
366
+ row["Solar_Voltage_V"] = avg_v("Solar_Voltage_V")
367
+ row["Battery_Voltage_V"] = avg_v("Battery_Voltage_V")
368
+
369
+ else:
370
+ row["Solar_Total_kWh"] = to_kwh("Solar_Flow_kW")
371
+ row["Shore_Total_kWh"] = to_kwh("Shore_Flow_kW")
372
+ # Battery split: charged (+) and discharged (-) as separate positive columns
373
+ row["Battery_Charged_kWh"] = round(
374
+ sum(r["Battery_Flow_kW"] for r in bucket if r["Battery_Flow_kW"] > 0) / 60, 6)
375
+ row["Battery_Discharged_kWh"] = round(
376
+ sum(abs(r["Battery_Flow_kW"]) for r in bucket if r["Battery_Flow_kW"] < 0) / 60, 6)
377
+ for c in CIRCUITS:
378
+ row[f"{c}_Total_kWh"] = to_kwh(f"{c}_Flow_kW")
379
+ row["Battery_Level_Ah"] = last["Battery_Level_Ah"]
380
+ row["Battery_Level_Pct"] = last["Battery_Level_Pct"]
381
+ row["Solar_Voltage_Avg_V"] = avg_v("Solar_Voltage_V")
382
+ row["Battery_Voltage_Avg_V"] = avg_v("Battery_Voltage_V")
383
+
384
+ out.append(row)
385
+ return out
386
+
387
+
388
+ def resample_water(rows: list[dict], interval_mins: int, mode: str = "mean",
389
+ fresh_cap_L: float = 378.5, grey_cap_L: float = 189.3,
390
+ black_cap_L: float = 170.3) -> list[dict]:
391
+ """
392
+ mode='mean' β†’ Lpm (15MIN flow file)
393
+ mode='sum' β†’ L (1H, 1DAY energy files) [Lpm Γ— 1 min = L]
394
+
395
+ Black tank level is a snapshot carried from 1MIN rows (derived from toilet flow).
396
+ """
397
+ CIRCUITS = ["Inlet", "Pump", "Shower", "Kitchen", "Toilet", "Unmetered"]
398
+ out = []
399
+
400
+ for i in range(0, len(rows), interval_mins):
401
+ bucket = rows[i: i + interval_mins]
402
+ if not bucket:
403
+ continue
404
+ first, last = bucket[0], bucket[-1]
405
+
406
+ def mean_lpm(col):
407
+ return round(sum(r[col] for r in bucket) / len(bucket), 4)
408
+
409
+ def to_L(col):
410
+ return round(sum(r[col] for r in bucket), 4) # Lpm Γ— 1 min = L
411
+
412
+ row = {"Time": first["Time"]}
413
+
414
+ if mode == "mean":
415
+ for c in CIRCUITS:
416
+ row[f"{c}_Flow_Lpm"] = mean_lpm(f"{c}_Flow_Lpm")
417
+ row["FreshTank_Level_L"] = last["FreshTank_Level_L"]
418
+ row["GreyTank_Level_L"] = last["GreyTank_Level_L"]
419
+ row["BlackTank_Level_L"] = last["BlackTank_Level_L"]
420
+
421
+ else:
422
+ for c in CIRCUITS:
423
+ row[f"{c}_Total_L"] = to_L(f"{c}_Flow_Lpm")
424
+ row["FreshTank_Level_L"] = last["FreshTank_Level_L"]
425
+ row["FreshTank_Level_Pct"] = round(last["FreshTank_Level_L"] / fresh_cap_L * 100, 2)
426
+ row["GreyTank_Level_L"] = last["GreyTank_Level_L"]
427
+ row["GreyTank_Level_Pct"] = round(last["GreyTank_Level_L"] / grey_cap_L * 100, 2)
428
+ row["BlackTank_Level_L"] = last["BlackTank_Level_L"]
429
+ row["BlackTank_Level_Pct"] = round(last["BlackTank_Level_L"] / black_cap_L * 100, 2)
430
+
431
+ out.append(row)
432
+ return out
433
+
434
+
435
+ # ---------------------------------------------------------------------------
436
+ # CSV WRITER
437
+ # ---------------------------------------------------------------------------
438
+
439
+ def write_csv(path: str, rows: list[dict]):
440
+ if not rows:
441
+ return
442
+ os.makedirs(os.path.dirname(path), exist_ok=True)
443
+ with open(path, "w", newline="") as f:
444
+ w = csv.DictWriter(f, fieldnames=rows[0].keys())
445
+ w.writeheader()
446
+ w.writerows(rows)
447
+ print(f" Wrote {len(rows):>6,} rows -> {path}")
448
+
449
+
450
+ # ---------------------------------------------------------------------------
451
+ # MAIN
452
+ # ---------------------------------------------------------------------------
453
+
454
+ def main():
455
+ parser = argparse.ArgumentParser(description="EV Camper Mock Data Generator")
456
+ parser.add_argument("--config", default="data.json", help="Path to data.json")
457
+ parser.add_argument("--out", default="output", help="Output directory")
458
+ parser.add_argument("--seed", type=int, default=42, help="Random seed")
459
+ parser.add_argument("--user", default=None, help="Glamper / Typical / Expert")
460
+ parser.add_argument("--people", type=int, default=None, help="Number of occupants")
461
+ parser.add_argument("--days", type=int, default=None, help="Trip duration in days")
462
+ parser.add_argument("--temp", default=None, help="Hot / Temperate / Cold")
463
+ parser.add_argument("--start", default="2026-02-18T00:00:00", help="Trip start (ISO datetime)")
464
+ args = parser.parse_args()
465
+
466
+ rng = random.Random(args.seed)
467
+ data = load_data(args.config)
468
+
469
+ params = data["inputs"]["params"]
470
+ specs = data["trailer_specs"]["specs"]
471
+
472
+ user = args.user or params["user_type"]["value"]
473
+ people = args.people or params["num_people"]["value"]
474
+ days = args.days or params["trip_duration_days"]["value"]
475
+ temp = args.temp or params["temperature"]["value"]
476
+ hvac_hrs = params["hvac_runtime_hrs"]["value"]
477
+
478
+ bat_cap_kwh = specs["battery_capacity_kwh"]["value"]
479
+ fresh_cap_gal = specs["freshwater_capacity_gal"]["value"]
480
+ grey_cap_gal = specs["greywater_capacity_gal"]["value"]
481
+ black_cap_gal = specs["blackwater_capacity_gal"]["value"]
482
+ fresh_cap_L = fresh_cap_gal * GAL_TO_LITRES
483
+ grey_cap_L = grey_cap_gal * GAL_TO_LITRES
484
+ black_cap_L = black_cap_gal * GAL_TO_LITRES
485
+ start = datetime.fromisoformat(args.start)
486
+
487
+ print(f"\n{'='*60}")
488
+ print(f" EV Camper Mock Data Generator")
489
+ print(f"{'='*60}")
490
+ print(f" Profile : {user} | People: {people} | Days: {days}")
491
+ print(f" Temp : {temp} | Start : {start.strftime('%Y-%m-%d')}")
492
+ print(f" Battery : {bat_cap_kwh} kWh | Fresh: {fresh_cap_gal} gal | Black: {black_cap_gal} gal")
493
+ print(f"{'='*60}\n")
494
+
495
+ pw = calc_power_budget(data, user, people, temp, hvac_hrs)
496
+ wat = calc_water_budget(data, user, people)
497
+
498
+ print(" Daily Power Budget:")
499
+ for k, v in pw.items(): print(f" {k:<22} {v:.3f} kWh")
500
+ print(f"\n Daily Water Budget:")
501
+ for k, v in wat.items(): print(f" {k:<22} {v:.1f} L")
502
+ print()
503
+
504
+ print(" Generating 1-minute base series...")
505
+ power_mins = build_power_minutes(pw, temp, bat_cap_kwh, start, days, rng)
506
+ water_mins = build_water_minutes(wat, fresh_cap_L, grey_cap_L, black_cap_L, start, days, rng)
507
+
508
+ out = Path(args.out)
509
+
510
+ # ------------------------------------------------------------------
511
+ # POWER FILES
512
+ # ------------------------------------------------------------------
513
+ pw_dir = out / "power"
514
+ print(" Resampling power files...")
515
+
516
+ # 1SEC: expand first 3h of 1-min rows to per-second with jitter
517
+ FLOW_COLS_KW = ["Solar_Flow_kW", "Shore_Flow_kW", "Battery_Flow_kW",
518
+ "HVAC_Flow_kW", "Lighting_Flow_kW", "Devices_Flow_kW",
519
+ "Fridge_Flow_kW", "WaterPump_Flow_kW", "Cooking_Flow_kW",
520
+ "Inverter_Flow_kW", "Unmetered_Flow_kW"]
521
+ sec_rows = []
522
+ for row in power_mins[:180]:
523
+ ts_base = datetime.strptime(row["Time"], "%Y-%m-%dT%H:%M:%SZ")
524
+ for s in range(60):
525
+ sr = dict(row)
526
+ sr["Time"] = (ts_base + timedelta(seconds=s)).strftime("%Y-%m-%dT%H:%M:%SZ")
527
+ for col in FLOW_COLS_KW:
528
+ sr[col] = round(max(0, row[col] * jitter(rng, 0.03)), 4)
529
+ sec_rows.append(sr)
530
+
531
+ write_csv(str(pw_dir / "1SEC.csv"), sec_rows)
532
+ write_csv(str(pw_dir / "1MIN.csv"), power_mins)
533
+ write_csv(str(pw_dir / "15MIN.csv"), resample_power(power_mins, 15, "mean"))
534
+ write_csv(str(pw_dir / "1H.csv"), resample_power(power_mins, 60, "sum"))
535
+ write_csv(str(pw_dir / "1DAY.csv"), resample_power(power_mins, MINS_PER_DAY,"sum"))
536
+
537
+ # ------------------------------------------------------------------
538
+ # WATER FILES
539
+ # ------------------------------------------------------------------
540
+ wt_dir = out / "water"
541
+ print(" Resampling water files...")
542
+
543
+ write_csv(str(wt_dir / "1MIN.csv"), water_mins)
544
+ write_csv(str(wt_dir / "15MIN.csv"), resample_water(water_mins, 15, "mean", fresh_cap_L, grey_cap_L, black_cap_L))
545
+ write_csv(str(wt_dir / "1H.csv"), resample_water(water_mins, 60, "sum", fresh_cap_L, grey_cap_L, black_cap_L))
546
+ write_csv(str(wt_dir / "1DAY.csv"), resample_water(water_mins, MINS_PER_DAY, "sum", fresh_cap_L, grey_cap_L, black_cap_L))
547
+
548
+
549
+ if __name__ == "__main__":
550
+ main()
main.py CHANGED
@@ -1,64 +1,418 @@
1
- from fastapi import FastAPI, HTTPException
2
- from fastapi.staticfiles import StaticFiles
3
- from fastapi.responses import FileResponse
4
- from pydantic import BaseModel
5
- import os
6
- import logging
7
- from calculator import calculate_usage
8
-
9
- # Configure logging
10
- logging.basicConfig(level=logging.INFO)
11
- logger = logging.getLogger(__name__)
12
-
13
- app = FastAPI(title="Water Intelligence Management System")
14
-
15
- # Pydantic model for request validation
16
- class CalculationRequirements(BaseModel):
17
- num_expert: int = 0
18
- num_typical: int = 0
19
- num_glamper: int = 0
20
- num_children: int = 0
21
- fresh_cap: float = 0
22
- grey_cap: float = 0
23
- black_cap: float = 0
24
- fresh_level: float = 0
25
- grey_level: float = 0
26
- black_level: float = 0
27
- climate_mult: float = 1.0
28
-
29
- @app.post("/calculate")
30
- async def calculate(requirements: CalculationRequirements):
31
- try:
32
- results = calculate_usage(requirements.model_dump())
33
- return results
34
- except Exception as e:
35
- logger.error(f"Calculation error: {e}")
36
- raise HTTPException(status_code=500, detail=str(e))
37
-
38
- # Setup paths - everything is now in the root
39
- current_dir = os.path.dirname(os.path.abspath(__file__))
40
-
41
- logger.info(f"Current directory: {current_dir}")
42
- logger.info(f"Contents of directory: {os.listdir(current_dir)}")
43
-
44
- # Serve specific static files to avoid exposing the whole directory
45
- @app.get("/script.js")
46
- async def get_js():
47
- return FileResponse(os.path.join(current_dir, "script.js"))
48
-
49
- @app.get("/style.css")
50
- async def get_css():
51
- return FileResponse(os.path.join(current_dir, "style.css"))
52
-
53
- @app.get("/")
54
- async def read_root():
55
- index_file = os.path.join(current_dir, "index.html")
56
- if os.path.exists(index_file):
57
- return FileResponse(index_file)
58
- logger.error(f"index.html NOT found at {index_file}")
59
- return {"message": "index.html not found", "path": index_file}
60
-
61
- if __name__ == "__main__":
62
- import uvicorn
63
- port = int(os.environ.get("PORT", 7860))
64
- uvicorn.run(app, host="0.0.0.0", port=port, forwarded_allow_ips='*')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ EV Camper Dashboard - FastAPI Backend
3
+ """
4
+ import csv
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ from typing import Optional
9
+ from fastapi import FastAPI, Query
10
+ from fastapi.middleware.cors import CORSMiddleware
11
+ from fastapi.staticfiles import StaticFiles
12
+ from fastapi.responses import FileResponse, JSONResponse
13
+
14
+ app = FastAPI(title="EV Camper Dashboard API", version="1.0")
15
+
16
+ app.add_middleware(
17
+ CORSMiddleware,
18
+ allow_origins=["*"],
19
+ allow_methods=["*"],
20
+ allow_headers=["*"],
21
+ )
22
+
23
+ DATA_DIR = Path("output")
24
+ CONFIG_PATH = Path("data.json")
25
+
26
+ # ── Helpers ──────────────────────────────────────────────────────────────
27
+
28
+ def read_csv(path: Path, limit: int = None, offset: int = 0) -> list[dict]:
29
+ if not path.exists():
30
+ return []
31
+ with open(path) as f:
32
+ reader = csv.DictReader(f)
33
+ rows = []
34
+ for i, row in enumerate(reader):
35
+ if i < offset:
36
+ continue
37
+ parsed = {}
38
+ for k, v in row.items():
39
+ if k == "Time":
40
+ parsed[k] = v
41
+ continue
42
+ try:
43
+ parsed[k] = float(v)
44
+ except (ValueError, TypeError):
45
+ parsed[k] = v
46
+ rows.append(parsed)
47
+ if limit and len(rows) >= limit:
48
+ break
49
+ return rows
50
+
51
+
52
+ def load_config() -> dict:
53
+ with open(CONFIG_PATH) as f:
54
+ return json.load(f)
55
+
56
+
57
+ # ── Config & Specs ───────────────────────────────────────────────────────
58
+
59
+ @app.get("/api/config")
60
+ def get_config():
61
+ """Return trip configuration and trailer specs."""
62
+ cfg = load_config()
63
+ return {
64
+ "inputs": cfg["inputs"],
65
+ "trailer_specs": cfg["trailer_specs"],
66
+ }
67
+
68
+
69
+ @app.get("/api/components")
70
+ def get_components():
71
+ """Return all trailer components by category."""
72
+ cfg = load_config()
73
+ comps = cfg["lookups"]["components"]
74
+ schema = comps["schema"]
75
+ result = {}
76
+ for category in ["living", "cooking", "toilet", "shower", "laundry", "actuation", "dumping", "hvac"]:
77
+ if category in comps:
78
+ result[category] = [
79
+ dict(zip(schema, item)) for item in comps[category]
80
+ ]
81
+ return result
82
+
83
+
84
+ # ── Budget Endpoints ─────────────────────────────────────────────────────
85
+
86
+ @app.get("/api/power/budget")
87
+ def get_power_budget():
88
+ """Daily power budget breakdown in kWh."""
89
+ cfg = load_config()
90
+ from generate_mock_data import calc_power_budget
91
+ params = cfg["inputs"]["params"]
92
+ return calc_power_budget(
93
+ cfg,
94
+ params["user_type"]["value"],
95
+ params["num_people"]["value"],
96
+ params["temperature"]["value"],
97
+ params["hvac_runtime_hrs"]["value"],
98
+ )
99
+
100
+
101
+ @app.get("/api/water/budget")
102
+ def get_water_budget():
103
+ """Daily water budget breakdown in litres."""
104
+ cfg = load_config()
105
+ from generate_mock_data import calc_water_budget
106
+ params = cfg["inputs"]["params"]
107
+ return calc_water_budget(
108
+ cfg,
109
+ params["user_type"]["value"],
110
+ params["num_people"]["value"],
111
+ )
112
+
113
+
114
+ # ── Time Series Endpoints ────────────────────────────────────────────────
115
+
116
+ POWER_RESOLUTIONS = {"1SEC", "1MIN", "15MIN", "1H", "1DAY"}
117
+ WATER_RESOLUTIONS = {"1MIN", "15MIN", "1H", "1DAY"}
118
+
119
+
120
+ # ── Hourly Profile (MUST be before /{resolution} routes) ────────────────
121
+
122
+ @app.get("/api/power/hourly-profile")
123
+ def get_hourly_profile():
124
+ """Average hourly power profile across all trip days."""
125
+ hourly = read_csv(DATA_DIR / "power" / "1H.csv")
126
+ circuits = ["HVAC", "Lighting", "Devices", "Fridge", "WaterPump", "Cooking", "Inverter"]
127
+
128
+ from collections import defaultdict
129
+ from datetime import datetime as dt
130
+ hour_buckets = defaultdict(lambda: defaultdict(list))
131
+
132
+ for row in hourly:
133
+ ts = dt.fromisoformat(row["Time"].replace("Z", ""))
134
+ h = ts.hour
135
+ hour_buckets[h]["solar"].append(row.get("Solar_Total_kWh", 0))
136
+ for c in circuits:
137
+ hour_buckets[h][c].append(row.get(f"{c}_Total_kWh", 0))
138
+ hour_buckets[h]["battery_pct"].append(row.get("Battery_Level_Pct", 0))
139
+
140
+ profile = []
141
+ for h in range(24):
142
+ entry = {"hour": h}
143
+ if h in hour_buckets:
144
+ b = hour_buckets[h]
145
+ entry["solar_kwh"] = round(sum(b["solar"]) / len(b["solar"]), 4)
146
+ entry["battery_pct"] = round(sum(b["battery_pct"]) / len(b["battery_pct"]), 1)
147
+ for c in circuits:
148
+ entry[f"{c}_kwh"] = round(sum(b[c]) / len(b[c]), 4)
149
+ entry["total_load_kwh"] = round(sum(entry.get(f"{c}_kwh", 0) for c in circuits), 4)
150
+ profile.append(entry)
151
+ return profile
152
+
153
+
154
+ @app.get("/api/water/hourly-profile")
155
+ def get_water_hourly_profile():
156
+ """Average hourly water usage profile."""
157
+ hourly = read_csv(DATA_DIR / "water" / "1H.csv")
158
+ circuits = ["Shower", "Kitchen", "Toilet"]
159
+
160
+ from collections import defaultdict
161
+ from datetime import datetime as dt
162
+ hour_buckets = defaultdict(lambda: defaultdict(list))
163
+
164
+ for row in hourly:
165
+ ts = dt.fromisoformat(row["Time"].replace("Z", ""))
166
+ h = ts.hour
167
+ for c in circuits:
168
+ hour_buckets[h][c].append(row.get(f"{c}_Total_L", 0))
169
+ hour_buckets[h]["fresh_pct"].append(
170
+ row.get("FreshTank_Level_Pct", row.get("FreshTank_Level_L", 0) / 378.5 * 100)
171
+ )
172
+
173
+ profile = []
174
+ for h in range(24):
175
+ entry = {"hour": h}
176
+ if h in hour_buckets:
177
+ b = hour_buckets[h]
178
+ for c in circuits:
179
+ entry[f"{c}_L"] = round(sum(b[c]) / len(b[c]), 3)
180
+ entry["total_L"] = round(sum(entry.get(f"{c}_L", 0) for c in circuits), 3)
181
+ entry["fresh_pct"] = round(sum(b["fresh_pct"]) / len(b["fresh_pct"]), 1)
182
+ profile.append(entry)
183
+ return profile
184
+
185
+
186
+ @app.get("/api/power/peaks")
187
+ def get_power_peaks():
188
+ """Identify peak power usage hours across the trip."""
189
+ hourly = read_csv(DATA_DIR / "power" / "1H.csv")
190
+ circuits = ["HVAC", "Lighting", "Devices", "Fridge", "WaterPump", "Cooking", "Inverter"]
191
+
192
+ peaks = []
193
+ for row in hourly:
194
+ total = sum(row.get(f"{c}_Total_kWh", 0) for c in circuits)
195
+ peaks.append({
196
+ "time": row["Time"],
197
+ "total_kwh": round(total, 4),
198
+ "solar_kwh": row.get("Solar_Total_kWh", 0),
199
+ "battery_pct": row.get("Battery_Level_Pct", 0),
200
+ })
201
+
202
+ peaks.sort(key=lambda x: x["total_kwh"], reverse=True)
203
+ return {
204
+ "top_10_peak_hours": peaks[:10],
205
+ "bottom_10_hours": sorted(peaks, key=lambda x: x["total_kwh"])[:10],
206
+ }
207
+
208
+
209
+ @app.get("/api/power/{resolution}")
210
+ def get_power_data(
211
+ resolution: str,
212
+ limit: Optional[int] = Query(None, ge=1, le=50000),
213
+ offset: int = Query(0, ge=0),
214
+ day: Optional[int] = Query(None, ge=1, le=30, description="Filter by day number"),
215
+ ):
216
+ """Power time-series data at given resolution."""
217
+ res = resolution.upper()
218
+ if res not in POWER_RESOLUTIONS:
219
+ return JSONResponse({"error": f"Invalid resolution. Use: {POWER_RESOLUTIONS}"}, 400)
220
+
221
+ path = DATA_DIR / "power" / f"{res}.csv"
222
+
223
+ # When day filter is active, read all rows first, then filter
224
+ if day is not None:
225
+ rows = read_csv(path)
226
+ if rows:
227
+ from datetime import datetime
228
+ start_date = datetime.fromisoformat(rows[0]["Time"].replace("Z", "")).date()
229
+ rows = [r for r in rows
230
+ if (datetime.fromisoformat(r["Time"].replace("Z", "")).date() - start_date).days + 1 == day]
231
+ if limit:
232
+ rows = rows[offset:offset + limit]
233
+ else:
234
+ rows = read_csv(path, limit=limit, offset=offset)
235
+
236
+ return {"resolution": res, "count": len(rows), "data": rows}
237
+
238
+
239
+ @app.get("/api/water/{resolution}")
240
+ def get_water_data(
241
+ resolution: str,
242
+ limit: Optional[int] = Query(None, ge=1, le=50000),
243
+ offset: int = Query(0, ge=0),
244
+ day: Optional[int] = Query(None, ge=1, le=30),
245
+ ):
246
+ """Water time-series data at given resolution."""
247
+ res = resolution.upper()
248
+ if res not in WATER_RESOLUTIONS:
249
+ return JSONResponse({"error": f"Invalid resolution. Use: {WATER_RESOLUTIONS}"}, 400)
250
+
251
+ path = DATA_DIR / "water" / f"{res}.csv"
252
+
253
+ if day is not None:
254
+ rows = read_csv(path)
255
+ if rows:
256
+ from datetime import datetime
257
+ start_date = datetime.fromisoformat(rows[0]["Time"].replace("Z", "")).date()
258
+ rows = [r for r in rows
259
+ if (datetime.fromisoformat(r["Time"].replace("Z", "")).date() - start_date).days + 1 == day]
260
+ if limit:
261
+ rows = rows[offset:offset + limit]
262
+ else:
263
+ rows = read_csv(path, limit=limit, offset=offset)
264
+
265
+ return {"resolution": res, "count": len(rows), "data": rows}
266
+
267
+
268
+ # ── Summary / Aggregated Stats ───────────────────────────────────────────
269
+
270
+ @app.get("/api/summary")
271
+ def get_summary():
272
+ """Trip-wide summary statistics."""
273
+ daily_power = read_csv(DATA_DIR / "power" / "1DAY.csv")
274
+ daily_water = read_csv(DATA_DIR / "water" / "1DAY.csv")
275
+ hourly_power = read_csv(DATA_DIR / "power" / "1H.csv")
276
+
277
+ if not daily_power:
278
+ return {"error": "No data"}
279
+
280
+ circuits = ["HVAC", "Lighting", "Devices", "Fridge", "WaterPump", "Cooking", "Inverter", "Unmetered"]
281
+
282
+ total_solar = sum(d.get("Solar_Total_kWh", 0) for d in daily_power)
283
+ total_shore = sum(d.get("Shore_Total_kWh", 0) for d in daily_power)
284
+ total_consumption = sum(
285
+ sum(d.get(f"{c}_Total_kWh", 0) for c in circuits)
286
+ for d in daily_power
287
+ )
288
+
289
+ # Per-circuit totals
290
+ circuit_totals = {}
291
+ for c in circuits:
292
+ circuit_totals[c] = round(sum(d.get(f"{c}_Total_kWh", 0) for d in daily_power), 3)
293
+
294
+ # Battery stats
295
+ min_battery = min(d.get("Battery_Level_Pct", 100) for d in hourly_power) if hourly_power else 0
296
+ max_battery = max(d.get("Battery_Level_Pct", 0) for d in hourly_power) if hourly_power else 100
297
+ avg_battery = sum(d.get("Battery_Level_Pct", 0) for d in hourly_power) / len(hourly_power) if hourly_power else 0
298
+
299
+ # Water totals
300
+ total_fresh_used = 0
301
+ if daily_water:
302
+ total_fresh_used = sum(d.get("Pump_Total_L", 0) for d in daily_water)
303
+
304
+ # Final tank levels
305
+ last_water = daily_water[-1] if daily_water else {}
306
+ last_power = hourly_power[-1] if hourly_power else {}
307
+
308
+ # Daily breakdown
309
+ daily_breakdown = []
310
+ for i, dp in enumerate(daily_power):
311
+ dw = daily_water[i] if i < len(daily_water) else {}
312
+ daily_breakdown.append({
313
+ "day": i + 1,
314
+ "solar_kwh": round(dp.get("Solar_Total_kWh", 0), 2),
315
+ "consumption_kwh": round(sum(dp.get(f"{c}_Total_kWh", 0) for c in circuits), 2),
316
+ "shore_kwh": round(dp.get("Shore_Total_kWh", 0), 2),
317
+ "battery_end_pct": dp.get("Battery_Level_Pct", 0),
318
+ "fresh_used_L": round(dw.get("Pump_Total_L", 0), 1),
319
+ "fresh_remaining_pct": round(dw.get("FreshTank_Level_Pct", 0), 1),
320
+ })
321
+
322
+ return {
323
+ "trip_days": len(daily_power),
324
+ "total_solar_kwh": round(total_solar, 2),
325
+ "total_shore_kwh": round(total_shore, 2),
326
+ "total_consumption_kwh": round(total_consumption, 2),
327
+ "self_sufficiency_pct": round(total_solar / max(total_consumption, 0.01) * 100, 1),
328
+ "circuit_totals_kwh": circuit_totals,
329
+ "battery": {
330
+ "min_pct": round(min_battery, 1),
331
+ "max_pct": round(max_battery, 1),
332
+ "avg_pct": round(avg_battery, 1),
333
+ "current_pct": last_power.get("Battery_Level_Pct", 0),
334
+ },
335
+ "water": {
336
+ "total_fresh_used_L": round(total_fresh_used, 1),
337
+ "fresh_remaining_L": last_water.get("FreshTank_Level_L", 0),
338
+ "fresh_remaining_pct": last_water.get("FreshTank_Level_Pct", 0),
339
+ "grey_level_L": last_water.get("GreyTank_Level_L", 0),
340
+ "grey_level_pct": last_water.get("GreyTank_Level_Pct", 0),
341
+ "black_level_L": last_water.get("BlackTank_Level_L", 0),
342
+ "black_level_pct": last_water.get("BlackTank_Level_Pct", 0),
343
+ },
344
+ "daily_breakdown": daily_breakdown,
345
+ }
346
+
347
+
348
+ # ── Serve Frontend ───────────────────────────────────────────────────────
349
+
350
+ # ── Data Regeneration ────────────────────────────────────────────────────
351
+
352
+ from pydantic import BaseModel
353
+
354
+ class GenerateRequest(BaseModel):
355
+ user_type: str = "Typical"
356
+ num_people: int = 2
357
+ trip_duration_days: int = 5
358
+ temperature: str = "Hot"
359
+ sunlight: str = "Hi- Sunny"
360
+ humidity: str = "Comfortable"
361
+ seed: int = 42
362
+
363
+ @app.post("/api/generate")
364
+ def regenerate_data(req: GenerateRequest):
365
+ """Regenerate mock data with new parameters."""
366
+ import subprocess, sys
367
+
368
+ # Update config in memory for budget endpoints
369
+ cfg = load_config()
370
+ cfg["inputs"]["params"]["user_type"]["value"] = req.user_type
371
+ cfg["inputs"]["params"]["num_people"]["value"] = req.num_people
372
+ cfg["inputs"]["params"]["trip_duration_days"]["value"] = req.trip_duration_days
373
+ cfg["inputs"]["params"]["temperature"]["value"] = req.temperature
374
+ cfg["inputs"]["params"]["sunlight"]["value"] = req.sunlight
375
+ cfg["inputs"]["params"]["humidity"]["value"] = req.humidity
376
+
377
+ # Write updated config
378
+ with open(CONFIG_PATH, "w") as f:
379
+ json.dump(cfg, f, indent=2)
380
+
381
+ # Run the generator
382
+ result = subprocess.run(
383
+ [sys.executable, "generate_mock_data.py",
384
+ "--config", str(CONFIG_PATH),
385
+ "--out", str(DATA_DIR),
386
+ "--seed", str(req.seed),
387
+ "--user", req.user_type,
388
+ "--people", str(req.num_people),
389
+ "--days", str(req.trip_duration_days),
390
+ "--temp", req.temperature],
391
+ capture_output=True, text=True, timeout=120,
392
+ )
393
+
394
+ if result.returncode != 0:
395
+ return JSONResponse({"error": result.stderr}, 500)
396
+
397
+ return {"status": "ok", "message": f"Generated {req.trip_duration_days}-day trip for {req.user_type} profile"}
398
+
399
+
400
+ # ── Serve Frontend (catch-all, must be LAST) ─────────────────────────────
401
+
402
+ STATIC_DIR = Path("static")
403
+
404
+ @app.get("/")
405
+ def serve_index():
406
+ return FileResponse(STATIC_DIR / "index.html")
407
+
408
+ @app.get("/{path:path}")
409
+ def serve_static(path: str):
410
+ file_path = STATIC_DIR / path
411
+ if file_path.exists() and file_path.is_file():
412
+ return FileResponse(file_path)
413
+ return FileResponse(STATIC_DIR / "index.html")
414
+
415
+
416
+ if __name__ == "__main__":
417
+ import uvicorn
418
+ uvicorn.run(app, host="0.0.0.0", port=8000)
output/power/15MIN.csv ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Time,Solar_Flow_kW,Shore_Flow_kW,Battery_Flow_kW,HVAC_Flow_kW,Lighting_Flow_kW,Devices_Flow_kW,Fridge_Flow_kW,WaterPump_Flow_kW,Cooking_Flow_kW,Inverter_Flow_kW,Unmetered_Flow_kW,Battery_Level_Ah,Battery_Level_Pct,Solar_Voltage_V,Battery_Voltage_V
2
+ 2026-02-18T00:00:00Z,0.0,0.0,-0.8439,0.6457,0.002,0.1361,0.0593,0.0,0.0,0.0008,0.0,1662.3,79.79,0.0,50.75
3
+ 2026-02-18T00:15:00Z,0.0,0.0,-0.8473,0.6497,0.002,0.1344,0.0604,0.0,0.0,0.0009,0.0,1657.9,79.58,0.0,50.82
4
+ 2026-02-18T00:30:00Z,0.0,0.0,-0.886,0.6815,0.002,0.1349,0.0667,0.0,0.0,0.0009,0.0,1653.2,79.36,0.0,50.79
5
+ 2026-02-18T00:45:00Z,0.0,0.0,-0.8442,0.6411,0.002,0.1374,0.0629,0.0,0.0,0.0008,0.0,1648.8,79.14,0.0,50.82
6
+ 2026-02-18T01:00:00Z,0.0,0.0,-0.8365,0.6364,0.002,0.1341,0.0632,0.0,0.0,0.0009,0.0,1644.5,78.94,0.0,50.78
7
+ 2026-02-18T01:15:00Z,0.0,0.0,-0.8253,0.6328,0.002,0.1355,0.0542,0.0,0.0,0.0008,0.0,1640.2,78.73,0.0,50.68
8
+ 2026-02-18T01:30:00Z,0.0,0.0,-0.8569,0.6603,0.002,0.1331,0.0606,0.0,0.0,0.0008,0.0,1635.7,78.51,0.0,50.71
9
+ 2026-02-18T01:45:00Z,0.0,0.0,-0.8666,0.666,0.002,0.1331,0.0647,0.0,0.0,0.0008,0.0,1631.2,78.3,0.0,50.61
10
+ 2026-02-18T02:00:00Z,0.0,0.0,-0.8384,0.6349,0.002,0.135,0.0657,0.0,0.0,0.0009,0.0,1626.8,78.09,0.0,50.72
11
+ 2026-02-18T02:15:00Z,0.0,0.0,-0.872,0.6702,0.002,0.1376,0.0614,0.0,0.0,0.0009,0.0,1622.3,77.87,0.0,50.7
12
+ 2026-02-18T02:30:00Z,0.0,0.0,-0.826,0.63,0.002,0.135,0.0581,0.0,0.0,0.0009,0.0,1618.0,77.66,0.0,50.65
13
+ 2026-02-18T02:45:00Z,0.0,0.0,-0.8538,0.6556,0.002,0.1358,0.0596,0.0,0.0,0.0008,0.0,1613.6,77.45,0.0,50.61
14
+ 2026-02-18T03:00:00Z,0.0,0.0,-0.8751,0.6698,0.002,0.1358,0.0667,0.0,0.0,0.0008,0.0,1609.0,77.23,0.0,50.65
15
+ 2026-02-18T03:15:00Z,0.0,0.0,-0.8784,0.6805,0.002,0.1349,0.0603,0.0,0.0,0.0008,0.0,1604.4,77.01,0.0,50.69
16
+ 2026-02-18T03:30:00Z,0.0,0.0,-0.8682,0.6666,0.002,0.1363,0.0625,0.0,0.0,0.0008,0.0,1599.9,76.8,0.0,50.64
17
+ 2026-02-18T03:45:00Z,0.0,0.0,-0.8562,0.6563,0.002,0.136,0.0609,0.0,0.0,0.0008,0.0,1595.4,76.58,0.0,50.61
18
+ 2026-02-18T04:00:00Z,0.0,0.0,-0.848,0.6503,0.002,0.1343,0.0604,0.0001,0.0,0.0008,0.0,1591.0,76.37,0.0,50.56
19
+ 2026-02-18T04:15:00Z,0.0,0.0,-0.8574,0.6537,0.002,0.1347,0.0658,0.0003,0.0,0.0009,0.0,1586.6,76.15,0.0,50.69
20
+ 2026-02-18T04:30:00Z,0.0,0.0,-0.8579,0.6544,0.002,0.1351,0.0642,0.0006,0.0,0.0016,0.0,1582.1,75.94,0.0,50.68
21
+ 2026-02-18T04:45:00Z,0.0,0.0,-0.8443,0.6425,0.002,0.1359,0.06,0.0011,0.0,0.0028,0.0,1577.7,75.73,0.0,50.54
22
+ 2026-02-18T05:00:00Z,0.0,0.0,-0.8285,0.6211,0.002,0.1353,0.0635,0.002,0.0,0.0047,0.0,1573.4,75.52,0.0,50.49
23
+ 2026-02-18T05:15:00Z,0.0,0.0,-0.8561,0.6441,0.002,0.1342,0.0648,0.0034,0.0,0.0076,0.0,1568.9,75.31,0.0,50.54
24
+ 2026-02-18T05:30:00Z,0.0,0.0,-0.8796,0.6649,0.002,0.1352,0.0604,0.0058,0.0,0.0113,0.0,1564.3,75.09,0.0,50.54
25
+ 2026-02-18T05:45:00Z,0.0,0.0,-0.8912,0.6674,0.002,0.1351,0.0603,0.009,0.0,0.0175,0.0,1559.7,74.87,0.0,50.55
26
+ 2026-02-18T06:00:00Z,0.1547,0.0,-1.4081,0.6182,0.0652,0.138,0.0596,0.0137,0.6419,0.0262,0.0,1552.4,74.51,39.89,50.44
27
+ 2026-02-18T06:15:00Z,0.4883,0.0,-2.3503,0.6452,0.0852,0.135,0.0619,0.0162,1.8616,0.0334,0.0,1540.1,73.93,45.75,50.39
28
+ 2026-02-18T06:30:00Z,0.8025,0.0,-2.3551,0.6183,0.1103,0.1337,0.0612,0.0213,2.1688,0.0441,0.0,1527.9,73.34,43.41,50.44
29
+ 2026-02-18T06:45:00Z,1.1306,0.0,-2.8756,0.6027,0.1384,0.1374,0.0592,0.0264,2.9856,0.0564,0.0,1512.9,72.62,43.48,50.39
30
+ 2026-02-18T07:00:00Z,1.476,0.0,-3.2121,0.6039,0.1614,0.1363,0.0624,0.0319,3.6273,0.0649,0.0,1496.2,71.82,45.84,50.36
31
+ 2026-02-18T07:15:00Z,1.7733,0.0,-3.3301,0.5751,0.185,0.1338,0.0649,0.0325,4.039,0.0732,0.0,1478.8,70.98,46.25,50.28
32
+ 2026-02-18T07:30:00Z,2.0023,0.0,-3.6063,0.5594,0.2082,0.1349,0.0649,0.0326,4.5303,0.0784,0.0,1460.0,70.08,43.81,50.25
33
+ 2026-02-18T07:45:00Z,2.4342,0.0,-3.2063,0.5409,0.2123,0.1355,0.0592,0.03,4.5796,0.0831,0.0,1443.3,69.28,43.97,50.17
34
+ 2026-02-18T08:00:00Z,2.7277,0.0,-3.0941,0.562,0.2173,0.1379,0.0591,0.0271,4.7354,0.0829,0.0,1427.2,68.51,43.35,50.06
35
+ 2026-02-18T08:15:00Z,2.8681,0.0,-2.4137,0.5295,0.2055,0.1355,0.0572,0.0229,4.2538,0.0773,0.0,1414.6,67.9,43.83,50.15
36
+ 2026-02-18T08:30:00Z,3.0694,0.0,-2.0128,0.5341,0.19,0.1359,0.0592,0.0174,4.0722,0.0735,0.0,1404.2,67.4,43.27,50.1
37
+ 2026-02-18T08:45:00Z,3.3925,0.0,-1.306,0.5199,0.1694,0.1331,0.0654,0.0127,3.7326,0.0654,0.0,1397.4,67.07,46.74,50.08
38
+ 2026-02-18T09:00:00Z,3.5239,0.0,-0.1379,0.5072,0.1369,0.1335,0.0597,0.0093,2.7617,0.0537,0.0,1396.6,67.04,45.58,50.03
39
+ 2026-02-18T09:15:00Z,3.8021,0.0,0.5365,0.4732,0.1112,0.1346,0.0629,0.0058,2.4329,0.045,0.0,1399.4,67.17,43.25,50.01
40
+ 2026-02-18T09:30:00Z,4.0544,0.0,1.5088,0.492,0.0874,0.1349,0.0563,0.0034,1.7376,0.0338,0.0,1407.3,67.55,44.67,50.08
41
+ 2026-02-18T09:45:00Z,4.1875,0.0,2.7069,0.4918,0.0646,0.1347,0.0627,0.002,0.7005,0.0243,0.0,1421.4,68.23,44.43,50.11
42
+ 2026-02-18T10:00:00Z,4.394,0.0,3.6574,0.4761,0.0457,0.134,0.0628,0.0011,0.0,0.0171,0.0,1440.4,69.14,44.67,50.08
43
+ 2026-02-18T10:15:00Z,4.5218,0.0,3.8006,0.482,0.0313,0.1346,0.061,0.0005,0.0,0.0119,0.0,1460.2,70.09,43.98,50.11
44
+ 2026-02-18T10:30:00Z,4.6468,0.0,3.9491,0.473,0.02,0.1352,0.061,0.0003,0.0,0.0082,0.0,1480.8,71.08,44.07,50.35
45
+ 2026-02-18T10:45:00Z,4.7168,0.0,4.0487,0.458,0.0122,0.1326,0.0601,0.0001,0.0,0.0052,0.0,1501.9,72.09,43.99,50.37
46
+ 2026-02-18T11:00:00Z,4.8673,0.0,4.2057,0.4549,0.0075,0.1362,0.0599,0.0001,0.0,0.003,0.0,1523.8,73.14,42.84,50.38
47
+ 2026-02-18T11:15:00Z,5.0185,0.0,4.3544,0.4637,0.0043,0.1363,0.058,0.0,0.0,0.0017,0.0,1546.5,74.23,44.75,50.35
48
+ 2026-02-18T11:30:00Z,5.0904,0.0,4.4412,0.4503,0.0024,0.1328,0.0627,0.0,0.0,0.001,0.0,1569.6,75.34,41.87,50.44
49
+ 2026-02-18T11:45:00Z,4.9529,0.0,4.2942,0.4593,0.0021,0.1351,0.0612,0.0,0.0,0.0008,0.0,1592.0,76.41,45.41,50.49
50
+ 2026-02-18T12:00:00Z,4.9252,0.0,4.2666,0.4564,0.0021,0.1348,0.0644,0.0,0.0,0.0008,0.0,1614.2,77.48,43.79,50.73
51
+ 2026-02-18T12:15:00Z,4.9515,0.0,4.2858,0.4651,0.0022,0.1348,0.0627,0.0,0.0,0.0009,0.0,1636.5,78.55,43.46,50.67
52
+ 2026-02-18T12:30:00Z,4.8855,0.0,4.2247,0.4638,0.0022,0.1357,0.0582,0.0,0.0,0.0008,0.0,1658.5,79.61,41.75,50.79
53
+ 2026-02-18T12:45:00Z,4.7808,0.0,4.1235,0.4556,0.0021,0.1357,0.063,0.0,0.0,0.0009,0.0,1680.0,80.64,45.85,50.77
54
+ 2026-02-18T13:00:00Z,4.7031,0.0,4.0384,0.4628,0.0022,0.1362,0.0626,0.0,0.0,0.0008,0.0,1701.0,81.65,41.81,50.87
55
+ 2026-02-18T13:15:00Z,4.6808,0.0,4.016,0.4676,0.0021,0.1355,0.0587,0.0,0.0,0.0008,0.0,1721.9,82.65,44.11,51.04
56
+ 2026-02-18T13:30:00Z,4.5547,0.0,3.9081,0.4543,0.0022,0.1339,0.0554,0.0,0.0,0.0009,0.0,1742.3,83.63,42.06,50.95
57
+ 2026-02-18T13:45:00Z,4.3837,0.0,3.7111,0.4709,0.0022,0.1358,0.0629,0.0,0.0,0.0008,0.0,1761.6,84.56,45.29,51.04
58
+ 2026-02-18T14:00:00Z,4.2822,0.0,3.5967,0.4866,0.0021,0.1355,0.0605,0.0,0.0,0.0008,0.0,1780.4,85.46,42.67,51.09
59
+ 2026-02-18T14:15:00Z,4.1572,0.0,3.4622,0.4924,0.0022,0.1362,0.0634,0.0,0.0,0.0008,0.0,1798.4,86.32,43.85,51.17
60
+ 2026-02-18T14:30:00Z,3.8167,0.0,3.1125,0.505,0.0022,0.1348,0.0614,0.0,0.0,0.0008,0.0,1814.6,87.1,43.29,51.18
61
+ 2026-02-18T14:45:00Z,3.7577,0.0,3.058,0.4962,0.0022,0.1375,0.063,0.0,0.0,0.0008,0.0,1830.5,87.87,42.67,51.3
62
+ 2026-02-18T15:00:00Z,3.3135,0.0,2.5952,0.5215,0.0025,0.1349,0.0585,0.0,0.0,0.001,0.0,1844.0,88.51,43.62,51.29
63
+ 2026-02-18T15:15:00Z,3.1505,0.0,2.4161,0.5342,0.004,0.1335,0.0611,0.0,0.0,0.0015,0.0,1856.6,89.12,44.39,51.24
64
+ 2026-02-18T15:30:00Z,2.9524,0.0,2.2083,0.5366,0.006,0.1354,0.0637,0.0001,0.0,0.0023,0.0,1868.1,89.67,42.43,51.4
65
+ 2026-02-18T15:45:00Z,2.6099,0.0,1.8451,0.5606,0.0091,0.1353,0.0562,0.0001,0.0,0.0035,0.0,1877.7,90.13,43.51,51.34
66
+ 2026-02-18T16:00:00Z,2.3811,0.0,1.6161,0.5499,0.0137,0.1355,0.0603,0.0003,0.0,0.0053,0.0,1886.2,90.54,44.5,51.41
67
+ 2026-02-18T16:15:00Z,2.0176,0.0,1.2219,0.5742,0.0196,0.1316,0.0619,0.0007,0.0,0.0077,0.0,1892.5,90.84,44.67,51.4
68
+ 2026-02-18T16:30:00Z,1.7913,0.0,0.9673,0.5878,0.0277,0.1346,0.0618,0.0012,0.0,0.0109,0.0,1897.6,91.08,41.83,51.52
69
+ 2026-02-18T16:45:00Z,1.47,0.0,0.6377,0.5815,0.0367,0.1355,0.062,0.002,0.0,0.0146,0.0,1900.9,91.24,44.65,51.45
70
+ 2026-02-18T17:00:00Z,1.1324,0.0,0.2636,0.6012,0.0501,0.1354,0.0604,0.0034,0.0,0.0185,0.0,1902.3,91.31,44.43,51.53
71
+ 2026-02-18T17:15:00Z,0.836,0.0,-0.7035,0.6164,0.0631,0.136,0.0649,0.0052,0.63,0.0239,0.0,1898.6,91.13,44.54,51.46
72
+ 2026-02-18T17:30:00Z,0.5021,0.0,-2.0536,0.6296,0.0777,0.1361,0.0619,0.0079,1.6125,0.03,0.0,1887.9,90.62,44.88,51.52
73
+ 2026-02-18T17:45:00Z,0.1751,0.0,-2.6996,0.6293,0.0954,0.1364,0.0679,0.0107,1.8983,0.0368,0.0,1873.8,89.94,43.13,51.39
74
+ 2026-02-18T18:00:00Z,0.0,0.0,-3.4552,0.6313,0.114,0.1362,0.0632,0.0133,2.4542,0.0429,0.0,1855.8,89.08,0.0,51.3
75
+ 2026-02-18T18:15:00Z,0.0,0.0,-3.6425,0.6472,0.1308,0.134,0.0603,0.017,2.6014,0.0516,0.0,1836.9,88.17,0.0,51.31
76
+ 2026-02-18T18:30:00Z,0.0,0.0,-4.3965,0.6656,0.1447,0.1356,0.0597,0.0182,3.3159,0.0568,0.0,1814.0,87.07,0.0,51.3
77
+ 2026-02-18T18:45:00Z,0.0,0.0,-4.5807,0.6603,0.1575,0.1366,0.0599,0.0187,3.4854,0.0624,0.0,1790.1,85.93,0.0,51.21
78
+ 2026-02-18T19:00:00Z,0.0,0.0,-4.5989,0.6562,0.1669,0.135,0.0618,0.0194,3.4926,0.0671,0.0,1766.2,84.78,0.0,51.13
79
+ 2026-02-18T19:15:00Z,0.0,0.0,-4.7505,0.6527,0.1733,0.1386,0.0652,0.0182,3.6374,0.0651,0.0,1741.4,83.59,0.0,51.03
80
+ 2026-02-18T19:30:00Z,0.0,0.0,-4.6486,0.6344,0.1702,0.1346,0.062,0.0169,3.5598,0.0708,0.0,1717.2,82.43,0.0,51.0
81
+ 2026-02-18T19:45:00Z,0.0,0.0,-4.3798,0.6494,0.1648,0.1376,0.0578,0.0133,3.2914,0.0655,0.0,1694.4,81.33,0.0,50.94
82
+ 2026-02-18T20:00:00Z,0.0,0.0,-4.3565,0.6827,0.1601,0.1351,0.0634,0.0102,3.2427,0.0623,0.0,1671.7,80.24,0.0,50.86
83
+ 2026-02-18T20:15:00Z,0.0,0.0,-4.1691,0.6546,0.1491,0.1355,0.0588,0.0079,3.1063,0.0569,0.0,1650.0,79.2,0.0,50.82
84
+ 2026-02-18T20:30:00Z,0.0,0.0,-3.7595,0.6522,0.1304,0.1358,0.065,0.0054,2.7178,0.0528,0.0,1630.4,78.26,0.0,50.73
85
+ 2026-02-18T20:45:00Z,0.0,0.0,-3.3481,0.6422,0.1137,0.1328,0.0649,0.0035,2.3489,0.0421,0.0,1613.0,77.42,0.0,50.66
86
+ 2026-02-18T21:00:00Z,0.0,0.0,-2.8641,0.667,0.0956,0.136,0.0642,0.002,1.863,0.0364,0.0,1598.0,76.71,0.0,50.7
87
+ 2026-02-18T21:15:00Z,0.0,0.0,-2.6264,0.6514,0.0794,0.1352,0.0584,0.0011,1.6704,0.0304,0.0,1584.4,76.05,0.0,50.64
88
+ 2026-02-18T21:30:00Z,0.0,0.0,-1.6831,0.6542,0.0652,0.1376,0.0641,0.0007,0.7361,0.0252,0.0,1575.6,75.63,0.0,50.46
89
+ 2026-02-18T21:45:00Z,0.0,0.0,-0.9166,0.6499,0.0505,0.1335,0.0628,0.0004,0.0,0.0196,0.0,1570.8,75.4,0.0,50.57
90
+ 2026-02-18T22:00:00Z,0.0,0.0,-0.9243,0.6784,0.0377,0.1358,0.0572,0.0002,0.0,0.0151,0.0,1566.0,75.17,0.0,50.52
91
+ 2026-02-18T22:15:00Z,0.0,0.0,-0.8679,0.6286,0.0281,0.136,0.064,0.0001,0.0,0.0111,0.0,1561.5,74.95,0.0,50.48
92
+ 2026-02-18T22:30:00Z,0.0,0.0,-0.872,0.6489,0.0203,0.137,0.0579,0.0,0.0,0.0079,0.0,1557.0,74.73,0.0,50.47
93
+ 2026-02-18T22:45:00Z,0.0,0.0,-0.8408,0.6286,0.0139,0.1336,0.0594,0.0,0.0,0.0053,0.0,1552.6,74.52,0.0,50.5
94
+ 2026-02-18T23:00:00Z,0.0,0.0,-0.8581,0.6504,0.0026,0.1348,0.0668,0.0,0.0,0.0035,0.0,1548.1,74.31,0.0,50.42
95
+ 2026-02-18T23:15:00Z,0.0,0.0,-0.86,0.6595,0.002,0.1339,0.0621,0.0,0.0,0.0025,0.0,1543.6,74.09,0.0,50.4
96
+ 2026-02-18T23:30:00Z,0.0,0.0,-0.8734,0.6718,0.002,0.1362,0.0618,0.0,0.0,0.0016,0.0,1539.1,73.88,0.0,50.43
97
+ 2026-02-18T23:45:00Z,0.0,0.0,-0.8557,0.659,0.002,0.1363,0.0574,0.0,0.0,0.001,0.0,1534.6,73.66,0.0,50.41
98
+ 2026-02-19T00:00:00Z,0.0,0.0,-0.853,0.6484,0.002,0.1368,0.0649,0.0,0.0,0.0009,0.0,1530.2,73.45,0.0,50.4
99
+ 2026-02-19T00:15:00Z,0.0,0.0,-0.8754,0.6726,0.002,0.1382,0.0618,0.0,0.0,0.0008,0.0,1525.6,73.23,0.0,50.4
100
+ 2026-02-19T00:30:00Z,0.0,0.0,-0.8626,0.6603,0.002,0.1359,0.0636,0.0,0.0,0.0008,0.0,1521.1,73.01,0.0,50.41
101
+ 2026-02-19T00:45:00Z,0.0,0.0,-0.8665,0.6613,0.002,0.1359,0.0665,0.0,0.0,0.0008,0.0,1516.6,72.8,0.0,50.4
102
+ 2026-02-19T01:00:00Z,0.0,0.0,-0.846,0.6454,0.002,0.1361,0.0617,0.0,0.0,0.0008,0.0,1512.2,72.59,0.0,50.3
103
+ 2026-02-19T01:15:00Z,0.0,0.0,-0.8622,0.6634,0.002,0.1348,0.061,0.0,0.0,0.0009,0.0,1507.7,72.37,0.0,50.37
104
+ 2026-02-19T01:30:00Z,0.0,0.0,-0.8342,0.6305,0.002,0.1351,0.0657,0.0,0.0,0.0009,0.0,1503.4,72.16,0.0,50.27
105
+ 2026-02-19T01:45:00Z,0.0,0.0,-0.8356,0.6393,0.002,0.1355,0.0579,0.0,0.0,0.0008,0.0,1499.0,71.95,0.0,50.31
106
+ 2026-02-19T02:00:00Z,0.0,0.0,-0.8687,0.6669,0.002,0.1351,0.0638,0.0,0.0,0.0009,0.0,1494.5,71.74,0.0,50.37
107
+ 2026-02-19T02:15:00Z,0.0,0.0,-0.8779,0.678,0.002,0.1368,0.0603,0.0,0.0,0.0008,0.0,1489.9,71.52,0.0,50.18
108
+ 2026-02-19T02:30:00Z,0.0,0.0,-0.8225,0.6243,0.002,0.1325,0.0628,0.0,0.0,0.0008,0.0,1485.6,71.31,0.0,50.29
109
+ 2026-02-19T02:45:00Z,0.0,0.0,-0.8674,0.6624,0.002,0.1366,0.0656,0.0,0.0,0.0009,0.0,1481.1,71.09,0.0,50.35
110
+ 2026-02-19T03:00:00Z,0.0,0.0,-0.83,0.63,0.002,0.1351,0.0621,0.0,0.0,0.0008,0.0,1476.8,70.89,0.0,50.29
111
+ 2026-02-19T03:15:00Z,0.0,0.0,-0.8377,0.633,0.002,0.1362,0.0656,0.0,0.0,0.0008,0.0,1472.4,70.68,0.0,50.2
112
+ 2026-02-19T03:30:00Z,0.0,0.0,-0.8456,0.6488,0.002,0.1327,0.0613,0.0,0.0,0.0009,0.0,1468.0,70.47,0.0,50.31
113
+ 2026-02-19T03:45:00Z,0.0,0.0,-0.8576,0.656,0.002,0.1316,0.0671,0.0,0.0,0.0008,0.0,1463.6,70.25,0.0,50.19
114
+ 2026-02-19T04:00:00Z,0.0,0.0,-0.8808,0.6729,0.002,0.1366,0.0684,0.0001,0.0,0.0008,0.0,1459.0,70.03,0.0,50.19
115
+ 2026-02-19T04:15:00Z,0.0,0.0,-0.8554,0.655,0.002,0.1369,0.0603,0.0003,0.0,0.0009,0.0,1454.5,69.82,0.0,50.26
116
+ 2026-02-19T04:30:00Z,0.0,0.0,-0.8755,0.6702,0.002,0.1365,0.0647,0.0005,0.0,0.0016,0.0,1450.0,69.6,0.0,50.15
117
+ 2026-02-19T04:45:00Z,0.0,0.0,-0.8527,0.6513,0.002,0.1367,0.0589,0.001,0.0,0.0027,0.0,1445.5,69.38,0.0,50.22
118
+ 2026-02-19T05:00:00Z,0.0,0.0,-0.8641,0.656,0.002,0.1363,0.0634,0.0019,0.0,0.0046,0.0,1441.0,69.17,0.0,50.14
119
+ 2026-02-19T05:15:00Z,0.0,0.0,-0.8655,0.6566,0.002,0.1345,0.0615,0.0035,0.0,0.0073,0.0,1436.5,68.95,0.0,50.12
120
+ 2026-02-19T05:30:00Z,0.0,0.0,-0.8753,0.6648,0.002,0.1346,0.0574,0.0053,0.0,0.0113,0.0,1432.0,68.73,0.0,50.15
121
+ 2026-02-19T05:45:00Z,0.0,0.0,-0.8667,0.642,0.002,0.1379,0.0596,0.0084,0.0,0.0168,0.0,1427.4,68.52,0.0,50.11
122
+ 2026-02-19T06:00:00Z,0.151,0.0,-1.2821,0.6498,0.0634,0.1362,0.0578,0.0134,0.4868,0.0256,0.0,1420.8,68.2,41.37,50.07
123
+ 2026-02-19T06:15:00Z,0.4775,0.0,-2.4468,0.638,0.0868,0.1351,0.0655,0.0176,1.9487,0.0326,0.0,1408.0,67.58,43.63,50.05
124
+ 2026-02-19T06:30:00Z,0.7803,0.0,-2.6627,0.6168,0.1113,0.1366,0.061,0.0213,2.4523,0.0437,0.0,1394.1,66.92,44.13,49.97
125
+ 2026-02-19T06:45:00Z,1.1081,0.0,-3.018,0.5831,0.1376,0.1357,0.0639,0.0285,3.1206,0.0567,0.0,1378.4,66.16,43.37,50.03
126
+ 2026-02-19T07:00:00Z,1.4704,0.0,-2.8918,0.5869,0.1675,0.1345,0.0602,0.0309,3.3171,0.0649,0.0,1363.4,65.44,44.73,50.01
127
+ 2026-02-19T07:15:00Z,1.7488,0.0,-3.4629,0.5879,0.1853,0.138,0.0656,0.0327,4.1281,0.0742,0.0,1345.3,64.58,43.93,49.83
128
+ 2026-02-19T07:30:00Z,2.0456,0.0,-3.4529,0.5864,0.2046,0.1345,0.0646,0.0341,4.3971,0.0772,0.0,1327.3,63.71,45.83,49.79
129
+ 2026-02-19T07:45:00Z,2.3915,0.0,-3.4549,0.5637,0.2117,0.1354,0.057,0.0324,4.7636,0.0828,0.0,1309.4,62.85,45.95,49.81
130
+ 2026-02-19T08:00:00Z,2.6762,0.0,-2.7634,0.5642,0.2138,0.1335,0.0564,0.0282,4.359,0.0845,0.0,1295.0,62.16,43.59,49.79
131
+ 2026-02-19T08:15:00Z,2.9406,0.0,-2.4264,0.5285,0.2009,0.1363,0.0602,0.022,4.3391,0.0799,0.0,1282.3,61.55,43.58,49.69
132
+ 2026-02-19T08:30:00Z,3.1321,0.0,-1.7445,0.5518,0.1848,0.1337,0.06,0.0186,3.8541,0.0736,0.0,1273.2,61.12,44.49,49.58
133
+ 2026-02-19T08:45:00Z,3.342,0.0,-1.2096,0.5121,0.1629,0.1345,0.0619,0.0125,3.6042,0.0634,0.0,1266.9,60.81,42.97,49.54
134
+ 2026-02-19T09:00:00Z,3.537,0.0,-0.3748,0.5034,0.1419,0.1363,0.0595,0.0087,3.01,0.052,0.0,1265.0,60.72,44.05,49.68
135
+ 2026-02-19T09:15:00Z,3.9344,0.0,0.7336,0.4912,0.1119,0.1352,0.0646,0.0056,2.347,0.0454,0.0,1268.8,60.9,45.27,49.49
136
+ 2026-02-19T09:30:00Z,4.1323,0.0,1.4221,0.4931,0.0878,0.1374,0.0598,0.0036,1.8946,0.0339,0.0,1276.2,61.26,45.26,49.71
137
+ 2026-02-19T09:45:00Z,4.1663,0.0,2.6938,0.4994,0.0661,0.1376,0.0616,0.002,0.6813,0.0244,0.0,1290.2,61.93,43.17,49.67
138
+ 2026-02-19T10:00:00Z,4.4393,0.0,3.7015,0.4711,0.0459,0.1389,0.0623,0.0012,0.0,0.0184,0.0,1309.5,62.86,44.59,49.77
139
+ 2026-02-19T10:15:00Z,4.5702,0.0,3.8532,0.4764,0.0305,0.1358,0.0617,0.0005,0.0,0.0122,0.0,1329.6,63.82,43.44,49.88
140
+ 2026-02-19T10:30:00Z,4.6637,0.0,3.954,0.486,0.0202,0.1352,0.0601,0.0003,0.0,0.008,0.0,1350.2,64.81,45.92,49.86
141
+ 2026-02-19T10:45:00Z,4.8317,0.0,4.145,0.4698,0.0122,0.1326,0.0669,0.0001,0.0,0.0051,0.0,1371.8,65.85,44.78,49.9
142
+ 2026-02-19T11:00:00Z,4.8247,0.0,4.1506,0.4696,0.0074,0.1367,0.0575,0.0,0.0,0.0028,0.0,1393.4,66.88,44.1,50.0
143
+ 2026-02-19T11:15:00Z,4.7725,0.0,4.1173,0.4532,0.0043,0.1348,0.0613,0.0,0.0,0.0017,0.0,1414.8,67.91,43.47,50.16
144
+ 2026-02-19T11:30:00Z,4.8855,0.0,4.2361,0.4523,0.0025,0.1333,0.0602,0.0,0.0,0.001,0.0,1436.9,68.97,40.56,50.14
145
+ 2026-02-19T11:45:00Z,4.867,0.0,4.2149,0.4555,0.0022,0.1367,0.0569,0.0,0.0,0.0008,0.0,1458.9,70.02,46.41,50.25
146
+ 2026-02-19T12:00:00Z,5.0259,0.0,4.3716,0.4532,0.0022,0.1366,0.0616,0.0,0.0,0.0009,0.0,1481.6,71.12,44.86,50.32
147
+ 2026-02-19T12:15:00Z,5.0283,0.0,4.3763,0.4516,0.0022,0.1343,0.0631,0.0,0.0,0.0008,0.0,1504.4,72.21,43.05,50.17
148
+ 2026-02-19T12:30:00Z,4.912,0.0,4.2739,0.4419,0.0022,0.1351,0.058,0.0,0.0,0.0009,0.0,1526.7,73.28,42.49,50.32
149
+ 2026-02-19T12:45:00Z,4.9445,0.0,4.2892,0.4554,0.0021,0.1346,0.0623,0.0,0.0,0.0009,0.0,1549.0,74.35,41.94,50.43
150
+ 2026-02-19T13:00:00Z,4.7993,0.0,4.1451,0.4573,0.0022,0.1341,0.0598,0.0,0.0,0.0009,0.0,1570.6,75.39,44.35,50.49
151
+ 2026-02-19T13:15:00Z,4.6388,0.0,3.9683,0.4684,0.0022,0.135,0.0642,0.0,0.0,0.0008,0.0,1591.3,76.38,46.4,50.62
152
+ 2026-02-19T13:30:00Z,4.4508,0.0,3.7796,0.4737,0.0022,0.1369,0.0576,0.0,0.0,0.0008,0.0,1611.0,77.33,43.07,50.52
153
+ 2026-02-19T13:45:00Z,4.4633,0.0,3.7787,0.482,0.0022,0.1335,0.0662,0.0,0.0,0.0009,0.0,1630.6,78.27,42.25,50.65
154
+ 2026-02-19T14:00:00Z,4.2152,0.0,3.537,0.4763,0.0022,0.1356,0.0634,0.0,0.0,0.0008,0.0,1649.1,79.15,45.06,50.72
155
+ 2026-02-19T14:15:00Z,4.0671,0.0,3.3705,0.4989,0.0021,0.1325,0.0622,0.0,0.0,0.0008,0.0,1666.6,80.0,44.15,50.78
156
+ 2026-02-19T14:30:00Z,3.8031,0.0,3.0939,0.5078,0.0021,0.1359,0.0626,0.0,0.0,0.0008,0.0,1682.7,80.77,42.32,50.85
157
+ 2026-02-19T14:45:00Z,3.629,0.0,2.921,0.5112,0.0021,0.1338,0.06,0.0,0.0,0.0008,0.0,1697.9,81.5,42.59,50.87
158
+ 2026-02-19T15:00:00Z,3.4211,0.0,2.7079,0.5152,0.0026,0.1362,0.0583,0.0,0.0,0.001,0.0,1712.0,82.18,43.21,50.9
159
+ 2026-02-19T15:15:00Z,3.1851,0.0,2.4619,0.5244,0.0039,0.1347,0.0587,0.0,0.0,0.0015,0.0,1724.9,82.79,44.86,50.91
160
+ 2026-02-19T15:30:00Z,2.8646,0.0,2.1215,0.5357,0.0061,0.1346,0.0641,0.0001,0.0,0.0025,0.0,1735.9,83.32,44.33,50.93
161
+ 2026-02-19T15:45:00Z,2.6475,0.0,1.9141,0.5291,0.0093,0.1341,0.0573,0.0001,0.0,0.0036,0.0,1745.9,83.8,43.93,51.03
162
+ 2026-02-19T16:00:00Z,2.4057,0.0,1.6514,0.541,0.0134,0.1375,0.0569,0.0003,0.0,0.0053,0.0,1754.5,84.22,43.79,51.02
163
+ 2026-02-19T16:15:00Z,1.998,0.0,1.2166,0.5593,0.0198,0.135,0.059,0.0006,0.0,0.0078,0.0,1760.8,84.52,45.74,51.02
164
+ 2026-02-19T16:30:00Z,1.7708,0.0,0.961,0.5715,0.0278,0.1358,0.0628,0.0011,0.0,0.0109,0.0,1765.8,84.76,43.04,51.06
165
+ 2026-02-19T16:45:00Z,1.4335,0.0,0.574,0.6071,0.0373,0.1387,0.0603,0.0021,0.0,0.014,0.0,1768.8,84.9,44.73,51.09
166
+ 2026-02-19T17:00:00Z,1.1409,0.0,0.2828,0.5912,0.0483,0.1356,0.0608,0.0033,0.0,0.0188,0.0,1770.3,84.97,43.15,51.18
167
+ 2026-02-19T17:15:00Z,0.8126,0.0,-0.6251,0.6215,0.0641,0.1355,0.0603,0.0048,0.5263,0.0253,0.0,1767.0,84.82,44.37,51.03
168
+ 2026-02-19T17:30:00Z,0.5027,0.0,-2.1382,0.6356,0.079,0.1369,0.0614,0.0077,1.6912,0.029,0.0,1755.9,84.28,44.4,51.09
169
+ 2026-02-19T17:45:00Z,0.1801,0.0,-2.9472,0.6357,0.0979,0.1372,0.0626,0.0103,2.1469,0.0368,0.0,1740.5,83.55,43.03,51.12
170
+ 2026-02-19T18:00:00Z,0.0,0.0,-3.464,0.6688,0.1148,0.1372,0.057,0.0135,2.4284,0.0443,0.0,1722.5,82.68,0.0,50.92
171
+ 2026-02-19T18:15:00Z,0.0,0.0,-3.8771,0.646,0.1275,0.1348,0.0619,0.0166,2.8402,0.05,0.0,1702.3,81.71,0.0,50.9
172
+ 2026-02-19T18:30:00Z,0.0,0.0,-3.9866,0.6461,0.1467,0.1344,0.0642,0.0184,2.9194,0.0574,0.0,1681.5,80.71,0.0,50.86
173
+ 2026-02-19T18:45:00Z,0.0,0.0,-4.5255,0.6573,0.1578,0.1342,0.0609,0.0201,3.4354,0.0598,0.0,1658.0,79.58,0.0,50.68
174
+ 2026-02-19T19:00:00Z,0.0,0.0,-4.4537,0.6799,0.1673,0.1352,0.061,0.0201,3.3248,0.0655,0.0,1634.8,78.47,0.0,50.78
175
+ 2026-02-19T19:15:00Z,0.0,0.0,-4.8467,0.6524,0.1732,0.1355,0.0638,0.0176,3.7381,0.0661,0.0,1609.5,77.26,0.0,50.67
176
+ 2026-02-19T19:30:00Z,0.0,0.0,-5.0059,0.6548,0.1725,0.1376,0.0627,0.0154,3.8971,0.0659,0.0,1583.5,76.01,0.0,50.53
177
+ 2026-02-19T19:45:00Z,0.0,0.0,-4.5128,0.6535,0.1715,0.1371,0.0629,0.0137,3.407,0.0671,0.0,1560.0,74.88,0.0,50.5
178
+ 2026-02-19T20:00:00Z,0.0,0.0,-4.1913,0.6576,0.1577,0.1354,0.0609,0.0106,3.1104,0.0587,0.0,1538.1,73.83,0.0,50.5
179
+ 2026-02-19T20:15:00Z,0.0,0.0,-4.3798,0.6311,0.1456,0.1348,0.0645,0.008,3.3406,0.0552,0.0,1515.3,72.74,0.0,50.47
180
+ 2026-02-19T20:30:00Z,0.0,0.0,-3.6585,0.6517,0.1289,0.1343,0.0622,0.0053,2.6246,0.0515,0.0,1496.3,71.82,0.0,50.27
181
+ 2026-02-19T20:45:00Z,0.0,0.0,-3.5839,0.6436,0.1128,0.1338,0.0603,0.0033,2.5876,0.0426,0.0,1477.6,70.92,0.0,50.32
182
+ 2026-02-19T21:00:00Z,0.0,0.0,-2.9842,0.6536,0.0973,0.1356,0.0636,0.002,1.9956,0.0365,0.0,1462.1,70.18,0.0,50.22
183
+ 2026-02-19T21:15:00Z,0.0,0.0,-2.6737,0.6471,0.0798,0.1358,0.0629,0.0012,1.7157,0.0312,0.0,1448.1,69.51,0.0,50.2
184
+ 2026-02-19T21:30:00Z,0.0,0.0,-1.5066,0.6371,0.0657,0.1341,0.069,0.0006,0.5752,0.025,0.0,1440.3,69.13,0.0,50.23
185
+ 2026-02-19T21:45:00Z,0.0,0.0,-0.9008,0.6381,0.0498,0.1337,0.0601,0.0004,0.0,0.0187,0.0,1435.6,68.91,0.0,50.18
186
+ 2026-02-19T22:00:00Z,0.0,0.0,-0.9283,0.6754,0.0378,0.1359,0.064,0.0001,0.0,0.0149,0.0,1430.8,68.68,0.0,50.27
187
+ 2026-02-19T22:15:00Z,0.0,0.0,-0.9041,0.6643,0.0285,0.1361,0.0646,0.0001,0.0,0.0105,0.0,1426.1,68.45,0.0,50.11
188
+ 2026-02-19T22:30:00Z,0.0,0.0,-0.8656,0.6401,0.0198,0.1326,0.0651,0.0,0.0,0.008,0.0,1421.5,68.23,0.0,50.07
189
+ 2026-02-19T22:45:00Z,0.0,0.0,-0.8719,0.6612,0.014,0.1341,0.057,0.0,0.0,0.0056,0.0,1417.0,68.02,0.0,50.04
190
+ 2026-02-19T23:00:00Z,0.0,0.0,-0.8407,0.6349,0.0026,0.1346,0.065,0.0,0.0,0.0036,0.0,1412.6,67.81,0.0,49.96
191
+ 2026-02-19T23:15:00Z,0.0,0.0,-0.857,0.6568,0.002,0.1357,0.0601,0.0,0.0,0.0024,0.0,1408.2,67.59,0.0,50.08
192
+ 2026-02-19T23:30:00Z,0.0,0.0,-0.8693,0.669,0.002,0.1357,0.0612,0.0,0.0,0.0015,0.0,1403.6,67.37,0.0,50.07
193
+ 2026-02-19T23:45:00Z,0.0,0.0,-0.8568,0.6625,0.002,0.1345,0.0569,0.0,0.0,0.0009,0.0,1399.2,67.16,0.0,50.04
194
+ 2026-02-20T00:00:00Z,0.0,0.0,-0.8644,0.6711,0.002,0.1341,0.0563,0.0,0.0,0.0009,0.0,1394.7,66.94,0.0,50.03
195
+ 2026-02-20T00:15:00Z,0.0,0.0,-0.86,0.6576,0.002,0.1376,0.0621,0.0,0.0,0.0008,0.0,1390.2,66.73,0.0,49.96
196
+ 2026-02-20T00:30:00Z,0.0,0.0,-0.8394,0.6298,0.002,0.1361,0.0707,0.0,0.0,0.0009,0.0,1385.8,66.52,0.0,50.03
197
+ 2026-02-20T00:45:00Z,0.0,0.0,-0.8505,0.6432,0.002,0.1374,0.0671,0.0,0.0,0.0008,0.0,1381.4,66.31,0.0,50.02
198
+ 2026-02-20T01:00:00Z,0.0,0.0,-0.8738,0.6722,0.002,0.1357,0.063,0.0,0.0,0.0008,0.0,1376.8,66.09,0.0,49.97
199
+ 2026-02-20T01:15:00Z,0.0,0.0,-0.8157,0.6152,0.002,0.1342,0.0635,0.0,0.0,0.0008,0.0,1372.6,65.88,0.0,49.97
200
+ 2026-02-20T01:30:00Z,0.0,0.0,-0.8524,0.6533,0.002,0.1341,0.0621,0.0,0.0,0.0008,0.0,1368.1,65.67,0.0,49.91
201
+ 2026-02-20T01:45:00Z,0.0,0.0,-0.8368,0.6396,0.002,0.1353,0.0591,0.0,0.0,0.0008,0.0,1363.8,65.46,0.0,49.91
202
+ 2026-02-20T02:00:00Z,0.0,0.0,-0.8412,0.6442,0.002,0.1346,0.0595,0.0,0.0,0.0008,0.0,1359.4,65.25,0.0,49.95
203
+ 2026-02-20T02:15:00Z,0.0,0.0,-0.8453,0.6436,0.002,0.1352,0.0637,0.0,0.0,0.0008,0.0,1355.0,65.04,0.0,49.94
204
+ 2026-02-20T02:30:00Z,0.0,0.0,-0.8495,0.6498,0.002,0.1354,0.0615,0.0,0.0,0.0008,0.0,1350.6,64.83,0.0,49.86
205
+ 2026-02-20T02:45:00Z,0.0,0.0,-0.8541,0.6582,0.002,0.1352,0.0579,0.0,0.0,0.0009,0.0,1346.1,64.61,0.0,49.95
206
+ 2026-02-20T03:00:00Z,0.0,0.0,-0.8298,0.6329,0.002,0.1327,0.0614,0.0,0.0,0.0009,0.0,1341.8,64.41,0.0,49.83
207
+ 2026-02-20T03:15:00Z,0.0,0.0,-0.8653,0.6606,0.002,0.1366,0.0652,0.0,0.0,0.0009,0.0,1337.3,64.19,0.0,49.9
208
+ 2026-02-20T03:30:00Z,0.0,0.0,-0.8297,0.6302,0.002,0.134,0.0626,0.0,0.0,0.0008,0.0,1333.0,63.98,0.0,49.84
209
+ 2026-02-20T03:45:00Z,0.0,0.0,-0.8444,0.6429,0.002,0.1335,0.065,0.0,0.0,0.0009,0.0,1328.6,63.77,0.0,49.89
210
+ 2026-02-20T04:00:00Z,0.0,0.0,-0.8558,0.6562,0.002,0.1359,0.0608,0.0001,0.0,0.0008,0.0,1324.1,63.56,0.0,49.81
211
+ 2026-02-20T04:15:00Z,0.0,0.0,-0.8199,0.6192,0.002,0.1348,0.0627,0.0003,0.0,0.0009,0.0,1319.9,63.35,0.0,49.85
212
+ 2026-02-20T04:30:00Z,0.0,0.0,-0.8429,0.6411,0.002,0.1355,0.0622,0.0005,0.0,0.0016,0.0,1315.5,63.14,0.0,49.7
213
+ 2026-02-20T04:45:00Z,0.0,0.0,-0.8634,0.6617,0.002,0.1341,0.0617,0.001,0.0,0.0029,0.0,1311.0,62.93,0.0,49.78
214
+ 2026-02-20T05:00:00Z,0.0,0.0,-0.8395,0.6395,0.002,0.132,0.0596,0.002,0.0,0.0045,0.0,1306.6,62.72,0.0,49.74
215
+ 2026-02-20T05:15:00Z,0.0,0.0,-0.8714,0.6576,0.002,0.1363,0.0642,0.0036,0.0,0.0078,0.0,1302.1,62.5,0.0,49.67
216
+ 2026-02-20T05:30:00Z,0.0,0.0,-0.859,0.6463,0.002,0.1361,0.0581,0.0053,0.0,0.0112,0.0,1297.6,62.28,0.0,49.78
217
+ 2026-02-20T05:45:00Z,0.0,0.0,-0.8886,0.6666,0.002,0.1351,0.0577,0.0093,0.0,0.0179,0.0,1293.0,62.06,0.0,49.71
218
+ 2026-02-20T06:00:00Z,0.1519,0.0,-1.3372,0.6412,0.0642,0.1352,0.0631,0.0129,0.5487,0.0237,0.0,1286.0,61.73,41.86,49.69
219
+ 2026-02-20T06:15:00Z,0.4661,0.0,-2.3492,0.6538,0.086,0.1331,0.0665,0.018,1.8238,0.0341,0.0,1273.8,61.14,43.94,49.65
220
+ 2026-02-20T06:30:00Z,0.8079,0.0,-2.5474,0.614,0.1108,0.1327,0.0596,0.0224,2.3727,0.0431,0.0,1260.5,60.5,43.72,49.65
221
+ 2026-02-20T06:45:00Z,1.1612,0.0,-2.6783,0.6373,0.1383,0.1335,0.0619,0.0267,2.7895,0.0523,0.0,1246.5,59.83,42.54,49.53
222
+ 2026-02-20T07:00:00Z,1.4431,0.0,-3.0125,0.5961,0.1609,0.1345,0.0601,0.031,3.4061,0.0669,0.0,1230.8,59.08,44.15,49.45
223
+ 2026-02-20T07:15:00Z,1.6767,0.0,-3.1841,0.5912,0.185,0.137,0.0586,0.0327,3.7829,0.0734,0.0,1214.3,58.28,43.29,49.53
224
+ 2026-02-20T07:30:00Z,2.0663,0.0,-3.8276,0.5874,0.2012,0.134,0.0624,0.0311,4.7974,0.0804,0.0,1194.3,57.33,44.95,49.5
225
+ 2026-02-20T07:45:00Z,2.3645,0.0,-3.1065,0.5521,0.2144,0.1328,0.06,0.0314,4.3995,0.0808,0.0,1178.1,56.55,43.33,49.37
226
+ 2026-02-20T08:00:00Z,2.669,0.0,-2.5511,0.5639,0.2162,0.138,0.0617,0.0284,4.129,0.0829,0.0,1164.9,55.91,42.45,49.46
227
+ 2026-02-20T08:15:00Z,2.9262,0.0,-2.436,0.5114,0.1996,0.1357,0.0577,0.0223,4.3571,0.0786,0.0,1152.2,55.3,45.07,49.31
228
+ 2026-02-20T08:30:00Z,3.2404,0.0,-1.6817,0.522,0.1893,0.1352,0.0655,0.0167,3.9177,0.0756,0.0,1143.4,54.88,43.9,49.34
229
+ 2026-02-20T08:45:00Z,3.4333,0.0,-1.0655,0.5109,0.1689,0.1336,0.0637,0.0132,3.5454,0.0631,0.0,1137.9,54.62,43.49,49.24
230
+ 2026-02-20T09:00:00Z,3.5487,0.0,-0.5474,0.5153,0.1417,0.1339,0.064,0.0089,3.1772,0.055,0.0,1135.0,54.48,43.46,49.22
231
+ 2026-02-20T09:15:00Z,3.9054,0.0,0.5971,0.503,0.1149,0.135,0.0658,0.0055,2.4413,0.0428,0.0,1138.1,54.63,45.03,49.13
232
+ 2026-02-20T09:30:00Z,4.0322,0.0,1.0954,0.4976,0.0869,0.1338,0.0585,0.0035,2.1229,0.0336,0.0,1143.8,54.9,43.66,49.3
233
+ 2026-02-20T09:45:00Z,4.2096,0.0,2.7576,0.4893,0.0647,0.1348,0.0627,0.0021,0.674,0.0245,0.0,1158.2,55.59,46.59,49.32
234
+ 2026-02-20T10:00:00Z,4.4915,0.0,3.7608,0.4683,0.0448,0.134,0.0648,0.0011,0.0,0.0177,0.0,1177.8,56.53,43.26,49.43
235
+ 2026-02-20T10:15:00Z,4.4647,0.0,3.7439,0.4807,0.0316,0.1335,0.0629,0.0005,0.0,0.0116,0.0,1197.3,57.47,44.01,49.39
236
+ 2026-02-20T10:30:00Z,4.7062,0.0,4.0028,0.4785,0.0201,0.1365,0.06,0.0003,0.0,0.008,0.0,1218.1,58.47,42.87,49.59
237
+ 2026-02-20T10:45:00Z,4.8748,0.0,4.1874,0.4697,0.0129,0.1368,0.0629,0.0001,0.0,0.0051,0.0,1239.9,59.52,41.91,49.48
238
+ 2026-02-20T11:00:00Z,4.8785,0.0,4.2066,0.4676,0.0075,0.1337,0.0601,0.0,0.0,0.0029,0.0,1261.8,60.57,45.06,49.63
239
+ 2026-02-20T11:15:00Z,4.9486,0.0,4.2907,0.4574,0.0042,0.1348,0.0599,0.0,0.0,0.0017,0.0,1284.2,61.64,44.57,49.7
240
+ 2026-02-20T11:30:00Z,5.0652,0.0,4.4032,0.4597,0.0024,0.1368,0.062,0.0,0.0,0.001,0.0,1307.1,62.74,43.31,49.82
241
+ 2026-02-20T11:45:00Z,5.0904,0.0,4.4294,0.4575,0.0022,0.1353,0.0653,0.0,0.0,0.0008,0.0,1330.2,63.85,41.33,49.85
242
+ 2026-02-20T12:00:00Z,4.9286,0.0,4.2685,0.461,0.0021,0.1351,0.061,0.0,0.0,0.0008,0.0,1352.4,64.92,43.93,49.82
243
+ 2026-02-20T12:15:00Z,4.912,0.0,4.2462,0.4675,0.0022,0.1358,0.0594,0.0,0.0,0.0009,0.0,1374.5,65.98,44.32,49.97
244
+ 2026-02-20T12:30:00Z,4.8945,0.0,4.235,0.456,0.0022,0.1365,0.0639,0.0,0.0,0.0008,0.0,1396.6,67.04,42.25,50.04
245
+ 2026-02-20T12:45:00Z,4.9222,0.0,4.2501,0.4715,0.0022,0.136,0.0615,0.0,0.0,0.0008,0.0,1418.7,68.1,43.26,50.14
246
+ 2026-02-20T13:00:00Z,4.9169,0.0,4.2344,0.4782,0.0021,0.1353,0.0661,0.0,0.0,0.0008,0.0,1440.8,69.16,43.29,50.09
247
+ 2026-02-20T13:15:00Z,4.6886,0.0,4.0226,0.4687,0.0022,0.1352,0.0592,0.0,0.0,0.0009,0.0,1461.7,70.16,41.79,50.18
248
+ 2026-02-20T13:30:00Z,4.6055,0.0,3.9232,0.4864,0.0022,0.1341,0.0588,0.0,0.0,0.0008,0.0,1482.2,71.14,43.74,50.12
249
+ 2026-02-20T13:45:00Z,4.4171,0.0,3.729,0.4868,0.0022,0.1344,0.0639,0.0,0.0,0.0008,0.0,1501.6,72.08,41.35,50.2
250
+ 2026-02-20T14:00:00Z,4.1494,0.0,3.4804,0.4728,0.0021,0.1334,0.0598,0.0,0.0,0.0008,0.0,1519.7,72.95,41.69,50.42
251
+ 2026-02-20T14:15:00Z,4.0716,0.0,3.3777,0.49,0.0022,0.1361,0.0649,0.0,0.0,0.0008,0.0,1537.3,73.79,42.66,50.38
252
+ 2026-02-20T14:30:00Z,3.8596,0.0,3.1591,0.4996,0.0022,0.1342,0.0636,0.0,0.0,0.0009,0.0,1553.8,74.58,44.94,50.5
253
+ 2026-02-20T14:45:00Z,3.755,0.0,3.0402,0.5117,0.0022,0.1366,0.0635,0.0,0.0,0.0008,0.0,1569.6,75.34,42.21,50.45
254
+ 2026-02-20T15:00:00Z,3.4313,0.0,2.7122,0.5189,0.0025,0.1353,0.0613,0.0,0.0,0.001,0.0,1583.7,76.02,42.67,50.62
255
+ 2026-02-20T15:15:00Z,3.2411,0.0,2.5225,0.5196,0.0039,0.1338,0.0597,0.0,0.0,0.0016,0.0,1596.9,76.65,44.18,50.63
256
+ 2026-02-20T15:30:00Z,2.995,0.0,2.2834,0.5087,0.0062,0.1329,0.0614,0.0001,0.0,0.0023,0.0,1608.8,77.22,45.76,50.56
257
+ 2026-02-20T15:45:00Z,2.7044,0.0,1.9534,0.5455,0.0092,0.1332,0.0594,0.0001,0.0,0.0036,0.0,1618.9,77.71,44.33,50.62
258
+ 2026-02-20T16:00:00Z,2.3623,0.0,1.586,0.5623,0.0135,0.1351,0.0598,0.0003,0.0,0.0053,0.0,1627.2,78.11,46.53,50.65
259
+ 2026-02-20T16:15:00Z,2.0647,0.0,1.2828,0.5549,0.0197,0.1355,0.0635,0.0006,0.0,0.0076,0.0,1633.9,78.43,43.93,50.65
260
+ 2026-02-20T16:30:00Z,1.7901,0.0,0.9809,0.5724,0.0272,0.1358,0.062,0.0011,0.0,0.0106,0.0,1639.0,78.67,44.66,50.74
261
+ 2026-02-20T16:45:00Z,1.4698,0.0,0.626,0.6011,0.0366,0.134,0.0556,0.0021,0.0,0.0143,0.0,1642.2,78.83,45.59,50.72
262
+ 2026-02-20T17:00:00Z,1.1259,0.0,0.2403,0.6209,0.0493,0.1348,0.0586,0.0032,0.0,0.0189,0.0,1643.5,78.89,43.1,50.77
263
+ 2026-02-20T17:15:00Z,0.803,0.0,-0.6732,0.6223,0.0631,0.1359,0.06,0.0052,0.566,0.0238,0.0,1640.0,78.72,43.72,50.77
264
+ 2026-02-20T17:30:00Z,0.5024,0.0,-2.1073,0.6208,0.0793,0.1342,0.0633,0.0076,1.6733,0.0312,0.0,1629.0,78.19,42.45,50.69
265
+ 2026-02-20T17:45:00Z,0.1751,0.0,-2.7761,0.6371,0.0961,0.1326,0.0633,0.0106,1.9726,0.0388,0.0,1614.6,77.5,44.75,50.77
266
+ 2026-02-20T18:00:00Z,0.0,0.0,-3.3154,0.6437,0.113,0.1362,0.0618,0.0135,2.3025,0.0449,0.0,1597.3,76.67,0.0,50.54
267
+ 2026-02-20T18:15:00Z,0.0,0.0,-3.9251,0.654,0.1315,0.1355,0.0616,0.0152,2.8758,0.0515,0.0,1576.8,75.69,0.0,50.44
268
+ 2026-02-20T18:30:00Z,0.0,0.0,-3.8225,0.6455,0.1476,0.1341,0.062,0.0195,2.7571,0.0568,0.0,1556.9,74.73,0.0,50.55
269
+ 2026-02-20T18:45:00Z,0.0,0.0,-4.4925,0.6534,0.1587,0.134,0.0618,0.0202,3.4013,0.0631,0.0,1533.5,73.61,0.0,50.48
270
+ 2026-02-20T19:00:00Z,0.0,0.0,-4.6783,0.6243,0.1682,0.1342,0.0659,0.0198,3.6013,0.0647,0.0,1509.2,72.44,0.0,50.34
271
+ 2026-02-20T19:15:00Z,0.0,0.0,-4.6644,0.6508,0.1728,0.1366,0.0626,0.0188,3.5581,0.0648,0.0,1484.9,71.27,0.0,50.32
272
+ 2026-02-20T19:30:00Z,0.0,0.0,-5.1319,0.6616,0.175,0.136,0.0634,0.0161,4.0118,0.0681,0.0,1458.2,69.99,0.0,50.21
273
+ 2026-02-20T19:45:00Z,0.0,0.0,-5.0621,0.6559,0.1663,0.1321,0.061,0.0133,3.9674,0.0662,0.0,1431.8,68.73,0.0,50.21
274
+ 2026-02-20T20:00:00Z,0.0,0.0,-4.4078,0.6425,0.1609,0.1352,0.0663,0.0104,3.3324,0.0601,0.0,1408.8,67.62,0.0,50.02
275
+ 2026-02-20T20:15:00Z,0.0,0.0,-3.924,0.6846,0.1478,0.1366,0.0618,0.0074,2.829,0.0567,0.0,1388.4,66.64,0.0,50.03
276
+ 2026-02-20T20:30:00Z,0.0,0.0,-3.7895,0.6377,0.1304,0.1343,0.0593,0.0052,2.7712,0.0514,0.0,1368.7,65.7,0.0,49.94
277
+ 2026-02-20T20:45:00Z,0.0,0.0,-3.1214,0.6444,0.1154,0.1347,0.0656,0.0035,2.1121,0.0457,0.0,1352.4,64.92,0.0,49.83
278
+ 2026-02-20T21:00:00Z,0.0,0.0,-2.9659,0.6536,0.0983,0.1353,0.0586,0.0022,1.98,0.0379,0.0,1337.0,64.17,0.0,49.88
279
+ 2026-02-20T21:15:00Z,0.0,0.0,-2.6475,0.612,0.0795,0.1352,0.0636,0.0012,1.7258,0.0302,0.0,1323.2,63.51,0.0,49.8
280
+ 2026-02-20T21:30:00Z,0.0,0.0,-1.6224,0.6457,0.0635,0.1344,0.0659,0.0007,0.687,0.0252,0.0,1314.7,63.11,0.0,49.79
281
+ 2026-02-20T21:45:00Z,0.0,0.0,-0.9102,0.6478,0.0492,0.1348,0.0587,0.0003,0.0,0.0193,0.0,1310.0,62.88,0.0,49.71
282
+ 2026-02-20T22:00:00Z,0.0,0.0,-0.9122,0.6592,0.0379,0.137,0.0633,0.0001,0.0,0.0146,0.0,1305.2,62.65,0.0,49.73
283
+ 2026-02-20T22:15:00Z,0.0,0.0,-0.8768,0.6481,0.0278,0.1339,0.0563,0.0001,0.0,0.0107,0.0,1300.7,62.43,0.0,49.63
284
+ 2026-02-20T22:30:00Z,0.0,0.0,-0.8943,0.6652,0.0198,0.1352,0.0659,0.0,0.0,0.0082,0.0,1296.0,62.21,0.0,49.78
285
+ 2026-02-20T22:45:00Z,0.0,0.0,-0.8661,0.6484,0.0138,0.1338,0.0646,0.0,0.0,0.0055,0.0,1291.5,61.99,0.0,49.71
286
+ 2026-02-20T23:00:00Z,0.0,0.0,-0.8505,0.6462,0.0026,0.1347,0.0633,0.0,0.0,0.0037,0.0,1287.1,61.78,0.0,49.7
287
+ 2026-02-20T23:15:00Z,0.0,0.0,-0.8635,0.663,0.002,0.1379,0.0582,0.0,0.0,0.0024,0.0,1282.6,61.56,0.0,49.74
288
+ 2026-02-20T23:30:00Z,0.0,0.0,-0.8353,0.6345,0.002,0.1343,0.0629,0.0,0.0,0.0016,0.0,1278.2,61.35,0.0,49.72
289
+ 2026-02-20T23:45:00Z,0.0,0.0,-0.8472,0.6518,0.002,0.1345,0.0579,0.0,0.0,0.001,0.0,1273.8,61.14,0.0,49.64
290
+ 2026-02-21T00:00:00Z,0.0,0.0,-0.8642,0.6633,0.002,0.1359,0.0622,0.0,0.0,0.0008,0.0,1269.3,60.93,0.0,49.61
291
+ 2026-02-21T00:15:00Z,0.0,0.0,-0.8534,0.6512,0.002,0.1375,0.062,0.0,0.0,0.0008,0.0,1264.8,60.71,0.0,49.55
292
+ 2026-02-21T00:30:00Z,0.0,0.0,-0.8508,0.6498,0.002,0.1353,0.0628,0.0,0.0,0.0009,0.0,1260.4,60.5,0.0,49.55
293
+ 2026-02-21T00:45:00Z,0.0,0.0,-0.8635,0.6618,0.002,0.1348,0.0641,0.0,0.0,0.0009,0.0,1255.9,60.28,0.0,49.67
294
+ 2026-02-21T01:00:00Z,0.0,0.0,-0.8483,0.6526,0.002,0.1338,0.0591,0.0,0.0,0.0008,0.0,1251.5,60.07,0.0,49.63
295
+ 2026-02-21T01:15:00Z,0.0,0.0,-0.8571,0.6602,0.002,0.1336,0.0605,0.0,0.0,0.0008,0.0,1247.0,59.86,0.0,49.62
296
+ 2026-02-21T01:30:00Z,0.0,0.0,-0.8329,0.6365,0.002,0.1363,0.0572,0.0,0.0,0.0009,0.0,1242.7,59.65,0.0,49.63
297
+ 2026-02-21T01:45:00Z,0.0,0.0,-0.8539,0.6557,0.002,0.135,0.0604,0.0,0.0,0.0008,0.0,1238.3,59.44,0.0,49.58
298
+ 2026-02-21T02:00:00Z,0.0,0.0,-0.876,0.675,0.002,0.1337,0.0644,0.0,0.0,0.0008,0.0,1233.7,59.22,0.0,49.65
299
+ 2026-02-21T02:15:00Z,0.0,0.0,-0.8775,0.6777,0.002,0.1367,0.0602,0.0,0.0,0.0008,0.0,1229.1,59.0,0.0,49.57
300
+ 2026-02-21T02:30:00Z,0.0,0.0,-0.8532,0.6549,0.002,0.1336,0.0618,0.0,0.0,0.0008,0.0,1224.7,58.78,0.0,49.63
301
+ 2026-02-21T02:45:00Z,0.0,0.0,-0.8447,0.6459,0.002,0.1358,0.0602,0.0,0.0,0.0008,0.0,1220.3,58.57,0.0,49.64
302
+ 2026-02-21T03:00:00Z,0.0,0.0,-0.8623,0.6669,0.002,0.134,0.0584,0.0,0.0,0.0008,0.0,1215.8,58.36,0.0,49.49
303
+ 2026-02-21T03:15:00Z,0.0,0.0,-0.8327,0.6372,0.002,0.133,0.0597,0.0,0.0,0.0008,0.0,1211.4,58.15,0.0,49.55
304
+ 2026-02-21T03:30:00Z,0.0,0.0,-0.8351,0.6349,0.002,0.1347,0.0626,0.0,0.0,0.0008,0.0,1207.1,57.94,0.0,49.46
305
+ 2026-02-21T03:45:00Z,0.0,0.0,-0.8471,0.6466,0.002,0.135,0.0626,0.0,0.0,0.0008,0.0,1202.7,57.73,0.0,49.46
306
+ 2026-02-21T04:00:00Z,0.0,0.0,-0.8298,0.6248,0.002,0.133,0.0691,0.0001,0.0,0.0008,0.0,1198.4,57.52,0.0,49.49
307
+ 2026-02-21T04:15:00Z,0.0,0.0,-0.8538,0.6495,0.002,0.1356,0.0655,0.0003,0.0,0.0009,0.0,1193.9,57.31,0.0,49.44
308
+ 2026-02-21T04:30:00Z,0.0,0.0,-0.836,0.635,0.002,0.1344,0.0626,0.0005,0.0,0.0015,0.0,1189.6,57.1,0.0,49.37
309
+ 2026-02-21T04:45:00Z,0.0,0.0,-0.8497,0.6475,0.002,0.1359,0.0605,0.001,0.0,0.0028,0.0,1185.1,56.89,0.0,49.48
310
+ 2026-02-21T05:00:00Z,0.0,0.0,-0.8675,0.6638,0.002,0.1332,0.0619,0.0021,0.0,0.0046,0.0,1180.6,56.67,0.0,49.44
311
+ 2026-02-21T05:15:00Z,0.0,0.0,-0.8368,0.6266,0.002,0.1353,0.0619,0.0032,0.0,0.0077,0.0,1176.3,56.46,0.0,49.43
312
+ 2026-02-21T05:30:00Z,0.0,0.0,-0.865,0.6493,0.002,0.1348,0.0616,0.0054,0.0,0.012,0.0,1171.8,56.24,0.0,49.39
313
+ 2026-02-21T05:45:00Z,0.0,0.0,-0.8553,0.626,0.002,0.1366,0.0643,0.0084,0.0,0.0181,0.0,1167.3,56.03,0.0,49.38
314
+ 2026-02-21T06:00:00Z,0.1565,0.0,-1.2947,0.6652,0.0633,0.1338,0.0631,0.0137,0.4888,0.0233,0.0,1160.6,55.71,40.85,49.38
315
+ 2026-02-21T06:15:00Z,0.4786,0.0,-2.4206,0.6616,0.0847,0.1342,0.0609,0.0182,1.9047,0.0348,0.0,1148.0,55.1,45.81,49.35
316
+ 2026-02-21T06:30:00Z,0.7951,0.0,-2.563,0.6135,0.111,0.1327,0.0665,0.0238,2.366,0.0445,0.0,1134.6,54.46,44.79,49.37
317
+ 2026-02-21T06:45:00Z,1.118,0.0,-3.0968,0.6071,0.1385,0.133,0.0658,0.0281,3.1879,0.0545,0.0,1118.5,53.69,46.45,49.25
318
+ 2026-02-21T07:00:00Z,1.4519,0.0,-3.2223,0.5941,0.1675,0.1348,0.0628,0.0313,3.6227,0.061,0.0,1101.7,52.88,47.63,49.26
319
+ 2026-02-21T07:15:00Z,1.7258,0.0,-3.3967,0.5625,0.1957,0.1345,0.0632,0.0353,4.057,0.0743,0.0,1084.0,52.03,45.58,49.14
320
+ 2026-02-21T07:30:00Z,1.9835,0.0,-3.7702,0.5603,0.2064,0.1365,0.0613,0.0311,4.6792,0.0788,0.0,1064.4,51.09,43.9,49.16
321
+ 2026-02-21T07:45:00Z,2.3779,0.0,-3.4049,0.5614,0.2183,0.1321,0.0609,0.0321,4.694,0.084,0.0,1046.6,50.24,45.02,48.96
322
+ 2026-02-21T08:00:00Z,2.6321,0.0,-2.6799,0.5396,0.2172,0.1351,0.0609,0.0262,4.254,0.079,0.0,1032.7,49.57,43.43,49.04
323
+ 2026-02-21T08:15:00Z,2.9712,0.0,-2.6439,0.5496,0.2081,0.1341,0.065,0.0235,4.5526,0.0823,0.0,1018.9,48.91,43.09,48.95
324
+ 2026-02-21T08:30:00Z,3.1626,0.0,-1.8382,0.5238,0.1881,0.136,0.0597,0.017,4.0032,0.073,0.0,1009.3,48.45,45.51,48.83
325
+ 2026-02-21T08:45:00Z,3.4024,0.0,-1.3029,0.5381,0.166,0.131,0.0657,0.014,3.7283,0.0623,0.0,1002.5,48.12,45.51,48.96
326
+ 2026-02-21T09:00:00Z,3.5919,0.0,-0.2531,0.532,0.1394,0.1357,0.061,0.0087,2.9133,0.0549,0.0,1001.2,48.06,43.58,48.89
327
+ 2026-02-21T09:15:00Z,3.8807,0.0,0.6672,0.5077,0.1122,0.1356,0.0594,0.0061,2.3485,0.0439,0.0,1004.7,48.23,45.3,48.88
328
+ 2026-02-21T09:30:00Z,3.9537,0.0,1.376,0.4868,0.0894,0.1349,0.0605,0.0035,1.7678,0.0347,0.0,1011.9,48.57,44.65,48.89
329
+ 2026-02-21T09:45:00Z,4.2681,0.0,2.8384,0.4934,0.0636,0.1364,0.0619,0.0021,0.6482,0.0241,0.0,1026.6,49.28,43.77,48.98
330
+ 2026-02-21T10:00:00Z,4.423,0.0,3.6803,0.4813,0.0459,0.1357,0.0608,0.0011,0.0,0.0178,0.0,1045.8,50.2,41.99,49.03
331
+ 2026-02-21T10:15:00Z,4.5404,0.0,3.8357,0.4684,0.0312,0.133,0.0591,0.0005,0.0,0.0123,0.0,1065.8,51.16,41.99,49.07
332
+ 2026-02-21T10:30:00Z,4.6114,0.0,3.9176,0.4673,0.0206,0.136,0.0616,0.0003,0.0,0.008,0.0,1086.2,52.14,42.31,49.09
333
+ 2026-02-21T10:45:00Z,4.9017,0.0,4.2245,0.4578,0.0124,0.1379,0.0641,0.0001,0.0,0.0048,0.0,1108.2,53.19,43.26,49.18
334
+ 2026-02-21T11:00:00Z,4.7646,0.0,4.0894,0.4652,0.0075,0.1382,0.0614,0.0,0.0,0.0029,0.0,1129.5,54.22,41.83,49.19
335
+ 2026-02-21T11:15:00Z,5.0468,0.0,4.4052,0.4398,0.0043,0.1354,0.0604,0.0,0.0,0.0017,0.0,1152.4,55.32,44.19,49.31
336
+ 2026-02-21T11:30:00Z,5.0098,0.0,4.3405,0.4648,0.0025,0.1383,0.0629,0.0,0.0,0.001,0.0,1175.1,56.4,42.86,49.25
337
+ 2026-02-21T11:45:00Z,5.0667,0.0,4.4202,0.447,0.0022,0.1364,0.0601,0.0,0.0,0.0008,0.0,1198.1,57.51,44.75,49.37
338
+ 2026-02-21T12:00:00Z,4.9246,0.0,4.2746,0.4542,0.0022,0.1355,0.0572,0.0,0.0,0.0009,0.0,1220.3,58.58,42.34,49.4
339
+ 2026-02-21T12:15:00Z,4.9728,0.0,4.325,0.4534,0.0022,0.1316,0.0598,0.0,0.0,0.0008,0.0,1242.9,59.66,42.73,49.54
340
+ 2026-02-21T12:30:00Z,5.0541,0.0,4.4038,0.4479,0.0022,0.1374,0.062,0.0,0.0,0.0008,0.0,1265.8,60.76,43.7,49.59
341
+ 2026-02-21T12:45:00Z,4.9293,0.0,4.28,0.4502,0.0022,0.1352,0.0609,0.0,0.0,0.0008,0.0,1288.1,61.83,44.42,49.68
342
+ 2026-02-21T13:00:00Z,4.8403,0.0,4.1707,0.4687,0.0022,0.134,0.0639,0.0,0.0,0.0008,0.0,1309.8,62.87,43.79,49.66
343
+ 2026-02-21T13:15:00Z,4.5028,0.0,3.8342,0.4683,0.0021,0.1361,0.0612,0.0,0.0,0.0008,0.0,1329.8,63.83,42.79,49.78
344
+ 2026-02-21T13:30:00Z,4.541,0.0,3.8712,0.4728,0.0021,0.1355,0.0586,0.0,0.0,0.0008,0.0,1349.9,64.8,44.56,49.84
345
+ 2026-02-21T13:45:00Z,4.3365,0.0,3.6621,0.4774,0.0021,0.134,0.06,0.0,0.0,0.0009,0.0,1369.0,65.71,42.5,50.0
346
+ 2026-02-21T14:00:00Z,4.3479,0.0,3.6682,0.4781,0.0022,0.1373,0.0612,0.0,0.0,0.0008,0.0,1388.1,66.63,43.27,49.91
347
+ 2026-02-21T14:15:00Z,4.0599,0.0,3.3784,0.4774,0.0021,0.1342,0.0669,0.0,0.0,0.0009,0.0,1405.7,67.47,45.54,50.08
348
+ 2026-02-21T14:30:00Z,3.8371,0.0,3.1504,0.4817,0.0022,0.1342,0.0678,0.0,0.0,0.0008,0.0,1422.1,68.26,42.89,50.08
349
+ 2026-02-21T14:45:00Z,3.7501,0.0,3.0443,0.506,0.0022,0.1343,0.0624,0.0,0.0,0.0008,0.0,1438.0,69.02,43.19,50.15
350
+ 2026-02-21T15:00:00Z,3.448,0.0,2.7558,0.4977,0.0024,0.1328,0.0583,0.0,0.0,0.0009,0.0,1452.3,69.71,43.74,50.27
351
+ 2026-02-21T15:15:00Z,3.2274,0.0,2.5153,0.5086,0.0039,0.1332,0.0649,0.0,0.0,0.0015,0.0,1465.4,70.34,43.96,50.3
352
+ 2026-02-21T15:30:00Z,2.9415,0.0,2.2179,0.516,0.0061,0.1381,0.0609,0.0001,0.0,0.0024,0.0,1477.0,70.9,43.89,50.34
353
+ 2026-02-21T15:45:00Z,2.6108,0.0,1.8443,0.5587,0.0095,0.1348,0.0596,0.0001,0.0,0.0037,0.0,1486.6,71.36,41.46,50.26
354
+ 2026-02-21T16:00:00Z,2.3631,0.0,1.605,0.5455,0.0139,0.1354,0.0576,0.0003,0.0,0.0054,0.0,1495.0,71.76,44.77,50.25
355
+ 2026-02-21T16:15:00Z,2.0619,0.0,1.2683,0.5709,0.0198,0.1345,0.0599,0.0006,0.0,0.0078,0.0,1501.6,72.07,43.03,50.31
356
+ 2026-02-21T16:30:00Z,1.7514,0.0,0.9311,0.5817,0.027,0.1368,0.063,0.0012,0.0,0.0107,0.0,1506.4,72.31,44.66,50.32
357
+ 2026-02-21T16:45:00Z,1.4486,0.0,0.6129,0.587,0.0372,0.1343,0.0605,0.0021,0.0,0.0146,0.0,1509.6,72.46,43.39,50.33
358
+ 2026-02-21T17:00:00Z,1.1224,0.0,0.2489,0.6023,0.0484,0.1363,0.0644,0.0035,0.0,0.0186,0.0,1510.9,72.52,42.57,50.4
359
+ 2026-02-21T17:15:00Z,0.8246,0.0,-0.7206,0.6559,0.0635,0.1336,0.0619,0.0053,0.5999,0.0253,0.0,1507.1,72.34,46.35,50.26
360
+ 2026-02-21T17:30:00Z,0.5044,0.0,-2.1902,0.6219,0.0789,0.1368,0.0609,0.0075,1.7577,0.0309,0.0,1495.7,71.8,43.79,50.28
361
+ 2026-02-21T17:45:00Z,0.1757,0.0,-2.8519,0.6434,0.0953,0.1347,0.0621,0.0114,2.0417,0.0391,0.0,1480.9,71.08,43.29,50.34
362
+ 2026-02-21T18:00:00Z,0.0,0.0,-3.275,0.6615,0.1121,0.1341,0.0587,0.0133,2.2507,0.0445,0.0,1463.8,70.26,0.0,50.17
363
+ 2026-02-21T18:15:00Z,0.0,0.0,-3.7737,0.6498,0.1301,0.1363,0.0608,0.0165,2.7284,0.0518,0.0,1444.2,69.32,0.0,50.25
364
+ 2026-02-21T18:30:00Z,0.0,0.0,-4.1158,0.6401,0.1502,0.138,0.0604,0.0179,3.0487,0.0605,0.0,1422.7,68.29,0.0,50.18
365
+ 2026-02-21T18:45:00Z,0.0,0.0,-4.5377,0.6735,0.158,0.1362,0.0627,0.0202,3.4238,0.0635,0.0,1399.1,67.16,0.0,50.12
366
+ 2026-02-21T19:00:00Z,0.0,0.0,-4.5908,0.6546,0.1669,0.1346,0.0563,0.0204,3.4913,0.0667,0.0,1375.2,66.01,0.0,49.95
367
+ 2026-02-21T19:15:00Z,0.0,0.0,-4.8523,0.6339,0.1749,0.1342,0.0641,0.0192,3.7624,0.0636,0.0,1349.9,64.8,0.0,49.8
368
+ 2026-02-21T19:30:00Z,0.0,0.0,-5.0197,0.6582,0.1719,0.1346,0.0625,0.0172,3.9086,0.0668,0.0,1323.8,63.54,0.0,49.75
369
+ 2026-02-21T19:45:00Z,0.0,0.0,-4.509,0.6547,0.1654,0.1355,0.0648,0.0145,3.4089,0.0651,0.0,1300.3,62.41,0.0,49.76
370
+ 2026-02-21T20:00:00Z,0.0,0.0,-4.1981,0.6388,0.157,0.1375,0.0646,0.0112,3.1285,0.0607,0.0,1278.4,61.36,0.0,49.71
371
+ 2026-02-21T20:15:00Z,0.0,0.0,-4.4103,0.6543,0.1459,0.1367,0.0626,0.0076,3.3453,0.058,0.0,1255.5,60.26,0.0,49.57
372
+ 2026-02-21T20:30:00Z,0.0,0.0,-3.7535,0.639,0.1303,0.1339,0.0665,0.0052,2.729,0.0496,0.0,1235.9,59.32,0.0,49.57
373
+ 2026-02-21T20:45:00Z,0.0,0.0,-3.5337,0.6465,0.1106,0.1368,0.062,0.0035,2.5294,0.0449,0.0,1217.5,58.44,0.0,49.56
374
+ 2026-02-21T21:00:00Z,0.0,0.0,-2.8963,0.6304,0.0964,0.1376,0.0589,0.0021,1.9339,0.0369,0.0,1202.4,57.72,0.0,49.49
375
+ 2026-02-21T21:15:00Z,0.0,0.0,-2.6789,0.6661,0.0801,0.1328,0.0654,0.0012,1.7013,0.032,0.0,1188.5,57.05,0.0,49.46
376
+ 2026-02-21T21:30:00Z,0.0,0.0,-1.5566,0.6308,0.0623,0.1362,0.0622,0.0006,0.6396,0.0248,0.0,1180.4,56.66,0.0,49.4
377
+ 2026-02-21T21:45:00Z,0.0,0.0,-0.9052,0.6374,0.0506,0.1354,0.0618,0.0004,0.0,0.0198,0.0,1175.6,56.43,0.0,49.39
378
+ 2026-02-21T22:00:00Z,0.0,0.0,-0.9044,0.6532,0.0383,0.1349,0.063,0.0002,0.0,0.0149,0.0,1170.9,56.2,0.0,49.33
379
+ 2026-02-21T22:15:00Z,0.0,0.0,-0.8977,0.6615,0.0278,0.1339,0.0635,0.0001,0.0,0.0108,0.0,1166.3,55.98,0.0,49.31
380
+ 2026-02-21T22:30:00Z,0.0,0.0,-0.8381,0.6157,0.0203,0.1327,0.0618,0.0,0.0,0.0076,0.0,1161.9,55.77,0.0,49.28
381
+ 2026-02-21T22:45:00Z,0.0,0.0,-0.8676,0.6547,0.0139,0.1337,0.06,0.0,0.0,0.0054,0.0,1157.4,55.55,0.0,49.24
382
+ 2026-02-21T23:00:00Z,0.0,0.0,-0.8466,0.6434,0.0027,0.1356,0.0613,0.0,0.0,0.0037,0.0,1153.0,55.34,0.0,49.26
383
+ 2026-02-21T23:15:00Z,0.0,0.0,-0.8674,0.6649,0.002,0.1352,0.0629,0.0,0.0,0.0024,0.0,1148.4,55.13,0.0,49.3
384
+ 2026-02-21T23:30:00Z,0.0,0.0,-0.8438,0.6419,0.002,0.1339,0.0643,0.0,0.0,0.0016,0.0,1144.0,54.91,0.0,49.26
385
+ 2026-02-21T23:45:00Z,0.0,0.0,-0.846,0.646,0.002,0.1354,0.0616,0.0,0.0,0.001,0.0,1139.6,54.7,0.0,49.34
output/power/1DAY.csv ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Time,Solar_Total_kWh,Shore_Total_kWh,Battery_Charged_kWh,Battery_Discharged_kWh,HVAC_Total_kWh,Lighting_Total_kWh,Devices_Total_kWh,Fridge_Total_kWh,WaterPump_Total_kWh,Cooking_Total_kWh,Inverter_Total_kWh,Unmetered_Total_kWh,Battery_Level_Ah,Battery_Level_Pct,Solar_Voltage_Avg_V,Battery_Voltage_Avg_V
2
+ 2026-02-18T00:00:00Z,38.076662,0.0,24.32976,30.667985,14.135598,1.282062,3.24653,1.475973,0.131885,23.631153,0.511685,0.0,1534.6,73.66,21.94,50.71
3
+ 2026-02-19T00:00:00Z,38.057265,0.0,24.323765,30.82545,14.151865,1.281073,3.250425,1.48185,0.133242,23.752057,0.508438,0.0,1399.2,67.16,21.98,50.32
4
+ 2026-02-20T00:00:00Z,38.352155,0.0,24.442578,30.460525,14.116797,1.283768,3.23797,1.4857,0.132327,23.6025,0.51104,0.0,1273.8,61.14,21.82,49.95
5
+ 2026-02-21T00:00:00Z,38.222703,0.0,24.515092,30.954448,14.08935,1.2869,3.241302,1.48587,0.135302,23.911335,0.512002,0.0,1139.6,54.7,21.92,49.58
output/power/1H.csv ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Time,Solar_Total_kWh,Shore_Total_kWh,Battery_Charged_kWh,Battery_Discharged_kWh,HVAC_Total_kWh,Lighting_Total_kWh,Devices_Total_kWh,Fridge_Total_kWh,WaterPump_Total_kWh,Cooking_Total_kWh,Inverter_Total_kWh,Unmetered_Total_kWh,Battery_Level_Ah,Battery_Level_Pct,Solar_Voltage_Avg_V,Battery_Voltage_Avg_V
2
+ 2026-02-18T00:00:00Z,0.0,0.0,0.0,0.855355,0.654488,0.002,0.135705,0.062305,0.0,0.0,0.000857,0.0,1648.8,79.14,0.0,50.8
3
+ 2026-02-18T01:00:00Z,0.0,0.0,0.0,0.846337,0.648863,0.002,0.13397,0.060677,0.0,0.0,0.000827,0.0,1631.2,78.3,0.0,50.7
4
+ 2026-02-18T02:00:00Z,0.0,0.0,0.0,0.847543,0.64766,0.002,0.13584,0.061182,0.0,0.0,0.000862,0.0,1613.6,77.45,0.0,50.67
5
+ 2026-02-18T03:00:00Z,0.0,0.0,0.0,0.869457,0.668278,0.002,0.135743,0.062607,1e-05,0.0,0.000818,0.0,1595.4,76.58,0.0,50.65
6
+ 2026-02-18T04:00:00Z,0.0,0.0,0.0,0.851883,0.650238,0.002,0.13502,0.062605,0.000498,0.0,0.001522,0.0,1577.7,75.73,0.0,50.62
7
+ 2026-02-18T05:00:00Z,0.0,0.0,0.0,0.863852,0.649357,0.002,0.134942,0.06225,0.005038,0.0,0.010265,0.0,1559.7,74.87,0.0,50.53
8
+ 2026-02-18T06:00:00Z,0.644015,0.0,0.0,2.247272,0.621125,0.099772,0.136025,0.060473,0.019397,1.914483,0.040012,0.0,1512.9,72.62,43.13,50.41
9
+ 2026-02-18T07:00:00Z,1.921423,0.0,0.0,3.338727,0.569808,0.19172,0.135102,0.062827,0.031782,4.19402,0.074892,0.0,1443.3,69.28,44.97,50.27
10
+ 2026-02-18T08:00:00Z,3.014447,0.0,0.002447,2.209082,0.536385,0.195567,0.135612,0.060225,0.020018,4.19849,0.074785,0.0,1397.4,67.07,44.3,50.1
11
+ 2026-02-18T09:00:00Z,3.891975,0.0,1.238545,0.084965,0.491053,0.10003,0.134425,0.06039,0.005128,1.908168,0.0392,0.0,1421.4,68.23,44.48,50.06
12
+ 2026-02-18T10:00:00Z,4.569852,0.0,3.863942,0.0,0.472238,0.027295,0.134097,0.0612,0.0005,0.0,0.01058,0.0,1501.9,72.09,44.18,50.23
13
+ 2026-02-18T11:00:00Z,4.98226,0.0,4.323893,0.0,0.457055,0.004103,0.135117,0.060467,1.3e-05,0.0,0.001612,0.0,1592.0,76.41,43.72,50.42
14
+ 2026-02-18T12:00:00Z,4.885727,0.0,4.225135,0.0,0.46023,0.002155,0.135265,0.06209,0.0,0.0,0.000852,0.0,1680.0,80.64,43.71,50.74
15
+ 2026-02-18T13:00:00Z,4.580578,0.0,3.918417,0.0,0.463922,0.002153,0.135343,0.059905,0.0,0.0,0.000838,0.0,1761.6,84.56,43.32,50.98
16
+ 2026-02-18T14:00:00Z,4.003418,0.0,3.307328,0.0,0.495052,0.002147,0.136,0.062057,0.0,0.0,0.000835,0.0,1830.5,87.87,43.12,51.18
17
+ 2026-02-18T15:00:00Z,3.006577,0.0,2.266167,0.0,0.538208,0.0054,0.134763,0.059893,5.8e-05,0.0,0.002087,0.0,1877.7,90.13,43.49,51.32
18
+ 2026-02-18T16:00:00Z,1.91499,0.0,1.110767,0.0,0.573375,0.024422,0.1343,0.0615,0.00103,0.0,0.009597,0.0,1900.9,91.24,43.91,51.45
19
+ 2026-02-18T17:00:00Z,0.6614,0.0,0.07312,1.371418,0.619123,0.07158,0.135953,0.063783,0.006788,1.035183,0.027287,0.0,1873.8,89.94,44.24,51.47
20
+ 2026-02-18T18:00:00Z,0.0,0.0,0.0,4.018715,0.651103,0.136768,0.135622,0.060758,0.016807,2.964232,0.053425,0.0,1790.1,85.93,0.0,51.28
21
+ 2026-02-18T19:00:00Z,0.0,0.0,0.0,4.594473,0.648175,0.168807,0.136447,0.061685,0.016943,3.495293,0.067123,0.0,1694.4,81.33,0.0,51.03
22
+ 2026-02-18T20:00:00Z,0.0,0.0,0.0,3.908307,0.657942,0.138337,0.134782,0.063028,0.006775,2.853917,0.053527,0.0,1613.0,77.42,0.0,50.77
23
+ 2026-02-18T21:00:00Z,0.0,0.0,0.0,2.02254,0.655618,0.072667,0.135553,0.062408,0.001038,1.067367,0.027888,0.0,1570.8,75.4,0.0,50.59
24
+ 2026-02-18T22:00:00Z,0.0,0.0,0.0,0.876237,0.646107,0.024997,0.135578,0.059633,6e-05,0.0,0.009862,0.0,1552.6,74.52,0.0,50.49
25
+ 2026-02-18T23:00:00Z,0.0,0.0,0.0,0.861823,0.660193,0.002143,0.135327,0.062025,0.0,0.0,0.002135,0.0,1534.6,73.66,0.0,50.41
26
+ 2026-02-19T00:00:00Z,0.0,0.0,0.0,0.864395,0.660658,0.002,0.136717,0.064188,0.0,0.0,0.000832,0.0,1516.6,72.8,0.0,50.4
27
+ 2026-02-19T01:00:00Z,0.0,0.0,0.0,0.844488,0.644677,0.002,0.13536,0.061595,0.0,0.0,0.000857,0.0,1499.0,71.95,0.0,50.31
28
+ 2026-02-19T02:00:00Z,0.0,0.0,0.0,0.859117,0.657887,0.002,0.135262,0.063127,0.0,0.0,0.000842,0.0,1481.1,71.09,0.0,50.3
29
+ 2026-02-19T03:00:00Z,0.0,0.0,0.0,0.84273,0.641955,0.002,0.133912,0.064013,1.2e-05,0.0,0.000838,0.0,1463.6,70.25,0.0,50.25
30
+ 2026-02-19T04:00:00Z,0.0,0.0,0.0,0.8661,0.662362,0.002,0.136693,0.063057,0.00047,0.0,0.001518,0.0,1445.5,69.38,0.0,50.21
31
+ 2026-02-19T05:00:00Z,0.0,0.0,0.0,0.8679,0.654865,0.002,0.135802,0.060467,0.004782,0.0,0.009985,0.0,1427.4,68.52,0.0,50.13
32
+ 2026-02-19T06:00:00Z,0.629232,0.0,0.0,2.35239,0.621932,0.099787,0.135895,0.062042,0.020212,2.002137,0.039618,0.0,1378.4,66.16,43.12,50.03
33
+ 2026-02-19T07:00:00Z,1.914067,0.0,0.0,3.315613,0.581233,0.19227,0.135597,0.06184,0.032523,4.151463,0.074753,0.0,1309.4,62.85,45.11,49.86
34
+ 2026-02-19T08:00:00Z,3.022743,0.0,0.00425,2.040217,0.539148,0.190627,0.134532,0.059632,0.020327,4.039117,0.075328,0.0,1266.9,60.81,43.66,49.65
35
+ 2026-02-19T09:00:00Z,3.9425,0.0,1.275183,0.156533,0.496765,0.101917,0.136633,0.06138,0.004968,1.983247,0.03894,0.0,1290.2,61.93,44.44,49.64
36
+ 2026-02-19T10:00:00Z,4.626238,0.0,3.91343,0.0,0.47581,0.027178,0.135627,0.062745,0.00052,0.0,0.010928,0.0,1371.8,65.85,44.68,49.85
37
+ 2026-02-19T11:00:00Z,4.837432,0.0,4.179718,0.0,0.457663,0.0041,0.135393,0.058982,7e-06,0.0,0.001568,0.0,1458.9,70.02,43.63,50.14
38
+ 2026-02-19T12:00:00Z,4.977692,0.0,4.32778,0.0,0.450518,0.00216,0.135128,0.061255,0.0,0.0,0.00085,0.0,1549.0,74.35,43.09,50.31
39
+ 2026-02-19T13:00:00Z,4.58805,0.0,3.917898,0.0,0.470335,0.002163,0.134855,0.061952,0.0,0.0,0.000847,0.0,1630.6,78.27,44.02,50.57
40
+ 2026-02-19T14:00:00Z,3.928615,0.0,3.23061,0.0,0.498535,0.002132,0.134463,0.06205,0.0,0.0,0.000825,0.0,1697.9,81.5,43.53,50.8
41
+ 2026-02-19T15:00:00Z,3.029595,0.0,2.301332,0.0,0.526113,0.005455,0.134892,0.05958,6e-05,0.0,0.002163,0.0,1745.9,83.8,44.08,50.94
42
+ 2026-02-19T16:00:00Z,1.902025,0.0,1.100757,0.0,0.569732,0.024558,0.136747,0.059728,0.001015,0.0,0.009488,0.0,1768.8,84.9,44.33,51.05
43
+ 2026-02-19T17:00:00Z,0.659077,0.0,0.072807,1.429737,0.620997,0.072333,0.136285,0.061272,0.006543,1.091105,0.027472,0.0,1740.5,83.55,43.74,51.1
44
+ 2026-02-19T18:00:00Z,0.0,0.0,0.0,3.963292,0.654547,0.136685,0.13514,0.061032,0.017163,2.905835,0.05289,0.0,1658.0,79.58,0.0,50.84
45
+ 2026-02-19T19:00:00Z,0.0,0.0,0.0,4.704807,0.660122,0.171132,0.13634,0.062585,0.01673,3.591748,0.06615,0.0,1560.0,74.88,0.0,50.62
46
+ 2026-02-19T20:00:00Z,0.0,0.0,0.0,3.95338,0.645982,0.136253,0.134583,0.061975,0.006807,2.91577,0.05201,0.0,1477.6,70.92,0.0,50.39
47
+ 2026-02-19T21:00:00Z,0.0,0.0,0.0,2.016312,0.643958,0.073155,0.134787,0.063883,0.001048,1.071635,0.027845,0.0,1435.6,68.91,0.0,50.21
48
+ 2026-02-19T22:00:00Z,0.0,0.0,0.0,0.892467,0.660283,0.025013,0.13467,0.062688,5.5e-05,0.0,0.009757,0.0,1417.0,68.02,0.0,50.12
49
+ 2026-02-19T23:00:00Z,0.0,0.0,0.0,0.855973,0.655788,0.002155,0.135113,0.060783,0.0,0.0,0.002133,0.0,1399.2,67.16,0.0,50.04
50
+ 2026-02-20T00:00:00Z,0.0,0.0,0.0,0.853588,0.650425,0.002,0.136305,0.064018,0.0,0.0,0.00084,0.0,1381.4,66.31,0.0,50.01
51
+ 2026-02-20T01:00:00Z,0.0,0.0,0.0,0.844687,0.645078,0.002,0.13485,0.061925,0.0,0.0,0.000833,0.0,1363.8,65.46,0.0,49.94
52
+ 2026-02-20T02:00:00Z,0.0,0.0,0.0,0.847528,0.648953,0.002,0.135095,0.060635,0.0,0.0,0.000845,0.0,1346.1,64.61,0.0,49.93
53
+ 2026-02-20T03:00:00Z,0.0,0.0,0.0,0.842303,0.641663,0.002,0.13423,0.063553,7e-06,0.0,0.00085,0.0,1328.6,63.77,0.0,49.87
54
+ 2026-02-20T04:00:00Z,0.0,0.0,0.0,0.84551,0.644558,0.002,0.135057,0.061847,0.00049,0.0,0.001558,0.0,1311.0,62.93,0.0,49.78
55
+ 2026-02-20T05:00:00Z,0.0,0.0,0.0,0.864638,0.652483,0.002,0.134847,0.059918,0.00505,0.0,0.01034,0.0,1293.0,62.06,0.0,49.73
56
+ 2026-02-20T06:00:00Z,0.646785,0.0,0.0,2.22802,0.636592,0.099833,0.133628,0.062762,0.020003,1.88368,0.038307,0.0,1246.5,59.83,43.02,49.63
57
+ 2026-02-20T07:00:00Z,1.887662,0.0,0.0,3.282692,0.581712,0.190385,0.134572,0.06027,0.031532,4.0965,0.075383,0.0,1178.1,56.55,43.93,49.46
58
+ 2026-02-20T08:00:00Z,3.067253,0.0,0.000733,1.934322,0.527063,0.19349,0.135628,0.062145,0.020165,3.987303,0.075047,0.0,1137.9,54.62,43.73,49.34
59
+ 2026-02-20T09:00:00Z,3.92396,0.0,1.14406,0.168363,0.501317,0.102022,0.134357,0.062758,0.00499,2.103833,0.038987,0.0,1158.2,55.59,44.68,49.24
60
+ 2026-02-20T10:00:00Z,4.634325,0.0,3.923728,0.0,0.474307,0.027343,0.13518,0.062642,0.000502,0.0,0.010623,0.0,1239.9,59.52,43.01,49.47
61
+ 2026-02-20T11:00:00Z,4.995682,0.0,4.332482,0.0,0.460548,0.004068,0.135145,0.06183,8e-06,0.0,0.0016,0.0,1330.2,63.85,43.57,49.75
62
+ 2026-02-20T12:00:00Z,4.914305,0.0,4.249965,0.0,0.464017,0.002178,0.135838,0.061463,0.0,0.0,0.000843,0.0,1418.7,68.1,43.44,49.99
63
+ 2026-02-20T13:00:00Z,4.65703,0.0,3.9773,0.0,0.480003,0.002152,0.134765,0.06197,0.0,0.0,0.00084,0.0,1501.6,72.08,42.54,50.15
64
+ 2026-02-20T14:00:00Z,3.958903,0.0,3.264372,0.0,0.4935,0.002168,0.135105,0.062937,0.0,0.0,0.000822,0.0,1569.6,75.34,42.88,50.44
65
+ 2026-02-20T15:00:00Z,3.092937,0.0,2.367902,0.0,0.5232,0.005437,0.133787,0.06046,5.5e-05,0.0,0.002097,0.0,1618.9,77.71,44.24,50.61
66
+ 2026-02-20T16:00:00Z,1.921722,0.0,1.11893,0.0,0.572655,0.024262,0.135103,0.060243,0.001048,0.0,0.00948,0.0,1642.2,78.83,45.18,50.69
67
+ 2026-02-20T17:00:00Z,0.651592,0.0,0.063107,1.392203,0.625277,0.071943,0.134373,0.061287,0.006663,1.052973,0.028172,0.0,1614.6,77.5,43.5,50.75
68
+ 2026-02-20T18:00:00Z,0.0,0.0,0.0,3.888878,0.649148,0.13769,0.134925,0.061803,0.017087,2.834162,0.054063,0.0,1533.5,73.61,0.0,50.5
69
+ 2026-02-20T19:00:00Z,0.0,0.0,0.0,4.884177,0.648145,0.170572,0.134718,0.063197,0.016967,3.784643,0.065935,0.0,1431.8,68.73,0.0,50.27
70
+ 2026-02-20T20:00:00Z,0.0,0.0,0.0,3.810663,0.652282,0.13865,0.135207,0.063238,0.0066,2.761195,0.053492,0.0,1352.4,64.92,0.0,49.95
71
+ 2026-02-20T21:00:00Z,0.0,0.0,0.0,2.036488,0.639782,0.072628,0.134928,0.061698,0.001103,1.09821,0.028138,0.0,1310.0,62.88,0.0,49.79
72
+ 2026-02-20T22:00:00Z,0.0,0.0,0.0,0.887358,0.655225,0.0248,0.134983,0.062527,5.7e-05,0.0,0.009767,0.0,1291.5,61.99,0.0,49.71
73
+ 2026-02-20T23:00:00Z,0.0,0.0,0.0,0.849105,0.648863,0.002147,0.135343,0.060573,0.0,0.0,0.002178,0.0,1273.8,61.14,0.0,49.7
74
+ 2026-02-21T00:00:00Z,0.0,0.0,0.0,0.85798,0.656547,0.002,0.135848,0.062755,0.0,0.0,0.00083,0.0,1255.9,60.28,0.0,49.6
75
+ 2026-02-21T01:00:00Z,0.0,0.0,0.0,0.848032,0.651248,0.002,0.134653,0.05931,0.0,0.0,0.00082,0.0,1238.3,59.44,0.0,49.62
76
+ 2026-02-21T02:00:00Z,0.0,0.0,0.0,0.862865,0.663402,0.002,0.134965,0.061663,0.0,0.0,0.000835,0.0,1220.3,58.57,0.0,49.62
77
+ 2026-02-21T03:00:00Z,0.0,0.0,0.0,0.844287,0.646392,0.002,0.134202,0.06085,1e-05,0.0,0.000833,0.0,1202.7,57.73,0.0,49.49
78
+ 2026-02-21T04:00:00Z,0.0,0.0,0.0,0.842325,0.639238,0.002,0.134703,0.064408,0.000467,0.0,0.001508,0.0,1185.1,56.89,0.0,49.44
79
+ 2026-02-21T05:00:00Z,0.0,0.0,0.0,0.856137,0.64139,0.002,0.13496,0.062413,0.00478,0.0,0.010593,0.0,1167.3,56.03,0.0,49.41
80
+ 2026-02-21T06:00:00Z,0.637045,0.0,0.0,2.343768,0.636853,0.099385,0.133423,0.064053,0.020943,1.986862,0.039293,0.0,1118.5,53.69,44.48,49.34
81
+ 2026-02-21T07:00:00Z,1.884788,0.0,0.0,3.448528,0.569553,0.196968,0.134495,0.062078,0.032462,4.263247,0.074513,0.0,1046.6,50.24,45.53,49.13
82
+ 2026-02-21T08:00:00Z,3.042072,0.0,0.000938,2.117172,0.537758,0.194842,0.134042,0.062825,0.020188,4.134515,0.074135,0.0,1002.5,48.12,44.39,48.95
83
+ 2026-02-21T09:00:00Z,3.923607,0.0,1.266867,0.109737,0.50496,0.101155,0.135678,0.06069,0.005122,1.91945,0.039422,0.0,1026.6,49.28,44.32,48.91
84
+ 2026-02-21T10:00:00Z,4.619105,0.0,3.914548,0.0,0.468698,0.027537,0.135643,0.061432,0.000512,0.0,0.010735,0.0,1108.2,53.19,42.39,49.09
85
+ 2026-02-21T11:00:00Z,4.971983,0.0,4.313828,0.0,0.45418,0.004102,0.137067,0.061218,1e-05,0.0,0.001578,0.0,1198.1,57.51,43.41,49.28
86
+ 2026-02-21T12:00:00Z,4.970208,0.0,4.320863,0.0,0.451418,0.002177,0.134937,0.05997,0.0,0.0,0.000843,0.0,1288.1,61.83,43.3,49.55
87
+ 2026-02-21T13:00:00Z,4.555165,0.0,3.88455,0.0,0.471777,0.002148,0.134893,0.06095,0.0,0.0,0.000847,0.0,1369.0,65.71,43.41,49.82
88
+ 2026-02-21T14:00:00Z,3.99873,0.0,3.310335,0.0,0.485812,0.00219,0.135007,0.064567,0.0,0.0,0.00082,0.0,1438.0,69.02,43.73,50.05
89
+ 2026-02-21T15:00:00Z,3.056947,0.0,2.333318,0.0,0.520255,0.005497,0.134738,0.060937,5.7e-05,0.0,0.002145,0.0,1486.6,71.36,43.26,50.29
90
+ 2026-02-21T16:00:00Z,1.906257,0.0,1.104308,0.0,0.571275,0.02447,0.135245,0.060233,0.001072,0.0,0.009653,0.0,1509.6,72.46,43.96,50.3
91
+ 2026-02-21T17:00:00Z,0.656797,0.0,0.065535,1.443993,0.630855,0.071535,0.135348,0.06232,0.006928,1.099808,0.02846,0.0,1480.9,71.08,44.0,50.32
92
+ 2026-02-21T18:00:00Z,0.0,0.0,0.0,3.925553,0.656213,0.137565,0.136172,0.060648,0.016975,2.862907,0.055073,0.0,1399.1,67.16,0.0,50.18
93
+ 2026-02-21T19:00:00Z,0.0,0.0,0.0,4.742977,0.650355,0.169778,0.134737,0.061953,0.01781,3.642805,0.065538,0.0,1300.3,62.41,0.0,49.82
94
+ 2026-02-21T20:00:00Z,0.0,0.0,0.0,3.973915,0.644658,0.135947,0.136205,0.063938,0.006855,2.933032,0.05328,0.0,1217.5,58.44,0.0,49.6
95
+ 2026-02-21T21:00:00Z,0.0,0.0,0.0,2.009253,0.641173,0.072343,0.13551,0.062078,0.001045,1.06871,0.028393,0.0,1175.6,56.43,0.0,49.44
96
+ 2026-02-21T22:00:00Z,0.0,0.0,0.0,0.876968,0.64627,0.025095,0.13378,0.062075,6.7e-05,0.0,0.009682,0.0,1157.4,55.55,0.0,49.29
97
+ 2026-02-21T23:00:00Z,0.0,0.0,0.0,0.850958,0.649068,0.002167,0.13505,0.062503,0.0,0.0,0.00217,0.0,1139.6,54.7,0.0,49.29
output/power/1MIN.csv ADDED
The diff for this file is too large to render. See raw diff
 
output/power/1SEC.csv ADDED
The diff for this file is too large to render. See raw diff
 
output/stability_index.csv ADDED
The diff for this file is too large to render. See raw diff
 
output/water/15MIN.csv ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Time,Inlet_Flow_Lpm,Pump_Flow_Lpm,Shower_Flow_Lpm,Kitchen_Flow_Lpm,Toilet_Flow_Lpm,Unmetered_Flow_Lpm,FreshTank_Level_L,GreyTank_Level_L,BlackTank_Level_L
2
+ 2026-02-18T00:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
3
+ 2026-02-18T00:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
4
+ 2026-02-18T00:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
5
+ 2026-02-18T00:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
6
+ 2026-02-18T01:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
7
+ 2026-02-18T01:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
8
+ 2026-02-18T01:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
9
+ 2026-02-18T01:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
10
+ 2026-02-18T02:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
11
+ 2026-02-18T02:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
12
+ 2026-02-18T02:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
13
+ 2026-02-18T02:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
14
+ 2026-02-18T03:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
15
+ 2026-02-18T03:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,359.61,0.0,0.0
16
+ 2026-02-18T03:30:00Z,0.0,0.0002,0.0001,0.0,0.0,0.0,359.61,0.0,0.0
17
+ 2026-02-18T03:45:00Z,0.0,0.0004,0.0003,0.0001,0.0,0.0,359.6,0.01,0.0
18
+ 2026-02-18T04:00:00Z,0.0,0.0011,0.0006,0.0003,0.0001,0.0,359.59,0.02,0.0
19
+ 2026-02-18T04:15:00Z,0.0,0.0023,0.0014,0.0007,0.0003,0.0,359.55,0.05,0.01
20
+ 2026-02-18T04:30:00Z,0.0,0.0049,0.0029,0.0015,0.0005,0.0,359.48,0.11,0.01
21
+ 2026-02-18T04:45:00Z,0.0,0.01,0.0063,0.0027,0.001,0.0,359.33,0.23,0.03
22
+ 2026-02-18T05:00:00Z,0.0,0.0186,0.0118,0.0049,0.0018,0.0,359.05,0.45,0.06
23
+ 2026-02-18T05:15:00Z,0.0,0.0334,0.0208,0.0092,0.0034,0.0,358.55,0.86,0.11
24
+ 2026-02-18T05:30:00Z,0.0,0.0541,0.0336,0.0151,0.0055,0.0,357.74,1.52,0.19
25
+ 2026-02-18T05:45:00Z,0.0,0.0835,0.051,0.0233,0.0091,0.0,356.49,2.52,0.33
26
+ 2026-02-18T06:00:00Z,0.0,0.1249,0.0785,0.0337,0.0127,0.0,354.61,4.03,0.52
27
+ 2026-02-18T06:15:00Z,0.0,0.1652,0.1047,0.045,0.0155,0.0,352.14,6.05,0.75
28
+ 2026-02-18T06:30:00Z,0.0,0.2114,0.1302,0.0579,0.0233,0.0,348.97,8.59,1.1
29
+ 2026-02-18T06:45:00Z,0.0,0.264,0.1659,0.0712,0.0269,0.0,345.01,11.79,1.5
30
+ 2026-02-18T07:00:00Z,0.7036,0.2976,0.1908,0.079,0.0278,0.0,351.1,15.44,1.92
31
+ 2026-02-18T07:15:00Z,0.0,0.3098,0.1931,0.0839,0.0327,0.0,346.45,19.18,2.41
32
+ 2026-02-18T07:30:00Z,0.0,0.3232,0.1997,0.0894,0.0342,0.0,341.6,23.08,2.93
33
+ 2026-02-18T07:45:00Z,0.0,0.298,0.1828,0.0854,0.0298,0.0,337.13,26.7,3.37
34
+ 2026-02-18T08:00:00Z,0.0,0.2616,0.1636,0.0703,0.0278,0.0,333.21,29.85,3.79
35
+ 2026-02-18T08:15:00Z,0.0,0.2262,0.139,0.0621,0.0251,0.0,329.81,32.57,4.17
36
+ 2026-02-18T08:30:00Z,0.0,0.1731,0.1083,0.0478,0.0171,0.0,327.22,34.67,4.42
37
+ 2026-02-18T08:45:00Z,0.0,0.1243,0.0763,0.0347,0.0134,0.0,325.35,36.17,4.62
38
+ 2026-02-18T09:00:00Z,0.0,0.0836,0.0495,0.0251,0.009,0.0,324.1,37.18,4.76
39
+ 2026-02-18T09:15:00Z,0.0,0.0548,0.0337,0.0151,0.006,0.0,323.28,37.84,4.85
40
+ 2026-02-18T09:30:00Z,0.0,0.0322,0.0191,0.0095,0.0037,0.0,322.79,38.22,4.9
41
+ 2026-02-18T09:45:00Z,0.0,0.0199,0.0121,0.0057,0.0021,0.0,322.49,38.47,4.93
42
+ 2026-02-18T10:00:00Z,0.0,0.0103,0.0062,0.0029,0.0011,0.0,322.34,38.59,4.95
43
+ 2026-02-18T10:15:00Z,0.0,0.0052,0.0032,0.0014,0.0005,0.0,322.26,38.65,4.96
44
+ 2026-02-18T10:30:00Z,0.0,0.0024,0.0015,0.0007,0.0002,0.0,322.23,38.68,4.96
45
+ 2026-02-18T10:45:00Z,0.0,0.0011,0.0007,0.0003,0.0001,0.0,322.21,38.7,4.96
46
+ 2026-02-18T11:00:00Z,0.0,0.0005,0.0003,0.0001,0.0,0.0,322.2,38.7,4.96
47
+ 2026-02-18T11:15:00Z,0.0,0.0002,0.0001,0.0001,0.0,0.0,322.2,38.7,4.96
48
+ 2026-02-18T11:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
49
+ 2026-02-18T11:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
50
+ 2026-02-18T12:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
51
+ 2026-02-18T12:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
52
+ 2026-02-18T12:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
53
+ 2026-02-18T12:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
54
+ 2026-02-18T13:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
55
+ 2026-02-18T13:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
56
+ 2026-02-18T13:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
57
+ 2026-02-18T13:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
58
+ 2026-02-18T14:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
59
+ 2026-02-18T14:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
60
+ 2026-02-18T14:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
61
+ 2026-02-18T14:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,322.2,38.7,4.96
62
+ 2026-02-18T15:00:00Z,0.0,0.0001,0.0001,0.0,0.0,0.0,322.2,38.71,4.96
63
+ 2026-02-18T15:15:00Z,0.0,0.0003,0.0002,0.0001,0.0,0.0,322.19,38.71,4.96
64
+ 2026-02-18T15:30:00Z,0.0,0.0006,0.0004,0.0002,0.0001,0.0,322.18,38.72,4.97
65
+ 2026-02-18T15:45:00Z,0.0,0.0014,0.0009,0.0004,0.0001,0.0,322.16,38.73,4.97
66
+ 2026-02-18T16:00:00Z,0.0,0.0029,0.0017,0.0009,0.0003,0.0,322.12,38.77,4.97
67
+ 2026-02-18T16:15:00Z,0.0,0.0061,0.0038,0.0016,0.0006,0.0,322.03,38.84,4.98
68
+ 2026-02-18T16:30:00Z,0.0,0.0118,0.0074,0.0031,0.0013,0.0,321.85,38.98,5.0
69
+ 2026-02-18T16:45:00Z,0.0,0.0209,0.0131,0.0058,0.002,0.0,321.54,39.24,5.03
70
+ 2026-02-18T17:00:00Z,0.0,0.0335,0.0208,0.0093,0.0033,0.0,321.03,39.65,5.08
71
+ 2026-02-18T17:15:00Z,0.0,0.0507,0.0307,0.0145,0.0054,0.0,320.27,40.26,5.16
72
+ 2026-02-18T17:30:00Z,0.0,0.0757,0.0456,0.0216,0.0085,0.0,319.14,41.16,5.29
73
+ 2026-02-18T17:45:00Z,0.0,0.1028,0.0648,0.0276,0.0105,0.0,317.6,42.41,5.45
74
+ 2026-02-18T18:00:00Z,0.0,0.1288,0.0802,0.0351,0.0135,0.0,315.66,43.97,5.65
75
+ 2026-02-18T18:15:00Z,0.0,0.1554,0.0971,0.0424,0.0159,0.0,313.33,45.85,5.89
76
+ 2026-02-18T18:30:00Z,0.0,0.1693,0.1037,0.0474,0.0181,0.0,310.79,47.89,6.16
77
+ 2026-02-18T18:45:00Z,0.0,0.1916,0.1161,0.0554,0.0201,0.0,307.92,50.21,6.46
78
+ 2026-02-18T19:00:00Z,0.0,0.1895,0.1197,0.0509,0.0189,0.0,305.08,52.51,6.75
79
+ 2026-02-18T19:15:00Z,0.0,0.18,0.1126,0.0501,0.0173,0.0,302.38,54.71,7.0
80
+ 2026-02-18T19:30:00Z,0.0,0.1553,0.0969,0.0431,0.0153,0.0,300.05,56.6,7.23
81
+ 2026-02-18T19:45:00Z,0.0,0.1326,0.083,0.0361,0.0136,0.0,298.06,58.2,7.44
82
+ 2026-02-18T20:00:00Z,0.0,0.1029,0.0636,0.0288,0.0105,0.0,296.52,59.45,7.6
83
+ 2026-02-18T20:15:00Z,0.0,0.0719,0.0436,0.0212,0.0072,0.0,295.44,60.32,7.7
84
+ 2026-02-18T20:30:00Z,0.0,0.0499,0.0307,0.0138,0.0055,0.0,294.69,60.92,7.78
85
+ 2026-02-18T20:45:00Z,0.0,0.0329,0.0199,0.0091,0.0039,0.0,294.2,61.32,7.84
86
+ 2026-02-18T21:00:00Z,0.0,0.0207,0.0127,0.0058,0.0023,0.0,293.88,61.57,7.88
87
+ 2026-02-18T21:15:00Z,0.0,0.0117,0.0073,0.0032,0.0012,0.0,293.71,61.71,7.9
88
+ 2026-02-18T21:30:00Z,0.0,0.0062,0.0039,0.0017,0.0007,0.0,293.62,61.78,7.91
89
+ 2026-02-18T21:45:00Z,0.0,0.0032,0.002,0.0009,0.0003,0.0,293.57,61.82,7.91
90
+ 2026-02-18T22:00:00Z,0.0,0.0015,0.0009,0.0004,0.0002,0.0,293.55,61.84,7.91
91
+ 2026-02-18T22:15:00Z,0.0,0.0007,0.0004,0.0002,0.0001,0.0,293.54,61.85,7.91
92
+ 2026-02-18T22:30:00Z,0.0,0.0003,0.0002,0.0001,0.0,0.0,293.53,61.85,7.91
93
+ 2026-02-18T22:45:00Z,0.0,0.0001,0.0001,0.0,0.0,0.0,293.53,61.85,7.91
94
+ 2026-02-18T23:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
95
+ 2026-02-18T23:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
96
+ 2026-02-18T23:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
97
+ 2026-02-18T23:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
98
+ 2026-02-19T00:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
99
+ 2026-02-19T00:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
100
+ 2026-02-19T00:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
101
+ 2026-02-19T00:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
102
+ 2026-02-19T01:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
103
+ 2026-02-19T01:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
104
+ 2026-02-19T01:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
105
+ 2026-02-19T01:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
106
+ 2026-02-19T02:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
107
+ 2026-02-19T02:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
108
+ 2026-02-19T02:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
109
+ 2026-02-19T02:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
110
+ 2026-02-19T03:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
111
+ 2026-02-19T03:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,293.53,61.85,7.91
112
+ 2026-02-19T03:30:00Z,0.0,0.0001,0.0001,0.0,0.0,0.0,293.53,61.85,7.91
113
+ 2026-02-19T03:45:00Z,0.0,0.0004,0.0003,0.0001,0.0,0.0,293.52,61.86,7.91
114
+ 2026-02-19T04:00:00Z,0.0,0.0011,0.0007,0.0003,0.0001,0.0,293.5,61.87,7.92
115
+ 2026-02-19T04:15:00Z,0.0,0.0023,0.0014,0.0007,0.0003,0.0,293.47,61.9,7.92
116
+ 2026-02-19T04:30:00Z,0.0,0.0049,0.003,0.0014,0.0005,0.0,293.4,61.96,7.93
117
+ 2026-02-19T04:45:00Z,0.0,0.01,0.0061,0.0029,0.001,0.0,293.24,62.08,7.94
118
+ 2026-02-19T05:00:00Z,0.0,0.0196,0.012,0.0054,0.0022,0.0,292.95,62.32,7.98
119
+ 2026-02-19T05:15:00Z,0.0,0.0337,0.0213,0.0087,0.0037,0.0,292.45,62.72,8.03
120
+ 2026-02-19T05:30:00Z,0.0,0.0541,0.034,0.0143,0.0057,0.0,291.63,63.37,8.12
121
+ 2026-02-19T05:45:00Z,0.0,0.083,0.051,0.023,0.009,0.0,290.39,64.37,8.25
122
+ 2026-02-19T06:00:00Z,0.0,0.125,0.0776,0.0346,0.0128,0.0,288.51,65.89,8.44
123
+ 2026-02-19T06:15:00Z,0.0,0.1663,0.1009,0.0465,0.0189,0.0,286.02,67.88,8.73
124
+ 2026-02-19T06:30:00Z,0.0,0.2081,0.1278,0.0604,0.0199,0.0,282.9,70.42,9.03
125
+ 2026-02-19T06:45:00Z,0.0,0.2552,0.1556,0.0713,0.0284,0.0,279.07,73.48,9.45
126
+ 2026-02-19T07:00:00Z,0.0,0.2886,0.1739,0.084,0.0306,0.0,274.74,76.97,9.91
127
+ 2026-02-19T07:15:00Z,0.0,0.3242,0.201,0.0894,0.0337,0.0,269.88,80.89,10.42
128
+ 2026-02-19T07:30:00Z,0.0,0.3137,0.1969,0.0831,0.0337,0.0,265.17,84.67,10.92
129
+ 2026-02-19T07:45:00Z,0.0,0.3008,0.1885,0.0844,0.0279,0.0,260.66,88.35,11.34
130
+ 2026-02-19T08:00:00Z,0.0,0.2675,0.1677,0.0722,0.0276,0.0,256.65,91.59,11.75
131
+ 2026-02-19T08:15:00Z,0.0,0.2219,0.1383,0.0601,0.0235,0.0,253.32,94.27,12.11
132
+ 2026-02-19T08:30:00Z,0.0,0.1725,0.1057,0.0478,0.0191,0.0,250.73,96.34,12.39
133
+ 2026-02-19T08:45:00Z,0.0,0.128,0.0794,0.0364,0.0122,0.0,248.81,97.9,12.58
134
+ 2026-02-19T09:00:00Z,0.0,0.0916,0.0578,0.0244,0.0094,0.0,247.44,99.01,12.72
135
+ 2026-02-19T09:15:00Z,0.0,0.0582,0.0368,0.016,0.0054,0.0,246.57,99.72,12.8
136
+ 2026-02-19T09:30:00Z,0.0,0.0344,0.0216,0.0094,0.0034,0.0,246.05,100.14,12.85
137
+ 2026-02-19T09:45:00Z,0.0,0.0189,0.0116,0.0054,0.0019,0.0,245.76,100.37,12.88
138
+ 2026-02-19T10:00:00Z,0.0,0.0109,0.0067,0.003,0.0012,0.0,245.6,100.5,12.9
139
+ 2026-02-19T10:15:00Z,0.0,0.0049,0.0029,0.0014,0.0006,0.0,245.53,100.56,12.9
140
+ 2026-02-19T10:30:00Z,0.0,0.0026,0.0016,0.0007,0.0003,0.0,245.49,100.59,12.91
141
+ 2026-02-19T10:45:00Z,0.0,0.0012,0.0007,0.0003,0.0001,0.0,245.47,100.61,12.91
142
+ 2026-02-19T11:00:00Z,0.0,0.0005,0.0003,0.0001,0.0,0.0,245.46,100.61,12.91
143
+ 2026-02-19T11:15:00Z,0.0,0.0002,0.0001,0.0001,0.0,0.0,245.46,100.62,12.91
144
+ 2026-02-19T11:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
145
+ 2026-02-19T11:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
146
+ 2026-02-19T12:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
147
+ 2026-02-19T12:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
148
+ 2026-02-19T12:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
149
+ 2026-02-19T12:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
150
+ 2026-02-19T13:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
151
+ 2026-02-19T13:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
152
+ 2026-02-19T13:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
153
+ 2026-02-19T13:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
154
+ 2026-02-19T14:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
155
+ 2026-02-19T14:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
156
+ 2026-02-19T14:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
157
+ 2026-02-19T14:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,245.46,100.62,12.91
158
+ 2026-02-19T15:00:00Z,0.0,0.0001,0.0001,0.0,0.0,0.0,245.46,100.62,12.91
159
+ 2026-02-19T15:15:00Z,0.0,0.0003,0.0002,0.0001,0.0,0.0,245.46,100.62,12.91
160
+ 2026-02-19T15:30:00Z,0.0,0.0006,0.0004,0.0002,0.0001,0.0,245.45,100.63,12.91
161
+ 2026-02-19T15:45:00Z,0.0,0.0014,0.0009,0.0004,0.0001,0.0,245.43,100.64,12.91
162
+ 2026-02-19T16:00:00Z,0.0,0.0031,0.002,0.0009,0.0003,0.0,245.38,100.68,12.92
163
+ 2026-02-19T16:15:00Z,0.0,0.0061,0.0037,0.0017,0.0007,0.0,245.29,100.76,12.93
164
+ 2026-02-19T16:30:00Z,0.0,0.0111,0.0067,0.0031,0.0013,0.0,245.12,100.89,12.95
165
+ 2026-02-19T16:45:00Z,0.0,0.0209,0.0131,0.0056,0.0022,0.0,244.81,101.14,12.98
166
+ 2026-02-19T17:00:00Z,0.0,0.034,0.0219,0.0085,0.0036,0.0,244.3,101.55,13.04
167
+ 2026-02-19T17:15:00Z,0.0,0.0511,0.032,0.0139,0.0052,0.0,243.53,102.17,13.11
168
+ 2026-02-19T17:30:00Z,0.0,0.0758,0.0476,0.0208,0.0073,0.0,242.4,103.1,13.22
169
+ 2026-02-19T17:45:00Z,0.0,0.104,0.0632,0.0296,0.0112,0.0,240.84,104.35,13.39
170
+ 2026-02-19T18:00:00Z,0.0,0.1283,0.0793,0.035,0.014,0.0,238.91,105.89,13.6
171
+ 2026-02-19T18:15:00Z,0.0,0.1546,0.095,0.0429,0.0167,0.0,236.59,107.75,13.85
172
+ 2026-02-19T18:30:00Z,0.0,0.1725,0.107,0.0475,0.0179,0.0,234.0,109.84,14.12
173
+ 2026-02-19T18:45:00Z,0.0,0.1847,0.1114,0.054,0.0192,0.0,231.24,112.07,14.41
174
+ 2026-02-19T19:00:00Z,0.0,0.1837,0.1121,0.0531,0.0186,0.0,228.48,114.3,14.69
175
+ 2026-02-19T19:15:00Z,0.0,0.1781,0.1097,0.0513,0.0171,0.0,225.81,116.48,14.94
176
+ 2026-02-19T19:30:00Z,0.0,0.161,0.101,0.0443,0.0157,0.0,223.39,118.44,15.18
177
+ 2026-02-19T19:45:00Z,0.0,0.1267,0.0763,0.0367,0.0137,0.0,221.49,119.96,15.38
178
+ 2026-02-19T20:00:00Z,0.0,0.0994,0.0604,0.0286,0.0105,0.0,220.0,121.16,15.54
179
+ 2026-02-19T20:15:00Z,0.0,0.0752,0.0469,0.0212,0.0071,0.0,218.87,122.08,15.65
180
+ 2026-02-19T20:30:00Z,0.0,0.0528,0.0335,0.014,0.0053,0.0,218.08,122.72,15.73
181
+ 2026-02-19T20:45:00Z,0.0,0.0339,0.021,0.0097,0.0032,0.0,217.57,123.14,15.78
182
+ 2026-02-19T21:00:00Z,0.0,0.0204,0.0129,0.0054,0.0021,0.0,217.27,123.39,15.81
183
+ 2026-02-19T21:15:00Z,0.0,0.012,0.0075,0.0032,0.0014,0.0,217.09,123.53,15.83
184
+ 2026-02-19T21:30:00Z,0.0,0.0064,0.004,0.0018,0.0006,0.0,216.99,123.61,15.84
185
+ 2026-02-19T21:45:00Z,0.0,0.0032,0.002,0.0009,0.0003,0.0,216.94,123.65,15.84
186
+ 2026-02-19T22:00:00Z,0.0,0.0015,0.0009,0.0004,0.0002,0.0,216.92,123.66,15.84
187
+ 2026-02-19T22:15:00Z,0.0,0.0007,0.0005,0.0002,0.0001,0.0,216.91,123.67,15.84
188
+ 2026-02-19T22:30:00Z,0.0,0.0003,0.0002,0.0001,0.0,0.0,216.91,123.68,15.84
189
+ 2026-02-19T22:45:00Z,0.0,0.0001,0.0001,0.0,0.0,0.0,216.9,123.68,15.84
190
+ 2026-02-19T23:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
191
+ 2026-02-19T23:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
192
+ 2026-02-19T23:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
193
+ 2026-02-19T23:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
194
+ 2026-02-20T00:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
195
+ 2026-02-20T00:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
196
+ 2026-02-20T00:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
197
+ 2026-02-20T00:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
198
+ 2026-02-20T01:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
199
+ 2026-02-20T01:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
200
+ 2026-02-20T01:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
201
+ 2026-02-20T01:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
202
+ 2026-02-20T02:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
203
+ 2026-02-20T02:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
204
+ 2026-02-20T02:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
205
+ 2026-02-20T02:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
206
+ 2026-02-20T03:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
207
+ 2026-02-20T03:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,216.9,123.68,15.84
208
+ 2026-02-20T03:30:00Z,0.0,0.0001,0.0001,0.0,0.0,0.0,216.9,123.68,15.84
209
+ 2026-02-20T03:45:00Z,0.0,0.0004,0.0003,0.0001,0.0001,0.0,216.9,123.69,15.84
210
+ 2026-02-20T04:00:00Z,0.0,0.0011,0.0007,0.0003,0.0001,0.0,216.88,123.7,15.85
211
+ 2026-02-20T04:15:00Z,0.0,0.0025,0.0015,0.0007,0.0002,0.0,216.84,123.73,15.85
212
+ 2026-02-20T04:30:00Z,0.0,0.005,0.0031,0.0015,0.0005,0.0,216.77,123.79,15.86
213
+ 2026-02-20T04:45:00Z,0.0,0.0103,0.0064,0.0028,0.001,0.0,216.61,123.91,15.87
214
+ 2026-02-20T05:00:00Z,0.0,0.0185,0.0114,0.0052,0.0019,0.0,216.33,124.14,15.9
215
+ 2026-02-20T05:15:00Z,0.0,0.0321,0.0196,0.0088,0.0036,0.0,215.85,124.52,15.96
216
+ 2026-02-20T05:30:00Z,0.0,0.0556,0.0349,0.0151,0.0056,0.0,215.02,125.2,16.04
217
+ 2026-02-20T05:45:00Z,0.0,0.0811,0.0491,0.0236,0.0084,0.0,213.8,126.18,16.17
218
+ 2026-02-20T06:00:00Z,0.0,0.1257,0.0775,0.0351,0.0131,0.0,211.92,127.7,16.36
219
+ 2026-02-20T06:15:00Z,0.0,0.1731,0.109,0.0475,0.0166,0.0,209.32,129.81,16.61
220
+ 2026-02-20T06:30:00Z,0.0,0.2185,0.1343,0.0618,0.0224,0.0,206.04,132.46,16.95
221
+ 2026-02-20T06:45:00Z,0.0,0.2576,0.1594,0.0728,0.0254,0.0,202.18,135.59,17.33
222
+ 2026-02-20T07:00:00Z,0.0,0.2939,0.1792,0.0824,0.0323,0.0,197.77,139.13,17.81
223
+ 2026-02-20T07:15:00Z,0.0,0.3158,0.1931,0.0899,0.0327,0.0,193.03,142.95,18.3
224
+ 2026-02-20T07:30:00Z,0.0,0.3055,0.1858,0.089,0.0307,0.0,188.45,146.66,18.76
225
+ 2026-02-20T07:45:00Z,0.0,0.2903,0.175,0.0824,0.033,0.0,184.1,150.13,19.26
226
+ 2026-02-20T08:00:00Z,0.0,0.2624,0.1608,0.0746,0.027,0.0,180.16,153.31,19.66
227
+ 2026-02-20T08:15:00Z,0.0,0.2148,0.1332,0.0592,0.0224,0.0,176.94,155.91,20.0
228
+ 2026-02-20T08:30:00Z,0.0,0.1793,0.1131,0.0479,0.0182,0.0,174.25,158.08,20.27
229
+ 2026-02-20T08:45:00Z,0.0,0.1276,0.077,0.0362,0.0144,0.0,172.34,159.61,20.49
230
+ 2026-02-20T09:00:00Z,0.0,0.085,0.0507,0.0243,0.01,0.0,171.06,160.62,20.64
231
+ 2026-02-20T09:15:00Z,0.0,0.056,0.0349,0.0156,0.0055,0.0,170.22,161.3,20.72
232
+ 2026-02-20T09:30:00Z,0.0,0.0331,0.02,0.0095,0.0037,0.0,169.72,161.7,20.78
233
+ 2026-02-20T09:45:00Z,0.0,0.0201,0.0127,0.0054,0.0021,0.0,169.42,161.94,20.81
234
+ 2026-02-20T10:00:00Z,0.0,0.0104,0.0065,0.0029,0.001,0.0,169.27,162.07,20.82
235
+ 2026-02-20T10:15:00Z,0.0,0.0051,0.0031,0.0015,0.0006,0.0,169.19,162.13,20.83
236
+ 2026-02-20T10:30:00Z,0.0,0.0026,0.0016,0.0007,0.0002,0.0,169.15,162.16,20.83
237
+ 2026-02-20T10:45:00Z,0.0,0.0011,0.0007,0.0003,0.0001,0.0,169.13,162.18,20.84
238
+ 2026-02-20T11:00:00Z,0.0,0.0005,0.0003,0.0001,0.0,0.0,169.13,162.18,20.84
239
+ 2026-02-20T11:15:00Z,0.0,0.0002,0.0001,0.0001,0.0,0.0,169.12,162.19,20.84
240
+ 2026-02-20T11:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
241
+ 2026-02-20T11:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
242
+ 2026-02-20T12:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
243
+ 2026-02-20T12:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
244
+ 2026-02-20T12:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
245
+ 2026-02-20T12:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
246
+ 2026-02-20T13:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
247
+ 2026-02-20T13:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
248
+ 2026-02-20T13:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
249
+ 2026-02-20T13:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
250
+ 2026-02-20T14:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
251
+ 2026-02-20T14:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
252
+ 2026-02-20T14:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
253
+ 2026-02-20T14:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,169.12,162.19,20.84
254
+ 2026-02-20T15:00:00Z,0.0,0.0001,0.0001,0.0,0.0,0.0,169.12,162.19,20.84
255
+ 2026-02-20T15:15:00Z,0.0,0.0003,0.0002,0.0001,0.0,0.0,169.12,162.19,20.84
256
+ 2026-02-20T15:30:00Z,0.0,0.0006,0.0004,0.0002,0.0001,0.0,169.11,162.2,20.84
257
+ 2026-02-20T15:45:00Z,0.0,0.0014,0.0009,0.0004,0.0001,0.0,169.09,162.22,20.84
258
+ 2026-02-20T16:00:00Z,0.0,0.0031,0.0019,0.0009,0.0003,0.0,169.04,162.25,20.85
259
+ 2026-02-20T16:15:00Z,0.0,0.0059,0.0034,0.0018,0.0007,0.0,168.95,162.32,20.86
260
+ 2026-02-20T16:30:00Z,0.0,0.0113,0.0068,0.0032,0.0013,0.0,168.78,162.46,20.87
261
+ 2026-02-20T16:45:00Z,0.0,0.0198,0.0124,0.0054,0.002,0.0,168.49,162.7,20.9
262
+ 2026-02-20T17:00:00Z,0.0,0.0342,0.022,0.0091,0.0032,0.0,167.97,163.12,20.95
263
+ 2026-02-20T17:15:00Z,0.0,0.0492,0.0301,0.0144,0.0047,0.0,167.24,163.72,21.02
264
+ 2026-02-20T17:30:00Z,0.0,0.0723,0.0455,0.0201,0.0067,0.0,166.15,164.61,21.12
265
+ 2026-02-20T17:45:00Z,0.0,0.1025,0.065,0.0278,0.0097,0.0,164.61,165.86,21.27
266
+ 2026-02-20T18:00:00Z,0.0,0.1273,0.0787,0.035,0.0136,0.0,162.7,167.39,21.47
267
+ 2026-02-20T18:15:00Z,0.0,0.1558,0.0976,0.0427,0.0155,0.0,160.37,169.29,21.7
268
+ 2026-02-20T18:30:00Z,0.0,0.1792,0.1102,0.0498,0.0193,0.0,157.68,171.45,21.99
269
+ 2026-02-20T18:45:00Z,0.0,0.1809,0.1105,0.0506,0.0197,0.0,154.96,173.62,22.29
270
+ 2026-02-20T19:00:00Z,0.0,0.1942,0.1192,0.055,0.02,0.0,152.05,175.97,22.59
271
+ 2026-02-20T19:15:00Z,0.0,0.1768,0.1075,0.0503,0.0189,0.0,149.4,178.11,22.87
272
+ 2026-02-20T19:30:00Z,0.0,0.1534,0.0905,0.0456,0.0173,0.0,147.1,179.94,23.13
273
+ 2026-02-20T19:45:00Z,0.0,0.134,0.0844,0.0353,0.0143,0.0,145.09,181.56,23.35
274
+ 2026-02-20T20:00:00Z,0.0,0.1043,0.0665,0.0272,0.0105,0.0,143.52,182.82,23.5
275
+ 2026-02-20T20:15:00Z,0.0,0.0752,0.0471,0.0207,0.0074,0.0,142.4,183.74,23.62
276
+ 2026-02-20T20:30:00Z,0.0,0.0522,0.0325,0.0144,0.0053,0.0,141.61,184.37,23.7
277
+ 2026-02-20T20:45:00Z,0.0,0.0339,0.0205,0.0095,0.0038,0.0,141.11,184.78,23.75
278
+ 2026-02-20T21:00:00Z,0.0,0.0204,0.0127,0.0057,0.002,0.0,140.8,185.03,23.78
279
+ 2026-02-20T21:15:00Z,0.0,0.0118,0.0072,0.0033,0.0012,0.0,140.62,185.17,23.8
280
+ 2026-02-20T21:30:00Z,0.0,0.0064,0.004,0.0018,0.0007,0.0,140.53,185.25,23.81
281
+ 2026-02-20T21:45:00Z,0.0,0.0032,0.002,0.0009,0.0004,0.0,140.48,185.29,23.82
282
+ 2026-02-20T22:00:00Z,0.0,0.0015,0.0009,0.0004,0.0002,0.0,140.46,185.3,23.82
283
+ 2026-02-20T22:15:00Z,0.0,0.0007,0.0004,0.0002,0.0001,0.0,140.45,185.31,23.82
284
+ 2026-02-20T22:30:00Z,0.0,0.0003,0.0002,0.0001,0.0,0.0,140.44,185.32,23.82
285
+ 2026-02-20T22:45:00Z,0.0,0.0001,0.0001,0.0,0.0,0.0,140.44,185.32,23.82
286
+ 2026-02-20T23:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
287
+ 2026-02-20T23:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
288
+ 2026-02-20T23:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
289
+ 2026-02-20T23:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
290
+ 2026-02-21T00:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
291
+ 2026-02-21T00:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
292
+ 2026-02-21T00:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
293
+ 2026-02-21T00:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
294
+ 2026-02-21T01:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
295
+ 2026-02-21T01:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
296
+ 2026-02-21T01:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
297
+ 2026-02-21T01:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
298
+ 2026-02-21T02:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
299
+ 2026-02-21T02:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
300
+ 2026-02-21T02:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
301
+ 2026-02-21T02:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
302
+ 2026-02-21T03:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
303
+ 2026-02-21T03:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,140.44,185.32,23.82
304
+ 2026-02-21T03:30:00Z,0.0,0.0002,0.0001,0.0,0.0,0.0,140.44,185.32,23.82
305
+ 2026-02-21T03:45:00Z,0.0,0.0004,0.0003,0.0001,0.0,0.0,140.43,185.32,23.82
306
+ 2026-02-21T04:00:00Z,0.0,0.0011,0.0007,0.0003,0.0001,0.0,140.41,185.34,23.82
307
+ 2026-02-21T04:15:00Z,0.0,0.0024,0.0015,0.0007,0.0003,0.0,140.38,185.37,23.83
308
+ 2026-02-21T04:30:00Z,0.0,0.0052,0.0032,0.0014,0.0005,0.0,140.3,185.43,23.83
309
+ 2026-02-21T04:45:00Z,0.0,0.0105,0.0066,0.0028,0.0011,0.0,140.14,185.56,23.85
310
+ 2026-02-21T05:00:00Z,0.0,0.0192,0.0122,0.0052,0.0019,0.0,139.85,185.79,23.88
311
+ 2026-02-21T05:15:00Z,0.0,0.0337,0.0209,0.0095,0.0033,0.0,139.35,186.2,23.93
312
+ 2026-02-21T05:30:00Z,0.0,0.0528,0.0329,0.0143,0.0055,0.0,138.56,186.84,24.01
313
+ 2026-02-21T05:45:00Z,0.0,0.0876,0.055,0.0233,0.0093,0.0,137.24,187.9,24.15
314
+ 2026-02-21T06:00:00Z,0.0,0.1218,0.0737,0.0346,0.0134,0.0,135.42,189.27,24.35
315
+ 2026-02-21T06:15:00Z,0.0,0.1695,0.1048,0.047,0.0177,0.0,132.87,189.27,24.62
316
+ 2026-02-21T06:30:00Z,0.0,0.2259,0.1438,0.0599,0.0222,0.0,129.49,189.27,24.95
317
+ 2026-02-21T06:45:00Z,0.0,0.254,0.1549,0.0728,0.0264,0.0,125.68,189.27,25.35
318
+ 2026-02-21T07:00:00Z,0.0,0.295,0.1819,0.0817,0.0314,0.0,121.25,189.27,25.82
319
+ 2026-02-21T07:15:00Z,0.0,0.3268,0.2073,0.0877,0.0318,0.0,116.35,189.27,26.29
320
+ 2026-02-21T07:30:00Z,0.0,0.3215,0.2039,0.0849,0.0327,0.0,111.53,189.27,26.78
321
+ 2026-02-21T07:45:00Z,0.0,0.3083,0.1936,0.0839,0.0308,0.0,106.9,189.27,27.25
322
+ 2026-02-21T08:00:00Z,0.0,0.2598,0.1585,0.0736,0.0277,0.0,103.01,189.27,27.66
323
+ 2026-02-21T08:15:00Z,0.0,0.212,0.1306,0.059,0.0223,0.0,99.83,189.27,28.0
324
+ 2026-02-21T08:30:00Z,0.0,0.1691,0.1034,0.0467,0.019,0.0,97.29,189.27,28.28
325
+ 2026-02-21T08:45:00Z,0.0,0.123,0.0762,0.0338,0.013,0.0,95.44,189.27,28.48
326
+ 2026-02-21T09:00:00Z,0.0,0.0887,0.0562,0.024,0.0086,0.0,94.11,189.27,28.61
327
+ 2026-02-21T09:15:00Z,0.0,0.0554,0.0343,0.0154,0.0056,0.0,93.28,189.27,28.69
328
+ 2026-02-21T09:30:00Z,0.0,0.0341,0.0204,0.0099,0.0037,0.0,92.77,189.27,28.75
329
+ 2026-02-21T09:45:00Z,0.0,0.0187,0.0115,0.0051,0.002,0.0,92.49,189.27,28.78
330
+ 2026-02-21T10:00:00Z,0.0,0.0108,0.0067,0.003,0.0011,0.0,92.33,189.27,28.79
331
+ 2026-02-21T10:15:00Z,0.0,0.0054,0.0033,0.0015,0.0006,0.0,92.25,189.27,28.8
332
+ 2026-02-21T10:30:00Z,0.0,0.0027,0.0017,0.0007,0.0002,0.0,92.21,189.27,28.8
333
+ 2026-02-21T10:45:00Z,0.0,0.0011,0.0007,0.0003,0.0001,0.0,92.19,189.27,28.81
334
+ 2026-02-21T11:00:00Z,0.0,0.0005,0.0003,0.0001,0.0,0.0,92.19,189.27,28.81
335
+ 2026-02-21T11:15:00Z,0.0,0.0002,0.0001,0.0,0.0,0.0,92.18,189.27,28.81
336
+ 2026-02-21T11:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
337
+ 2026-02-21T11:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
338
+ 2026-02-21T12:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
339
+ 2026-02-21T12:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
340
+ 2026-02-21T12:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
341
+ 2026-02-21T12:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
342
+ 2026-02-21T13:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
343
+ 2026-02-21T13:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
344
+ 2026-02-21T13:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
345
+ 2026-02-21T13:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
346
+ 2026-02-21T14:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
347
+ 2026-02-21T14:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
348
+ 2026-02-21T14:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
349
+ 2026-02-21T14:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,92.18,189.27,28.81
350
+ 2026-02-21T15:00:00Z,0.0,0.0001,0.0001,0.0,0.0,0.0,92.18,189.27,28.81
351
+ 2026-02-21T15:15:00Z,0.0,0.0003,0.0002,0.0001,0.0,0.0,92.18,189.27,28.81
352
+ 2026-02-21T15:30:00Z,0.0,0.0006,0.0004,0.0002,0.0001,0.0,92.17,189.27,28.81
353
+ 2026-02-21T15:45:00Z,0.0,0.0015,0.001,0.0004,0.0001,0.0,92.15,189.27,28.81
354
+ 2026-02-21T16:00:00Z,0.0,0.003,0.0018,0.0009,0.0003,0.0,92.1,189.27,28.82
355
+ 2026-02-21T16:15:00Z,0.0,0.0058,0.0035,0.0017,0.0006,0.0,92.01,189.27,28.82
356
+ 2026-02-21T16:30:00Z,0.0,0.0107,0.0064,0.0031,0.0012,0.0,91.85,189.27,28.84
357
+ 2026-02-21T16:45:00Z,0.0,0.02,0.0126,0.0053,0.0021,0.0,91.55,189.27,28.87
358
+ 2026-02-21T17:00:00Z,0.0,0.0309,0.0183,0.0089,0.0037,0.0,91.09,189.27,28.93
359
+ 2026-02-21T17:15:00Z,0.0,0.0518,0.033,0.0139,0.0049,0.0,90.31,189.27,29.0
360
+ 2026-02-21T17:30:00Z,0.0,0.0736,0.0452,0.0206,0.0077,0.0,89.21,189.27,29.12
361
+ 2026-02-21T17:45:00Z,0.0,0.1003,0.0621,0.0279,0.0102,0.0,87.7,189.27,29.27
362
+ 2026-02-21T18:00:00Z,0.0,0.1239,0.0751,0.0358,0.013,0.0,85.84,189.27,29.47
363
+ 2026-02-21T18:15:00Z,0.0,0.157,0.0975,0.0431,0.0164,0.0,83.49,189.27,29.71
364
+ 2026-02-21T18:30:00Z,0.0,0.178,0.1129,0.0475,0.0176,0.0,80.82,189.27,29.98
365
+ 2026-02-21T18:45:00Z,0.0,0.1895,0.1191,0.0519,0.0185,0.0,77.98,189.27,30.25
366
+ 2026-02-21T19:00:00Z,0.0,0.1846,0.1138,0.0519,0.0189,0.0,75.21,189.27,30.54
367
+ 2026-02-21T19:15:00Z,0.0,0.1723,0.1066,0.0487,0.017,0.0,72.62,189.27,30.79
368
+ 2026-02-21T19:30:00Z,0.0,0.1566,0.0939,0.0452,0.0175,0.0,70.27,189.27,31.05
369
+ 2026-02-21T19:45:00Z,0.0,0.13,0.0791,0.0371,0.0138,0.0,68.32,189.27,31.26
370
+ 2026-02-21T20:00:00Z,0.0,0.102,0.0629,0.0286,0.0106,0.0,66.79,189.27,31.42
371
+ 2026-02-21T20:15:00Z,0.0,0.0743,0.0472,0.0203,0.0068,0.0,65.68,189.27,31.52
372
+ 2026-02-21T20:30:00Z,0.0,0.0543,0.0343,0.0148,0.0052,0.0,64.87,189.27,31.6
373
+ 2026-02-21T20:45:00Z,0.0,0.0338,0.0211,0.0092,0.0035,0.0,64.36,189.27,31.65
374
+ 2026-02-21T21:00:00Z,0.0,0.0212,0.0134,0.0057,0.0021,0.0,64.04,189.27,31.68
375
+ 2026-02-21T21:15:00Z,0.0,0.0115,0.0072,0.003,0.0013,0.0,63.87,189.27,31.7
376
+ 2026-02-21T21:30:00Z,0.0,0.0063,0.0039,0.0018,0.0006,0.0,63.77,189.27,31.71
377
+ 2026-02-21T21:45:00Z,0.0,0.0033,0.002,0.0009,0.0004,0.0,63.72,189.27,31.72
378
+ 2026-02-21T22:00:00Z,0.0,0.0015,0.0009,0.0004,0.0002,0.0,63.7,189.27,31.72
379
+ 2026-02-21T22:15:00Z,0.0,0.0007,0.0004,0.0002,0.0001,0.0,63.69,189.27,31.72
380
+ 2026-02-21T22:30:00Z,0.0,0.0003,0.0002,0.0001,0.0,0.0,63.69,189.27,31.72
381
+ 2026-02-21T22:45:00Z,0.0,0.0001,0.0001,0.0,0.0,0.0,63.69,189.27,31.72
382
+ 2026-02-21T23:00:00Z,0.0,0.0,0.0,0.0,0.0,0.0,63.69,189.27,31.72
383
+ 2026-02-21T23:15:00Z,0.0,0.0,0.0,0.0,0.0,0.0,63.69,189.27,31.72
384
+ 2026-02-21T23:30:00Z,0.0,0.0,0.0,0.0,0.0,0.0,63.69,189.27,31.72
385
+ 2026-02-21T23:45:00Z,0.0,0.0,0.0,0.0,0.0,0.0,63.69,189.27,31.72
output/water/1DAY.csv ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Time,Inlet_Total_L,Pump_Total_L,Shower_Total_L,Kitchen_Total_L,Toilet_Total_L,Unmetered_Total_L,FreshTank_Level_L,FreshTank_Level_Pct,GreyTank_Level_L,GreyTank_Level_Pct,BlackTank_Level_L,BlackTank_Level_Pct
2
+ 2026-02-18T00:00:00Z,10.554,76.638,47.5783,21.1458,7.9139,0.0,293.53,77.54,61.85,32.68,7.91,4.64
3
+ 2026-02-19T00:00:00Z,0.0,76.6258,47.3512,21.3447,7.9299,0.0,216.9,57.3,123.68,65.35,15.84,9.3
4
+ 2026-02-20T00:00:00Z,0.0,76.4636,47.0497,21.4373,7.9766,0.0,140.44,37.1,185.32,97.91,23.82,13.98
5
+ 2026-02-21T00:00:00Z,0.0,76.7549,47.6639,21.1903,7.9007,0.0,63.69,16.83,189.27,100.0,31.72,18.62
output/water/1H.csv ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Time,Inlet_Total_L,Pump_Total_L,Shower_Total_L,Kitchen_Total_L,Toilet_Total_L,Unmetered_Total_L,FreshTank_Level_L,FreshTank_Level_Pct,GreyTank_Level_L,GreyTank_Level_Pct,BlackTank_Level_L,BlackTank_Level_Pct
2
+ 2026-02-18T00:00:00Z,0.0,0.0,0.0,0.0,0.0,0,359.61,95.0,0.0,0.0,0.0,0.0
3
+ 2026-02-18T01:00:00Z,0.0,0.0,0.0,0.0,0.0,0,359.61,95.0,0.0,0.0,0.0,0.0
4
+ 2026-02-18T02:00:00Z,0.0,0.0,0.0,0.0,0.0,0,359.61,95.0,0.0,0.0,0.0,0.0
5
+ 2026-02-18T03:00:00Z,0.0,0.0092,0.0062,0.0025,0.0005,0.0,359.6,95.0,0.01,0.01,0.0,0.0
6
+ 2026-02-18T04:00:00Z,0.0,0.2746,0.1681,0.0774,0.0291,0.0,359.33,94.92,0.23,0.12,0.03,0.02
7
+ 2026-02-18T05:00:00Z,0.0,2.8425,1.7569,0.7879,0.2977,0.0,356.49,94.17,2.52,1.33,0.33,0.19
8
+ 2026-02-18T06:00:00Z,0.0,11.4825,7.1884,3.1167,1.1774,0.0,345.01,91.14,11.79,6.23,1.5,0.88
9
+ 2026-02-18T07:00:00Z,10.554,18.4286,11.4952,5.065,1.8684,0.0,337.13,89.06,26.7,14.11,3.37,1.98
10
+ 2026-02-18T08:00:00Z,0.0,11.7782,7.3056,3.2225,1.2501,0.0,325.35,85.95,36.17,19.11,4.62,2.71
11
+ 2026-02-18T09:00:00Z,0.0,2.8574,1.7167,0.8303,0.3104,0.0,322.49,85.19,38.47,20.33,4.93,2.89
12
+ 2026-02-18T10:00:00Z,0.0,0.2863,0.1751,0.0809,0.0303,0.0,322.21,85.12,38.7,20.45,4.96,2.91
13
+ 2026-02-18T11:00:00Z,0.0,0.0099,0.0064,0.003,0.0005,0.0,322.2,85.12,38.7,20.45,4.96,2.91
14
+ 2026-02-18T12:00:00Z,0.0,0.0,0.0,0.0,0.0,0,322.2,85.12,38.7,20.45,4.96,2.91
15
+ 2026-02-18T13:00:00Z,0.0,0.0,0.0,0.0,0.0,0,322.2,85.12,38.7,20.45,4.96,2.91
16
+ 2026-02-18T14:00:00Z,0.0,0.0,0.0,0.0,0.0,0,322.2,85.12,38.7,20.45,4.96,2.91
17
+ 2026-02-18T15:00:00Z,0.0,0.036,0.0227,0.01,0.0033,0.0,322.16,85.11,38.73,20.46,4.97,2.92
18
+ 2026-02-18T16:00:00Z,0.0,0.6267,0.3916,0.171,0.0641,0.0,321.54,84.94,39.24,20.73,5.03,2.95
19
+ 2026-02-18T17:00:00Z,0.0,3.94,2.4291,1.094,0.4169,0.0,317.6,83.9,42.41,22.41,5.45,3.2
20
+ 2026-02-18T18:00:00Z,0.0,9.6751,5.9574,2.7049,1.0128,0.0,307.92,81.34,50.21,26.53,6.46,3.79
21
+ 2026-02-18T19:00:00Z,0.0,9.8604,6.1813,2.703,0.9761,0.0,298.06,78.74,58.2,30.75,7.44,4.37
22
+ 2026-02-18T20:00:00Z,0.0,3.8651,2.3665,1.0933,0.4053,0.0,294.2,77.72,61.32,32.4,7.84,4.6
23
+ 2026-02-18T21:00:00Z,0.0,0.628,0.3878,0.1729,0.0673,0.0,293.57,77.55,61.82,32.66,7.91,4.64
24
+ 2026-02-18T22:00:00Z,0.0,0.0375,0.0233,0.0105,0.0037,0.0,293.53,77.54,61.85,32.68,7.91,4.64
25
+ 2026-02-18T23:00:00Z,0.0,0.0,0.0,0.0,0.0,0,293.53,77.54,61.85,32.68,7.91,4.64
26
+ 2026-02-19T00:00:00Z,0.0,0.0,0.0,0.0,0.0,0,293.53,77.54,61.85,32.68,7.91,4.64
27
+ 2026-02-19T01:00:00Z,0.0,0.0,0.0,0.0,0.0,0,293.53,77.54,61.85,32.68,7.91,4.64
28
+ 2026-02-19T02:00:00Z,0.0,0.0,0.0,0.0,0.0,0,293.53,77.54,61.85,32.68,7.91,4.64
29
+ 2026-02-19T03:00:00Z,0.0,0.0091,0.006,0.0026,0.0005,0.0,293.52,77.54,61.86,32.68,7.91,4.64
30
+ 2026-02-19T04:00:00Z,0.0,0.276,0.1675,0.0798,0.0287,0.0,293.24,77.47,62.08,32.8,7.94,4.66
31
+ 2026-02-19T05:00:00Z,0.0,2.8554,1.7745,0.7724,0.3085,0.0,290.39,76.71,64.37,34.01,8.25,4.84
32
+ 2026-02-19T06:00:00Z,0.0,11.3203,6.9281,3.1922,1.2,0.0,279.07,73.72,73.48,38.82,9.45,5.55
33
+ 2026-02-19T07:00:00Z,0.0,18.4076,11.4041,5.1151,1.8884,0.0,260.66,68.86,88.35,46.68,11.34,6.66
34
+ 2026-02-19T08:00:00Z,0.0,11.8492,7.3657,3.2469,1.2366,0.0,248.81,65.73,97.9,51.72,12.58,7.39
35
+ 2026-02-19T09:00:00Z,0.0,3.0474,1.9177,0.8276,0.3021,0.0,245.76,64.92,100.37,53.03,12.88,7.56
36
+ 2026-02-19T10:00:00Z,0.0,0.2934,0.1796,0.0816,0.0322,0.0,245.47,64.85,100.61,53.16,12.91,7.58
37
+ 2026-02-19T11:00:00Z,0.0,0.01,0.0068,0.0028,0.0004,0.0,245.46,64.84,100.62,53.16,12.91,7.58
38
+ 2026-02-19T12:00:00Z,0.0,0.0,0.0,0.0,0.0,0,245.46,64.84,100.62,53.16,12.91,7.58
39
+ 2026-02-19T13:00:00Z,0.0,0.0,0.0,0.0,0.0,0,245.46,64.84,100.62,53.16,12.91,7.58
40
+ 2026-02-19T14:00:00Z,0.0,0.0,0.0,0.0,0.0,0,245.46,64.84,100.62,53.16,12.91,7.58
41
+ 2026-02-19T15:00:00Z,0.0,0.0358,0.0225,0.01,0.0033,0.0,245.43,64.84,100.64,53.17,12.91,7.58
42
+ 2026-02-19T16:00:00Z,0.0,0.6173,0.3825,0.1677,0.0671,0.0,244.81,64.67,101.14,53.44,12.98,7.62
43
+ 2026-02-19T17:00:00Z,0.0,3.9726,2.472,1.0923,0.4083,0.0,240.84,63.62,104.35,55.13,13.39,7.86
44
+ 2026-02-19T18:00:00Z,0.0,9.6008,5.8913,2.6925,1.017,0.0,231.24,61.09,112.07,59.21,14.41,8.46
45
+ 2026-02-19T19:00:00Z,0.0,9.7421,5.9861,2.7796,0.9764,0.0,221.49,58.51,119.96,63.38,15.38,9.03
46
+ 2026-02-19T20:00:00Z,0.0,3.9207,2.4269,1.1019,0.3919,0.0,217.57,57.48,123.14,65.06,15.78,9.26
47
+ 2026-02-19T21:00:00Z,0.0,0.6292,0.3952,0.1691,0.0649,0.0,216.94,57.31,123.65,65.33,15.84,9.3
48
+ 2026-02-19T22:00:00Z,0.0,0.0389,0.0247,0.0106,0.0036,0.0,216.9,57.3,123.68,65.35,15.84,9.3
49
+ 2026-02-19T23:00:00Z,0.0,0.0,0.0,0.0,0.0,0,216.9,57.3,123.68,65.35,15.84,9.3
50
+ 2026-02-20T00:00:00Z,0.0,0.0,0.0,0.0,0.0,0,216.9,57.3,123.68,65.35,15.84,9.3
51
+ 2026-02-20T01:00:00Z,0.0,0.0,0.0,0.0,0.0,0,216.9,57.3,123.68,65.35,15.84,9.3
52
+ 2026-02-20T02:00:00Z,0.0,0.0,0.0,0.0,0.0,0,216.9,57.3,123.68,65.35,15.84,9.3
53
+ 2026-02-20T03:00:00Z,0.0,0.009,0.0058,0.0024,0.0008,0.0,216.9,57.3,123.69,65.35,15.84,9.3
54
+ 2026-02-20T04:00:00Z,0.0,0.2834,0.1753,0.0791,0.029,0.0,216.61,57.22,123.91,65.47,15.87,9.32
55
+ 2026-02-20T05:00:00Z,0.0,2.8096,1.7248,0.7924,0.2924,0.0,213.8,56.48,126.18,66.67,16.17,9.49
56
+ 2026-02-20T06:00:00Z,0.0,11.6232,7.2033,3.2576,1.1623,0.0,202.18,53.41,135.59,71.64,17.33,10.17
57
+ 2026-02-20T07:00:00Z,0.0,18.0831,10.9966,5.156,1.9305,0.0,184.1,48.63,150.13,79.32,19.26,11.31
58
+ 2026-02-20T08:00:00Z,0.0,11.7606,7.2625,3.2684,1.2297,0.0,172.34,45.53,159.61,84.33,20.49,12.03
59
+ 2026-02-20T09:00:00Z,0.0,2.9139,1.7728,0.8214,0.3197,0.0,169.42,44.76,161.94,85.56,20.81,12.22
60
+ 2026-02-20T10:00:00Z,0.0,0.2872,0.1785,0.0804,0.0283,0.0,169.13,44.68,162.18,85.69,20.84,12.23
61
+ 2026-02-20T11:00:00Z,0.0,0.0103,0.0071,0.0026,0.0006,0.0,169.12,44.68,162.19,85.69,20.84,12.23
62
+ 2026-02-20T12:00:00Z,0.0,0.0,0.0,0.0,0.0,0,169.12,44.68,162.19,85.69,20.84,12.23
63
+ 2026-02-20T13:00:00Z,0.0,0.0,0.0,0.0,0.0,0,169.12,44.68,162.19,85.69,20.84,12.23
64
+ 2026-02-20T14:00:00Z,0.0,0.0,0.0,0.0,0.0,0,169.12,44.68,162.19,85.69,20.84,12.23
65
+ 2026-02-20T15:00:00Z,0.0,0.0353,0.0225,0.0098,0.003,0.0,169.09,44.67,162.22,85.71,20.84,12.23
66
+ 2026-02-20T16:00:00Z,0.0,0.6016,0.3689,0.1684,0.0643,0.0,168.49,44.51,162.7,85.96,20.9,12.27
67
+ 2026-02-20T17:00:00Z,0.0,3.8733,2.4396,1.071,0.3627,0.0,164.61,43.49,165.86,87.63,21.27,12.49
68
+ 2026-02-20T18:00:00Z,0.0,9.6489,5.9556,2.6714,1.0219,0.0,154.96,40.94,173.62,91.73,22.29,13.09
69
+ 2026-02-20T19:00:00Z,0.0,9.8755,6.024,2.7937,1.0578,0.0,145.09,38.33,181.56,95.93,23.35,13.71
70
+ 2026-02-20T20:00:00Z,0.0,3.984,2.5001,1.0775,0.4064,0.0,141.11,37.28,184.78,97.63,23.75,13.94
71
+ 2026-02-20T21:00:00Z,0.0,0.6272,0.3884,0.175,0.0638,0.0,140.48,37.11,185.29,97.9,23.82,13.98
72
+ 2026-02-20T22:00:00Z,0.0,0.0375,0.0239,0.0102,0.0034,0.0,140.44,37.1,185.32,97.91,23.82,13.98
73
+ 2026-02-20T23:00:00Z,0.0,0.0,0.0,0.0,0.0,0,140.44,37.1,185.32,97.91,23.82,13.98
74
+ 2026-02-21T00:00:00Z,0.0,0.0,0.0,0.0,0.0,0,140.44,37.1,185.32,97.91,23.82,13.98
75
+ 2026-02-21T01:00:00Z,0.0,0.0,0.0,0.0,0.0,0,140.44,37.1,185.32,97.91,23.82,13.98
76
+ 2026-02-21T02:00:00Z,0.0,0.0,0.0,0.0,0.0,0,140.44,37.1,185.32,97.91,23.82,13.98
77
+ 2026-02-21T03:00:00Z,0.0,0.0095,0.0065,0.0025,0.0005,0.0,140.43,37.1,185.32,97.91,23.82,13.98
78
+ 2026-02-21T04:00:00Z,0.0,0.2884,0.1804,0.0785,0.0295,0.0,140.14,37.02,185.56,98.04,23.85,14.0
79
+ 2026-02-21T05:00:00Z,0.0,2.8995,1.8152,0.7841,0.3002,0.0,137.24,36.25,187.9,99.28,24.15,14.18
80
+ 2026-02-21T06:00:00Z,0.0,11.5674,7.1575,3.2144,1.1955,0.0,125.68,33.2,189.27,100.0,25.35,14.88
81
+ 2026-02-21T07:00:00Z,0.0,18.7733,11.8003,5.0715,1.9015,0.0,106.9,28.24,189.27,100.0,27.25,16.0
82
+ 2026-02-21T08:00:00Z,0.0,11.4578,7.031,3.1968,1.23,0.0,95.44,25.21,189.27,100.0,28.48,16.72
83
+ 2026-02-21T09:00:00Z,0.0,2.9528,1.8373,0.8171,0.2984,0.0,92.49,24.43,189.27,100.0,28.78,16.9
84
+ 2026-02-21T10:00:00Z,0.0,0.2995,0.1855,0.0833,0.0307,0.0,92.19,24.35,189.27,100.0,28.81,16.91
85
+ 2026-02-21T11:00:00Z,0.0,0.0098,0.0064,0.0027,0.0007,0.0,92.18,24.35,189.27,100.0,28.81,16.91
86
+ 2026-02-21T12:00:00Z,0.0,0.0,0.0,0.0,0.0,0,92.18,24.35,189.27,100.0,28.81,16.91
87
+ 2026-02-21T13:00:00Z,0.0,0.0,0.0,0.0,0.0,0,92.18,24.35,189.27,100.0,28.81,16.91
88
+ 2026-02-21T14:00:00Z,0.0,0.0,0.0,0.0,0.0,0,92.18,24.35,189.27,100.0,28.81,16.91
89
+ 2026-02-21T15:00:00Z,0.0,0.0363,0.0235,0.0093,0.0035,0.0,92.15,24.34,189.27,100.0,28.81,16.91
90
+ 2026-02-21T16:00:00Z,0.0,0.5942,0.3652,0.1655,0.0635,0.0,91.55,24.18,189.27,100.0,28.87,16.95
91
+ 2026-02-21T17:00:00Z,0.0,3.8488,2.3794,1.0711,0.3983,0.0,87.7,23.17,189.27,100.0,29.27,17.18
92
+ 2026-02-21T18:00:00Z,0.0,9.7271,6.0705,2.6744,0.9822,0.0,77.98,20.6,189.27,100.0,30.25,17.76
93
+ 2026-02-21T19:00:00Z,0.0,9.6521,5.9017,2.7439,1.0065,0.0,68.32,18.05,189.27,100.0,31.26,18.35
94
+ 2026-02-21T20:00:00Z,0.0,3.9649,2.4809,1.0946,0.3894,0.0,64.36,17.0,189.27,100.0,31.65,18.58
95
+ 2026-02-21T21:00:00Z,0.0,0.6353,0.3986,0.1701,0.0666,0.0,63.72,16.83,189.27,100.0,31.72,18.62
96
+ 2026-02-21T22:00:00Z,0.0,0.0382,0.024,0.0105,0.0037,0.0,63.69,16.83,189.27,100.0,31.72,18.62
97
+ 2026-02-21T23:00:00Z,0.0,0.0,0.0,0.0,0.0,0,63.69,16.83,189.27,100.0,31.72,18.62
output/water/1MIN.csv ADDED
The diff for this file is too large to render. See raw diff
 
requirements.txt CHANGED
@@ -1,4 +1,2 @@
1
- fastapi
2
- uvicorn
3
- pydantic
4
- aiofiles
 
1
+ fastapi>=0.109.0
2
+ uvicorn[standard]>=0.27.0
 
 
stability_index.py ADDED
@@ -0,0 +1,593 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Stability Index Engine
3
+ ======================
4
+ Computes a deterministic composite risk score SI ∈ [0, 10] per minute
5
+ from the generated power and water 1MIN CSV files.
6
+
7
+ Formula (v1.0 β€” matches stability_index_spec.docx):
8
+
9
+ SI(t) = SI_raw(t) Γ— V_dep(t)
10
+
11
+ SI_raw = 10 Γ— (w_batΒ·C_bat + w_freshΒ·C_fresh + w_wasteΒ·C_waste
12
+ + w_energyΒ·C_energy + w_stableΒ·C_stable)
13
+
14
+ V_dep = clamp(ttc / horizon_hr, 0.5, 1.0)
15
+ where ttc = min(time_to_crit_battery, time_to_crit_fresh) [hours]
16
+
17
+ Output CSV columns:
18
+ Time, SI, SI_raw, C_bat, C_fresh, C_waste, C_grey, C_black,
19
+ C_energy, C_stable, V_dep, ttc_hr, band, warm_up,
20
+ Battery_Level_Pct, FreshTank_Level_L, GreyTank_Level_L, BlackTank_Level_L
21
+
22
+ Usage:
23
+ python stability_index.py # uses ./output/
24
+ python stability_index.py --power_dir data/power --water_dir data/water
25
+ python stability_index.py --config si_config.json --out si_scores.csv
26
+ python stability_index.py --summary
27
+ """
28
+
29
+ import csv
30
+ import json
31
+ import math
32
+ import argparse
33
+ import statistics
34
+ from collections import deque
35
+ from pathlib import Path
36
+
37
+
38
+ # ─────────────────────────────────────────────────────────────────────────────
39
+ # DEFAULT CONFIGURATION
40
+ # ─────────────────────────────────────────────────────────────────────────────
41
+
42
+ DEFAULT_CONFIG = {
43
+ # --- Component weights (must sum to 1.0) ---
44
+ "w_bat": 0.35,
45
+ "w_fresh": 0.25,
46
+ "w_waste": 0.12,
47
+ "w_energy": 0.18,
48
+ "w_stable": 0.10,
49
+
50
+ # --- Battery thresholds (%) ---
51
+ "bat_crit_pct": 20.0,
52
+ "bat_warn_pct": 50.0,
53
+
54
+ # --- Fresh water thresholds (%) ---
55
+ "fresh_crit_pct": 15.0,
56
+ "fresh_warn_pct": 40.0,
57
+
58
+ # --- Tank capacities (litres) ---
59
+ "fresh_cap_L": 378.541, # 100 gal
60
+ "grey_cap_L": 189.271, # 50 gal
61
+ "black_cap_L": 170.344, # 45 gal
62
+
63
+ # --- Waste tank penalty start (fill fraction 0–1) ---
64
+ "grey_penalty_start": 0.70,
65
+ "black_penalty_start": 0.60,
66
+
67
+ # --- Waste composite weights ---
68
+ "grey_weight_in_waste": 0.60,
69
+ "black_weight_in_waste": 0.40,
70
+
71
+ # --- Energy balance (C_energy) ---
72
+ "energy_window_min": 60,
73
+ "solar_cap_kW": 5.25,
74
+
75
+ # --- Consumption stability (C_stable) ---
76
+ "stable_window_min": 30,
77
+ "stable_k": 2.0,
78
+ "stable_mu_floor": 0.05,
79
+
80
+ # --- Depletion velocity modifier (V_dep) ---
81
+ "velocity_window_min": 60,
82
+ "velocity_horizon_hr": 4.0,
83
+ "velocity_min_factor": 0.5,
84
+
85
+ # --- Score band thresholds ---
86
+ "bands": [
87
+ {"min": 8.0, "max": 10.0, "label": "Excellent"},
88
+ {"min": 6.0, "max": 8.0, "label": "Good"},
89
+ {"min": 4.0, "max": 6.0, "label": "Fair"},
90
+ {"min": 2.0, "max": 4.0, "label": "Poor"},
91
+ {"min": 0.0, "max": 2.0, "label": "Critical"},
92
+ ],
93
+ }
94
+
95
+ LOAD_COLS = [
96
+ "HVAC_Flow_kW", "Lighting_Flow_kW", "Devices_Flow_kW", "Fridge_Flow_kW",
97
+ "WaterPump_Flow_kW", "Cooking_Flow_kW", "Inverter_Flow_kW", "Unmetered_Flow_kW",
98
+ ]
99
+
100
+
101
+ # ─────────────────────────────────────────────────────────────────────────────
102
+ # HELPERS
103
+ # ─────────────────────────────────────────────────────────────────────────────
104
+
105
+ def clamp(val, lo, hi):
106
+ return max(lo, min(hi, val))
107
+
108
+
109
+ def band_label(si, bands):
110
+ for b in bands:
111
+ if b["min"] <= si <= b["max"]:
112
+ return b["label"]
113
+ return "Critical"
114
+
115
+
116
+ def load_config(path):
117
+ cfg = dict(DEFAULT_CONFIG)
118
+ if path:
119
+ with open(path) as f:
120
+ cfg.update(json.load(f))
121
+ total_w = cfg["w_bat"] + cfg["w_fresh"] + cfg["w_waste"] + cfg["w_energy"] + cfg["w_stable"]
122
+ if abs(total_w - 1.0) > 1e-6:
123
+ raise ValueError(f"Component weights must sum to 1.0, got {total_w:.6f}")
124
+ return cfg
125
+
126
+
127
+ def load_csv(path):
128
+ with open(path, newline="") as f:
129
+ return list(csv.DictReader(f))
130
+
131
+
132
+ def merge_rows(power_rows, water_rows):
133
+ """Inner join power + water rows on Time. Both files must be from same 1MIN export."""
134
+ if len(power_rows) == len(water_rows):
135
+ merged = []
136
+ for i, (p, w) in enumerate(zip(power_rows, water_rows)):
137
+ if p["Time"] != w["Time"]:
138
+ raise ValueError(
139
+ f"Timestamp mismatch at row {i}: power={p['Time']} water={w['Time']}"
140
+ )
141
+ merged.append({**p, **w})
142
+ return merged
143
+ # Fallback: join by timestamp key
144
+ water_idx = {r["Time"]: r for r in water_rows}
145
+ return [{**p, **water_idx[p["Time"]]} for p in power_rows if p["Time"] in water_idx]
146
+
147
+
148
+ # ─────────────────────────────────────────────────────────────────────────────
149
+ # COMPONENT SCORE FUNCTIONS
150
+ # ─────────────────────────────────────────────────────────────────────────────
151
+
152
+ def c_bat(bat_pct, crit, warn):
153
+ """
154
+ Piecewise linear Battery SOC score β†’ [0, 1].
155
+
156
+ Zones (configurable via crit/warn thresholds):
157
+ [0, crit) β†’ [0.00, 0.30) steep penalty
158
+ [crit, warn) β†’ [0.30, 0.70) moderate penalty
159
+ [warn, 100] β†’ [0.70, 1.00] comfort zone
160
+ """
161
+ p = clamp(bat_pct, 0.0, 100.0) / 100.0
162
+ c = crit / 100.0
163
+ w = warn / 100.0
164
+ if p < c:
165
+ return 0.00 + 0.30 * (p / c)
166
+ elif p < w:
167
+ return 0.30 + 0.40 * ((p - c) / (w - c))
168
+ else:
169
+ return 0.70 + 0.30 * ((p - w) / (1.0 - w))
170
+
171
+
172
+ def c_fresh(fresh_L, cap_L, crit_pct, warn_pct):
173
+ """
174
+ Piecewise linear Fresh Water score β€” identical formula to c_bat,
175
+ applied to fill percentage. β†’ [0, 1].
176
+ """
177
+ pct = clamp(fresh_L / cap_L * 100.0, 0.0, 100.0)
178
+ return c_bat(pct, crit_pct, warn_pct)
179
+
180
+
181
+ def headroom_score(fill, penalty_start):
182
+ """
183
+ Single-tank waste headroom score.
184
+ fill : fraction of tank capacity used [0, 1]
185
+ penalty_start : fill fraction above which extra penalty applies
186
+
187
+ Linear headroom below penalty_start; steeply penalised above.
188
+ Returns 1.0 (empty) β†’ 0.0 (overflow).
189
+ """
190
+ fill = clamp(fill, 0.0, 1.0)
191
+ headroom = 1.0 - fill
192
+ if fill <= penalty_start:
193
+ return headroom
194
+ excess = fill - penalty_start
195
+ range_above = 1.0 - penalty_start
196
+ factor = 1.0 - 3.0 * excess / range_above
197
+ return clamp(headroom * factor, 0.0, 1.0)
198
+
199
+
200
+ def c_waste(grey_L, black_L, grey_cap, black_cap,
201
+ grey_penalty, black_penalty, grey_w, black_w):
202
+ """
203
+ Composite waste headroom = grey_w Γ— C_grey + black_w Γ— C_black.
204
+ Returns (C_waste, C_grey, C_black).
205
+ """
206
+ cg = headroom_score(grey_L / grey_cap, grey_penalty)
207
+ cb = headroom_score(black_L / black_cap, black_penalty)
208
+ return grey_w * cg + black_w * cb, cg, cb
209
+
210
+
211
+ def c_energy(gen_window, load_window, solar_cap_kw):
212
+ """
213
+ Rolling-window energy balance.
214
+ net_norm = clamp((mean_gen - mean_load) / solar_cap, -1, +1)
215
+ C_energy = 0.5 + 0.5 Γ— net_norm
216
+ """
217
+ if not gen_window:
218
+ return 0.5
219
+ gen_mean = sum(gen_window) / len(gen_window)
220
+ load_mean = sum(load_window) / len(load_window)
221
+ net_norm = clamp((gen_mean - load_mean) / max(solar_cap_kw, 0.01), -1.0, 1.0)
222
+ return 0.5 + 0.5 * net_norm
223
+
224
+
225
+ def c_stable(load_window, k, mu_floor):
226
+ """
227
+ Consumption stability via Coefficient of Variation.
228
+ CV = Οƒ / max(ΞΌ, mu_floor)
229
+ C_stable = 1 / (1 + k Γ— CV)
230
+ """
231
+ n = len(load_window)
232
+ if n < 2:
233
+ return 1.0
234
+ mu = sum(load_window) / n
235
+ if mu < 1e-6:
236
+ return 1.0
237
+ variance = sum((x - mu) ** 2 for x in load_window) / (n - 1)
238
+ sigma = math.sqrt(variance)
239
+ cv = sigma / max(mu, mu_floor)
240
+ return 1.0 / (1.0 + k * cv)
241
+
242
+
243
+ def v_dep(bat_window, fresh_window,
244
+ bat_pct_now, fresh_L_now,
245
+ fresh_cap_L, bat_crit_pct,
246
+ horizon_hr, v_floor):
247
+ """
248
+ Depletion velocity modifier β†’ (V_dep, ttc_hr).
249
+
250
+ Computes velocity of battery (% / hr) and fresh water (L / hr),
251
+ estimates time to critical threshold for each, takes the minimum,
252
+ then maps to a [v_floor, 1.0] suppression factor over horizon_hr.
253
+ """
254
+ N = len(bat_window)
255
+ hrs = N / 60.0
256
+ INF = float("inf")
257
+
258
+ # Battery velocity (%/hr) β€” negative means depleting
259
+ v_bat = ((bat_pct_now - bat_window[0]) / hrs) if N >= 2 and hrs > 0 else 0.0
260
+ if v_bat < 0:
261
+ gap_bat = bat_pct_now - bat_crit_pct
262
+ ttc_bat = (gap_bat / max(abs(v_bat), 0.01)) if gap_bat > 0 else 0.0
263
+ else:
264
+ ttc_bat = INF
265
+
266
+ # Fresh water velocity (L/hr) β€” negative means depleting
267
+ crit_fresh_L = 0.15 * fresh_cap_L
268
+ v_fresh = ((fresh_L_now - fresh_window[0]) / hrs) if N >= 2 and hrs > 0 else 0.0
269
+ if v_fresh < 0:
270
+ gap_fresh = fresh_L_now - crit_fresh_L
271
+ ttc_fresh = (gap_fresh / max(abs(v_fresh), 0.1)) if gap_fresh > 0 else 0.0
272
+ else:
273
+ ttc_fresh = INF
274
+
275
+ ttc = min(ttc_bat, ttc_fresh)
276
+ ttc_hr = round(ttc, 4) if ttc != INF else 9999.0 # 9999 = "no depletion risk"
277
+ vd = clamp(ttc / horizon_hr, v_floor, 1.0) if ttc != INF else 1.0
278
+ return vd, ttc_hr
279
+
280
+
281
+ # ─────────────────────────────��───────────────────────────────────────────────
282
+ # MAIN CALCULATION LOOP
283
+ # ─────────────────────────────────────────────────────────────────────────────
284
+
285
+ def calculate(power_path, water_path, cfg):
286
+ """
287
+ Sequentially processes every 1-minute row.
288
+ Maintains rolling windows for multi-row computations.
289
+ Returns list of output dicts.
290
+ """
291
+ rows = merge_rows(load_csv(power_path), load_csv(water_path))
292
+
293
+ N_energy = cfg["energy_window_min"]
294
+ N_stable = cfg["stable_window_min"]
295
+ N_velocity = cfg["velocity_window_min"]
296
+ warm_up = max(N_energy, N_velocity) # longest window = warm-up horizon
297
+
298
+ gen_win = deque(maxlen=N_energy) # Solar + Shore [kW]
299
+ load_win_e = deque(maxlen=N_energy) # total load [kW] β€” energy window
300
+ load_win_s = deque(maxlen=N_stable) # total load [kW] β€” stability window
301
+ bat_win = deque(maxlen=N_velocity) # battery % β€” velocity window
302
+ fresh_win = deque(maxlen=N_velocity) # fresh water L β€” velocity window
303
+
304
+ results = []
305
+
306
+ for i, row in enumerate(rows):
307
+ # ── Parse ──────────────────────────────────────────────────────────
308
+ bat_pct = float(row["Battery_Level_Pct"])
309
+ fresh_L = float(row["FreshTank_Level_L"])
310
+ grey_L = float(row["GreyTank_Level_L"])
311
+ black_L = float(row["BlackTank_Level_L"])
312
+ solar_kw = float(row["Solar_Flow_kW"])
313
+ shore_kw = float(row["Shore_Flow_kW"])
314
+ total_load = sum(float(row[c]) for c in LOAD_COLS)
315
+
316
+ # ── Feed rolling windows ───────────────────────────────────────────
317
+ gen_win.append(solar_kw + shore_kw)
318
+ load_win_e.append(total_load)
319
+ load_win_s.append(total_load)
320
+ bat_win.append(bat_pct)
321
+ fresh_win.append(fresh_L)
322
+
323
+ # ── Component scores ───────────────────────────────────────────────
324
+ sc_bat = c_bat(bat_pct, cfg["bat_crit_pct"], cfg["bat_warn_pct"])
325
+
326
+ sc_fresh = c_fresh(
327
+ fresh_L, cfg["fresh_cap_L"],
328
+ cfg["fresh_crit_pct"], cfg["fresh_warn_pct"]
329
+ )
330
+
331
+ sc_waste, sc_grey, sc_black = c_waste(
332
+ grey_L, black_L,
333
+ cfg["grey_cap_L"], cfg["black_cap_L"],
334
+ cfg["grey_penalty_start"], cfg["black_penalty_start"],
335
+ cfg["grey_weight_in_waste"], cfg["black_weight_in_waste"]
336
+ )
337
+
338
+ sc_energy = c_energy(gen_win, load_win_e, cfg["solar_cap_kW"])
339
+
340
+ sc_stable = c_stable(load_win_s, cfg["stable_k"], cfg["stable_mu_floor"])
341
+
342
+ # ── Weighted sum ───────────────────────────────────────────────────
343
+ si_raw = 10.0 * (
344
+ cfg["w_bat"] * sc_bat +
345
+ cfg["w_fresh"] * sc_fresh +
346
+ cfg["w_waste"] * sc_waste +
347
+ cfg["w_energy"] * sc_energy +
348
+ cfg["w_stable"] * sc_stable
349
+ )
350
+ si_raw = clamp(si_raw, 0.0, 10.0)
351
+
352
+ # ── Depletion velocity modifier ────────────────────────────────────
353
+ vd, ttc_hr = v_dep(
354
+ bat_win, fresh_win,
355
+ bat_pct, fresh_L,
356
+ cfg["fresh_cap_L"], cfg["bat_crit_pct"],
357
+ cfg["velocity_horizon_hr"], cfg["velocity_min_factor"]
358
+ )
359
+
360
+ si_final = clamp(si_raw * vd, 0.0, 10.0)
361
+
362
+ results.append({
363
+ "Time": row["Time"],
364
+ "SI": round(si_final, 4),
365
+ "SI_raw": round(si_raw, 4),
366
+ "C_bat": round(sc_bat, 4),
367
+ "C_fresh": round(sc_fresh, 4),
368
+ "C_waste": round(sc_waste, 4),
369
+ "C_grey": round(sc_grey, 4),
370
+ "C_black": round(sc_black, 4),
371
+ "C_energy": round(sc_energy, 4),
372
+ "C_stable": round(sc_stable, 4),
373
+ "V_dep": round(vd, 4),
374
+ "ttc_hr": ttc_hr,
375
+ "band": band_label(si_final, cfg["bands"]),
376
+ "warm_up": "true" if i < warm_up else "false",
377
+ # Passthrough context columns
378
+ "Battery_Level_Pct": round(bat_pct, 2),
379
+ "FreshTank_Level_L": round(fresh_L, 2),
380
+ "GreyTank_Level_L": round(grey_L, 2),
381
+ "BlackTank_Level_L": round(black_L, 2),
382
+ })
383
+
384
+ return results
385
+
386
+
387
+ # ─────────────────────────────────────────────────────────────────────────────
388
+ # SUMMARY REPORT
389
+ # ─────────────────────────────────────────────────────────────────────────────
390
+
391
+ def print_summary(results, cfg):
392
+ warm = [r for r in results if r["warm_up"] == "false"] or results
393
+
394
+ si_vals = [r["SI"] for r in warm]
395
+ si_mean = statistics.mean(si_vals)
396
+ si_min = min(si_vals)
397
+ si_max = max(si_vals)
398
+ si_std = statistics.stdev(si_vals) if len(si_vals) > 1 else 0.0
399
+
400
+ band_counts = {}
401
+ for r in warm:
402
+ band_counts[r["band"]] = band_counts.get(r["band"], 0) + 1
403
+
404
+ worst = min(warm, key=lambda r: r["SI"])
405
+ best = max(warm, key=lambda r: r["SI"])
406
+
407
+ def cmean(key):
408
+ return round(statistics.mean(r[key] for r in warm), 3)
409
+
410
+ W = 62
411
+ sep = "─" * W
412
+
413
+ print(f"\n{'═' * W}")
414
+ print(f" STABILITY INDEX β€” SUMMARY REPORT")
415
+ print(f" Period : {results[0]['Time']} β†’ {results[-1]['Time']}")
416
+ print(f" Rows : {len(results):,} | Warm-up excluded: {len(results)-len(warm):,}")
417
+ print(f"{'═' * W}")
418
+
419
+ print(f"\n OVERALL SI")
420
+ print(f" {sep}")
421
+ print(f" Mean : {si_mean:6.3f} ({band_label(si_mean, cfg['bands'])})")
422
+ print(f" Min : {si_min:6.3f} @ {worst['Time']} [{worst['band']}]")
423
+ print(f" Max : {si_max:6.3f} @ {best['Time']} [{best['band']}]")
424
+ print(f" Std : {si_std:6.3f}")
425
+
426
+ print(f"\n BAND DISTRIBUTION")
427
+ print(f" {sep}")
428
+ total = len(warm)
429
+ for b in ["Excellent", "Good", "Fair", "Poor", "Critical"]:
430
+ n = band_counts.get(b, 0)
431
+ pct = n / total * 100 if total else 0
432
+ bar = "β–ˆ" * int(pct / 2)
433
+ print(f" {b:<12} {n:>5} min ({pct:5.1f}%) {bar}")
434
+
435
+ print(f"\n COMPONENT AVERAGES (0–1, higher = more stable)")
436
+ print(f" {sep}")
437
+ rows = [
438
+ ("C_bat", "Battery SOC ", cfg["w_bat"]),
439
+ ("C_fresh", "Fresh Water ", cfg["w_fresh"]),
440
+ ("C_waste", "Waste Headroom ", cfg["w_waste"]),
441
+ ("C_grey", " ↳ Grey tank ", cfg["grey_weight_in_waste"] * cfg["w_waste"]),
442
+ ("C_black", " ↳ Black tank ", cfg["black_weight_in_waste"] * cfg["w_waste"]),
443
+ ("C_energy", "Energy Balance ", cfg["w_energy"]),
444
+ ("C_stable", "Cons. Stability", cfg["w_stable"]),
445
+ ("V_dep", "Depl. Velocity ", None),
446
+ ]
447
+ for key, label, weight in rows:
448
+ val = cmean(key)
449
+ bar = "β–“" * int(val * 20)
450
+ wstr = f"w={weight:.2f}" if weight is not None else "modifier"
451
+ print(f" {label} {val:.3f} {bar:<22} {wstr}")
452
+
453
+ # Identify consecutive critical/poor runs
454
+ crit = [r for r in warm if r["band"] in ("Critical", "Poor")]
455
+ if crit:
456
+ # Build index for consecutive-run detection
457
+ idx_map = {r["Time"]: i for i, r in enumerate(results)}
458
+ runs, run_start, prev_i = [], None, None
459
+ for r in crit:
460
+ ri = idx_map[r["Time"]]
461
+ if prev_i is None or ri != prev_i + 1:
462
+ if run_start is not None:
463
+ runs.append((run_start, results[prev_i]))
464
+ run_start = r
465
+ prev_i = ri
466
+ if run_start:
467
+ runs.append((run_start, results[prev_i]))
468
+
469
+ print(f"\n ⚠ POOR / CRITICAL EVENTS ({len(crit)} minutes across {len(runs)} run(s))")
470
+ print(f" {sep}")
471
+ for start, end in runs[:10]:
472
+ dur = idx_map[end["Time"]] - idx_map[start["Time"]] + 1
473
+ print(f" {start['Time']} SI={start['SI']:.2f} "
474
+ f"[{start['band']}] duration={dur} min "
475
+ f"bat={start['Battery_Level_Pct']}% "
476
+ f"fresh={start['FreshTank_Level_L']:.0f}L")
477
+ if len(runs) > 10:
478
+ print(f" … and {len(runs)-10} more run(s)")
479
+ else:
480
+ print(f"\n βœ“ No Critical or Poor events detected.")
481
+
482
+ print(f"\n{'═' * W}\n")
483
+
484
+
485
+ # ─────────────────────────────────────────────────────────────────────────────
486
+ # CSV WRITER
487
+ # ─────────────────────────────────────────────────────────────────────────────
488
+
489
+ def write_csv(path, rows):
490
+ if not rows:
491
+ print(" No rows to write.")
492
+ return
493
+ Path(path).parent.mkdir(parents=True, exist_ok=True)
494
+ with open(path, "w", newline="") as f:
495
+ w = csv.DictWriter(f, fieldnames=rows[0].keys())
496
+ w.writeheader()
497
+ w.writerows(rows)
498
+ print(f" Wrote {len(rows):,} rows β†’ {path}")
499
+
500
+
501
+ # ─────────────────────────────────────────────────────────────────────────────
502
+ # ENTRY POINT
503
+ # ─────────────────────────────────────────────────────────────────────────────
504
+
505
+ def main():
506
+ parser = argparse.ArgumentParser(
507
+ description="EV Camper Stability Index Engine",
508
+ formatter_class=argparse.RawDescriptionHelpFormatter,
509
+ epilog="""
510
+ Examples:
511
+ python stability_index.py --summary
512
+ python stability_index.py --power_dir data/power --water_dir data/water
513
+ python stability_index.py --config si_config.json --out scores/si.csv --summary
514
+ python stability_index.py --w_bat 0.40 --w_fresh 0.20 --w_energy 0.20 --w_waste 0.10 --w_stable 0.10
515
+ """
516
+ )
517
+ # Paths
518
+ parser.add_argument("--power_dir", default="output/power",
519
+ help="Directory containing 1MIN.csv (default: output/power)")
520
+ parser.add_argument("--water_dir", default="output/water",
521
+ help="Directory containing 1MIN.csv (default: output/water)")
522
+ parser.add_argument("--out", default="output/stability_index.csv",
523
+ help="Output CSV path (default: output/stability_index.csv)")
524
+ parser.add_argument("--config", default=None,
525
+ help="JSON config file to override any DEFAULT_CONFIG value")
526
+
527
+ # Inline weight overrides
528
+ parser.add_argument("--w_bat", type=float, default=None, metavar="W",
529
+ help="Battery SOC weight (overrides config)")
530
+ parser.add_argument("--w_fresh", type=float, default=None, metavar="W")
531
+ parser.add_argument("--w_waste", type=float, default=None, metavar="W")
532
+ parser.add_argument("--w_energy", type=float, default=None, metavar="W")
533
+ parser.add_argument("--w_stable", type=float, default=None, metavar="W")
534
+
535
+ # Other inline overrides
536
+ parser.add_argument("--solar_cap_kW", type=float, default=None)
537
+ parser.add_argument("--energy_window_min", type=int, default=None)
538
+ parser.add_argument("--stable_window_min", type=int, default=None)
539
+ parser.add_argument("--velocity_horizon_hr", type=float, default=None)
540
+ parser.add_argument("--fresh_cap_L", type=float, default=None)
541
+ parser.add_argument("--grey_cap_L", type=float, default=None)
542
+ parser.add_argument("--black_cap_L", type=float, default=None)
543
+
544
+ parser.add_argument("--summary", action="store_true",
545
+ help="Print a human-readable summary report to stdout")
546
+
547
+ args = parser.parse_args()
548
+
549
+ # Build config with optional file + CLI overrides
550
+ cfg = load_config(args.config)
551
+ for key in ["w_bat", "w_fresh", "w_waste", "w_energy", "w_stable",
552
+ "solar_cap_kW", "energy_window_min", "stable_window_min",
553
+ "velocity_horizon_hr", "fresh_cap_L", "grey_cap_L", "black_cap_L"]:
554
+ val = getattr(args, key, None)
555
+ if val is not None:
556
+ cfg[key] = val
557
+
558
+ total_w = cfg["w_bat"] + cfg["w_fresh"] + cfg["w_waste"] + cfg["w_energy"] + cfg["w_stable"]
559
+ if abs(total_w - 1.0) > 1e-4:
560
+ raise SystemExit(
561
+ f"ERROR: weights must sum to 1.0 (got {total_w:.4f})\n"
562
+ f" bat={cfg['w_bat']} fresh={cfg['w_fresh']} waste={cfg['w_waste']} "
563
+ f"energy={cfg['w_energy']} stable={cfg['w_stable']}"
564
+ )
565
+
566
+ power_path = str(Path(args.power_dir) / "1MIN.csv")
567
+ water_path = str(Path(args.water_dir) / "1MIN.csv")
568
+
569
+ print(f"\n{'='*62}")
570
+ print(f" Stability Index Engine v1.0")
571
+ print(f"{'='*62}")
572
+ print(f" Power : {power_path}")
573
+ print(f" Water : {water_path}")
574
+ print(f" Weights: bat={cfg['w_bat']} fresh={cfg['w_fresh']} "
575
+ f"waste={cfg['w_waste']} energy={cfg['w_energy']} stable={cfg['w_stable']}")
576
+ print(f" Windows: energy={cfg['energy_window_min']}min "
577
+ f"stable={cfg['stable_window_min']}min "
578
+ f"velocity={cfg['velocity_window_min']}min")
579
+ print()
580
+
581
+ print(" Calculating...")
582
+ results = calculate(power_path, water_path, cfg)
583
+
584
+ write_csv(args.out, results)
585
+
586
+ if args.summary:
587
+ print_summary(results, cfg)
588
+
589
+ print(f" Done.\n")
590
+
591
+
592
+ if __name__ == "__main__":
593
+ main()
static/index.html ADDED
@@ -0,0 +1,466 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>EV Camper β€” Energy Dashboard</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-annotation@3"></script>
10
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700&family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600;9..40,700&display=swap" rel="stylesheet">
11
+ <style>
12
+ *{margin:0;padding:0;box-sizing:border-box}
13
+ :root{
14
+ --bg-0:#060a10;--bg-1:#0c1118;--bg-2:#121a24;--bg-3:#182230;
15
+ --border:#1c2838;--border-hi:#283848;
16
+ --t1:#e8edf4;--t2:#8a9bb0;--t3:#506070;
17
+ --accent:#2ee8a8;--accent-d:#1cba80;--accent-g:rgba(46,232,168,.1);
18
+ --solar:#ffb020;--solar-g:rgba(255,176,32,.08);
19
+ --batt:#4890ff;--batt-g:rgba(72,144,255,.08);
20
+ --shore:#b48afa;--hvac:#ff5050;--cook:#ff8830;
21
+ --fresh:#18d4e8;--grey-t:#7888a0;--black-t:#585858;
22
+ --danger:#ff4040;--warn:#ffa020;--ok:#30e090;
23
+ --mono:'JetBrains Mono',monospace;--sans:'DM Sans',sans-serif;
24
+ }
25
+ html,body{height:100%;background:var(--bg-0);color:var(--t1);font-family:var(--sans)}
26
+ ::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{background:var(--bg-0)}
27
+ ::-webkit-scrollbar-thumb{background:var(--border-hi);border-radius:3px}
28
+ #app{display:flex;height:100vh;overflow:hidden}
29
+ .sb{width:208px;background:var(--bg-1);border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0}
30
+ .sb-logo{padding:18px 16px;border-bottom:1px solid var(--border)}
31
+ .sb-logo h1{font:600 13px/1 var(--mono);letter-spacing:1.8px;color:var(--accent);text-transform:uppercase}
32
+ .sb-logo p{font:11px var(--mono);color:var(--t3);margin-top:5px}
33
+ .sb-nav{flex:1;padding:10px 0;overflow-y:auto}
34
+ .sb-sec{padding:10px 16px 4px;font:500 9px/1 var(--mono);letter-spacing:1.8px;text-transform:uppercase;color:var(--t3)}
35
+ .ni{display:flex;align-items:center;gap:9px;padding:8px 16px;cursor:pointer;transition:.12s;color:var(--t2);font:500 12.5px var(--sans);border-left:2px solid transparent;user-select:none}
36
+ .ni:hover{background:var(--bg-3);color:var(--t1)}
37
+ .ni.on{color:var(--accent);border-left-color:var(--accent);background:var(--accent-g)}
38
+ .ni svg{width:15px;height:15px;flex-shrink:0;opacity:.65}.ni.on svg{opacity:1}
39
+ .sb-ft{padding:12px 16px;border-top:1px solid var(--border);font:11px var(--mono);color:var(--t3)}
40
+ .mn{flex:1;overflow-y:auto;overflow-x:hidden}
41
+ .ph{padding:22px 28px 0;display:flex;align-items:flex-end;justify-content:space-between;gap:16px;flex-wrap:wrap}
42
+ .ph h2{font:600 20px/1.2 var(--sans);letter-spacing:-.3px}
43
+ .ph .sub{font:13px var(--sans);color:var(--t2);margin-top:3px}
44
+ .badges{display:flex;gap:6px;flex-wrap:wrap}
45
+ .bdg{display:inline-flex;align-items:center;gap:4px;padding:3px 9px;border-radius:5px;font:500 10px var(--mono);border:1px solid var(--border)}
46
+ .bdg-g{color:var(--accent);border-color:var(--accent-d);background:var(--accent-g)}
47
+ .bdg-s{color:var(--solar);border-color:#b07810;background:var(--solar-g)}
48
+ .pc{padding:18px 28px 48px}
49
+ .g{display:grid;gap:14px}
50
+ .g4{grid-template-columns:repeat(4,1fr)}.g3{grid-template-columns:repeat(3,1fr)}
51
+ .g2{grid-template-columns:repeat(2,1fr)}.g21{grid-template-columns:2fr 1fr}
52
+ .g12{grid-template-columns:1fr 2fr}.g1{grid-template-columns:1fr}
53
+ .s2{grid-column:span 2}.s3{grid-column:span 3}.s4{grid-column:span 4}
54
+ .cd{background:var(--bg-2);border:1px solid var(--border);border-radius:10px;overflow:hidden}
55
+ .cd-h{padding:13px 16px 0;display:flex;justify-content:space-between;align-items:center}
56
+ .cd-t{font:500 10.5px/1 var(--mono);letter-spacing:1.2px;text-transform:uppercase;color:var(--t3)}
57
+ .cd-b{padding:12px 16px 16px;position:relative}.cd-b canvas{width:100%!important}
58
+ .kpi{padding:16px}
59
+ .kpi-l{font:10px var(--mono);letter-spacing:1.4px;text-transform:uppercase;color:var(--t3);margin-bottom:6px}
60
+ .kpi-v{font:700 26px/1 var(--mono);letter-spacing:-1px}
61
+ .kpi-s{font:11px var(--mono);color:var(--t2);margin-top:5px}
62
+ .kpi-v.grn{color:var(--accent)}.kpi-v.sol{color:var(--solar)}.kpi-v.blu{color:var(--batt)}
63
+ .kpi-v.cyn{color:var(--fresh)}.kpi-v.red{color:var(--hvac)}.kpi-v.prp{color:var(--shore)}
64
+ .kpi-bar{height:3px;border-radius:2px;background:var(--bg-0);margin-top:8px;overflow:hidden}
65
+ .kpi-bar-f{height:100%;border-radius:2px;transition:width .6s ease}
66
+ .gauge-w{display:flex;align-items:center;justify-content:center;padding:16px}
67
+ .gauge-r{position:relative;width:140px;height:140px}
68
+ .gauge-r svg{transform:rotate(-90deg)}.gauge-r circle{fill:none;stroke-linecap:round}
69
+ .gauge-r .trk{stroke:var(--border)}.gauge-r .fil{transition:stroke-dashoffset .7s ease}
70
+ .gauge-c{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center}
71
+ .gauge-p{font:700 28px/1 var(--mono)}
72
+ .gauge-lb{font:9px var(--mono);letter-spacing:1.2px;text-transform:uppercase;color:var(--t3);margin-top:4px}
73
+ .tk{display:flex;align-items:center;gap:10px;padding:6px 0}
74
+ .tk-l{width:50px;font:10px var(--mono);color:var(--t2);text-transform:uppercase}
75
+ .tk-bg{flex:1;height:18px;background:var(--bg-0);border-radius:3px;overflow:hidden;position:relative}
76
+ .tk-f{height:100%;border-radius:3px;transition:width .5s ease;display:flex;align-items:center;justify-content:flex-end;padding-right:6px;font:600 9px var(--mono);color:rgba(255,255,255,.85);min-width:30px}
77
+ .tk-v{width:55px;text-align:right;font:11px var(--mono);color:var(--t2)}
78
+ .dtabs{display:flex;gap:3px;margin-bottom:14px;flex-wrap:wrap}
79
+ .dtab{padding:5px 12px;border-radius:5px;font:11px var(--mono);cursor:pointer;border:1px solid var(--border);background:transparent;color:var(--t2);transition:.12s}
80
+ .dtab:hover{border-color:var(--border-hi);color:var(--t1)}
81
+ .dtab.on{border-color:var(--accent-d);color:var(--accent);background:var(--accent-g)}
82
+ .tb{width:100%;border-collapse:collapse;font-size:11px}
83
+ .tb th{text-align:left;padding:7px 10px;font:500 9px var(--mono);text-transform:uppercase;letter-spacing:1px;color:var(--t3);border-bottom:1px solid var(--border)}
84
+ .tb td{padding:6px 10px;border-bottom:1px solid var(--border);color:var(--t2);font:11px var(--mono)}
85
+ .tb tr:hover td{background:var(--bg-3);color:var(--t1)}
86
+ .tb-cat{padding:8px 10px;font:600 10px var(--mono);letter-spacing:1.5px;text-transform:uppercase;color:var(--accent);background:var(--accent-g);border-bottom:1px solid var(--border)}
87
+ .donut-w{max-width:220px;margin:0 auto}
88
+ .insight{display:flex;align-items:flex-start;gap:8px;padding:10px 12px;border-radius:6px;background:var(--bg-3);border-left:3px solid var(--accent);margin-bottom:12px;font:12px/1.5 var(--sans);color:var(--t2)}
89
+ .insight.warn{border-left-color:var(--warn)}.insight.danger{border-left-color:var(--danger)}
90
+ .insight b{color:var(--t1);font-weight:600}
91
+ .insight-icon{font-size:14px;flex-shrink:0;margin-top:1px}
92
+ .form-group{margin-bottom:14px}
93
+ .form-label{display:block;font:500 10px var(--mono);letter-spacing:1px;text-transform:uppercase;color:var(--t3);margin-bottom:5px}
94
+ .form-select,.form-input{width:100%;padding:8px 10px;border-radius:6px;border:1px solid var(--border);background:var(--bg-1);color:var(--t1);font:13px var(--sans);outline:none;transition:.15s}
95
+ .form-select:focus,.form-input:focus{border-color:var(--accent)}
96
+ .form-select option{background:var(--bg-1)}
97
+ .form-row{display:flex;gap:12px}.form-row>*{flex:1}
98
+ .btn{padding:10px 20px;border-radius:7px;border:none;cursor:pointer;font:600 12px var(--sans);transition:.15s}
99
+ .btn-primary{background:var(--accent);color:var(--bg-0)}.btn-primary:hover{background:var(--accent-d)}
100
+ .btn-primary:disabled{opacity:.5;cursor:wait}
101
+ .gen-status{margin-top:10px;font:12px var(--mono);color:var(--t2);min-height:20px}
102
+ .gen-status.ok{color:var(--accent)}.gen-status.err{color:var(--danger)}
103
+ .ld{display:flex;align-items:center;justify-content:center;height:200px;color:var(--t3);font:12px var(--mono);letter-spacing:1px}
104
+ .pulse{animation:pulse 1.2s ease infinite}
105
+ @keyframes pulse{0%,100%{opacity:.3}50%{opacity:1}}
106
+ @media(max-width:900px){
107
+ .sb{width:54px}.sb-logo p,.ni span,.sb-ft,.sb-sec{display:none}
108
+ .sb-logo h1{font-size:10px;text-align:center}.ni{justify-content:center;padding:10px}
109
+ .ph,.pc{padding-left:14px;padding-right:14px}
110
+ .g4,.g3{grid-template-columns:repeat(2,1fr)}
111
+ .g2,.g12,.g21{grid-template-columns:1fr}
112
+ .s2,.s3,.s4{grid-column:span 1}
113
+ }
114
+ </style>
115
+ </head>
116
+ <body>
117
+ <div id="app">
118
+ <aside class="sb">
119
+ <div class="sb-logo"><h1>⚑ EV Camp</h1><p>Resource Monitor</p></div>
120
+ <nav class="sb-nav">
121
+ <div class="sb-sec">Dashboard</div>
122
+ <div class="ni" :class="{on:pg==='overview'}" @click="go('overview')">
123
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg><span>Overview</span>
124
+ </div>
125
+ <div class="ni" :class="{on:pg==='power'}" @click="go('power')">
126
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg><span>Power</span>
127
+ </div>
128
+ <div class="ni" :class="{on:pg==='water'}" @click="go('water')">
129
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2.69l5.66 5.66a8 8 0 11-11.31 0z"/></svg><span>Water</span>
130
+ </div>
131
+ <div class="ni" :class="{on:pg==='budget'}" @click="go('budget')">
132
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 010-4h14v4"/><path d="M3 5v14a2 2 0 002 2h16v-5"/><path d="M18 12a2 2 0 100 4 2 2 0 000-4z"/></svg><span>Budget</span>
133
+ </div>
134
+ <div class="ni" :class="{on:pg==='components'}" @click="go('components')">
135
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="4" y="4" width="16" height="16" rx="2"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3"/></svg><span>Components</span>
136
+ </div>
137
+ <div class="sb-sec" style="margin-top:8px">Settings</div>
138
+ <div class="ni" :class="{on:pg==='generate'}" @click="go('generate')">
139
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 01-2.83 2.83l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg><span>Generate Data</span>
140
+ </div>
141
+ </nav>
142
+ <div class="sb-ft">v1.0 Β· {{cfg?.inputs?.params?.user_type?.value||'...'}}</div>
143
+ </aside>
144
+
145
+ <main class="mn">
146
+ <!-- OVERVIEW -->
147
+ <template v-if="pg==='overview'">
148
+ <div class="ph">
149
+ <div><h2>Trip Overview</h2><div class="sub">{{S.trip_days}}-day trip Β· {{cfg?.inputs?.params?.num_people?.value}} occupants Β· {{cfg?.inputs?.params?.temperature?.value}}</div></div>
150
+ <div class="badges">
151
+ <span class="bdg bdg-s">β˜€ {{cfg?.inputs?.params?.sunlight?.value}}</span>
152
+ <span class="bdg bdg-g">{{S.self_sufficiency_pct}}% Solar Coverage</span>
153
+ </div>
154
+ </div>
155
+ <div class="pc">
156
+ <div v-if="S.self_sufficiency_pct>=100" class="insight"><span class="insight-icon">🌞</span><div>Solar generation <b>exceeds</b> total consumption by <b>{{Math.round(S.total_solar_kwh-S.total_consumption_kwh)}} kWh</b>. Fully self-sufficient β€” no shore power needed.</div></div>
157
+ <div v-else class="insight warn"><span class="insight-icon">⚠️</span><div>Solar covers only <b>{{S.self_sufficiency_pct}}%</b> of demand. Consider reducing HVAC usage or connecting shore power.</div></div>
158
+ <div v-if="(S.water?.fresh_remaining_pct||100)<30" class="insight danger"><span class="insight-icon">πŸ’§</span><div>Fresh water at <b>{{Math.round(S.water.fresh_remaining_pct)}}%</b>. At ~{{Math.round(S.water.total_fresh_used_L/S.trip_days)}} L/day, roughly <b>{{(S.water.fresh_remaining_L/(S.water.total_fresh_used_L/S.trip_days)).toFixed(1)}}</b> days remain.</div></div>
159
+ <div class="g g4" style="margin-bottom:14px">
160
+ <div class="cd kpi"><div class="kpi-l">Solar Generated</div><div class="kpi-v sol">{{S.total_solar_kwh}}</div><div class="kpi-s">kWh total Β· {{rd(S.total_solar_kwh/S.trip_days)}}/day</div><div class="kpi-bar"><div class="kpi-bar-f" style="width:100%;background:var(--solar)"></div></div></div>
161
+ <div class="cd kpi"><div class="kpi-l">Total Consumed</div><div class="kpi-v red">{{S.total_consumption_kwh}}</div><div class="kpi-s">kWh total Β· {{rd(S.total_consumption_kwh/S.trip_days)}}/day</div><div class="kpi-bar"><div class="kpi-bar-f" :style="{width:Math.min(100,S.total_consumption_kwh/Math.max(S.total_solar_kwh,.1)*100)+'%',background:'var(--hvac)'}"></div></div></div>
162
+ <div class="cd kpi"><div class="kpi-l">Battery Now</div><div class="kpi-v blu">{{S.battery?.current_pct}}%</div><div class="kpi-s">min {{S.battery?.min_pct}}% Β· avg {{S.battery?.avg_pct}}%</div><div class="kpi-bar"><div class="kpi-bar-f" :style="{width:(S.battery?.current_pct||0)+'%',background:battCol}"></div></div></div>
163
+ <div class="cd kpi"><div class="kpi-l">Fresh Water Left</div><div class="kpi-v cyn">{{Math.round(S.water?.fresh_remaining_pct||0)}}%</div><div class="kpi-s">{{fL(S.water?.fresh_remaining_L)}} L remaining</div><div class="kpi-bar"><div class="kpi-bar-f" :style="{width:(S.water?.fresh_remaining_pct||0)+'%',background:'var(--fresh)'}"></div></div></div>
164
+ </div>
165
+ <div class="g g21" style="margin-bottom:14px">
166
+ <div class="cd"><div class="cd-h"><span class="cd-t">Daily Energy Balance</span><span class="cd-t" style="color:var(--t2)">kWh/day</span></div><div class="cd-b"><canvas id="ov-energy" height="220"></canvas></div></div>
167
+ <div class="cd"><div class="cd-h"><span class="cd-t">System Status</span></div><div class="cd-b">
168
+ <div class="gauge-w"><div class="gauge-r"><svg viewBox="0 0 140 140" width="140" height="140"><circle cx="70" cy="70" r="58" class="trk" stroke-width="9"/><circle cx="70" cy="70" r="58" class="fil" stroke-width="9" :stroke="battCol" stroke-dasharray="364.4" :stroke-dashoffset="364.4*(1-(S.battery?.current_pct||0)/100)"/></svg><div class="gauge-c"><div class="gauge-p" :style="{color:battCol}">{{S.battery?.current_pct||0}}%</div><div class="gauge-lb">Battery SOC</div></div></div></div>
169
+ <div style="padding:0 2px">
170
+ <div class="tk"><span class="tk-l" style="color:var(--fresh)">Fresh</span><div class="tk-bg"><div class="tk-f" :style="{width:Math.max(8,S.water?.fresh_remaining_pct||0)+'%',background:'var(--fresh)'}">{{Math.round(S.water?.fresh_remaining_pct||0)}}%</div></div><span class="tk-v">{{fL(S.water?.fresh_remaining_L)}}L</span></div>
171
+ <div class="tk"><span class="tk-l" style="color:var(--grey-t)">Grey</span><div class="tk-bg"><div class="tk-f" :style="{width:Math.max(8,S.water?.grey_level_pct||0)+'%',background:'var(--grey-t)'}">{{Math.round(S.water?.grey_level_pct||0)}}%</div></div><span class="tk-v">{{fL(S.water?.grey_level_L)}}L</span></div>
172
+ <div class="tk"><span class="tk-l" style="color:var(--black-t)">Black</span><div class="tk-bg"><div class="tk-f" :style="{width:Math.max(8,S.water?.black_level_pct||0)+'%',background:'var(--black-t)'}">{{Math.round(S.water?.black_level_pct||0)}}%</div></div><span class="tk-v">{{fL(S.water?.black_level_L)}}L</span></div>
173
+ </div>
174
+ </div></div>
175
+ </div>
176
+ <div class="g g2">
177
+ <div class="cd"><div class="cd-h"><span class="cd-t">Consumption by Circuit</span><span class="cd-t" style="color:var(--t2)">kWh</span></div><div class="cd-b"><div class="donut-w"><canvas id="ov-donut" height="220"></canvas></div></div></div>
178
+ <div class="cd"><div class="cd-h"><span class="cd-t">Daily Breakdown</span></div><div class="cd-b"><table class="tb"><thead><tr><th>Day</th><th>Solar</th><th>Load</th><th>Net</th><th>Batt</th><th>Water</th></tr></thead><tbody><tr v-for="d in S.daily_breakdown||[]" :key="d.day"><td>Day {{d.day}}</td><td style="color:var(--solar)">{{d.solar_kwh}}</td><td style="color:var(--hvac)">{{d.consumption_kwh}}</td><td :style="{color:d.solar_kwh>=d.consumption_kwh?'var(--accent)':'var(--danger)'}">{{rd(d.solar_kwh-d.consumption_kwh)>0?'+':''}}{{rd(d.solar_kwh-d.consumption_kwh)}}</td><td :style="{color:d.battery_end_pct>40?'var(--accent)':'var(--danger)'}">{{d.battery_end_pct}}%</td><td style="color:var(--fresh)">{{d.fresh_used_L}}L</td></tr></tbody></table></div></div>
179
+ </div>
180
+ </div>
181
+ </template>
182
+
183
+ <!-- POWER -->
184
+ <template v-if="pg==='power'">
185
+ <div class="ph"><div><h2>Power Analytics</h2><div class="sub">Generation, consumption & battery analysis</div></div><div class="badges"><span class="bdg bdg-s">Avg Solar: {{rd(S.total_solar_kwh/S.trip_days)}} kWh/day</span></div></div>
186
+ <div class="pc">
187
+ <div class="dtabs"><div class="dtab" :class="{on:pDay===0}" @click="setPDay(0)">All Days</div><div class="dtab" v-for="d in S.trip_days" :key="d" :class="{on:pDay===d}" @click="setPDay(d)">Day {{d}}</div></div>
188
+ <div v-if="pDay>0&&pDayInsight" class="insight"><span class="insight-icon">πŸ“Š</span><div v-html="pDayInsight"></div></div>
189
+ <div class="g g1" style="margin-bottom:14px"><div class="cd"><div class="cd-h"><span class="cd-t">Solar Generation vs Total Load</span><span class="cd-t" style="color:var(--t2)">{{pDay?'Day '+pDay:'All'}} Β· 15-min kW</span></div><div class="cd-b"><canvas id="pw-solar" height="200" data-height="200"></canvas></div></div></div>
190
+ <div class="g g2" style="margin-bottom:14px">
191
+ <div class="cd"><div class="cd-h"><span class="cd-t">Battery State of Charge</span><span class="cd-t" style="color:var(--t2)">%</span></div><div class="cd-b"><canvas id="pw-batt" height="200" data-height="200"></canvas></div></div>
192
+ <div class="cd"><div class="cd-h"><span class="cd-t">Avg Hourly Load Profile</span><span class="cd-t" style="color:var(--t2)">kWh by circuit</span></div><div class="cd-b"><canvas id="pw-hourly" height="200" data-height="200"></canvas></div></div>
193
+ </div>
194
+ <div class="g g2">
195
+ <div class="cd"><div class="cd-h"><span class="cd-t">Circuit Breakdown (Hourly)</span><span class="cd-t" style="color:var(--t2)">Stacked kWh</span></div><div class="cd-b"><canvas id="pw-stack" height="220" data-height="220"></canvas></div></div>
196
+ <div class="cd"><div class="cd-h"><span class="cd-t">Energy Source Mix</span><span class="cd-t" style="color:var(--t2)">Solar / Battery / Shore</span></div><div class="cd-b"><canvas id="pw-source" height="220" data-height="220"></canvas></div></div>
197
+ </div>
198
+ </div>
199
+ </template>
200
+
201
+ <!-- WATER -->
202
+ <template v-if="pg==='water'">
203
+ <div class="ph"><div><h2>Water Management</h2><div class="sub">Tank levels, flow rates & usage patterns</div></div></div>
204
+ <div class="pc">
205
+ <div v-if="(S.water?.grey_level_pct||0)>80" class="insight warn"><span class="insight-icon">🚿</span><div>Grey tank at <b>{{Math.round(S.water.grey_level_pct)}}%</b>. Consider dumping soon.</div></div>
206
+ <div class="g g4" style="margin-bottom:14px">
207
+ <div class="cd kpi"><div class="kpi-l">Total Fresh Used</div><div class="kpi-v cyn">{{fL(S.water?.total_fresh_used_L)}}</div><div class="kpi-s">litres Β· {{fL((S.water?.total_fresh_used_L||0)/S.trip_days)}}/day</div></div>
208
+ <div class="cd kpi"><div class="kpi-l">Fresh Remaining</div><div class="kpi-v cyn">{{Math.round(S.water?.fresh_remaining_pct||0)}}%</div><div class="kpi-s">{{fL(S.water?.fresh_remaining_L)}} L</div><div class="kpi-bar"><div class="kpi-bar-f" :style="{width:(S.water?.fresh_remaining_pct||0)+'%',background:'var(--fresh)'}"></div></div></div>
209
+ <div class="cd kpi"><div class="kpi-l">Grey Tank</div><div class="kpi-v" style="color:var(--grey-t)">{{Math.round(S.water?.grey_level_pct||0)}}%</div><div class="kpi-s">{{fL(S.water?.grey_level_L)}} L</div><div class="kpi-bar"><div class="kpi-bar-f" :style="{width:(S.water?.grey_level_pct||0)+'%',background:'var(--grey-t)'}"></div></div></div>
210
+ <div class="cd kpi"><div class="kpi-l">Black Tank</div><div class="kpi-v" style="color:var(--black-t)">{{Math.round(S.water?.black_level_pct||0)}}%</div><div class="kpi-s">{{fL(S.water?.black_level_L)}} L</div><div class="kpi-bar"><div class="kpi-bar-f" :style="{width:(S.water?.black_level_pct||0)+'%',background:'var(--black-t)'}"></div></div></div>
211
+ </div>
212
+ <div class="g g1" style="margin-bottom:14px"><div class="cd"><div class="cd-h"><span class="cd-t">Tank Levels Over Time</span><span class="cd-t" style="color:var(--t2)">Hourly Β· Litres</span></div><div class="cd-b"><canvas id="wt-tanks" height="200"></canvas></div></div></div>
213
+ <div class="g g2" style="margin-bottom:14px">
214
+ <div class="cd"><div class="cd-h"><span class="cd-t">Daily Usage by Source</span><span class="cd-t" style="color:var(--t2)">Litres</span></div><div class="cd-b"><canvas id="wt-daily" height="220"></canvas></div></div>
215
+ <div class="cd"><div class="cd-h"><span class="cd-t">Avg Hourly Profile</span><span class="cd-t" style="color:var(--t2)">Litres/hour</span></div><div class="cd-b"><canvas id="wt-hourly" height="220"></canvas></div></div>
216
+ </div>
217
+ <div class="g g2">
218
+ <div class="cd"><div class="cd-h"><span class="cd-t">Total Water Split</span></div><div class="cd-b"><div class="donut-w"><canvas id="wt-donut" height="220"></canvas></div></div></div>
219
+ <div class="cd"><div class="cd-h"><span class="cd-t">Pump Activity</span><span class="cd-t" style="color:var(--t2)">15-min Lpm</span></div><div class="cd-b"><canvas id="wt-pump" height="220"></canvas></div></div>
220
+ </div>
221
+ </div>
222
+ </template>
223
+
224
+ <!-- BUDGET -->
225
+ <template v-if="pg==='budget'">
226
+ <div class="ph"><div><h2>Budget Analysis</h2><div class="sub">Planned vs actual resource consumption</div></div></div>
227
+ <div class="pc">
228
+ <div class="g g2" style="margin-bottom:14px">
229
+ <div class="cd"><div class="cd-h"><span class="cd-t">Power: Budget vs Actual</span><span class="cd-t" style="color:var(--t2)">Trip kWh</span></div><div class="cd-b"><canvas id="bg-power" height="260"></canvas></div></div>
230
+ <div class="cd"><div class="cd-h"><span class="cd-t">Water: Budget vs Actual</span><span class="cd-t" style="color:var(--t2)">Trip Litres</span></div><div class="cd-b"><canvas id="bg-water" height="260"></canvas></div></div>
231
+ </div>
232
+ <div class="g g1" style="margin-bottom:14px"><div class="cd"><div class="cd-h"><span class="cd-t">Daily Efficiency Trend</span><span class="cd-t" style="color:var(--t2)">Solar coverage & battery %</span></div><div class="cd-b"><canvas id="bg-trend" height="180"></canvas></div></div></div>
233
+ <div class="g g2">
234
+ <div class="cd"><div class="cd-h"><span class="cd-t">Power Budget Detail</span><span class="cd-t" style="color:var(--t2)">kWh/day</span></div><div class="cd-b"><div v-for="(v,k) in pBudget" :key="k" style="display:flex;align-items:center;gap:10px;padding:5px 0"><span style="width:90px;font:10px var(--mono);color:var(--t2);text-transform:uppercase">{{fmtK(k)}}</span><div style="flex:1;height:14px;background:var(--bg-0);border-radius:3px;overflow:hidden"><div :style="{height:'100%',width:bPct(v,pBMax)+'%',background:bCol(k),borderRadius:'3px'}"></div></div><span style="width:60px;text-align:right;font:11px var(--mono);color:var(--t1)">{{v}}</span></div></div></div>
235
+ <div class="cd"><div class="cd-h"><span class="cd-t">Water Budget Detail</span><span class="cd-t" style="color:var(--t2)">L/day</span></div><div class="cd-b"><div v-for="(v,k) in wBudget" :key="k" style="display:flex;align-items:center;gap:10px;padding:5px 0"><span style="width:90px;font:10px var(--mono);color:var(--t2);text-transform:uppercase">{{fmtK(k)}}</span><div style="flex:1;height:14px;background:var(--bg-0);border-radius:3px;overflow:hidden"><div :style="{height:'100%',width:bPct(v,wBMax)+'%',background:k.includes('shower')?'var(--fresh)':k.includes('toilet')?'var(--black-t)':'var(--accent)',borderRadius:'3px'}"></div></div><span style="width:60px;text-align:right;font:11px var(--mono);color:var(--t1)">{{v}}</span></div></div></div>
236
+ </div>
237
+ </div>
238
+ </template>
239
+
240
+ <!-- COMPONENTS -->
241
+ <template v-if="pg==='components'">
242
+ <div class="ph"><div><h2>Component Catalog</h2><div class="sub">All trailer electrical & water components</div></div></div>
243
+ <div class="pc"><div class="cd"><div class="cd-b" style="padding:0;max-height:calc(100vh - 130px);overflow-y:auto"><table class="tb"><thead><tr><th>Component</th><th>Voltage</th><th>Amps</th><th>Power</th><th>Water</th><th>Idle</th></tr></thead><tbody><template v-for="(items,cat) in comps" :key="cat"><tr><td colspan="6" class="tb-cat">{{cat}}</td></tr><tr v-for="(c,i) in items" :key="i"><td style="color:var(--t1)">{{c.name}}</td><td>{{c.voltage_v}}V</td><td>{{c.avg_amps}}A</td><td style="color:var(--solar)">{{(c.voltage_v*c.avg_amps).toFixed(1)}}W</td><td>{{c.water_gal_per_cycle||'β€”'}}</td><td>{{c.idle_amps||'β€”'}}</td></tr></template></tbody></table></div></div></div>
244
+ </template>
245
+
246
+ <!-- GENERATE -->
247
+ <template v-if="pg==='generate'">
248
+ <div class="ph"><div><h2>Generate Data</h2><div class="sub">Configure trip parameters and regenerate simulation</div></div></div>
249
+ <div class="pc"><div class="g g2">
250
+ <div class="cd"><div class="cd-h"><span class="cd-t">Trip Configuration</span></div><div class="cd-b">
251
+ <div class="form-row"><div class="form-group"><label class="form-label">User Profile</label><select class="form-select" v-model="gen.user_type"><option>Glamper</option><option>Typical</option><option>Expert</option></select></div><div class="form-group"><label class="form-label">People</label><select class="form-select" v-model.number="gen.num_people"><option :value="1">1</option><option :value="2">2</option><option :value="3">3</option><option :value="4">4</option></select></div></div>
252
+ <div class="form-row"><div class="form-group"><label class="form-label">Trip Duration (days)</label><input class="form-input" type="number" v-model.number="gen.trip_duration_days" min="1" max="30"></div><div class="form-group"><label class="form-label">Random Seed</label><input class="form-input" type="number" v-model.number="gen.seed" min="1"></div></div>
253
+ <div class="form-row"><div class="form-group"><label class="form-label">Temperature</label><select class="form-select" v-model="gen.temperature"><option>Hot</option><option>Temperate</option><option>Cold</option></select></div><div class="form-group"><label class="form-label">Humidity</label><select class="form-select" v-model="gen.humidity"><option>Humid</option><option>Comfortable</option><option>Dry</option></select></div></div>
254
+ <div class="form-group"><label class="form-label">Sunlight</label><select class="form-select" v-model="gen.sunlight"><option>Hi- Sunny</option><option>Mid- Cloudy</option><option>Lo- Shady</option></select></div>
255
+ <button class="btn btn-primary" :disabled="genBusy" @click="doGen" style="width:100%;margin-top:6px">{{genBusy?'Generating...':'Generate & Reload Dashboard'}}</button>
256
+ <div class="gen-status" :class="{ok:genOk,err:!genOk}" v-if="genMsg">{{genMsg}}</div>
257
+ </div></div>
258
+ <div class="cd"><div class="cd-h"><span class="cd-t">Profile Info</span></div><div class="cd-b" style="font:13px/1.7 var(--sans);color:var(--t2)">
259
+ <div style="margin-bottom:16px"><div style="font:600 14px var(--sans);color:var(--t1);margin-bottom:4px">{{gen.user_type==='Glamper'?'πŸ•οΈ Glamper':gen.user_type==='Expert'?'πŸ§‘β€πŸ”§ Expert':'πŸ‘€ Typical'}}</div><template v-if="gen.user_type==='Glamper'">High comfort β€” 3 meals/day, long showers, party lighting, dishwasher every meal. Highest resource usage.</template><template v-else-if="gen.user_type==='Expert'">Minimal usage β€” 1 meal/day, short showers, LED-only. Most efficient, longest off-grid.</template><template v-else>Balanced β€” 2 meals/day, standard showers, typical electronics. Good comfort/efficiency balance.</template></div>
260
+ <div style="margin-bottom:16px"><div style="font:600 14px var(--sans);color:var(--t1);margin-bottom:4px">{{gen.temperature==='Hot'?'🌑️ Hot':gen.temperature==='Cold'?'❄️ Cold':'🌀️ Temperate'}}</div><template v-if="gen.temperature==='Hot'">HVAC cooling ~6.9 kWh/day. Best solar. AC compressor is dominant load.</template><template v-else-if="gen.temperature==='Cold'">HVAC heating ~15.6 kWh/day. Reduced solar. Significant overnight drain.</template><template v-else>Mild HVAC ~0.7 kWh/day. Good solar. Most efficient climate.</template></div>
261
+ <div><div style="font:600 14px var(--sans);color:var(--t1);margin-bottom:4px">{{gen.sunlight==='Hi- Sunny'?'β˜€οΈ Full Sun':gen.sunlight==='Lo- Shady'?'🌲 Shady':'☁️ Cloudy'}}</div><template v-if="gen.sunlight==='Hi- Sunny'">100% efficiency. Max generation from 35-panel 5.25kW array.</template><template v-else-if="gen.sunlight==='Lo- Shady'">25% efficiency. Will likely need shore power.</template><template v-else>50% efficiency. May be self-sufficient with conservative use.</template></div>
262
+ </div></div>
263
+ </div></div>
264
+ </template>
265
+
266
+ <div v-if="loading" class="ld"><span class="pulse">LOADING DATA...</span></div>
267
+ </main>
268
+ </div>
269
+
270
+ <script>
271
+ const{createApp,ref,reactive,computed,onMounted,watch,nextTick}=Vue;
272
+ const CC={HVAC:'#ff5050',Lighting:'#ffb020',Devices:'#a78bfa',Fridge:'#18d4e8',WaterPump:'#4890ff',Cooking:'#ff8830',Inverter:'#e879f9',Unmetered:'#506070'};
273
+ const CL=['HVAC','Lighting','Devices','Fridge','WaterPump','Cooking','Inverter','Unmetered'];
274
+ Chart.defaults.font.family="'JetBrains Mono','monospace'";Chart.defaults.color='#506070';Chart.defaults.borderColor='#1c2838';
275
+
276
+ // ── Stable chart registry keyed by canvas ID ──
277
+ const _charts={};
278
+ function mk(id,type,data,opts){
279
+ const el=document.getElementById(id);if(!el)return null;
280
+ if(_charts[id]){_charts[id].destroy();delete _charts[id];
281
+ const parent=el.parentElement;
282
+ if(parent){
283
+ const fixedH=parseInt(el.getAttribute('data-height'),10);
284
+ el.width=parent.clientWidth;
285
+ el.height=Number.isFinite(fixedH)?fixedH:200;
286
+ }
287
+ }
288
+ const c=new Chart(el,{type,data,options:opts});_charts[id]=c;return c;
289
+ }
290
+
291
+ const B={responsive:true,maintainAspectRatio:false,animation:{duration:350},
292
+ plugins:{legend:{position:'bottom',labels:{color:'#8a9bb0',font:{size:10},boxWidth:10,padding:10}},tooltip:{backgroundColor:'#182230ee',borderColor:'#283848',borderWidth:1,titleFont:{size:11},bodyFont:{size:11},padding:10,cornerRadius:6}},
293
+ scales:{x:{ticks:{color:'#506070',font:{size:9},maxRotation:0},grid:{color:'#1c283880'}},y:{ticks:{color:'#506070',font:{size:9}},grid:{color:'#1c283880'}}}
294
+ };
295
+ function mO(ov){const o=JSON.parse(JSON.stringify(B));(function m(t,s){for(const k in s)if(s[k]&&typeof s[k]==='object'&&!Array.isArray(s[k])){t[k]=t[k]||{};m(t[k],s[k]);}else t[k]=s[k];})(o,ov);return o;}
296
+ function fT(ts){if(!ts)return'';const d=new Date(ts);return d.toLocaleString('en-US',{month:'short',day:'numeric',hour:'2-digit',minute:'2-digit',hour12:false});}
297
+ function fH(h){return`${String(h).padStart(2,'0')}:00`}
298
+
299
+ createApp({
300
+ setup(){
301
+ const pg=ref('overview'),loading=ref(true);
302
+ const cfg=ref({}),S=ref({trip_days:0,total_solar_kwh:0,total_consumption_kwh:0,self_sufficiency_pct:0,circuit_totals_kwh:{},battery:{},water:{},daily_breakdown:[]});
303
+ const pBudget=ref({}),wBudget=ref({}),comps=ref({}),hProfile=ref([]),whProfile=ref([]);
304
+ const pDay=ref(0),pDayInsight=ref('');
305
+ const gen=reactive({user_type:'Typical',num_people:2,trip_duration_days:5,temperature:'Hot',sunlight:'Hi- Sunny',humidity:'Comfortable',seed:42});
306
+ const genBusy=ref(false),genMsg=ref(''),genOk=ref(true);
307
+ let pw15=[],pwH=[],wtH=[],wtD=[],wt15=[];
308
+
309
+ const pBMax=computed(()=>Math.max(...Object.values(pBudget.value||{}),1));
310
+ const wBMax=computed(()=>Math.max(...Object.values(wBudget.value||{}),1));
311
+ const battCol=computed(()=>{const p=S.value.battery?.current_pct||0;return p>60?'#2ee8a8':p>30?'#ffb020':'#ff4040';});
312
+
313
+ function rd(v){return Math.round((v||0)*100)/100}
314
+ function fL(v){return v!=null?Math.round(v):'β€”'}
315
+ function fmtK(k){return k.replace(/_/g,' ').replace(/kwh|L/gi,'').trim()}
316
+ function bPct(v,mx){return Math.min(100,(v||0)/mx*100)}
317
+ function bCol(k){if(k.includes('solar'))return'var(--solar)';if(k.includes('hvac'))return'var(--hvac)';if(k.includes('cook'))return'var(--cook)';if(k.includes('light'))return'var(--solar)';if(k.includes('device'))return'#a78bfa';if(k.includes('fridge'))return'var(--fresh)';if(k.includes('pump'))return'var(--batt)';return'var(--accent)';}
318
+ async function api(u){return(await fetch(u)).json();}
319
+
320
+ async function loadAll(){
321
+ loading.value=true;
322
+ try{
323
+ const[c,s,pb,wb,co,hp,whp]=await Promise.all([api('/api/config'),api('/api/summary'),api('/api/power/budget'),api('/api/water/budget'),api('/api/components'),api('/api/power/hourly-profile'),api('/api/water/hourly-profile')]);
324
+ cfg.value=c;S.value=s;pBudget.value=pb;wBudget.value=wb;comps.value=co;hProfile.value=hp;whProfile.value=whp;
325
+ const p=c.inputs?.params||{};gen.user_type=p.user_type?.value||'Typical';gen.num_people=p.num_people?.value||2;gen.trip_duration_days=p.trip_duration_days?.value||5;gen.temperature=p.temperature?.value||'Hot';gen.sunlight=p.sunlight?.value||'Hi- Sunny';gen.humidity=p.humidity?.value||'Comfortable';
326
+ }catch(e){console.error(e);}
327
+ loading.value=false;
328
+ }
329
+
330
+ // ── Overview ──
331
+ function renderOV(){
332
+ const s=S.value;if(!s.daily_breakdown?.length)return;const db=s.daily_breakdown;
333
+ mk('ov-energy','bar',{labels:db.map(d=>`Day ${d.day}`),datasets:[
334
+ {label:'Solar',data:db.map(d=>d.solar_kwh),backgroundColor:'#ffb020cc',borderRadius:5,barPercentage:.55},
335
+ {label:'Load',data:db.map(d=>-d.consumption_kwh),backgroundColor:'#ff5050cc',borderRadius:5,barPercentage:.55},
336
+ {label:'Shore',data:db.map(d=>d.shore_kwh),backgroundColor:'#b48afacc',borderRadius:5,barPercentage:.55},
337
+ ]},mO({plugins:{legend:{display:true},tooltip:{callbacks:{label:c=>`${c.dataset.label}: ${Math.abs(c.raw).toFixed(2)} kWh`}},annotation:{annotations:{zero:{type:'line',yMin:0,yMax:0,borderColor:'#28384888',borderWidth:1,borderDash:[4,4]}}}},scales:{y:{title:{display:true,text:'kWh',color:'#506070',font:{size:10}}}}}));
338
+
339
+ const ct=s.circuit_totals_kwh||{};const ll=Object.keys(ct).filter(k=>ct[k]>0);const vv=ll.map(l=>Math.round(ct[l]*100)/100);const tot=vv.reduce((a,b)=>a+b,0);
340
+ mk('ov-donut','doughnut',{labels:ll,datasets:[{data:vv,backgroundColor:ll.map(l=>CC[l]||'#506070'),borderWidth:0,hoverOffset:6}]},
341
+ {responsive:true,maintainAspectRatio:true,cutout:'62%',plugins:{legend:{position:'right',labels:{color:'#8a9bb0',font:{size:10},boxWidth:10,padding:8}},tooltip:{callbacks:{label:c=>`${c.label}: ${c.raw} kWh (${Math.round(c.raw/tot*100)}%)`}}}});
342
+ }
343
+
344
+ // ── Power ──
345
+ async function loadPW(){
346
+ const dq=pDay.value>0?`&day=${pDay.value}`:'';
347
+ const[r1,r2]=await Promise.all([api(`/api/power/15MIN?limit=10000${dq}`),api(`/api/power/1H?limit=5000${dq}`)]);
348
+ pw15=r1.data||[];pwH=r2.data||[];
349
+ if(pDay.value>0){const db=(S.value.daily_breakdown||[]).find(d=>d.day===pDay.value);
350
+ if(db){const n=rd(db.solar_kwh-db.consumption_kwh);pDayInsight.value=`Day ${db.day}: <b>${db.solar_kwh} kWh</b> solar, <b>${db.consumption_kwh} kWh</b> consumed. Net: <b style="color:${n>=0?'var(--accent)':'var(--danger)'}">${n>0?'+':''}${n} kWh</b>. Battery ended at <b>${db.battery_end_pct}%</b>.`;}
351
+ }else pDayInsight.value='';
352
+ await nextTick();renderPW();
353
+ }
354
+ function renderPW(){
355
+ if(!pw15.length)return;const lb=pw15.map(r=>fT(r.Time));const tl=pDay.value?12:20;
356
+
357
+ mk('pw-solar','line',{labels:lb,datasets:[
358
+ {label:'Solar',data:pw15.map(r=>r.Solar_Flow_kW||0),borderColor:'#ffb020',backgroundColor:'rgba(255,176,32,.06)',fill:true,pointRadius:0,borderWidth:1.5,tension:.35},
359
+ {label:'Total Load',data:pw15.map(r=>CL.reduce((s,c)=>s+(r[`${c}_Flow_kW`]||0),0)),borderColor:'#ff5050',backgroundColor:'rgba(255,80,80,.04)',fill:true,pointRadius:0,borderWidth:1.5,tension:.35},
360
+ ]},mO({interaction:{intersect:false,mode:'index'},scales:{x:{ticks:{maxTicksLimit:tl}},y:{title:{display:true,text:'kW',color:'#506070',font:{size:10}}}},plugins:{tooltip:{callbacks:{label:c=>`${c.dataset.label}: ${c.raw.toFixed(3)} kW`}}}}));
361
+
362
+ mk('pw-batt','line',{labels:lb,datasets:[{label:'Battery %',data:pw15.map(r=>r.Battery_Level_Pct||0),borderColor:'#4890ff',backgroundColor:'rgba(72,144,255,.08)',fill:true,pointRadius:0,borderWidth:1.5,tension:.3}]},
363
+ mO({scales:{x:{ticks:{maxTicksLimit:tl}},y:{min:0,max:100,title:{display:true,text:'%',color:'#506070'}}},plugins:{annotation:{annotations:{
364
+ dzone:{type:'box',yMin:0,yMax:20,backgroundColor:'rgba(255,64,64,.05)',borderWidth:0},
365
+ wzone:{type:'box',yMin:20,yMax:40,backgroundColor:'rgba(255,160,32,.03)',borderWidth:0},
366
+ crit:{type:'line',yMin:20,yMax:20,borderColor:'#ff404055',borderWidth:1,borderDash:[4,4],label:{display:true,content:'Critical 20%',position:'end',color:'#ff4040',font:{size:8},backgroundColor:'transparent'}},
367
+ }}}}));
368
+
369
+ if(hProfile.value?.length){const hp=hProfile.value;
370
+ mk('pw-hourly','bar',{labels:hp.map(h=>fH(h.hour)),datasets:CL.filter(c=>c!=='Unmetered').map(c=>({label:c,data:hp.map(h=>h[`${c}_kwh`]||0),backgroundColor:CC[c]+'cc',borderRadius:2,barPercentage:.9,categoryPercentage:.85}))},mO({scales:{x:{stacked:true,ticks:{maxTicksLimit:12}},y:{stacked:true,title:{display:true,text:'kWh',color:'#506070'}}}}));}
371
+
372
+ if(pwH.length){
373
+ mk('pw-stack','bar',{labels:pwH.map(r=>fT(r.Time)),datasets:CL.filter(c=>c!=='Unmetered').map(c=>({label:c,data:pwH.map(r=>r[`${c}_Total_kWh`]||0),backgroundColor:CC[c]+'aa',borderRadius:1,barPercentage:.95}))},mO({scales:{x:{stacked:true,ticks:{maxTicksLimit:tl}},y:{stacked:true,title:{display:true,text:'kWh',color:'#506070'}}}}));
374
+
375
+ mk('pw-source','line',{labels:pwH.map(r=>fT(r.Time)),datasets:[
376
+ {label:'Solar',data:pwH.map(r=>r.Solar_Total_kWh||0),borderColor:'#ffb020',backgroundColor:'rgba(255,176,32,.12)',fill:true,pointRadius:0,borderWidth:1.5,tension:.3},
377
+ {label:'Battery Out',data:pwH.map(r=>r.Battery_Discharged_kWh||0),borderColor:'#4890ff',backgroundColor:'rgba(72,144,255,.1)',fill:true,pointRadius:0,borderWidth:1.5,tension:.3},
378
+ {label:'Shore',data:pwH.map(r=>r.Shore_Total_kWh||0),borderColor:'#b48afa',backgroundColor:'rgba(180,138,250,.1)',fill:true,pointRadius:0,borderWidth:1.5,tension:.3},
379
+ ]},mO({interaction:{intersect:false,mode:'index'},scales:{x:{ticks:{maxTicksLimit:tl}},y:{stacked:true,title:{display:true,text:'kWh',color:'#506070'}}}}));
380
+ }
381
+ }
382
+ function setPDay(d){pDay.value=d;loadPW();}
383
+
384
+ // ── Water ──
385
+ async function loadWT(){
386
+ const[a,b,c]=await Promise.all([api('/api/water/1H?limit=5000'),api('/api/water/1DAY?limit=30'),api('/api/water/15MIN?limit=5000')]);
387
+ wtH=a.data||[];wtD=b.data||[];wt15=c.data||[];await nextTick();renderWT();
388
+ }
389
+ function renderWT(){
390
+ if(!wtH.length)return;
391
+ const fc=cfg.value?.trailer_specs?.specs?.freshwater_capacity_gal?.value*3.785||378;
392
+ const gc=cfg.value?.trailer_specs?.specs?.greywater_capacity_gal?.value*3.785||189;
393
+
394
+ mk('wt-tanks','line',{labels:wtH.map(r=>fT(r.Time)),datasets:[
395
+ {label:'Fresh',data:wtH.map(r=>r.FreshTank_Level_L||0),borderColor:'#18d4e8',backgroundColor:'rgba(24,212,232,.06)',fill:true,pointRadius:0,borderWidth:1.5,tension:.3},
396
+ {label:'Grey',data:wtH.map(r=>r.GreyTank_Level_L||0),borderColor:'#7888a0',backgroundColor:'rgba(120,136,160,.04)',fill:true,pointRadius:0,borderWidth:1.5,tension:.3},
397
+ {label:'Black',data:wtH.map(r=>r.BlackTank_Level_L||0),borderColor:'#585858',backgroundColor:'rgba(88,88,88,.04)',fill:true,pointRadius:0,borderWidth:1.5,tension:.3},
398
+ ]},mO({interaction:{intersect:false,mode:'index'},scales:{x:{ticks:{maxTicksLimit:14}},y:{title:{display:true,text:'Litres',color:'#506070'}}},plugins:{tooltip:{callbacks:{label:c=>`${c.dataset.label}: ${c.raw.toFixed(1)} L`}},annotation:{annotations:{fcap:{type:'line',yMin:fc,yMax:fc,borderColor:'#18d4e833',borderWidth:1,borderDash:[3,3],label:{display:true,content:`Fresh cap (${Math.round(fc)}L)`,position:'start',color:'#18d4e8',font:{size:8},backgroundColor:'transparent'}},gcap:{type:'line',yMin:gc,yMax:gc,borderColor:'#7888a033',borderWidth:1,borderDash:[3,3]}}}}}));
399
+
400
+ if(wtD.length){mk('wt-daily','bar',{labels:wtD.map((_,i)=>`Day ${i+1}`),datasets:[
401
+ {label:'Shower',data:wtD.map(r=>Math.round(r.Shower_Total_L||0)),backgroundColor:'#18d4e8cc',borderRadius:4},
402
+ {label:'Kitchen',data:wtD.map(r=>Math.round(r.Kitchen_Total_L||0)),backgroundColor:'#2ee8a8cc',borderRadius:4},
403
+ {label:'Toilet',data:wtD.map(r=>Math.round(r.Toilet_Total_L||0)),backgroundColor:'#585858cc',borderRadius:4},
404
+ ]},mO({scales:{x:{stacked:true},y:{stacked:true,title:{display:true,text:'Litres',color:'#506070'}}}}));}
405
+
406
+ if(whProfile.value?.length){const wp=whProfile.value;mk('wt-hourly','bar',{labels:wp.map(h=>fH(h.hour)),datasets:[
407
+ {label:'Shower',data:wp.map(h=>h.Shower_L||0),backgroundColor:'#18d4e8cc',borderRadius:2},
408
+ {label:'Kitchen',data:wp.map(h=>h.Kitchen_L||0),backgroundColor:'#2ee8a8cc',borderRadius:2},
409
+ {label:'Toilet',data:wp.map(h=>h.Toilet_L||0),backgroundColor:'#585858cc',borderRadius:2},
410
+ ]},mO({scales:{x:{stacked:true,ticks:{maxTicksLimit:12}},y:{stacked:true,title:{display:true,text:'Litres',color:'#506070'}}}}));}
411
+
412
+ if(wtD.length){const t={S:0,K:0,T:0};wtD.forEach(r=>{t.S+=r.Shower_Total_L||0;t.K+=r.Kitchen_Total_L||0;t.T+=r.Toilet_Total_L||0;});const tt=t.S+t.K+t.T;
413
+ mk('wt-donut','doughnut',{labels:['Shower','Kitchen','Toilet'],datasets:[{data:[Math.round(t.S),Math.round(t.K),Math.round(t.T)],backgroundColor:['#18d4e8','#2ee8a8','#585858'],borderWidth:0,hoverOffset:6}]},
414
+ {responsive:true,maintainAspectRatio:true,cutout:'62%',plugins:{legend:{position:'right',labels:{color:'#8a9bb0',font:{size:10},boxWidth:10,padding:8}},tooltip:{callbacks:{label:c=>`${c.label}: ${c.raw} L (${Math.round(c.raw/tt*100)}%)`}}}});}
415
+
416
+ if(wt15.length){const pm=wt15.map(r=>r.Pump_Flow_Lpm||0);
417
+ mk('wt-pump','bar',{labels:wt15.map(r=>fT(r.Time)),datasets:[{label:'Pump',data:pm,backgroundColor:pm.map(v=>`rgba(24,212,232,${Math.min(.9,v/.4+.08)})`),borderRadius:1,barPercentage:1,categoryPercentage:1}]},mO({plugins:{legend:{display:false}},scales:{x:{ticks:{maxTicksLimit:14}},y:{title:{display:true,text:'Lpm',color:'#506070'}}}}));}
418
+ }
419
+
420
+ // ── Budget ──
421
+ async function loadBG(){if(!wtD.length){const r=await api('/api/water/1DAY?limit=30');wtD=r.data||[];}await nextTick();renderBG();}
422
+ function renderBG(){
423
+ const s=S.value;if(!s.trip_days)return;const pb=pBudget.value,wb=wBudget.value,days=s.trip_days,ct=s.circuit_totals_kwh||{};
424
+ const cM={hvac_kwh:'HVAC',lighting_kwh:'Lighting',devices_kwh:'Devices',fridge_kwh:'Fridge',water_pump_kwh:'WaterPump',cooking_kwh:'Cooking',inverter_kwh:'Inverter'};
425
+ const bL=Object.keys(cM).map(k=>cM[k]),bB=Object.keys(cM).map(k=>rd((pb[k]||0)*days)),bA=Object.keys(cM).map(k=>rd(ct[cM[k]]||0));
426
+
427
+ mk('bg-power','bar',{labels:bL,datasets:[
428
+ {label:'Budget',data:bB,backgroundColor:'rgba(46,232,168,.3)',borderColor:'#2ee8a8',borderWidth:1,borderRadius:5,barPercentage:.55},
429
+ {label:'Actual',data:bA,backgroundColor:'rgba(255,80,80,.3)',borderColor:'#ff5050',borderWidth:1,borderRadius:5,barPercentage:.55},
430
+ ]},mO({scales:{y:{title:{display:true,text:'kWh (trip)',color:'#506070'}}},plugins:{tooltip:{callbacks:{label:c=>`${c.dataset.label}: ${c.raw} kWh`}}}}));
431
+
432
+ const wL=['Shower','Kitchen','Toilet'],wB=[rd(wb.shower_L*days),rd(wb.kitchen_L*days),rd(wb.toilet_L*days)];let wA=[0,0,0];wtD.forEach(r=>{wA[0]+=r.Shower_Total_L||0;wA[1]+=r.Kitchen_Total_L||0;wA[2]+=r.Toilet_Total_L||0;});wA=wA.map(v=>Math.round(v));
433
+ mk('bg-water','bar',{labels:wL,datasets:[
434
+ {label:'Budget',data:wB,backgroundColor:'rgba(24,212,232,.3)',borderColor:'#18d4e8',borderWidth:1,borderRadius:5,barPercentage:.55},
435
+ {label:'Actual',data:wA,backgroundColor:'rgba(88,88,88,.35)',borderColor:'#585858',borderWidth:1,borderRadius:5,barPercentage:.55},
436
+ ]},mO({scales:{y:{title:{display:true,text:'Litres (trip)',color:'#506070'}}}}));
437
+
438
+ const db=s.daily_breakdown||[];
439
+ mk('bg-trend','line',{labels:db.map(d=>`Day ${d.day}`),datasets:[
440
+ {label:'Solar Coverage %',data:db.map(d=>Math.min(200,Math.round(d.solar_kwh/Math.max(d.consumption_kwh,.01)*100))),borderColor:'#2ee8a8',backgroundColor:'rgba(46,232,168,.06)',fill:true,pointRadius:5,pointBackgroundColor:'#2ee8a8',borderWidth:2,tension:.3},
441
+ {label:'Battery End %',data:db.map(d=>d.battery_end_pct),borderColor:'#4890ff',backgroundColor:'rgba(72,144,255,.04)',fill:false,pointRadius:5,pointBackgroundColor:'#4890ff',borderWidth:2,tension:.3},
442
+ ]},mO({scales:{y:{min:0,title:{display:true,text:'%',color:'#506070'}}},plugins:{annotation:{annotations:{target:{type:'line',yMin:100,yMax:100,borderColor:'#2ee8a855',borderWidth:1,borderDash:[4,4],label:{display:true,content:'100% self-sufficient',position:'end',color:'#2ee8a8',font:{size:8},backgroundColor:'transparent'}}}}}}));
443
+ }
444
+
445
+ // ── Generate ──
446
+ async function doGen(){
447
+ genBusy.value=true;genMsg.value='Generating simulation data...';genOk.value=true;
448
+ try{
449
+ const r=await fetch('/api/generate',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(gen)});
450
+ const d=await r.json();
451
+ if(r.ok){genMsg.value='βœ“ '+d.message+'. Reloading...';genOk.value=true;pw15=[];pwH=[];wtH=[];wtD=[];wt15=[];await loadAll();genMsg.value='βœ“ Dashboard refreshed!';}
452
+ else{genMsg.value='βœ— '+(d.error||'Error');genOk.value=false;}
453
+ }catch(e){genMsg.value='βœ— '+e.message;genOk.value=false;}
454
+ genBusy.value=false;
455
+ }
456
+
457
+ function go(p){pg.value=p;}
458
+ watch(pg,async p=>{await nextTick();if(p==='overview')renderOV();if(p==='power')loadPW();if(p==='water')loadWT();if(p==='budget')loadBG();});
459
+ onMounted(async()=>{await loadAll();await nextTick();renderOV();});
460
+
461
+ return{pg,loading,cfg,S,pBudget,wBudget,comps,hProfile,whProfile,pDay,pDayInsight,gen,genBusy,genMsg,genOk,battCol,pBMax,wBMax,rd,fL,fmtK,bPct,bCol,go,setPDay,doGen};
462
+ }
463
+ }).mount('#app');
464
+ </script>
465
+ </body>
466
+ </html>