bayernwerk-client
Inoffizielle Python-Clients für Bayernwerk-Onlinedienste - reverse-engineered, nicht von Bayernwerk/E.ON unterstützt oder autorisiert. Nutzung auf eigene Verantwortung und nur mit einem eigenen, berechtigten Account.
Das Package ist nach Service aufgeteilt:
bayernwerk_client.map- Mein.Auftragsportal (meinauftragsportal.html/ REST-APIicon-api.eon.com)bayernwerk_client.efix- e-fix Installateur-Portal (bayernwerk.e-fix.info/ GraphQL-APIbackend.e-fix.info), gleiche Zugangsdaten wie MAP
Beide sitzen hinter demselben Salesforce-Aura-Login (nur unterschiedliche
Communities: account.bayernwerk-netz.de bzw. login.e-fix.info), daher
gilt der folgende Abschnitt für beide.
Warum ein Browser fürs Login?
bayernwerk-netz.de sitzt hinter einer Cloudflare-JS-Challenge, und die
Login-Seite selbst ist keine einfache HTML-Form, sondern eine Salesforce
Experience-Cloud-App (Aura-Framework mit signierten, sessionbehafteten
Requests). Beides lässt sich nicht robust mit reinem requests/httpx
nachbauen. Deshalb übernimmt Playwright
einmalig den Login (Cloudflare-Challenge lösen + Formular ausfüllen) und
liefert die Tokens. Alle eigentlichen API-Calls danach laufen über
reines httpx - kein Browser nötig, solange der Token gültig ist.
Playwright ist deshalb eine normale (Kern-)Abhängigkeit, keine optionale Extra - ohne funktionierenden Login ist der Client kaum zu gebrauchen, also lohnt sich der Split nicht. Nötig ist trotzdem ein zusätzlicher Schritt, um die Browser-Binary selbst zu installieren (siehe unten).
Token-Lebensdauer: Access-Tokens gelten ca. 1 Stunde. Bei MAP wird
zwar ein refresh_token mitgeliefert, das ist aber ein
Salesforce-Community-Refresh-Token - er erneuert zwar erfolgreich gegen
.../services/oauth2/token, liefert dabei aber ein natives
Salesforce-Session-Token statt des JWTs, das icon-api.eon.com erwartet
(das JWT entsteht in einem separaten, nicht identifizierten "Funke
IAM"-Austauschschritt, den nur das SPA-JS selbst ausführt). e-fix liefert
gar keinen Refresh-Token. Praktisch bedeutet das für beide: bei Ablauf
einfach erneut login_interactive aufrufen (dauert nur wenige Sekunden)
statt zu versuchen, den Token still zu erneuern - siehe on_token_expired
im Beispiel unten.
Installation
Als CLI-Tool (empfohlen), sobald das Package auf PyPI liegt:
uv tool install bayernwerk-client
playwright install chromium
Aktuell noch nicht auf PyPI - bis dahin direkt aus dem Repo installieren:
uv tool install git+https://github.com/the78mole/bayernwerk-client
playwright install chromium
Als Abhängigkeit in einem eigenen Python-Projekt (Library-Nutzung, siehe
unten): uv add bayernwerk-client bzw. uv add git+https://github.com/the78mole/bayernwerk-client, solange noch nicht
auf PyPI.
Zugangsdaten
map und efix nutzen denselben Bayernwerk-Netz-Account (dieselbe
E-Mail/Passwort-Kombination aus dem Mein.Auftragsportal). Der login-Befehl
jedes Services (und der automatische Re-Login bei abgelaufenem Token) liest
die Zugangsdaten aus zwei Umgebungsvariablen:
export MAP_EMAIL="deine@email.de"
export MAP_PASSWORD="dein-passwort"
Sind sie nicht gesetzt, fragt bayernwerk map login / bayernwerk efix login interaktiv danach (E-Mail per input(), Passwort per getpass -
landet nicht in der Shell-History). Der automatische Re-Login bei
abgelaufenem Token funktioniert dagegen nur, wenn die Variablen gesetzt
sind - sonst bricht der jeweilige Befehl mit einer Fehlermeldung ab, die
zum erneuten manuellen login auffordert (siehe on_token_expired in den
cli.py-Modulen).
Aus einer .env-Datei laden, statt die Zugangsdaten in jeder Shell neu
zu exportieren: bayernwerk lädt beim Start automatisch eine .env aus
dem aktuellen (oder einem übergeordneten) Verzeichnis, via
python-dotenv. Bereits
gesetzte Umgebungsvariablen haben immer Vorrang vor der .env-Datei.
# .env (liegt bereits in .gitignore - nie committen!)
MAP_EMAIL=deine@email.de
MAP_PASSWORD=dein-passwort
bayernwerk map login # liest MAP_EMAIL/MAP_PASSWORD automatisch aus .env
Kein manuelles source nötig. Wer lieber ein allgemeineres Tool dafür
nutzt: direnv lädt eine .envrc automatisch
beim Betreten des Verzeichnisses (dann auch für andere Tools verfügbar,
nicht nur bayernwerk).
Die gecachten Tokens (nicht die Zugangsdaten selbst) landen danach lokal
unter ~/.cache/bayernwerk-client/map-tokens.json bzw.
.../efix-tokens.json, mit restriktiven Dateirechten (chmod 600).
CLI
bayernwerk ist der Einstiegspunkt, map/efix sind die Subcommands je
Service - jeder cacht seinen Token getrennt (map-tokens.json /
efix-tokens.json), ein Login gilt nicht für den jeweils anderen Service.
-j/--json ist ein globales Flag vor dem Service
(bayernwerk -j map orders, nicht bayernwerk map orders -j) und schaltet
JSON- statt menschenlesbare Ausgabe für jeden beliebigen Befehl ein.
bayernwerk --help, bayernwerk map --help bzw. bayernwerk efix --help
zeigen die Befehle direkt im Terminal inkl. aller Argumente.
bayernwerk map - Mein.Auftragsportal
| Befehl | Beschreibung |
|---|---|
login [--headless] |
Einmalig einloggen (sichtbarer Browser, außer mit --headless) und Token cachen. Nötig bevor irgendein anderer map-Befehl funktioniert. |
orders [-s] [-f|-o] |
Alle Aufträge auflisten. Menschenlesbar als ein Absatz pro Auftrag mit Kopfzeile (Status, Datum, Auftragsnummer, Kunde, Produkt, Adresse - wie im Portal), Rest eingerückt darunter. -s/--short gibt nur die Kopfzeile aus, eine pro Zeile (wirkt nicht auf -j). -f/--finished bzw. -o/--open filtern auf abgeschlossene bzw. offene Aufträge (schließen sich gegenseitig aus, auch kombinierbar mit -s, z.B. -fs). |
order ORDER_ID |
Einen einzelnen Auftrag anzeigen (Rohdaten der orders-Liste, gefiltert auf eine Auftragsnummer). |
order-details ORDER_ID |
Zusätzliche Detaildaten zu einem Auftrag (separater Endpunkt als order). |
order-documents ORDER_ID |
Dokumente eines Auftrags auflisten (Dateiname, Typ, Scan-Status, ...) - zum Download siehe sync-documents. |
order-notes ORDER_ID |
Notizen zu einem Auftrag auflisten. |
order-instances ORDER_ID |
Der "Anschluss"-Baum eines Auftrags: Zähler → Verbraucher/Erzeuger → Wechselrichter → Speicher/PV, als eingerückter Baum (menschenlesbar) bzw. flache Liste (-j). |
sync-documents ORDER_ID FOLDER |
Dokumente eines Auftrags mit einem lokalen Ordner abgleichen: lädt nur die Dokumente herunter, die dort (per Dateiname) noch fehlen; vorhandene Dateien bleiben unangetastet. FOLDER wird bei Bedarf angelegt. |
installer |
Eigene Installateur-Stammdaten (Firma, Adresse, Kontakt) laut MAP-Backend. |
inverters --primary-energy-form FORM |
Wechselrichter-Stammdaten. FORM ist Pflicht (z.B. PV oder AC_STORAGE) und mehrfach angebbar. |
storages |
Speicher-Stammdaten (verfügbare Modelle/Kapazitäten). |
product-orders |
Produktaufträge auflisten (separat von orders). |
bayernwerk efix - e-fix Installateur-Portal
| Befehl | Beschreibung |
|---|---|
login [--headless] |
Einmalig einloggen (eigener Token-Cache, unabhängig von map) und Token cachen. |
installer |
Eigene Installateur-Stammdaten laut e-fix-Backend (Firma, Adresse, Netzbetreiber-Zuordnung, Ausweis-Status, ...) - inhaltlich umfangreicher als map installer, da e-fix eigene Stammdaten pflegt. |
antraege |
Eigene Anträge ("Antraege") mit Status, Typ/Subtyp und Eingangsdatum. |
status |
Account-Status: Rolle, Benachrichtigungszähler, Abo-Status, Name/E-Mail. |
events |
Registrierte Veranstaltungen/Schulungen mit Terminen. |
Shell-Completion
bayernwerk completion install
Erkennt die Shell automatisch ($SHELL, bash/zsh) und trägt eine
eval-Zeile in ~/.bashrc bzw. ~/.zshrc ein (idempotent - beim
zweiten Aufruf passiert nichts). Neue Shell starten oder die rc-Datei neu
sourcen, danach vervollständigt Tab sowohl Services (map/efix) als
auch alle Subcommands auf jeder Ebene, inklusive --primary-energy-form
& Co. Läuft über argcomplete
und wird direkt aus der tatsächlichen argparse-Struktur generiert -
kein händisch gepflegtes Completion-Skript, das veralten könnte.
Andere Shell oder manuelle Einrichtung: bayernwerk completion bash
(bzw. zsh/fish) gibt nur das Skript aus, zum selbst Einbinden. Für
Fish z.B. bayernwerk completion fish > ~/.config/fish/completions/bayernwerk.fish.
Verwendung als Library
from bayernwerk_client.map import MAP_TOKEN_PATH, MapClient, TokenStore
from bayernwerk_client.map.auth import login_interactive
store = TokenStore(MAP_TOKEN_PATH)
tokens = store.load()
if tokens is None or tokens.is_expired:
tokens = login_interactive("email@example.com", "passwort", headless=False)
store.save(tokens)
with MapClient(tokens, token_store=store) as client:
for order in client.list_orders():
print(order)
Siehe examples/list_orders.py für ein
vollständiges Beispiel inklusive automatischem Re-Login bei abgelaufenem
Token. bayernwerk_client.efix funktioniert analog (EfixClient,
EFIX_TOKEN_PATH, bayernwerk_client.efix.auth.login_interactive).
API-Oberfläche (bayernwerk_client.map)
MapClient deckt die bekannten icon-api.eon.com-Endpunkte mit
Convenience-Methoden ab (list_orders, get_order, get_order_details,
get_order_instances, get_order_documents, get_order_notes,
download_order_document, sync_order_documents, list_installers,
list_inverters, list_storages, list_product_orders). Für alles
andere steht die generische client.request(method, path, **httpx_kwargs)
zur Verfügung.
Anschluss-Baum (Zähler/Wallbox/Wärmepumpe/Wechselrichter/...)
get_order_instances liefert die Baumstruktur hinter dem "Anschluss"-Tab
im Portal (Zähler → Verbraucher/Erzeuger → Wechselrichter → Speicher/PV, ...).
Die Verschachtelung variiert je nach Ausstattung (itemType/schemaType)
und Bayernwerk kann jederzeit neue Gerätetypen ergänzen - ein starres
Datenmodell dafür würde ständig hinterherlaufen. iter_instance_items
läuft den Baum deshalb schemalos ab: alles mit einem itemType-Feld wird
als Knoten erkannt, egal unter welchem Schlüssel (meters, actors,
inverterGroups, inverters, ...) es hängt, und Duplikate (manche Akteure
tauchen im Rohformat zweimal auf - einmal direkt, einmal nochmal
verschachtelt unterm zugehörigen Wechselrichter) werden über ivyId
herausgefiltert.
for item in client.iter_order_instance_items("2577481104"):
print(item["itemType"], "/", item.get("schemaType"), "-", item.get("formData"))
bayernwerk_client.map.iter_instance_items(tree) ist die reine Funktion
dahinter, falls du bereits ein get_order_instances-Ergebnis vorliegen hast.
Dokumente eines Auftrags mit einem lokalen Ordner abgleichen
downloaded = client.sync_order_documents(
"2577481104", "/pfad/zum/kundenordner"
)
print(f"{len(downloaded)} neue Dokument(e) heruntergeladen:", downloaded)
Vergleicht die Dokumentenliste des Auftrags mit den vorhandenen Dateien im Ordner (per Dateiname) und lädt nur fehlende Dokumente herunter. Bestehende Dateien werden nicht angerührt.
tenant (Default BAG) und lang (Default de) werden automatisch an
jeden Request angehängt - das Backend antwortet ohne diese beiden
Query-Parameter mit einem 500er. list_inverters braucht zusätzlich
primary_energy_forms (z.B. ["PV"]).
API-Oberfläche (bayernwerk_client.efix)
EfixClient ist ein GraphQL-Client (nicht REST wie MAP) für
backend.e-fix.info: client.query(query, operation_name=..., variables=...)
für beliebige Queries/Mutations, plus Convenience-Methoden für die bekannten
Queries: get_installer(), list_installer_antraege(), get_user_status(),
list_my_registered_events().
Package-Struktur
bayernwerk_client/
├── exceptions.py # BayernwerkClientError, AuthenticationError, ApiError - service-uebergreifend
├── formatting.py # generische Absatz-/Dict-Formatierung fuers CLI - service-uebergreifend
├── _jwt.py # generische JWT-Payload-Dekodierung - service-uebergreifend
├── tokens.py # generischer JWT-Token-Store (TokenSet/TokenStore) - service-uebergreifend
├── completion.py # `bayernwerk completion` - argcomplete-Setup, kein eigener Service
├── cli.py # `bayernwerk`-Einstiegspunkt, registriert Service-Subcommands
├── map/ # Mein.Auftragsportal (REST)
│ ├── client.py, auth.py, instances.py
│ ├── formatting.py # `render_instance_tree`, `render_orders` (map-spezifisch)
│ └── cli.py # `map`-Subcommand
└── efix/ # e-fix Installateur-Portal (GraphQL)
├── client.py, auth.py
└── cli.py # `efix`-Subcommand
Entwicklung
uv sync
uv run pytest --cov=bayernwerk_client --cov-report=term-missing
pre-commit run --all-files
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bayernwerk_client-0.0.3.tar.gz.
File metadata
- Download URL: bayernwerk_client-0.0.3.tar.gz
- Upload date:
- Size: 63.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
14cdbe28aff59b41d52a7828749f473f76555b88031f5a441122fcd04baf02e6
|
|
| MD5 |
6a116dd43164ad51066a056e234c35b8
|
|
| BLAKE2b-256 |
c56148a707f0aa6f68a5be41fe80694554acefaa2dcd191b3f8932ede95c1e82
|
Provenance
The following attestation bundles were made for bayernwerk_client-0.0.3.tar.gz:
Publisher:
release.yml on the78mole/bayernwerk-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bayernwerk_client-0.0.3.tar.gz -
Subject digest:
14cdbe28aff59b41d52a7828749f473f76555b88031f5a441122fcd04baf02e6 - Sigstore transparency entry: 2497500532
- Sigstore integration time:
-
Permalink:
the78mole/bayernwerk-client@9e012c6458b628b3eb16aa8ecf94c181ce233b19 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/the78mole
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9e012c6458b628b3eb16aa8ecf94c181ce233b19 -
Trigger Event:
push
-
Statement type:
File details
Details for the file bayernwerk_client-0.0.3-py3-none-any.whl.
File metadata
- Download URL: bayernwerk_client-0.0.3-py3-none-any.whl
- Upload date:
- Size: 32.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3e42be6da8a84b8591584200cf2870d5187fa56a1794ce089f36eebc7779a0af
|
|
| MD5 |
6bcd5145ee21729a5e476071125bc5b5
|
|
| BLAKE2b-256 |
d50f919eedbbdb733d76c3a7a40957c12125cd80d666353d8c28adf85edb5577
|
Provenance
The following attestation bundles were made for bayernwerk_client-0.0.3-py3-none-any.whl:
Publisher:
release.yml on the78mole/bayernwerk-client
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bayernwerk_client-0.0.3-py3-none-any.whl -
Subject digest:
3e42be6da8a84b8591584200cf2870d5187fa56a1794ce089f36eebc7779a0af - Sigstore transparency entry: 2497501218
- Sigstore integration time:
-
Permalink:
the78mole/bayernwerk-client@9e012c6458b628b3eb16aa8ecf94c181ce233b19 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/the78mole
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9e012c6458b628b3eb16aa8ecf94c181ce233b19 -
Trigger Event:
push
-
Statement type: