Skip to content

Commit aeaecdf

Browse files
authored
Update wallbox_monitor.py
1 parent da70c98 commit aeaecdf

1 file changed

Lines changed: 67 additions & 91 deletions

File tree

wallbox_monitor.py

Lines changed: 67 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -169,58 +169,38 @@ def format_duration(seconds):
169169

170170
def german_timestamp():
171171
return datetime.now().strftime("%d.%m.%y, %H:%M")
172-
172+
173173
def get_last_state():
174-
"""Reads the last state and ensures `notified` is retained persistently."""
175174
try:
176175
with open(STATE_FILE, "r") as f:
177176
data = f.read().strip()
178177

179-
if data.startswith("charging:"):
178+
if data.startswith("charging:") or data.startswith("idle:"):
180179
parts = data.split(":")
181-
if len(parts) >= 5:
182-
_, start_time, stored_power, notified, total_energy_wh_for_summary = parts
180+
if len(parts) >= 6:
181+
_, start_time, stored_power, notified, total_energy_wh_for_summary, repeat_check = parts
183182
return {
184-
"state": "charging",
185-
"start_time": float(start_time),
183+
"state": "charging" if data.startswith("charging:") else "idle",
184+
"start_time": float(start_time) if data.startswith("charging:") else None,
186185
"stored_power": float(stored_power),
187186
"total_energy_wh_for_summary": float(total_energy_wh_for_summary),
188-
"notified": bool(int(notified)),
187+
"notified": bool(int(notified)),
188+
"repeat_check": bool(int(repeat_check)), # NEW FLAG
189189
}
190190

191-
elif data.startswith("idle:"):
191+
elif data.startswith("disconnected:"):
192192
parts = data.split(":")
193-
if len(parts) >= 3:
194-
_, stored_power, total_energy_wh_for_summary, notified = parts
195-
return {
196-
"state": "idle",
197-
"start_time": None,
198-
"stored_power": float(stored_power),
199-
"total_energy_wh_for_summary": float(total_energy_wh_for_summary),
200-
"notified": bool(int(notified)),
201-
}
202-
203-
elif data.startswith("disconnected:"):
204-
parts = data.split(":")
205-
if len(parts) == 3:
206-
_, total_energy_wh_for_summary, notified = parts
193+
if len(parts) >= 4:
194+
_, total_energy_wh_for_summary, notified, repeat_check = parts
207195
return {
208196
"state": "disconnected",
209197
"start_time": None,
210198
"stored_power": 0.0,
211199
"total_energy_wh_for_summary": float(total_energy_wh_for_summary),
212-
"notified": bool(int(notified)),
200+
"notified": bool(int(notified)),
201+
"repeat_check": bool(int(repeat_check)), # NEW FLAG
213202
}
214203

215-
elif data == "disconnected":
216-
return {
217-
"state": "disconnected",
218-
"start_time": None,
219-
"stored_power": 0.0,
220-
"total_energy_wh_for_summary": None,
221-
"notified": False,
222-
}
223-
224204
except (FileNotFoundError, ValueError, IndexError):
225205
logger.error("State file corrupted or missing. Resetting to default.")
226206
logger.error(f"State file content: {data}") # Debugging
@@ -229,24 +209,26 @@ def get_last_state():
229209
"state": "idle",
230210
"start_time": None,
231211
"stored_power": 0.0,
232-
"total_energy_wh_for_summary": None,
212+
"total_energy_wh_for_summary": 0.0,
233213
"notified": False,
214+
"repeat_check": False,
234215
}
235216

236-
def save_last_state(state, charging_power=0.0, total_energy_wh=None, total_energy_wh_for_summary=None, notified=False, start_time=None):
217+
218+
def save_last_state(state, charging_power=0.0, total_energy_wh=None, total_energy_wh_for_summary=None, notified=False, start_time=None, repeat_check=False):
237219
with open(STATE_FILE, "w") as f:
238-
if state == "charging":
239-
f.write(f"charging:{start_time if start_time is not None else 0}:{charging_power:.2f}:{int(notified)}:{total_energy_wh_for_summary or 0.0}")
240-
debug(f"charging:{start_time if start_time is not None else 0}:{charging_power:.2f}:{int(notified)}:{total_energy_wh_for_summary or 0.0}")
241-
elif state == "idle" and total_energy_wh is not None:
242-
f.write(f"idle:{total_energy_wh:.2f}:{total_energy_wh_for_summary or 0.0}:{int(notified)}")
243-
debug(f".. save_last_state(): idle:{total_energy_wh:.2f}:{total_energy_wh_for_summary or 0.0}:{int(notified)}")
220+
if state in ["charging", "idle"]:
221+
start_time_str = str(start_time) if state == "charging" and start_time is not None else "0"
222+
f.write(f"{state}:{start_time_str}:{charging_power:.2f}:{int(notified)}:{total_energy_wh_for_summary or 0.0}:{int(repeat_check)}")
223+
debug(f".. save_last_state(): {state}:{start_time_str}:{charging_power:.2f}:{int(notified)}:{total_energy_wh_for_summary or 0.0}:{int(repeat_check)}")
224+
244225
elif state == "disconnected":
245-
f.write(f"disconnected:{total_energy_wh_for_summary if total_energy_wh_for_summary is not None else '0.0'}:{int(notified)}")
246-
debug(f".. save_last_state(): disconnected:{total_energy_wh_for_summary if total_energy_wh_for_summary is not None else '0.0'}:{int(notified)}")
226+
f.write(f"disconnected:{total_energy_wh_for_summary if total_energy_wh_for_summary is not None else '0.0'}:{int(notified)}:{int(repeat_check)}")
227+
debug(f".. save_last_state(): disconnected:{total_energy_wh_for_summary if total_energy_wh_for_summary is not None else '0.0'}:{int(notified)}:{int(repeat_check)}")
228+
247229
else:
248-
f.write(f"idle:0.0:0.0:{int(notified)}")
249-
debug(f".. save_last_state(): idle:0.0:0.0:{int(notified)}")
230+
f.write(f"idle:0.0:0.0:{int(notified)}:{int(repeat_check)}")
231+
debug(f".. save_last_state(): idle:0.0:0.0:{int(notified)}:{int(repeat_check)}")
250232

251233
def send_energy_summary(total_energy_wh_for_summary):
252234
"""Sends a summary of total consumed energy when the cable is disconnected."""
@@ -339,6 +321,7 @@ def main():
339321
stored_power = state_data["stored_power"]
340322
total_energy_wh_for_summary = state_data["total_energy_wh_for_summary"]
341323
notified = state_data["notified"]
324+
repeat_check = state_data["repeat_check"] # NEW FLAG
342325

343326
if total_energy_wh is None:
344327
total_energy_wh = get_last_state().get("total_energy_wh")
@@ -347,86 +330,79 @@ def main():
347330
current_time = time.time()
348331

349332
print(f"🔄 Last State: {last_state}, New Fetch: {charging_rate}, Total Energy: {total_energy_wh}")
350-
debug(f".. get_last_state() #1 \n-- state file: \n Last State: {last_state} \n Start Time: {start_time} \n Stored Power: {stored_power} \n Total Energy for Summary: {total_energy_wh_for_summary} \n Notified: {notified} \n-- new fetch: \n Charging Rate: {charging_rate} \n Total Energy: {total_energy_wh}")
333+
debug(f".. get_last_state() #1 \n-- state file: \n Last State: {last_state} \n Start Time: {start_time} \n Stored Power: {stored_power} \n Total Energy for Summary: {total_energy_wh_for_summary} \n Notified: {notified} \n Repeat Check: {repeat_check} \n-- new fetch: \n Charging Rate: {charging_rate} \n Total Energy: {total_energy_wh}")
351334

352-
# Handle cable disconnection DURING charging
335+
# 🚨 If disconnect detected DURING charging, set repeat_check and exit
353336
if last_state == "charging" and (total_energy_wh is None or charging_rate == 0):
354-
send_notification(f"🔌 {timestamp}: charging interrupted - cable unplugged.")
337+
if not repeat_check: # First detection, re-run once
338+
send_notification(f"🔌 {timestamp}: charging interruption detected, verifying...")
339+
save_last_state("charging", charging_power=charging_rate, total_energy_wh=total_energy_wh, total_energy_wh_for_summary=total_energy_wh_for_summary, notified=notified, start_time=start_time, repeat_check=True)
340+
return # Exit, script will retry next run
355341

356-
# Use the last known total_energy_wh_for_summary
342+
# Second run, confirmed interruption
343+
send_notification(f"🔌 {timestamp}: charging interrupted - cable unplugged.")
357344
send_energy_summary(total_energy_wh_for_summary)
345+
save_last_state("disconnected", total_energy_wh_for_summary=total_energy_wh_for_summary, repeat_check=False) # Reset repeat_check
346+
return # Exit after confirming
347+
348+
# 🚨 If normal disconnect detected (idle state), set repeat_check and exit
349+
if total_energy_wh is None and last_state == "idle":
350+
if not repeat_check: # First detection, re-run once
351+
send_notification(f"🔌 {timestamp}: cable disconnect detected, verifying...")
352+
save_last_state("idle", total_energy_wh=total_energy_wh, total_energy_wh_for_summary=total_energy_wh_for_summary, notified=notified, repeat_check=True)
353+
return # Exit, script will retry next run
354+
355+
# Second run, confirmed disconnection
356+
send_notification(f"🔌 {timestamp}: cable disconnected.")
357+
send_energy_summary(total_energy_wh_for_summary)
358+
save_last_state("disconnected", total_energy_wh_for_summary=total_energy_wh_for_summary, repeat_check=False) # Reset repeat_check
359+
return # Exit after confirming
358360

359-
# Set state to "disconnected" instead of transitioning through "idle"
360-
save_last_state("disconnected")
361-
362-
return # Exit early since cable unplugging overrides other transitions
363-
364-
# Handle cable disconnection
365-
if total_energy_wh is None:
366-
if last_state != "disconnected":
367-
send_notification(f"🔌 {timestamp}: cable disconnected.")
368-
369-
# Use last known `total_energy_wh_for_summary`
370-
send_energy_summary(total_energy_wh_for_summary)
371-
372-
# Set state to disconnected
373-
save_last_state("disconnected")
374-
375-
return # Exit early, no further processing needed
376-
377-
# Handle cable connection
361+
# 🚀 Handle cable reconnection
378362
if last_state == "disconnected" and total_energy_wh is not None:
379363
send_notification(f"🔌 {timestamp}: cable connected.")
380-
save_last_state("idle", total_energy_wh=total_energy_wh)
364+
save_last_state("idle", total_energy_wh=total_energy_wh, repeat_check=False)
381365

382-
# Determine new state
366+
# 🔋 Determine new state
383367
new_state = "idle" if charging_rate < 1.0 else "charging"
384368

385-
# **STORE total_energy_wh_for_summary WHENEVER total_energy_wh IS NOT NONE**
386-
# required for the summary report after cable disconnection
369+
# 📌 Store latest total energy for summary
387370
if total_energy_wh is not None:
388-
save_last_state(new_state, charging_power=charging_rate, total_energy_wh=total_energy_wh, total_energy_wh_for_summary=total_energy_wh, notified=notified)
371+
save_last_state(new_state, charging_power=charging_rate, total_energy_wh=total_energy_wh, total_energy_wh_for_summary=total_energy_wh, notified=notified, repeat_check=False)
389372

390-
# Handle charging start
391-
if last_state != "charging" and new_state == "charging" and not notified:
373+
# Handle charging start
374+
if last_state != "charging" and new_state == "charging":
392375
send_notification(f"⚡ {timestamp}: charging started.")
393-
save_last_state(new_state, charging_power=charging_rate, total_energy_wh=total_energy_wh, total_energy_wh_for_summary=total_energy_wh_for_summary, notified=notified, start_time=current_time)
376+
save_last_state("charging", charging_power=charging_rate, total_energy_wh=total_energy_wh, total_energy_wh_for_summary=total_energy_wh_for_summary, notified=False, start_time=current_time)
394377

395-
# notify once per session about charging rate
378+
# ⚡ Notify charging rate once per session
396379
if last_state == "charging" and charging_rate > 0 and not notified:
397380
send_notification(f"⚡ {timestamp}: charging rate {charging_rate} kW")
398-
save_last_state(new_state, charging_power=charging_rate, total_energy_wh=total_energy_wh, total_energy_wh_for_summary=total_energy_wh_for_summary, notified=True, start_time=start_time)
381+
save_last_state("charging", charging_power=charging_rate, total_energy_wh=total_energy_wh, total_energy_wh_for_summary=total_energy_wh_for_summary, notified=True, start_time=start_time)
399382

400-
# Handle charging stop
383+
# 🔋 Handle charging stop
401384
if last_state == "charging" and new_state == "idle":
402385
send_notification(f"🔋 {timestamp}: charging stopped.")
403-
save_last_state(new_state, total_energy_wh=total_energy_wh, total_energy_wh_for_summary=total_energy_wh, start_time=start_time)
386+
save_last_state("idle", total_energy_wh=total_energy_wh, total_energy_wh_for_summary=total_energy_wh, start_time=start_time)
404387

405388
if total_energy_wh is not None and start_time:
406389
elapsed_time = max(current_time - start_time, 60)
407390
elapsed_formatted = format_duration(elapsed_time)
408-
391+
409392
previous_stored_power = stored_power or total_energy_wh or 0
410393
session_energy_wh = max(total_energy_wh - previous_stored_power, 0)
411-
394+
412395
debug(f".. session-summary \n-- stored_power: {stored_power} \n total_energy_wh: {total_energy_wh} \n previous_stored_power: {previous_stored_power} \n session_energy_wh: {session_energy_wh} \n start_time: {start_time} \n current_time: {current_time} \n elapsed_time: {elapsed_time}")
413396

414397
if format_energy(session_energy_wh) == format_energy(total_energy_wh):
415398
message = f"🔍 {format_energy(session_energy_wh)} in {elapsed_formatted}"
416399
else:
417400
message = f"🔍 {format_energy(session_energy_wh)} of {format_energy(total_energy_wh)} in {elapsed_formatted}"
418-
419401
send_notification(message)
420402

421-
# get current values from the state file in order to log it
403+
# 🔍 Log final state
422404
state_data = get_last_state()
423-
last_state = state_data["state"]
424-
start_time = state_data["start_time"]
425-
stored_power = state_data["stored_power"]
426-
total_energy_wh_for_summary = state_data["total_energy_wh_for_summary"]
427-
notified = state_data["notified"]
428-
429-
debug(f".. get_last_state() #2 \n-- state file: \n Last State: {last_state} \n Start Time: {start_time} \n Stored Power: {stored_power} \n Total Energy for Summary: {total_energy_wh_for_summary} \n Notified: {notified} \n-- new fetch: \n Charging Rate: {charging_rate} \n Total Energy: {total_energy_wh}")
405+
debug(f".. get_last_state() #2 \n-- state file: \n Last State: {state_data['state']} \n Start Time: {state_data['start_time']} \n Stored Power: {state_data['stored_power']} \n Total Energy for Summary: {state_data['total_energy_wh_for_summary']} \n Notified: {state_data['notified']} \n Repeat Check: {state_data['repeat_check']} \n-- new fetch: \n Charging Rate: {charging_rate} \n Total Energy: {total_energy_wh}")
430406

431407
except Exception as e:
432408
send_notification(f"🚨 ALERT: {e}")

0 commit comments

Comments
 (0)