import asyncio import websockets import json from ocpp.routing import on from ocpp.v16 import ChargePoint as cp from ocpp.v16 import call_result from datetime import datetime import os def get_log_filename(): """Genereer log bestandsnaam met datum""" date_str = datetime.now().strftime("%Y-%m-%d") return f"alfen_ocpp_{date_str}.log" def log_message(message, level="INFO"): """Log naar bestand en console""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") log_entry = f"[{timestamp}] [{level}] {message}" # Print naar console print(log_entry) # Append naar dagelijks log bestand log_file = get_log_filename() with open(log_file, 'a', encoding='utf-8') as f: f.write(log_entry + "\n") def log_json(title, data): """Log JSON data mooi geformatteerd""" log_message(f"\n{title}:") formatted = json.dumps(data, indent=2, ensure_ascii=False) for line in formatted.split('\n'): log_message(f" {line}") class ChargePoint(cp): @on('BootNotification') def on_boot_notification(self, charge_point_model, charge_point_vendor, **kwargs): log_message("=" * 60, "SUCCESS") log_message("LAADPAAL VERBONDEN!", "SUCCESS") log_message("=" * 60, "SUCCESS") log_json("Boot Notification data", { "model": charge_point_model, "vendor": charge_point_vendor, **kwargs }) return call_result.BootNotification( current_time=datetime.utcnow().isoformat(), interval=300, status='Accepted' ) @on('Heartbeat') def on_heartbeat(self, **kwargs): log_message("šŸ’“ Heartbeat ontvangen", "HEARTBEAT") if kwargs: log_json("Heartbeat data", kwargs) return call_result.Heartbeat( current_time=datetime.utcnow().isoformat() ) @on('StatusNotification') def on_status_notification(self, connector_id, error_code, status, **kwargs): log_message(f"šŸ“Š Status Update - Connector {connector_id}", "STATUS") log_json("Status details", { "connector_id": connector_id, "status": status, "error_code": error_code, **kwargs }) return call_result.StatusNotification() @on('StartTransaction') def on_start_transaction(self, connector_id, id_tag, meter_start, timestamp, **kwargs): log_message("=" * 60, "TRANSACTION") log_message("šŸ”Œ LAADSESSIE GESTART!", "TRANSACTION") log_message("=" * 60, "TRANSACTION") log_json("Start Transaction data", { "connector_id": connector_id, "id_tag": id_tag, "meter_start": meter_start, "timestamp": timestamp, **kwargs }) return call_result.StartTransaction( transaction_id=int(datetime.now().timestamp()), id_tag_info={'status': 'Accepted'} ) @on('StopTransaction') def on_stop_transaction(self, meter_stop, timestamp, transaction_id, **kwargs): log_message("=" * 60, "TRANSACTION") log_message("šŸ”‹ LAADSESSIE GESTOPT!", "TRANSACTION") log_message("=" * 60, "TRANSACTION") # Bereken verbruik als meter_start beschikbaar is transaction_data = { "transaction_id": transaction_id, "meter_stop": meter_stop, "timestamp": timestamp, **kwargs } # Check of reason aanwezig is in kwargs if 'reason' in kwargs: transaction_data['reason'] = kwargs['reason'] # Check of transaction_data aanwezig is in kwargs if 'transaction_data' in kwargs: # Parse meter values uit transaction_data for data_item in kwargs['transaction_data']: if 'sampled_value' in data_item: for value in data_item['sampled_value']: if value.get('measurand') == 'Energy.Active.Import.Register': meter_start_value = int(value.get('value', 0)) verbruik_wh = meter_stop - meter_start_value transaction_data['meter_start'] = meter_start_value transaction_data['verbruik_wh'] = verbruik_wh transaction_data['verbruik_kwh'] = round(verbruik_wh / 1000, 3) log_json("Stop Transaction data", transaction_data) return call_result.StopTransaction( id_tag_info={'status': 'Accepted'} ) @on('MeterValues') def on_meter_values(self, connector_id, meter_value, **kwargs): log_message(f"⚔ Meter waarden - Connector {connector_id}", "METER") log_json("Meter Values", { "connector_id": connector_id, "meter_value": meter_value, **kwargs }) return call_result.MeterValues() @on('Authorize') def on_authorize(self, id_tag, **kwargs): log_message(f"šŸ” Autorisatie aanvraag - Tag: {id_tag}", "AUTH") if kwargs: log_json("Authorize data", kwargs) return call_result.Authorize( id_tag_info={'status': 'Accepted'} ) @on('DataTransfer') def on_data_transfer(self, vendor_id, **kwargs): log_message(f"šŸ“¦ Data Transfer - Vendor: {vendor_id}", "DATA") log_json("Data Transfer", { "vendor_id": vendor_id, **kwargs }) return call_result.DataTransfer(status='Accepted') @on('DiagnosticsStatusNotification') def on_diagnostics_status(self, status, **kwargs): log_message(f"šŸ”§ Diagnostics Status: {status}", "DIAG") if kwargs: log_json("Diagnostics data", kwargs) return call_result.DiagnosticsStatusNotification() @on('FirmwareStatusNotification') def on_firmware_status(self, status, **kwargs): log_message(f"šŸ”„ Firmware Status: {status}", "FIRMWARE") if kwargs: log_json("Firmware data", kwargs) return call_result.FirmwareStatusNotification() async def on_connect(websocket, path): charge_point_id = path.strip('/') log_message("\n" + "=" * 60, "CONNECTION") log_message(f"Nieuwe verbinding!", "CONNECTION") log_message(f" Charge Point ID: {charge_point_id}", "CONNECTION") log_message(f" IP adres: {websocket.remote_address[0]}", "CONNECTION") log_message(f" Poort: {websocket.remote_address[1]}", "CONNECTION") log_message("=" * 60, "CONNECTION") cp = ChargePoint(charge_point_id, websocket) try: await cp.start() except websockets.exceptions.ConnectionClosed: log_message("Verbinding verbroken", "WARNING") except Exception as e: log_message(f"Fout opgetreden: {e}", "ERROR") import traceback log_message(traceback.format_exc(), "ERROR") async def main(): log_file = get_log_filename() # Check of log bestand voor vandaag al bestaat if not os.path.exists(log_file): # Nieuw dagelijks log bestand with open(log_file, 'w', encoding='utf-8') as f: f.write(f"OCPP Log gestart op {datetime.now()}\n") f.write("=" * 60 + "\n\n") else: # Bestand bestaat al, voeg scheidingslijn toe with open(log_file, 'a', encoding='utf-8') as f: f.write("\n" + "=" * 60 + "\n") f.write(f"Server herstart op {datetime.now()}\n") f.write("=" * 60 + "\n\n") log_message("šŸš€ OCPP Test Server gestart", "INFO") log_message("=" * 60, "INFO") log_message("Poort: 9000", "INFO") log_message(f"Log bestand: {os.path.abspath(log_file)}", "INFO") log_message("Ondersteunde OCPP versie: 1.6", "INFO") log_message("=" * 60, "INFO") log_message("\nWacht op verbinding van laadpaal...\n", "INFO") server = await websockets.serve( on_connect, '0.0.0.0', 9000, subprotocols=['ocpp1.6'], ping_interval=None # Disable websocket pings, OCPP heeft eigen heartbeat ) await server.wait_closed() if __name__ == '__main__': try: asyncio.run(main()) except KeyboardInterrupt: log_message("\n\nšŸ‘‹ Server gestopt door gebruiker", "INFO") except Exception as e: log_message(f"Fatale fout: {e}", "ERROR") import traceback log_message(traceback.format_exc(), "ERROR")