CoderHassan commited on
Commit
16cd13a
Β·
verified Β·
1 Parent(s): 28b582e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +73 -59
app.py CHANGED
@@ -178,6 +178,32 @@ with st.sidebar:
178
  else:
179
  st.markdown('<p class="status-off">β—‰ DISCONNECTED</p>', unsafe_allow_html=True)
180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  st.markdown('<div class="eg-section">Settings</div>', unsafe_allow_html=True)
182
  rate = st.number_input("πŸ’° Tariff (PKR / kWh)", 1.0, 500.0, 50.0, 1.0)
183
  carbon = st.number_input("🌱 Carbon Factor (kg COβ‚‚ / kWh)", 0.1, 3.0, 0.82, 0.01)
@@ -205,15 +231,24 @@ with st.sidebar:
205
 
206
  # ── Data Functions ───────────────────────────────────────────
207
  def _demo_reading():
208
- """Simulate a realistic city-model reading."""
 
 
 
209
  t = st.session_state.demo_tick
210
  st.session_state.demo_tick += 1
211
 
212
- # AC voltage ~220 V with Β±6 V fluctuation
213
- v = 220 + 5 * math.sin(t * 0.07) + random.uniform(-2, 2)
214
- # Load current varies 0.8–2.8 A (city model lamps + motors)
215
- i = 1.8 + 0.6 * math.sin(t * 0.04) + 0.2 * math.sin(t * 0.13) + random.uniform(-0.05, 0.05)
216
- i = max(0.1, i)
 
 
 
 
 
 
217
  p = v * i
218
 
219
  dt_h = 1 / 3600
@@ -531,25 +566,20 @@ with tab3:
531
  st.plotly_chart(fig_h, use_container_width=True)
532
 
533
  # AI Prediction (linear regression on energy)
534
- reg_df = df.copy()
535
- reg_df['energy_kwh'] = pd.to_numeric(reg_df['energy_kwh'], errors='coerce')
536
- reg_df['timestamp'] = pd.to_datetime(reg_df['timestamp'], errors='coerce')
537
- reg_df = reg_df.dropna(subset=['energy_kwh', 'timestamp']).reset_index(drop=True)
538
-
539
- if len(reg_df) >= 15:
540
  st.markdown('<div class="eg-section">AI Energy Prediction (Linear Regression)</div>',
541
  unsafe_allow_html=True)
542
- x = np.arange(len(reg_df), dtype=float)
543
- y = reg_df['energy_kwh'].to_numpy(dtype=float)
544
  coeffs = np.polyfit(x, y, 1)
545
 
546
  n_future = 60
547
- fx = np.arange(len(reg_df), len(reg_df) + n_future, dtype=float)
548
  fy = np.polyval(coeffs, fx)
549
- ft = [reg_df['timestamp'].iloc[-1] + timedelta(seconds=i) for i in range(1, n_future+1)]
550
 
551
  fig_pr = go.Figure()
552
- fig_pr.add_trace(go.Scatter(x=reg_df['timestamp'], y=reg_df['energy_kwh'],
553
  name='Actual', line=dict(color='#00ff99', width=2)))
554
  fig_pr.add_trace(go.Scatter(x=ft, y=fy,
555
  name='Predicted (60 s)', yaxis='y1',
@@ -576,8 +606,7 @@ with tab3:
576
  st.markdown('<div class="eg-section">Data Log (last 50 readings)</div>',
577
  unsafe_allow_html=True)
578
  disp = df.tail(50).copy()
579
- disp['timestamp'] = pd.to_datetime(disp['timestamp'], errors='coerce')
580
- disp['timestamp'] = disp['timestamp'].dt.strftime('%H:%M:%S').fillna('--:--:--')
581
  st.dataframe(disp, use_container_width=True, height=260)
582
 
583
  csv_bytes = df.to_csv(index=False).encode()
@@ -628,31 +657,16 @@ with tab4:
628
  # ── AI Recommendations ──────────────────────
629
  recs = []
630
  if avg_p > 500:
631
- recs.append("HIGH load detected - consider switching off idle appliances.")
632
  if avg_v < 210 or avg_v > 235:
633
- recs.append("Voltage outside safe range (210-235 V) - check power supply.")
634
  if df['voltage'].std() > 8:
635
- recs.append("High voltage fluctuation - consider a voltage stabiliser.")
636
  recs.append("Use LED lighting to reduce city model consumption by ~70%.")
637
- recs.append("Schedule high-load demos during off-peak hours (22:00-06:00).")
638
  recs.append("Install capacitor banks to improve power factor.")
639
  recs.append("Regular maintenance reduces standby losses significantly.")
640
 
641
- def pdf_safe(text):
642
- if text is None:
643
- return ""
644
- normalized = str(text).translate(str.maketrans({
645
- "–": "-",
646
- "β€”": "-",
647
- "β€’": "|",
648
- "Β°": " deg",
649
- "β‚‚": "2",
650
- "₨": "Rs",
651
- "βœ“": "[+]",
652
- "⚠": "[!]",
653
- }))
654
- return normalized.encode("latin-1", "replace").decode("latin-1")
655
-
656
  # ── Build PDF ───────────────────────────────
657
  class EnergyPDF(FPDF):
658
  def header(self):
@@ -669,14 +683,14 @@ with tab4:
669
  self.set_font('Helvetica', 'B', 14)
670
  self.set_text_color(0, 200, 255)
671
  self.set_xy(8, 4)
672
- self.cell(100, 7, pdf_safe('ENERGYGURU - POWER CALCULUS'), ln=False)
673
 
674
  self.set_font('Helvetica', '', 7)
675
  self.set_text_color(80, 120, 160)
676
  self.set_xy(8, 13)
677
- self.cell(0, 5, pdf_safe(
678
  f'AI-Assisted Energy Usage Analyzer | '
679
- f'Generated: {datetime.now():%Y-%m-%d %H:%M:%S}'))
680
  self.ln(14)
681
 
682
  def footer(self):
@@ -684,7 +698,7 @@ with tab4:
684
  self.set_font('Helvetica', 'I', 7)
685
  self.set_text_color(50, 70, 100)
686
  self.cell(0, 8,
687
- pdf_safe(f'EnergyGuru Power Calculus | {institution} | Page {self.page_no()}'),
688
  align='C')
689
 
690
  def section_title(self, txt):
@@ -694,7 +708,7 @@ with tab4:
694
  self.rect(self.get_x(), self.get_y(), 185, 8, 'DF')
695
  self.set_font('Helvetica', 'B', 9)
696
  self.set_text_color(0, 200, 255)
697
- self.cell(0, 8, pdf_safe(f' {txt}'), ln=True)
698
  self.ln(2)
699
 
700
  def kv_row(self, label, value, fill_idx):
@@ -704,10 +718,10 @@ with tab4:
704
  self.set_fill_color(10, 16, 28)
705
  self.set_text_color(100, 140, 180)
706
  self.set_font('Helvetica', '', 9)
707
- self.cell(90, 7, pdf_safe(f' {label}'), fill=True)
708
  self.set_text_color(220, 230, 240)
709
  self.set_font('Helvetica', 'B', 9)
710
- self.cell(95, 7, pdf_safe(f' {value}'), fill=True, ln=True)
711
 
712
  pdf = EnergyPDF()
713
  pdf.set_auto_page_break(auto=True, margin=18)
@@ -716,16 +730,16 @@ with tab4:
716
  # Title block
717
  pdf.set_font('Helvetica', 'B', 17)
718
  pdf.set_text_color(0, 200, 255)
719
- pdf.cell(0, 10, pdf_safe(rpt_title), ln=True, align='C')
720
  pdf.ln(1)
721
 
722
  pdf.set_font('Helvetica', '', 9)
723
  pdf.set_text_color(80, 120, 160)
724
- pdf.cell(0, 6, pdf_safe(f'Institution: {institution} | Project: {project_id}'), ln=True, align='C')
725
- pdf.cell(0, 6, pdf_safe(f'Location: {city_choice} | Lat {loc["lat"]:.4f} deg Lon {loc["lon"]:.4f} deg'), ln=True, align='C')
726
  if operator:
727
- pdf.cell(0, 6, pdf_safe(f'Operator: {operator}'), ln=True, align='C')
728
- pdf.cell(0, 6, pdf_safe(f'Date: {datetime.now():%B %d, %Y} Time: {datetime.now():%H:%M:%S}'), ln=True, align='C')
729
  pdf.ln(4)
730
 
731
  # Divider
@@ -744,7 +758,7 @@ with tab4:
744
  ("Minimum Power", f"{min_p:.2f} W"),
745
  ("Total Energy Consumed", f"{tot_e:.6f} kWh"),
746
  ("Electricity Bill", f"PKR {tot_b:.4f}"),
747
- ("Carbon Footprint", f"{tot_co2:.6f} kg CO2"),
748
  ("Tariff Rate", f"PKR {rate:.2f} / kWh"),
749
  ("Carbon Factor", f"{carbon:.2f} kg COβ‚‚ / kWh"),
750
  ("Total Readings", f"{n_reads}"),
@@ -788,7 +802,7 @@ with tab4:
788
  # ── Section 3: Last 20 readings ──────────────
789
  pdf.section_title('3. RECENT READINGS (last 20)')
790
  hdrs = ['Time', 'V (V)', 'I (A)', 'P (W)',
791
- 'kWh', 'Bill Rs', 'CO2 kg']
792
  c_widths = [26, 22, 22, 25, 32, 30, 28]
793
 
794
  # Table header
@@ -796,7 +810,7 @@ with tab4:
796
  pdf.set_text_color(0, 200, 255)
797
  pdf.set_font('Helvetica', 'B', 8)
798
  for h, w in zip(hdrs, c_widths):
799
- pdf.cell(w, 7, pdf_safe(h), fill=True, align='C')
800
  pdf.ln()
801
 
802
  pdf.set_font('Helvetica', '', 8)
@@ -814,7 +828,7 @@ with tab4:
814
  f"{row['bill_pkr']:.4f}",
815
  f"{row['carbon_kg']:.6f}"]
816
  for v, w in zip(vals, c_widths):
817
- pdf.cell(w, 6, pdf_safe(v), fill=True, align='C')
818
  pdf.ln()
819
  pdf.ln(4)
820
 
@@ -822,10 +836,10 @@ with tab4:
822
  pdf.section_title('4. AI ENERGY RECOMMENDATIONS')
823
  pdf.set_font('Helvetica', '', 9)
824
  for i, rec in enumerate(recs):
825
- icon = '[!]' if rec.startswith('HIGH') or rec.startswith('Voltage') or rec.startswith('High') else '[+]'
826
- clr = (255, 180, 60) if icon == '[!]' else (100, 220, 130)
827
  pdf.set_text_color(*clr)
828
- pdf.cell(0, 8, pdf_safe(f' {icon} {rec}'), ln=True)
829
  pdf.ln(3)
830
 
831
  # ── Notes ────────────────────────────────────
@@ -839,7 +853,7 @@ with tab4:
839
  pdf.set_font('Helvetica', '', 8)
840
  pdf.set_text_color(140, 160, 180)
841
  for ln_text in notes.split('\n'):
842
- pdf.cell(0, 6, pdf_safe(ln_text), ln=True)
843
 
844
  # ── Output ───────────────────────────────────
845
  pdf_bytes = bytes(pdf.output())
@@ -867,4 +881,4 @@ with tab4:
867
  # ── Auto refresh (live updates) ──────────────────────────────
868
  if st.session_state.demo_mode or st.session_state.connected:
869
  time.sleep(1)
870
- st.rerun()
 
178
  else:
179
  st.markdown('<p class="status-off">β—‰ DISCONNECTED</p>', unsafe_allow_html=True)
180
 
181
+ st.markdown('<div class="eg-section">Manual Override</div>', unsafe_allow_html=True)
182
+ manual_override = st.toggle("✏️ Set Voltage & Current Manually", value=False)
183
+ if manual_override:
184
+ ov1, ov2 = st.columns(2)
185
+ with ov1:
186
+ manual_v = st.number_input(
187
+ "Voltage (V)", min_value=0.0, max_value=260.0,
188
+ value=220.0, step=0.5,
189
+ help="Overrides simulated / sensor voltage"
190
+ )
191
+ with ov2:
192
+ manual_i = st.number_input(
193
+ "Current (A)", min_value=0.0, max_value=30.0,
194
+ value=1.80, step=0.01,
195
+ help="Overrides simulated / sensor current"
196
+ )
197
+ st.markdown(
198
+ f'<div style="font-size:0.70rem;color:#00ff99;margin:-6px 0 4px 0;">'
199
+ f'P = {manual_v:.1f} V Γ— {manual_i:.3f} A = <strong style="color:#ff4466">'
200
+ f'{manual_v * manual_i:.1f} W</strong></div>',
201
+ unsafe_allow_html=True
202
+ )
203
+ else:
204
+ manual_v = None
205
+ manual_i = None
206
+
207
  st.markdown('<div class="eg-section">Settings</div>', unsafe_allow_html=True)
208
  rate = st.number_input("πŸ’° Tariff (PKR / kWh)", 1.0, 500.0, 50.0, 1.0)
209
  carbon = st.number_input("🌱 Carbon Factor (kg COβ‚‚ / kWh)", 0.1, 3.0, 0.82, 0.01)
 
231
 
232
  # ── Data Functions ───────────────────────────────────────────
233
  def _demo_reading():
234
+ """Simulate a realistic city-model reading.
235
+ If manual override is active, centres the reading on the user-supplied
236
+ voltage and current (with a tiny noise floor so charts stay alive).
237
+ """
238
  t = st.session_state.demo_tick
239
  st.session_state.demo_tick += 1
240
 
241
+ if manual_override and manual_v is not None and manual_i is not None:
242
+ # Use manual values β€” add Β±0.5 V / Β±0.005 A noise so the chart isn't flat
243
+ v = manual_v + random.uniform(-0.5, 0.5)
244
+ i = max(0.0, manual_i + random.uniform(-0.005, 0.005))
245
+ else:
246
+ # AC voltage ~220 V with Β±6 V fluctuation
247
+ v = 220 + 5 * math.sin(t * 0.07) + random.uniform(-2, 2)
248
+ # Load current varies 0.8–2.8 A (city model lamps + motors)
249
+ i = 1.8 + 0.6 * math.sin(t * 0.04) + 0.2 * math.sin(t * 0.13) + random.uniform(-0.05, 0.05)
250
+ i = max(0.1, i)
251
+
252
  p = v * i
253
 
254
  dt_h = 1 / 3600
 
566
  st.plotly_chart(fig_h, use_container_width=True)
567
 
568
  # AI Prediction (linear regression on energy)
569
+ if len(df) >= 15:
 
 
 
 
 
570
  st.markdown('<div class="eg-section">AI Energy Prediction (Linear Regression)</div>',
571
  unsafe_allow_html=True)
572
+ x = np.arange(len(df))
573
+ y = df['energy_kwh'].values
574
  coeffs = np.polyfit(x, y, 1)
575
 
576
  n_future = 60
577
+ fx = np.arange(len(df), len(df) + n_future)
578
  fy = np.polyval(coeffs, fx)
579
+ ft = [df['timestamp'].iloc[-1] + timedelta(seconds=i) for i in range(1, n_future+1)]
580
 
581
  fig_pr = go.Figure()
582
+ fig_pr.add_trace(go.Scatter(x=df['timestamp'], y=df['energy_kwh'],
583
  name='Actual', line=dict(color='#00ff99', width=2)))
584
  fig_pr.add_trace(go.Scatter(x=ft, y=fy,
585
  name='Predicted (60 s)', yaxis='y1',
 
606
  st.markdown('<div class="eg-section">Data Log (last 50 readings)</div>',
607
  unsafe_allow_html=True)
608
  disp = df.tail(50).copy()
609
+ disp['timestamp'] = disp['timestamp'].dt.strftime('%H:%M:%S')
 
610
  st.dataframe(disp, use_container_width=True, height=260)
611
 
612
  csv_bytes = df.to_csv(index=False).encode()
 
657
  # ── AI Recommendations ──────────────────────
658
  recs = []
659
  if avg_p > 500:
660
+ recs.append("HIGH load detected β€” consider switching off idle appliances.")
661
  if avg_v < 210 or avg_v > 235:
662
+ recs.append("Voltage outside safe range (210–235 V) β€” check power supply.")
663
  if df['voltage'].std() > 8:
664
+ recs.append("High voltage fluctuation β€” consider a voltage stabiliser.")
665
  recs.append("Use LED lighting to reduce city model consumption by ~70%.")
666
+ recs.append("Schedule high-load demos during off-peak hours (22:00–06:00).")
667
  recs.append("Install capacitor banks to improve power factor.")
668
  recs.append("Regular maintenance reduces standby losses significantly.")
669
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
670
  # ── Build PDF ───────────────────────────────
671
  class EnergyPDF(FPDF):
672
  def header(self):
 
683
  self.set_font('Helvetica', 'B', 14)
684
  self.set_text_color(0, 200, 255)
685
  self.set_xy(8, 4)
686
+ self.cell(100, 7, 'ENERGYGURU – POWER CALCULUS', ln=False)
687
 
688
  self.set_font('Helvetica', '', 7)
689
  self.set_text_color(80, 120, 160)
690
  self.set_xy(8, 13)
691
+ self.cell(0, 5,
692
  f'AI-Assisted Energy Usage Analyzer | '
693
+ f'Generated: {datetime.now():%Y-%m-%d %H:%M:%S}')
694
  self.ln(14)
695
 
696
  def footer(self):
 
698
  self.set_font('Helvetica', 'I', 7)
699
  self.set_text_color(50, 70, 100)
700
  self.cell(0, 8,
701
+ f'EnergyGuru Power Calculus | {institution} | Page {self.page_no()}',
702
  align='C')
703
 
704
  def section_title(self, txt):
 
708
  self.rect(self.get_x(), self.get_y(), 185, 8, 'DF')
709
  self.set_font('Helvetica', 'B', 9)
710
  self.set_text_color(0, 200, 255)
711
+ self.cell(0, 8, f' {txt}', ln=True)
712
  self.ln(2)
713
 
714
  def kv_row(self, label, value, fill_idx):
 
718
  self.set_fill_color(10, 16, 28)
719
  self.set_text_color(100, 140, 180)
720
  self.set_font('Helvetica', '', 9)
721
+ self.cell(90, 7, f' {label}', fill=True)
722
  self.set_text_color(220, 230, 240)
723
  self.set_font('Helvetica', 'B', 9)
724
+ self.cell(95, 7, f' {value}', fill=True, ln=True)
725
 
726
  pdf = EnergyPDF()
727
  pdf.set_auto_page_break(auto=True, margin=18)
 
730
  # Title block
731
  pdf.set_font('Helvetica', 'B', 17)
732
  pdf.set_text_color(0, 200, 255)
733
+ pdf.cell(0, 10, rpt_title, ln=True, align='C')
734
  pdf.ln(1)
735
 
736
  pdf.set_font('Helvetica', '', 9)
737
  pdf.set_text_color(80, 120, 160)
738
+ pdf.cell(0, 6, f'Institution: {institution} | Project: {project_id}', ln=True, align='C')
739
+ pdf.cell(0, 6, f'Location: {city_choice} β€’ Lat {loc["lat"]:.4f}Β° Lon {loc["lon"]:.4f}Β°', ln=True, align='C')
740
  if operator:
741
+ pdf.cell(0, 6, f'Operator: {operator}', ln=True, align='C')
742
+ pdf.cell(0, 6, f'Date: {datetime.now():%B %d, %Y} Time: {datetime.now():%H:%M:%S}', ln=True, align='C')
743
  pdf.ln(4)
744
 
745
  # Divider
 
758
  ("Minimum Power", f"{min_p:.2f} W"),
759
  ("Total Energy Consumed", f"{tot_e:.6f} kWh"),
760
  ("Electricity Bill", f"PKR {tot_b:.4f}"),
761
+ ("Carbon Footprint", f"{tot_co2:.6f} kg COβ‚‚"),
762
  ("Tariff Rate", f"PKR {rate:.2f} / kWh"),
763
  ("Carbon Factor", f"{carbon:.2f} kg COβ‚‚ / kWh"),
764
  ("Total Readings", f"{n_reads}"),
 
802
  # ── Section 3: Last 20 readings ──────────────
803
  pdf.section_title('3. RECENT READINGS (last 20)')
804
  hdrs = ['Time', 'V (V)', 'I (A)', 'P (W)',
805
+ 'kWh', 'Bill ₨', 'COβ‚‚ kg']
806
  c_widths = [26, 22, 22, 25, 32, 30, 28]
807
 
808
  # Table header
 
810
  pdf.set_text_color(0, 200, 255)
811
  pdf.set_font('Helvetica', 'B', 8)
812
  for h, w in zip(hdrs, c_widths):
813
+ pdf.cell(w, 7, h, fill=True, align='C')
814
  pdf.ln()
815
 
816
  pdf.set_font('Helvetica', '', 8)
 
828
  f"{row['bill_pkr']:.4f}",
829
  f"{row['carbon_kg']:.6f}"]
830
  for v, w in zip(vals, c_widths):
831
+ pdf.cell(w, 6, v, fill=True, align='C')
832
  pdf.ln()
833
  pdf.ln(4)
834
 
 
836
  pdf.section_title('4. AI ENERGY RECOMMENDATIONS')
837
  pdf.set_font('Helvetica', '', 9)
838
  for i, rec in enumerate(recs):
839
+ icon = '⚠' if rec.startswith('HIGH') or rec.startswith('Voltage') or rec.startswith('High') else 'βœ“'
840
+ clr = (255, 180, 60) if icon == '⚠' else (100, 220, 130)
841
  pdf.set_text_color(*clr)
842
+ pdf.cell(0, 8, f' {icon} {rec}', ln=True)
843
  pdf.ln(3)
844
 
845
  # ── Notes ────────────────────────────────────
 
853
  pdf.set_font('Helvetica', '', 8)
854
  pdf.set_text_color(140, 160, 180)
855
  for ln_text in notes.split('\n'):
856
+ pdf.cell(0, 6, ln_text, ln=True)
857
 
858
  # ── Output ───────────────────────────────────
859
  pdf_bytes = bytes(pdf.output())
 
881
  # ── Auto refresh (live updates) ──────────────────────────────
882
  if st.session_state.demo_mode or st.session_state.connected:
883
  time.sleep(1)
884
+ st.rerun()