Metadata-Version: 2.4
Name: e3dc-modbus
Version: 0.0.2
Summary: Asynchronous Python library for reading and writing E3/DC Hauskraftwerk systems over Modbus TCP.
Author: Kai Neuhaus
License: Apache-2.0
Project-URL: Repository, https://github.com/KaaNee/e3dc-modbus
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: modbus-connection<5,>=4.11
Provides-Extra: cli
Requires-Dist: modbus-connection[tmodbus]; extra == "cli"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Dynamic: license-file

# e3dc-modbus

Asynchrones Python-Package zum Lesen und Schreiben von E3/DC Hauskraftwerk-Systemen über
Modbus TCP.

> [!NOTE]
> Dieses Repository ist mit Unterstützung von KI (Claude Code) entstanden — Registermapping,
> Code und Doku wurden gegen die offizielle E3DC-Modbus-Doku und echte Hardware verifiziert.

Die Bibliothek ist backend-neutral: sie konsumiert eine
[`modbus_connection.ModbusUnit`](https://home-assistant-libs.github.io/modbus-connection/)
und öffnet die Verbindung nicht selbst. Anwendungen können daher `tmodbus`, `pymodbus` oder
ein anderes von `modbus-connection` unterstütztes Backend verwenden.

Architektur und Vorgehen orientieren sich an
[`trovis-modbus`](https://github.com/Tom-Bom-badil/trovis-modbus) (das vom Home-Assistant-Team
selbst als Vorbild für diese Art von Modbus-Device-Library empfohlen wird).

> [!NOTE]
> **Gegen echte Hardware verifiziert** (E3DC S10 X Compact, `script/query.py`): Modellerkennung,
> Info-Block, alle Power-Felder (inkl. word-geswapptem Int32-Decode), das gepackte
> Autarky/Eigenverbrauch-Register und die String-Messwerte liefern plausible reale Werte.
> Registeradressen in `src/e3dc_modbus/const.py` kommen aus HagerEnergy/E3DCs offizieller
> Modbus-Doku (V2.80), gegengeprüft gegen eine echte `modbus.yaml`.
>
> EMS-Status (Emergency Power, SG-Ready, Lock-Flags) wurde ebenfalls live gelesen und liefert
> plausible Werte; alle 8 Wallbox-Slots melden korrekt `available=False` (keine Wallbox an
> dieser Anlage).
>
> **Noch offen:** Wallbox-Subsystem ist komplett ungetestet gegen echte Wallbox-Hardware —
> insbesondere ist der Schreibweg unverifiziert: die Doku verlangt Function 05H für
> Bit-Schreibzugriffe, `modbus_connection`s `bit()`-Feld schreibt aber per Function 06H (siehe
> `wallbox.py`-Docstring). Kein Schreibzugriff wurde gegen die echte Anlage getestet.

## Unterstützte Modelle

| Modell | Status |
|---|---|
| E3DC S10 X Compact | einziges real vorhandenes Testgerät des Autors |
| weitere Modelle | Struktur ist vorbereitet (`src/e3dc_modbus/models/`), aber ungetestet — Beiträge willkommen |

## Umfang / bewusste Auslassungen

- **Wallbox**: vollständig als Subsystem angelegt (bis zu 8 Wallboxen), aber vom Autor mangels
  eigener Hardware nicht gegen ein echtes Gerät getestet.
- **Kein Kosten-/Tarif-Handling**: liefert nur Leistungs- und Energie-Messwerte. Preisberechnung
  übernimmt bewusst das Home-Assistant-Energy-Dashboard, nicht diese Library.
- **Keine Energie-Sensoren (kWh)**: E3DC hat keine nativen Lifetime-Zähler-Register (bestätigt,
  siehe `const.py`) — nur Momentanleistungen (W). Energie/Daily/Monthly/Yearly-Aggregation
  übernimmt HA (Riemann-Summe-Helfer + Energy Dashboard), nicht diese Library.

## Basic usage

```python
import asyncio

from modbus_connection.tmodbus import connect_tcp
from e3dc_modbus import E3DCDevice

async def main() -> None:
    # E3DC spricht natives Modbus TCP (MBAP-Framing), daher framer="socket" —
    # nicht "rtu" (das wäre für RTU-über-TCP-Gateways).
    connection = await connect_tcp("192.168.1.50", port=502, framer="socket")
    try:
        unit = connection.for_unit(1)  # Unit-/Device-ID prüfen, E3DC-Default ist häufig 1
        device = await E3DCDevice.async_probe(unit)
        await device.async_update()

        print("Modell:", device.profile.name)
        print("PV-Leistung:", device.power.pv_power, "W")
        print("Akkustand:", device.power.battery_soc, "%")
    finally:
        await connection.close()

asyncio.run(main())
```

## Entwicklung

```bash
python -m pip install -e ".[dev]"
pytest
```

Der Testsuite-Ansatz folgt `trovis-modbus`: Tests laufen gegen das In-Memory-Mock-Backend von
`modbus-connection` (`modbus_connection.mock.MockModbusConnection`), keine echte Hardware nötig.

## CLI-Tool

```bash
python -m pip install -e ".[cli]"
python script/query.py 192.168.1.50 --unit 1
```

## Lizenz

Apache-2.0, siehe `LICENSE`.
