11#!/usr/bin/env python3
22
3- # v1.0.3
3+ # v1.0.4
44# shellrecharge-wallbox-monitor - by bjoerrrn
55# github: https://github.com/bjoerrrn/shellrecharge-wallbox-monitor
66# This script is licensed under GNU GPL version 3.0 or above
1717from selenium .webdriver .support .ui import WebDriverWait
1818from selenium .webdriver .support import expected_conditions as EC
1919from logging .handlers import RotatingFileHandler
20+ import requests
2021
2122# Get script directory
2223SCRIPT_DIR = os .path .dirname (os .path .abspath (__file__ ))
@@ -62,7 +63,7 @@ def fetch_charging_status(driver):
6263 driver .get (WALLBOX_URL )
6364
6465 charging_rate = None
65- consumed_energy_wh = None
66+ total_energy_wh = None
6667
6768 # Wait up to 30 seconds for both values to be populated
6869 for _ in range (30 ): # Retry every second for up to 30 seconds
@@ -81,20 +82,20 @@ def fetch_charging_status(driver):
8182 if consumed_text :
8283 match_consumed = re .search (r"([\d.]+)\s*(wh|kWh)" , consumed_text )
8384 if match_consumed :
84- consumed_energy_wh = float (match_consumed .group (1 ))
85+ total_energy_wh = float (match_consumed .group (1 ))
8586 if "kWh" in consumed_text :
86- consumed_energy_wh *= 1000 # Convert kWh to Wh
87+ total_energy_wh *= 1000 # Convert kWh to Wh
8788
8889 # Exit loop early if both values are found
89- if charging_rate is not None and consumed_energy_wh is not None :
90+ if charging_rate is not None and total_energy_wh is not None :
9091 break
9192
9293 except Exception :
9394 pass # Ignore errors and retry
9495
9596 time .sleep (1 ) # Wait 1 second before retrying
9697
97- return charging_rate , consumed_energy_wh
98+ return charging_rate , total_energy_wh
9899
99100 except Exception as e :
100101 logging .error (f"Error fetching charging status: { e } " )
@@ -107,14 +108,20 @@ def format_energy(wh):
107108 if wh is None :
108109 return "0.00 kWh"
109110 return f"{ wh / 1000 :.2f} kWh" if wh >= 1000 else f"{ wh :.2f} Wh"
111+
112+ def format_duration (seconds ):
113+ """Formats seconds into hours and minutes, e.g., hh:mm."""
114+ minutes = int (seconds // 60 )
115+ hours = minutes // 60
116+ minutes %= 60
117+ return f"{ hours } :{ minutes :02d} h"
110118
111119def german_timestamp ():
112120 """Returns the current time in German short format: DD.MM.YY, HH:MM."""
113121 return datetime .now ().strftime ("%d.%m.%y, %H:%M" )
114122
115123def send_discord_notification (message ):
116124 """Sends a notification to Discord."""
117- import requests
118125 payload = {"content" : message }
119126 try :
120127 requests .post (DISCORD_WEBHOOK_URL , json = payload , timeout = 5 )
@@ -160,16 +167,17 @@ def main():
160167 driver = get_browser ()
161168
162169 try :
163- charging_rate , consumed_energy_wh = fetch_charging_status (driver )
170+ charging_rate , total_energy_wh = fetch_charging_status (driver )
164171
165- print (f"🔍 Charging Rate: { charging_rate } , Consumed Energy: { consumed_energy_wh } " )
166- logging .info (f"🔍 Charging Rate: { charging_rate } , Consumed Energy: { consumed_energy_wh } " )
172+ print (f"🔍 Charging Rate: { charging_rate } , Consumed Energy: { total_energy_wh } " )
173+ logging .info (f"🔍 Charging Rate: { charging_rate } , Consumed Energy: { total_energy_wh } " )
167174
168175 if charging_rate is not None :
169176 new_state = "charging" if charging_rate >= 1.0 else "idle"
170177 last_state , start_time , stored_power , notified = get_last_state ()
171178
172179 timestamp = german_timestamp ()
180+ current_time = time .time ()
173181
174182 print (f"🔄 Last State: { last_state } , New State: { new_state } " )
175183 logging .info (f"🔄 Last State: { last_state } , New State: { new_state } " )
@@ -190,7 +198,7 @@ def main():
190198
191199 # If charging, check if 5 minutes have passed
192200 elif new_state == "charging" and start_time is not None and not notified :
193- elapsed_time = time . time () - start_time
201+ elapsed_time = current_time - start_time if start_time else 0
194202 print (f"⏳ Elapsed Charging Time: { elapsed_time :.2f} seconds" )
195203 logging .info (f"⏳ Elapsed Charging Time: { elapsed_time :.2f} seconds" )
196204
@@ -203,9 +211,16 @@ def main():
203211 save_last_state ("charging" , latest_charging_rate , notified = True )
204212
205213 # If charging stopped, send a separate consumption message
206- if last_state == "charging" and new_state == "idle" and consumed_energy_wh is not None :
207- formatted_energy = format_energy (consumed_energy_wh )
208- message = f"🔍 consumed energy: { formatted_energy } "
214+ if last_state == "charging" and new_state == "idle" and total_energy_wh is not None :
215+ elapsed_time = current_time - start_time if start_time else 0
216+ elapsed_formatted = format_duration (elapsed_time )
217+ session_energy_wh = total_energy_wh - stored_power if stored_power is not None else total_energy_wh
218+
219+ if stored_power == 0 :
220+ message = f"🔍 consumed: { format_energy (session_energy_wh )} in { elapsed_formatted } "
221+ else :
222+ message = f"🔍 consumed: { format_energy (session_energy_wh )} of { format_energy (total_energy_wh )} in { elapsed_formatted } "
223+
209224 print (f"📢 Sending Discord Notification: { message } " )
210225 logging .info (f"📢 Sending Discord Notification: { message } " )
211226 send_discord_notification (message )
0 commit comments