added uitlezen laadpaal via rest API
This commit is contained in:
15313
VAN_01971_Transactions_januari_2026.csv
Normal file
15313
VAN_01971_Transactions_januari_2026.csv
Normal file
File diff suppressed because it is too large
Load Diff
10607
VAN_01971_Transactions_maart_2026.csv
Normal file
10607
VAN_01971_Transactions_maart_2026.csv
Normal file
File diff suppressed because it is too large
Load Diff
243
uitlezen_laadpaal.py
Normal file
243
uitlezen_laadpaal.py
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
import requests
|
||||||
|
import urllib3
|
||||||
|
from datetime import datetime
|
||||||
|
import re
|
||||||
|
|
||||||
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
|
# ── Configuratie ──────────────────────────────────────────────
|
||||||
|
HOST = "https://192.168.178.184"
|
||||||
|
USERNAME = "admin"
|
||||||
|
PASSWORD = "Thomas2020"
|
||||||
|
# ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
session = requests.Session()
|
||||||
|
session.verify = False
|
||||||
|
|
||||||
|
|
||||||
|
# ── Communicatie ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
def login():
|
||||||
|
session.post(f"{HOST}/api/login", json={
|
||||||
|
"username": USERNAME,
|
||||||
|
"password": PASSWORD
|
||||||
|
}).raise_for_status()
|
||||||
|
print("Ingelogd")
|
||||||
|
|
||||||
|
def logout():
|
||||||
|
session.post(f"{HOST}/api/logout", json={
|
||||||
|
"username": USERNAME,
|
||||||
|
"password": PASSWORD
|
||||||
|
})
|
||||||
|
print("Uitgelogd")
|
||||||
|
|
||||||
|
def get_info():
|
||||||
|
r = session.get(f"{HOST}/api/info")
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
def fetch_page(offset):
|
||||||
|
r = session.get(f"{HOST}/api/transactions", params={"offset": offset})
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.text
|
||||||
|
|
||||||
|
def get_all_raw():
|
||||||
|
"""Haalt alle transactiepagina's op via paginering.
|
||||||
|
Stopt zodra de record-nummers terugvallen (circulaire buffer bereikt).
|
||||||
|
"""
|
||||||
|
all_raw = ""
|
||||||
|
offset = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
raw = fetch_page(offset)
|
||||||
|
print(f" Offset {offset:>6}: {len(raw):>6} bytes")
|
||||||
|
|
||||||
|
stripped = raw.strip().rstrip('}').strip()
|
||||||
|
if not stripped or stripped in ('{"version":2,', '{"version":2'):
|
||||||
|
print("Lege response, klaar.")
|
||||||
|
break
|
||||||
|
|
||||||
|
all_raw += raw
|
||||||
|
|
||||||
|
offsets = re.findall(r'^(\d+)_\w+:', raw, re.MULTILINE)
|
||||||
|
if not offsets:
|
||||||
|
break
|
||||||
|
|
||||||
|
next_offset = max(int(x) for x in offsets) + 1
|
||||||
|
|
||||||
|
# Circulaire buffer: nummers vallen terug → we hebben alles gehad
|
||||||
|
if next_offset <= offset:
|
||||||
|
print("Circulaire data gedetecteerd, klaar.")
|
||||||
|
break
|
||||||
|
|
||||||
|
offset = next_offset
|
||||||
|
|
||||||
|
return all_raw
|
||||||
|
|
||||||
|
|
||||||
|
# ── Parser ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def parse_transactions(raw):
|
||||||
|
transactions = []
|
||||||
|
current_tx = None
|
||||||
|
stop_parsing = False
|
||||||
|
|
||||||
|
for line in raw.splitlines():
|
||||||
|
if stop_parsing:
|
||||||
|
break
|
||||||
|
|
||||||
|
line = line.strip().rstrip('}').strip()
|
||||||
|
if not line or line.startswith('{"version"'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = re.match(r'^(\d+)_(\w+):\s*(.+)$', line)
|
||||||
|
if not match:
|
||||||
|
continue
|
||||||
|
|
||||||
|
_, record_type, data = match.groups()
|
||||||
|
|
||||||
|
if record_type == 'txstart2':
|
||||||
|
m = re.match(
|
||||||
|
r'id (0x[0-9a-fA-F]+), socket (\d+), '
|
||||||
|
r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) '
|
||||||
|
r'([\d.]+)kWh (\S+)',
|
||||||
|
data
|
||||||
|
)
|
||||||
|
if m:
|
||||||
|
current_tx = {
|
||||||
|
'rfid': m.group(1),
|
||||||
|
'socket': int(m.group(2)),
|
||||||
|
'start_time': datetime.strptime(m.group(3), '%Y-%m-%d %H:%M:%S'),
|
||||||
|
'start_kwh': float(m.group(4)),
|
||||||
|
'tag': m.group(5),
|
||||||
|
'end_time': None,
|
||||||
|
'end_kwh': None,
|
||||||
|
'charged_kwh': None,
|
||||||
|
'status': 'lopend',
|
||||||
|
'measurements': []
|
||||||
|
}
|
||||||
|
transactions.append(current_tx)
|
||||||
|
|
||||||
|
elif record_type == 'txstop2' and current_tx:
|
||||||
|
# Correct record type — was eerder fout als 'txend'
|
||||||
|
m = re.match(
|
||||||
|
r'id (0x[0-9a-fA-F]+), socket (\d+), '
|
||||||
|
r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) '
|
||||||
|
r'([\d.]+)kWh',
|
||||||
|
data
|
||||||
|
)
|
||||||
|
if m:
|
||||||
|
current_tx['end_time'] = datetime.strptime(m.group(3), '%Y-%m-%d %H:%M:%S')
|
||||||
|
current_tx['end_kwh'] = float(m.group(4))
|
||||||
|
current_tx['status'] = 'afgesloten'
|
||||||
|
current_tx = None # Sessie klaar
|
||||||
|
|
||||||
|
elif record_type == 'mv' and current_tx:
|
||||||
|
# Sla korte mv-regels over (geen volledige meetwaarden)
|
||||||
|
m = re.match(
|
||||||
|
r'socket \d+, (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) '
|
||||||
|
r'([\d.]+) ([\d.]+) ([\d.]+) ([\d.]+) ([\d.]+) ([\d.]+)',
|
||||||
|
data
|
||||||
|
)
|
||||||
|
if m:
|
||||||
|
meting = {
|
||||||
|
'time': datetime.strptime(m.group(1), '%Y-%m-%d %H:%M:%S'),
|
||||||
|
'current_a': float(m.group(2)),
|
||||||
|
'energy_wh': float(m.group(3)),
|
||||||
|
'freq_hz': float(m.group(4)),
|
||||||
|
'power_w': float(m.group(5)),
|
||||||
|
'power_factor': float(m.group(6)),
|
||||||
|
'temp_c': float(m.group(7)),
|
||||||
|
}
|
||||||
|
current_tx['measurements'].append(meting)
|
||||||
|
# Alleen bijwerken als sessie nog niet afgesloten via txstop2
|
||||||
|
if current_tx['status'] == 'lopend':
|
||||||
|
current_tx['end_time'] = meting['time']
|
||||||
|
current_tx['end_kwh'] = meting['energy_wh'] / 1000
|
||||||
|
|
||||||
|
# Geladen kWh berekenen
|
||||||
|
for tx in transactions:
|
||||||
|
if tx['start_kwh'] is not None and tx['end_kwh'] is not None:
|
||||||
|
tx['charged_kwh'] = round(tx['end_kwh'] - tx['start_kwh'], 3)
|
||||||
|
|
||||||
|
return transactions
|
||||||
|
|
||||||
|
|
||||||
|
# ── Rapportage ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def print_summary(transactions):
|
||||||
|
print(f"\n{'='*68}")
|
||||||
|
print(f" Totaal sessies gevonden: {len(transactions)}")
|
||||||
|
print(f"{'='*68}")
|
||||||
|
for i, tx in enumerate(transactions, 1):
|
||||||
|
duur = "?"
|
||||||
|
if tx['start_time'] and tx['end_time']:
|
||||||
|
delta = tx['end_time'] - tx['start_time']
|
||||||
|
uur, rest = divmod(int(delta.total_seconds()), 3600)
|
||||||
|
min_ = rest // 60
|
||||||
|
duur = f"{uur}u {min_:02d}m"
|
||||||
|
|
||||||
|
kwh_str = f"{tx['charged_kwh']} kWh" if tx['charged_kwh'] is not None else "?"
|
||||||
|
eind_str = tx['end_time'].strftime('%Y-%m-%d %H:%M') if tx['end_time'] else '?'
|
||||||
|
|
||||||
|
print(
|
||||||
|
f" #{i:<3} {tx['start_time'].strftime('%Y-%m-%d %H:%M')} "
|
||||||
|
f"→ {eind_str} "
|
||||||
|
f"| {kwh_str:>10} "
|
||||||
|
f"| {duur:>7} "
|
||||||
|
f"| {tx['status']}"
|
||||||
|
)
|
||||||
|
print(f"{'='*68}")
|
||||||
|
totaal = sum(tx['charged_kwh'] for tx in transactions if tx['charged_kwh'])
|
||||||
|
print(f" Totaal geladen: {round(totaal, 3)} kWh")
|
||||||
|
print(f"{'='*68}\n")
|
||||||
|
|
||||||
|
def save_as_csv(raw, device_id, filename=None):
|
||||||
|
"""Schrijft de ruwe API-data naar een CSV in ACE Service Installer formaat."""
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
if filename is None:
|
||||||
|
filename = f"{device_id}_Transactions.csv"
|
||||||
|
|
||||||
|
now = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
|
||||||
|
lines = [
|
||||||
|
f"# Device, {device_id}",
|
||||||
|
f"# Generated, {now}",
|
||||||
|
]
|
||||||
|
|
||||||
|
for line in raw.splitlines():
|
||||||
|
# Verwijder {"version":2, aan het begin van een pagina
|
||||||
|
line = re.sub(r'\{"version":\d+,', '', line)
|
||||||
|
# Verwijder afsluitende }
|
||||||
|
line = line.rstrip('}').strip()
|
||||||
|
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Verwijder offset-nummers: "76_mv:" → "mv:", "0_txstart2:" → "txstart2:"
|
||||||
|
line = re.sub(r'^\d+_', '', line)
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
with open(filename, 'w', encoding='utf-8') as f:
|
||||||
|
f.write('\n'.join(lines) + '\n')
|
||||||
|
|
||||||
|
print(f"Opgeslagen als: {filename}")
|
||||||
|
return filename
|
||||||
|
|
||||||
|
# ── Main ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
info = get_info()
|
||||||
|
device_id = info.get('Identity', 'UNKNOWN')
|
||||||
|
print(f"Paal: {info.get('Model')} | FW: {info.get('FWVersion')} | ID: {info.get('ObjectId')}")
|
||||||
|
|
||||||
|
login()
|
||||||
|
try:
|
||||||
|
print("\nTransacties ophalen...")
|
||||||
|
raw = get_all_raw()
|
||||||
|
transactions = parse_transactions(raw)
|
||||||
|
print_summary(transactions)
|
||||||
|
save_as_csv(raw, device_id)
|
||||||
|
finally:
|
||||||
|
logout()
|
||||||
Reference in New Issue
Block a user