@@ -73,6 +73,8 @@ async def notify_startup_email(
7373 mode_names : dict [int , str ],
7474 event_time_utc : datetime ,
7575 logger : logging .Logger ,
76+ zappi_status : dict [str , Any ] | None = None ,
77+ zappi_daily : dict [str , Any ] | None = None ,
7678) -> None :
7779 """Send a startup email with current mode, SOC, and recent transition summary.
7880
@@ -84,6 +86,8 @@ async def notify_startup_email(
8486 mode_names: Mapping from mode value to human-readable mode label.
8587 event_time_utc: Startup timestamp in UTC.
8688 logger: Logger instance used for status/error output.
89+ zappi_status: Most recent Zappi live-status snapshot, or None when unavailable.
90+ zappi_daily: Today's Zappi daily charge totals, or None when unavailable.
8791 """
8892 sender = _get_email_sender_instance ()
8993 if sender is None :
@@ -110,6 +114,7 @@ async def notify_startup_email(
110114 f"SOC { soc_text } • Solar Today { today_solar_text } "
111115 )
112116 forecast_text , forecast_html = _build_today_forecast_email_sections (today_period_forecast )
117+ zappi_text , zappi_html = _build_zappi_email_sections (zappi_status , zappi_daily )
113118
114119 now_local = event_time_utc .astimezone (LOCAL_TZ )
115120 previous_2230 = (now_local - timedelta (days = 1 )).replace (
@@ -143,7 +148,7 @@ async def notify_startup_email(
143148 timeline_text = "\n " .join (timeline_lines )
144149 timeline_html = (
145150 f'<div style="margin-top:12px;padding:10px 12px;background:#f8fafc;border:1px solid #e4ebf3;border-radius:10px;">'
146- f'<div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#5b6b82 ;margin-bottom:6px;">Transitions since 10:30 PM</div>'
151+ f'<div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#143a52;font-weight:700 ;margin-bottom:6px;">Transitions since 10:30 PM</div>'
147152 f'<table role="presentation" style="width:100%;border-collapse:collapse;">{ timeline_rows } </table>'
148153 f'</div>'
149154 )
@@ -163,7 +168,8 @@ async def notify_startup_email(
163168 f"Battery SOC: { soc_text } \n \n "
164169 f"Solar Produced Today: { today_solar_text } \n \n "
165170 f"{ forecast_text } \n "
166- "Transitions Since 10:30 PM\n "
171+ + (f"{ zappi_text } \n " if zappi_text else "" )
172+ + "Transitions Since 10:30 PM\n "
167173 "---------------------------\n "
168174 f"{ timeline_text } \n "
169175 )
@@ -185,27 +191,28 @@ async def notify_startup_email(
185191 <tr>
186192 <td style="width:50%;padding:0 6px 0 0;vertical-align:top;">
187193 <div style="padding:10px 12px;background:#f8fafc;border:1px solid #e4ebf3;border-radius:10px;">
188- <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#5b6b82 ;margin-bottom:3px;">Current Mode</div>
194+ <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#143a52;font-weight:700 ;margin-bottom:3px;">Current Mode</div>
189195 <div style="font-size:14px;font-weight:700;color:#172033;">{ escape (current_mode_label )} </div>
190196 </div>
191197 </td>
192198 <td style="width:50%;padding:0 0 0 6px;vertical-align:top;">
193199 <div style="padding:10px 12px;background:#f8fafc;border:1px solid #e4ebf3;border-radius:10px;">
194- <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#5b6b82 ;margin-bottom:3px;">Battery SOC</div>
200+ <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#143a52;font-weight:700 ;margin-bottom:3px;">Battery SOC</div>
195201 <div style="font-size:14px;font-weight:700;color:#172033;">{ escape (soc_text )} </div>
196202 </div>
197203 </td>
198204 </tr>
199205 <tr>
200206 <td style="padding:12px 6px 0 0;vertical-align:top;" colspan="2">
201207 <div style="padding:10px 12px;background:#f8fafc;border:1px solid #e4ebf3;border-radius:10px;">
202- <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#5b6b82 ;margin-bottom:3px;">Solar Produced Today</div>
208+ <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#143a52;font-weight:700 ;margin-bottom:3px;">Solar Produced Today</div>
203209 <div style="font-size:14px;font-weight:700;color:#172033;">{ escape (today_solar_text )} </div>
204210 </div>
205211 </td>
206212 </tr>
207213 </table>
208214 { forecast_html }
215+ { zappi_html }
209216 { timeline_html }
210217 </div>
211218 </div>
@@ -271,10 +278,13 @@ def _build_zappi_email_sections(
271278 total_kwh = zappi_daily .get ("total_kwh" , 0.0 )
272279 diverted_kwh = zappi_daily .get ("diverted_kwh" , 0.0 )
273280 boosted_kwh = zappi_daily .get ("boosted_kwh" , 0.0 )
281+ grid_imported_kwh = zappi_daily .get ("grid_imported_kwh" )
274282 rows_text += [
275283 f"Today's Total to EV: { total_kwh :.2f} kWh "
276284 f"(Solar: { diverted_kwh :.2f} kWh | Grid: { boosted_kwh :.2f} kWh)" ,
277285 ]
286+ if grid_imported_kwh is not None :
287+ rows_text .append (f"Grid Imported Today: { grid_imported_kwh :.2f} kWh" )
278288 rows_html += (
279289 f'<tr><td style="padding:4px 8px 4px 0;font-size:12px;color:#5b6b82;white-space:nowrap;">Today — Total to EV</td>'
280290 f'<td style="padding:4px 0;font-size:12px;font-weight:600;color:#172033;">{ total_kwh :.2f} kWh</td></tr>'
@@ -283,6 +293,11 @@ def _build_zappi_email_sections(
283293 f'<tr><td style="padding:4px 8px 4px 0;font-size:12px;color:#5b6b82;white-space:nowrap;"> Grid boost</td>'
284294 f'<td style="padding:4px 0;font-size:12px;color:#172033;">{ boosted_kwh :.2f} kWh</td></tr>'
285295 )
296+ if grid_imported_kwh is not None :
297+ rows_html += (
298+ f'<tr><td style="padding:4px 8px 4px 0;font-size:12px;color:#5b6b82;white-space:nowrap;">Grid Imported Today</td>'
299+ f'<td style="padding:4px 0;font-size:12px;font-weight:600;color:#172033;">{ grid_imported_kwh :.2f} kWh</td></tr>'
300+ )
286301
287302 plain_text = (
288303 "EV Charger (Zappi)\n "
@@ -294,7 +309,7 @@ def _build_zappi_email_sections(
294309 '<div style="margin-top:12px;padding:10px 12px;background:#f8fafc;'
295310 'border:1px solid #e4ebf3;border-radius:10px;">'
296311 '<div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;'
297- 'color:#5b6b82 ;margin-bottom:6px;">EV Charger (Zappi)</div>'
312+ 'color:#143a52;font-weight:700 ;margin-bottom:6px;">EV Charger (Zappi)</div>'
298313 '<table role="presentation" style="width:100%;border-collapse:collapse;">'
299314 + rows_html
300315 + '</table>'
@@ -445,7 +460,7 @@ async def notify_mode_change_email(
445460 )
446461 timeline_section = (
447462 f'<div style="margin-top:12px;padding:10px 12px;background:#f8fafc;border:1px solid #e4ebf3;border-radius:10px;">'
448- f'<div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#5b6b82 ;margin-bottom:6px;">'
463+ f'<div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#143a52;font-weight:700 ;margin-bottom:6px;">'
449464 f'Transitions since 10:30 PM</div>'
450465 f'<table role="presentation" style="width:100%;border-collapse:collapse;">{ timeline_rows } </table>'
451466 f'</div>'
@@ -470,21 +485,21 @@ async def notify_mode_change_email(
470485 <tr>
471486 <td style="width:50%;padding:0 6px 0 0;vertical-align:top;">
472487 <div style="padding:10px 12px;background:#f8fafc;border:1px solid #e4ebf3;border-radius:10px;">
473- <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#5b6b82 ;margin-bottom:3px;">Time</div>
488+ <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#143a52;font-weight:700 ;margin-bottom:3px;">Time</div>
474489 <div style="font-size:14px;font-weight:700;color:#172033;">{ escape (local_time )} </div>
475490 </div>
476491 </td>
477492 <td style="width:50%;padding:0 0 0 6px;vertical-align:top;">
478493 <div style="padding:10px 12px;background:#f8fafc;border:1px solid #e4ebf3;border-radius:10px;">
479- <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#5b6b82 ;margin-bottom:3px;">Battery SOC</div>
494+ <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#143a52;font-weight:700 ;margin-bottom:3px;">Battery SOC</div>
480495 <div style="font-size:14px;font-weight:700;color:#172033;">{ escape (soc_text )} </div>
481496 </div>
482497 </td>
483498 </tr>
484499 <tr>
485500 <td style="padding:12px 6px 0 0;vertical-align:top;" colspan="2">
486501 <div style="padding:10px 12px;background:#f8fafc;border:1px solid #e4ebf3;border-radius:10px;">
487- <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#5b6b82 ;margin-bottom:3px;">Solar Produced Today</div>
502+ <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#143a52;font-weight:700 ;margin-bottom:3px;">Solar Produced Today</div>
488503 <div style="font-size:14px;font-weight:700;color:#172033;">{ escape (today_solar_text )} </div>
489504 </div>
490505 </td>
@@ -493,7 +508,7 @@ async def notify_mode_change_email(
493508 { forecast_html }
494509 { zappi_html }
495510 <div style="margin-bottom:12px;padding:10px 12px;background:#f8fafc;border:1px solid #e4ebf3;border-radius:10px;font-size:13px;line-height:1.5;color:#26354d;">
496- <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#5b6b82 ;margin-bottom:4px;">Reason</div>
511+ <div style="font-size:11px;text-transform:uppercase;letter-spacing:0.07em;color:#143a52;font-weight:700 ;margin-bottom:4px;">Reason</div>
497512 { escape (reason )}
498513 </div>
499514 <details style="margin-bottom:0;">
0 commit comments