Mit dem python code unten bekomme ich jetzt
start end kWh Σ kWh time
1970-01-01 01:01:42 1970-01-01 01:02:06 0.0 0.0 00:00
2024-09-02 11:01:45 2024-09-02 16:21:39 19.1 23.6 05:19
2024-09-03 13:17:41 2024-09-03 21:04:24 14.8 38.4 07:46
2024-09-22 09:17:11 2024-09-22 18:00:12 33.2 71.6 08:43
2024-09-23 08:46:04 2024-09-23 11:48:46 29.6 101.2 03:02
2024-09-23 22:31:13 2024-09-24 10:08:18 25.4 126.7 11:37
2024-09-24 16:22:12 2024-09-24 16:26:30 0.6 127.2 00:04
2024-09-24 16:48:41 2024-09-25 09:46:01 21.6 148.8 16:57
2024-09-25 15:14:37 2024-09-26 17:42:13 39.2 188.0 26:27
2024-09-26 19:39:40 2024-09-27 08:29:26 24.2 212.2 12:49
2024-09-27 15:14:59 2024-09-27 15:52:35 1.9 214.1 00:37
2024-09-30 09:06:28 2024-09-30 13:46:17 35.2 249.3 04:39
2024-10-04 11:14:34 2024-10-05 10:05:10 12.5 261.8 22:50
2024-10-06 10:57:58 2024-10-06 11:31:50 5.7 267.5 00:33
2024-10-06 13:40:57 2024-10-08 16:05:00 13.0 280.5 50:24
2024-10-18 09:26:16 2024-10-18 15:48:17 13.4 293.9 06:22
2024-10-20 14:03:36 2024-10-22 07:53:07 29.4 323.3 41:49
2024-10-27 10:31:28 2024-10-28 08:20:49 44.7 368.0 21:49
2024-11-12 08:27:33 2024-11-13 11:34:45 31.2 399.1 27:07
2024-11-16 08:10:47 2024-11-16 09:07:34 7.9 407.0 00:56
2024-11-16 09:15:50 2024-11-17 09:14:29 4.3 411.3 23:58
2024-11-17 14:57:55 2024-11-19 12:56:22 31.8 443.1 45:58
2024-11-21 08:21:37 2024-11-21 11:54:57 36.0 479.1 03:33
2024-11-21 13:01:55 2024-11-22 16:44:21 15.2 494.4 27:42
2024-11-24 13:53:52 2024-11-25 12:15:50 32.4 526.8 22:21
2024-11-29 13:18:26 2024-11-29 19:36:51 26.5 553.3 06:18
2024-12-04 18:47:21 2024-12-05 06:53:51 34.7 588.0 12:06
2024-12-07 08:05:53 2024-12-07 08:05:56 0.0 588.0 00:00
2024-12-07 08:06:11 2024-12-07 14:12:29 20.6 608.6 06:06
2024-12-07 20:05:50 2024-12-08 09:58:03 40.2 648.8 13:52
2024-12-11 17:41:16 2024-12-12 06:58:41 39.0 687.7 13:17
2024-12-15 10:31:12 2024-12-15 11:37:03 11.1 698.9 01:05
2024-12-15 13:33:13 2024-12-15 18:06:19 22.4 721.3 04:33
2024-12-18 09:47:12 2024-12-18 14:34:55 12.5 733.8 04:47
2024-12-18 20:21:19 2024-12-19 07:33:02 30.2 764.0 11:11
2024-12-19 09:40:59 2024-12-19 10:43:57 6.6 770.6 01:02
2024-12-19 10:48:23 2024-12-19 11:12:27 1.2 771.9 00:24
2024-12-22 13:40:30 2024-12-22 14:55:36 11.7 783.6 01:15
2024-12-22 17:00:22 42.4 826.0
Die erste und die letzte Zeile sind seltsam. CSV data received:
"sep=,"
Start,End,EVSE,EVSE Id,User Id,RFID,Wh,Total Wh,Flags,SpRatio,Start Wh,Budget Wh,B-Used Wh
1970-01-01 01:01:42,1970-01-01 01:02:06,1,E1-W00-04YM,,53f0aaa0,0,0,,0,0,0,0
2024-09-02 11:01:45,2024-09-02 16:21:39,1,E1-W00-04YM,,,19110,23650,,0,4540,0,0
2024-09-03 13:17:41,2024-09-03 21:04:24,1,E1-W00-04YM,,,14760,38410,,0,23650,0,0
2024-09-22 09:17:11,2024-09-22 18:00:12,1,E1-W00-04YM,,,33230,71640,,0,38410,0,0
2024-09-23 08:46:04,2024-09-23 11:48:46,1,E1-W00-04YM,,,29600,101240,,0,71640,0,0
2024-09-23 22:31:13,2024-09-24 10:08:18,1,E1-W00-04YM,,,25420,126660,,0,101240,0,0
2024-09-24 16:22:12,2024-09-24 16:26:30,1,E1-W00-04YM,,,560,127220,,0,126660,0,0
2024-09-24 16:48:41,2024-09-25 09:46:01,1,E1-W00-04YM,,,21630,148850,,0,127220,0,0
2024-09-25 15:14:37,2024-09-26 17:42:13,1,E1-W00-04YM,,,39180,188030,,0,148850,0,0
2024-09-26 19:39:40,2024-09-27 08:29:26,1,E1-W00-04YM,,,24170,212200,,0,188030,0,0
2024-09-27 15:14:59,2024-09-27 15:52:35,1,E1-W00-04YM,,,1870,214070,,0,212200,0,0
2024-09-30 09:06:28,2024-09-30 13:46:17,1,E1-W00-04YM,,,35250,249320,,0,214070,0,0
2024-10-04 11:14:34,2024-10-05 10:05:10,1,E1-W00-04YM,,,12480,261810,,0,249330,0,0
2024-10-06 10:57:58,2024-10-06 11:31:50,1,E1-W00-04YM,,,5690,267500,,0,261810,0,0
2024-10-06 13:40:57,2024-10-08 16:05:00,1,E1-W00-04YM,,,13040,280540,,0,267500,0,0
2024-10-18 09:26:16,2024-10-18 15:48:17,1,E1-W00-04YM,,,13360,293900,,0,280540,0,0
2024-10-20 14:03:36,2024-10-22 07:53:07,1,E1-W00-04YM,,,29380,323280,,0,293900,0,0
2024-10-27 10:31:28,2024-10-28 08:20:49,1,E1-W00-04YM,,,44680,367960,,0,323280,0,0
2024-11-12 08:27:33,2024-11-13 11:34:45,1,E1-W00-04YM,,,31180,399140,,0,367960,0,0
2024-11-16 08:10:47,2024-11-16 09:07:34,1,E1-W00-04YM,,,7860,407000,,0,399140,0,0
2024-11-16 09:15:50,2024-11-17 09:14:29,1,E1-W00-04YM,,,4310,411310,,0,407000,0,0
2024-11-17 14:57:55,2024-11-19 12:56:22,1,E1-W00-04YM,,,31800,443110,,0,411310,0,0
2024-11-21 08:21:37,2024-11-21 11:54:57,1,E1-W00-04YM,,,36010,479120,,0,443110,0,0
2024-11-21 13:01:55,2024-11-22 16:44:21,1,E1-W00-04YM,,,15240,494370,,0,479130,0,0
2024-11-24 13:53:52,2024-11-25 12:15:50,1,E1-W00-04YM,,,32390,526760,,0,494370,0,0
2024-11-29 13:18:26,2024-11-29 19:36:51,1,E1-W00-04YM,,,26500,553260,,0,526760,0,0
2024-12-04 18:47:21,2024-12-05 06:53:51,1,E1-W00-04YM,,,34740,588000,,0,553260,0,0
2024-12-07 08:05:53,2024-12-07 08:05:56,1,E1-W00-04YM,,,0,588000,,0,588000,0,0
2024-12-07 08:06:11,2024-12-07 14:12:29,1,E1-W00-04YM,,,20560,608560,,0,588000,0,0
2024-12-07 20:05:50,2024-12-08 09:58:03,1,E1-W00-04YM,,,40220,648780,,0,608560,0,0
2024-12-11 17:41:16,2024-12-12 06:58:41,1,E1-W00-04YM,,,38960,687740,,0,648780,0,0
2024-12-15 10:31:12,2024-12-15 11:37:03,1,E1-W00-04YM,,,11110,698850,,0,687740,0,0
2024-12-15 13:33:13,2024-12-15 18:06:19,1,E1-W00-04YM,,,22450,721300,,0,698850,0,0
2024-12-18 09:47:12,2024-12-18 14:34:55,1,E1-W00-04YM,,,12540,733840,,0,721300,0,0
2024-12-18 20:21:19,2024-12-19 07:33:02,1,E1-W00-04YM,,,30160,764000,,0,733840,0,0
2024-12-19 09:40:59,2024-12-19 10:43:57,1,E1-W00-04YM,,,6560,770560,,0,764000,0,0
2024-12-19 10:48:23,2024-12-19 11:12:27,1,E1-W00-04YM,,,1190,771880,,0,770690,0,0
2024-12-22 13:40:30,2024-12-22 14:55:36,1,E1-W00-04YM,,,11730,783610,,0,771880,0,0
2024-12-22 17:00:22,,1,E1-W00-04YM,,,42390,826010,,0,783620,0,0
"""
Created on 2024-12-23
in response to
https://www.cfos-emobility.de/network/antworten/wie-komme-ich-an-das-ladelog-per-api-ran/#answer-3300
see also
https://github.com/mb-software/homeassistant-powerbrain/blob/master/custom_components/powerbrain/powerbrain.py
https://pypi.org/project/powerbrain/#description
@author: wf with Claude-AI prompting
"""
import argparse
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
import csv
import json
from tabulate import tabulate
import yaml
import requests
import os
@dataclass
class WallboxRecord:
start: datetime
end: Optional[datetime]
evse: int
evse_id: str
user_id: Optional[str]
rfid: Optional[str]
wh: int
total_wh: int
flags: Optional[str]
sp_ratio: float
start_wh: int
budget_wh: int
b_used_wh: int
@property
def kWh(self) -> float:
"""
Returns energy in kWh
"""
return self.wh / 1000.0
@property
def total_kWh(self) -> float:
"""
Returns total energy in kWh
"""
return self.total_wh / 1000.0
@property
def duration_h(self) -> Optional[float]:
"""
Returns duration in hours
"""
if self.end:
delta = self.end - self.start
return delta.total_seconds() / 3600
return None
@property
def duration(self)->str:
hours=self.duration_h
if hours is None:
return ""
h = int(hours)
m = int((hours - h) * 60)
return f"{h:02d}:{m:02d}"
@staticmethod
def from_csv_row(row: dict) -> 'WallboxRecord':
return WallboxRecord(
start=datetime.strptime(row['Start'], '%Y-%m-%d %H:%M:%S'),
end=datetime.strptime(row['End'], '%Y-%m-%d %H:%M:%S') if row['End'] else None,
evse=int(row['EVSE']),
evse_id=row['EVSE Id'],
user_id=row['User Id'] or None,
rfid=row['RFID'] or None,
wh=int(row['Wh']),
total_wh=int(row['Total Wh']),
flags=row['Flags'] or None,
sp_ratio=float(row['SpRatio']),
start_wh=int(row['Start Wh']),
budget_wh=int(row['Budget Wh']),
b_used_wh=int(row['B-Used Wh'])
)
def as_table_row(self)->dict:
"""
format me as a table row
"""
row = {
'start': self.start,
'end': self.end,
'kWh': f"{self.kWh:.1f}",
'Σ kWh': f"{self.total_kWh:.1f}",
'time': self.duration
}
return row
class WallboxAPI:
"""
see
https://www.cfos-emobility.de/de/cfos-power-brain/http-api.htm
"""
def __init__(self, config_file: str = os.path.expanduser('~/.cfos/config.yaml')):
"""
Initialize the Wallbox API
Args:
config_file: path to config file - if not given ~/.cfos/config.yaml is used
"""
if not os.path.isfile(config_file):
example_config = {
'wallbox': {
'host': '192.168.2.111',
'username': 'admin',
'password': 'secret'
}
}
# create config directory if it doesn't exist
os.makedirs(os.path.dirname(config_file), exist_ok=True)
# create example config
with open(config_file, 'w') as f:
yaml.dump(example_config, f)
print(f"Created example config at {config_file} - please edit")
with open(config_file, 'r') as f:
self.config = yaml.safe_load(f)
self.base_url = f"http://{self.config['wallbox']['host']}/cnf"
self.auth = (self.config['wallbox']['username'], self.config['wallbox']['password'])
def get(self, params: str) -> str:
response = requests.get(
f"{self.base_url}?{params}",
auth=self.auth
)
response.raise_for_status()
return response.text
def get_log(self) -> list[WallboxRecord]:
csv_data = self.get("cmd=get_log&type=ta")
if self.args.debug:
print("CSV data received:")
print(csv_data)
lines = csv_data.splitlines()
# Get separator from first line
sep = lines[0].split('=')[1].strip('"')
# Skip the sep line
lines = lines[1:]
reader = csv.DictReader(lines, delimiter=sep)
# check first row format
rows = list(reader)
if len(rows) > 0 and self.args.debug:
print(f"Available columns: {rows[0].keys()}")
lor=[WallboxRecord.from_csv_row(row) for row in rows]
return lor
def as_table(self, wbr_list: list[WallboxRecord]):
lod = []
for wbr in wbr_list:
lod.append(wbr.as_table_row())
return lod
def get_status(self) -> str:
return self.get("cmd=get_dev_info")
def read_csv_file(self, filepath: str) -> list[WallboxRecord]:
with open(filepath, 'r') as f:
reader = csv.DictReader(f)
return [WallboxRecord.from_csv_row(row) for row in reader]
def main_instance(self,args):
"""
command line interface
"""
self.args=args
status_text = self.get_status()
self.status = json.loads(status_text)
print(json.dumps(self.status,indent=2))
wbr_list= self.get_log()
wbr_table=self.as_table(wbr_list)
if self.args.debug:
print("Status:")
print(json.dumps(self.status, indent=2))
if self.args.format:
# see https://github.com/astanin/python-tabulate
print(tabulate(wbr_table, headers='keys', tablefmt=args.format, floatfmt=".1f"))
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-d','--debug', action='store_true', help='show debug output including full status')
parser.add_argument('-f','--format', default='plain')
args = parser.parse_args()
wallbox = WallboxAPI()
wallbox.main_instance(args)