# Multi-Inverter & Hybrid-Speicher Kopplung mit Home Assistant

**Kategorie:** Projekt / Tutorial
**Themen:** `Home Assistant` `Photovoltaik` `Modbus TCP` `Shelly` `Automation` `Systemintegration`

Zwei herstellerfremde Speichersysteme – Ampere.StoragePro E3 (Dach) und Hoymiles HMS-1600-4T mit
3x HiBattery 1920 AC – werden durch einen softwaredefinierten Smart Meter auf Basis von Home Assistant
zu einem einzigen Gebäude-Energiemanagement (BEMS) gekoppelt. Die Lösung überwindet die RS485-Bus-Exklusivität
des Chint DTSU666 und die Hoymiles-Firmware-Sperre *„Zähleraddition des Mehrphasensystems anormal“* –
ohne zusätzliche Zähler-Hardware und mit 100 % dynamischer Überschussladung.

> **Ziel dieses Projekts:** Beseitigung der Hersteller-Inkompatibilitäten und Kommunikationsgrenzen zwischen
> einer bestehenden Dachanlage (Ampere.StoragePro E3, Chint DTSU666) und einem Balkonkraftwerk-Speichersystem
> (Hoymiles HMS-1600-4T + 3x HiBattery 1920 AC) durch einen lokalen Home-Assistant-Server als vermittelnder,
> virtueller Smart Meter. Ergebnis: dynamische Überschussladung am Tag, aktive Einspeisung in der Nacht,
> Tiefentladeschutz und manuelle Zwangsladung – zu 0 € Zusatzhardware für Messtechnik.

---

## 1. Ausgangslage & Problemstellung

Im Gebäude existieren zwei getrennte, herstellerinkompatible Photovoltaiksysteme:

- **Dachanlage (Ampere.StoragePro E3 / FoxESS-Plattform):** Hybrid-Wechselrichter mit fest verdrahtetem
  Chint DTSU666 Smart Meter über einen exklusiven RS485-Bus.
- **Balkon-/Zusatzsystem (Hoymiles HMS-1600-4T mit 3x HiBattery 1920 AC, 5,76 kWh):** aufgeteilt in
  Station „Turm“ (2x Akku, Phase A) und Station „Speicher 3“ (1x Akku, Phase B).

Der Chint DTSU666 kommuniziert über Modbus RTU (9600 Baud, 8N1) im Master-Slave-Prinzip. Die Dachanlage
beansprucht den RS485-Bus exklusiv als Master – ein paralleler Zugriff der Hoymiles-Hardware ist physikalisch
unzulässig (Bus-Kollision).

> **Wichtig:** Hoymiles blockiert Mehrphasen-Konfigurationen ohne eigenen Smart Meter. Sobald mehr als zwei
> HiBattery-Einheiten erkannt werden, erzwingt die S-Miles-Firmware eine Phasenaufteilung (Phase A: 2x Turm,
> Phase B: 1x Einzelspeicher) und meldet ohne zugeordneten Zähler den Fehler *„Zähleraddition des
> Mehrphasensystems anormal“*. Beide Speicher liefen daher nur starr nach Uhrzeit.

---

## 2. Lösungsarchitektur (BEMS)

Ein lokaler **Dell Wyse 5060 Thin Client** (AMD GX-424CC, x86_64) mit **Home Assistant OS** als
Bare-Metal-Installation übernimmt die Rolle eines *softwaredefinierten Smart Meters*. Er liest die
Netzübergabedaten der Dachanlage transparent über die Modbus-TCP-Bridge des Wechselrichters aus, errechnet
daraus den echten physikalischen PV-Überschuss und steuert die Ladeleistung von Speicher 3 über eine
Shelly-Aktorik. Zusätzliche Messtechnik entfällt – **0 € Zusatzhardware**.

| Komponente | Ausprägung |
| :--- | :--- |
| Plattform | Dell Wyse 5060 Thin Client |
| CPU | AMD Steppe Eagle GX-424CC (Quad-Core 2,4 GHz, x86_64) |
| RAM | 4–8 GB DDR3L SO-DIMM |
| Speicher | M.2 SATA SSD / eMMC |
| Netzwerk | Gigabit Ethernet (RJ45), feste IP via DHCP-Reservierung |

### Bare-Metal-Installation (HAOS Generic x86_64)

1. **BIOS/UEFI:** `UEFI`, `Secure Boot: Disabled`, `AC Power Recovery: Power On`, SATA Mode `AHCI`.
2. **Flashen:** Über ein Ubuntu-Live-System das HAOS-Raw-Image direkt auf die interne SSD schreiben.

```bash
xz -dc haos_generic-x86-64-13.1.img.xz | sudo dd of=/dev/sda bs=4M status=progress conv=fsync
```

3. **Erstinbetriebnahme:** `http://homeassistant.local:8123`, Administrator-Konto und Zeitzone setzen.
4. **Werkzeuge:** Add-on `Terminal & SSH` installieren, anschließend HACS einrichten:

```bash
wget -O - https://get.hacs.xyz | bash -
```

---

## 3. Dachanlagen-Integration (Modbus TCP)

Der Ampere.StoragePro E3 basiert auf der industriellen FoxESS-Plattform. Der WLAN-Kommunikationsstick
(ESP32-basiert, Hostname `espressif`) stellt die Netzübergabedaten des Chint-Zählers als Modbus-TCP-Server bereit.

| Parameter | Wert |
| :--- | :--- |
| Protokoll | Modbus TCP |
| Gateway / Hostname | `espressif` |
| Port | `502` |
| Slave-ID (Unit-ID) | `247` (Standard für FoxESS/Ampere E3) |
| Polling-Intervall | 10–30 s (empfohlen 15 s) |

### Port-Verifikation

```bash
python3 -c "import socket; s=socket.socket(); s.settimeout(2); print('PORT 502 OFFEN' if s.connect_ex(('IP_DES_ESPRESSIF_GERAETS', 502))==0 else 'NICHT ERREICHBAR')"
```

### Integration via HACS

1. HACS → **⋮ → Benutzerdefinierte Repositories**.
2. Repository `dboeni/home-assistant-ampere-storage-pro-modbus` (alternativ `Sven0111/Ampere-StoragePro-E3`),
   Kategorie `Integration`.
3. Herunterladen, Core neu starten, dann **Einstellungen → Geräte & Dienste → Integration hinzufügen →
   Ampere.StoragePro** mit Host, Port 502 und Slave-ID 247.

### Sensor-Mapping & Vorzeichen-Konvention

| Entität | Messgröße | Bedeutung |
| :--- | :--- | :--- |
| `sensor.ampere_storagepro_grid_power` | Wirkleistung Netzanschluss (W) | **Negativ (< 0):** Einspeisung/Überschuss · **Positiv (> 0):** Netzbezug |
| `sensor.ampere_storagepro_pv_flow` | PV-Momentanleistung (W) | Aktuelle Gesamtleistung der Module |
| `sensor.ampere_storagepro_battery_power` | Batterieleistung (W) | Lade-/Entladeleistung Dachspeicher |
| `sensor.ampere_storagepro_battery_percent` | Akkustand (%) | Ladezustand Dachspeicher |
| `sensor.ampere_storagepro_daily_pv_generation` | Tagesertrag (kWh) | PV-Erzeugung des laufenden Tages |
| `sensor.ampere_storagepro_total_pv_generation` | Gesamtenergie (kWh) | Lebensdauer-Energiezähler |

> **Systemintegrator-Hinweis:** `sensor.ampere_storagepro_grid_power` ist das Fundament der Regelung.
> `-850 W` = 850 W Überschuss, `+350 W` = 350 W Netzbezug. Die Vorzeichen-Konvention muss vor Inbetriebnahme
> der Automatisierung verifiziert werden.

---

## 4. Virtuelle Messtechnik & Mathematische Sensorik

Der physische Smart Meter liefert einen bidirektionalen Wirkleistungswert P(grid). Für Regelung und
Energie-Dashboard sind zwei Schritte nötig: Trennung in zwei positive Kanäle (Überschuss/Bezug) und
zeitliche Integration zu kumulierten kWh.

`E = (1 / 3600000) · ∫ P(t) dt [kWh]`

### Stufe 1 – Template-Sensoren (Jinja2)

```yaml
# configuration.yaml
template:
  - sensor:
      - name: "Aktueller PV Ueberschuss"
        unique_id: pv_surplus_watts
        unit_of_measurement: "W"
        device_class: power
        state_class: measurement
        state: >
          {% set grid = states('sensor.ampere_storagepro_grid_power') | float(0) %}
          {% if grid < 0 %}
            {{ (grid * -1) | round(0) }}
          {% else %}
            0
          {% endif %}

      - name: "Aktueller Netzbezug"
        unique_id: grid_import_watts
        unit_of_measurement: "W"
        device_class: power
        state_class: measurement
        state: >
          {% set grid = states('sensor.ampere_storagepro_grid_power') | float(0) %}
          {% if grid > 0 %}
            {{ grid | round(0) }}
          {% else %}
            0
          {% endif %}
```

### Stufe 2 – Riemann-Summen-Integralsensoren (kWh)

| Integralsensor | Eingang | Methode | Präfix / Einheit |
| :--- | :--- | :--- | :--- |
| `sensor.netzbezug_gesamt` | `sensor.aktueller_netzbezug` | `left` | `k` / h → kWh |
| `sensor.netzeinspeisung_gesamt` | `sensor.aktueller_pv_ueberschuss` | `left` | `k` / h → kWh |

Die **Linke Riemannsche Summe (`left`)** liefert bei abrupten Leistungsänderungen (Großverbraucher,
Ladevorgänge) die exakteste Energieakkumulation ohne Überschwingen.

---

## 5. Hoymiles S-Miles Cloud & TOU-Betriebsmodi

Der HMS-1600-4T kommuniziert per Sub-1-GHz-Funk, die HiBattery-Einheiten senden ihre Telemetrie per WLAN
an die S-Miles Cloud. Anbindung über die HACS-Integration `Philra94/homeassistant-hoymiles-cloud`
(Authentifizierung über den Hoymiles-v3-OAuth/IAM-Endpunkt). Beide Stationen (`Turm`, `Speicher 3`) werden
automatisch als Geräte angelegt.

### TOU-Konfiguration in der S-Miles App (Speicher 3)

| Zeitfenster | Modus | Wirkung |
| :--- | :--- | :--- |
| 06:00 – 20:00 Uhr | **Erzwungene Ladung** (z. B. 800 W) | Lädt über AC, sobald der Shelly bei Überschuss Strom freigibt |
| 20:00 – 06:00 Uhr | **Eigenverbrauch** (200–800 W) | Speist ab 20:00 Uhr die konfigurierte Leistung ins Hausnetz ein |

### Kern-Entitäten

| Entität | Beschreibung |
| :--- | :--- |
| `sensor.speicher_3_battery_state_of_charge` | SoC Speicher 3 (0–100 %) |
| `sensor.speicher_3_battery_power` | Wirkleistung Speicher 3 (W) |
| `sensor.speicher_3_battery_flow_direction` | `charging` / `discharging` / `idle` |
| `sensor.turm_battery_state_of_charge` | SoC Turm (0–100 %) |
| `sensor.turm_battery_power` | Wirkleistung Turm (W) |

> **Systemintegrator-Hinweis:** Die S-Miles Cloud API verweigert Endkunden-Accounts das Umschalten von
> Ladebetriebsarten zur Laufzeit („The device list is empty“). Die Ladesteuerung wird daher auf eine lokale,
> cloudunabhängige Shelly-Aktorik ausgelagert – die Cloud dient nur als Telemetrie-/TOU-Quelle.

---

## 6. Aktorik & Automatisierungs-Logik (24h-Zyklus)

Zwei **Shelly Plus Plug S Gen3** Zwischensteckdosen (2.500 W / 12 A, cloudunabhängiges WebSocket-RPC/CoIoT):

- **Shelly 1 (Aktor):** schaltet Speicher 3 – `switch.1_stock_shellyplugsg3_speicher3` sowie
  `sensor.1_stock_shellyplugsg3_speicher3_leistung`.
- **Shelly 2 (Monitoring):** erfasst den Turm – `sensor.1_stock_shellyplugsg3_turm_leistung`.

### Regelungslogik

| Phase | Ereignis | Schwellwert / Aktion |
| :--- | :--- | :--- |
| Tag (06:00–20:00) | Überschuss vorhanden | > 300 W für 1 min → **EIN** (nur bei SoC < 98 %) |
| Tag | Netzbezug (Wolken) | > 50 W für 2 min → **AUS** (ohne Zwangsladung) |
| Tag | Ladeschluss | SoC ≥ 98 % → **AUS** |
| Nacht (20:00–06:00) | Start Einspeisung | 20:00 Uhr → **EIN** (sofern SoC > 15 %) |
| Nacht | Tiefentladeschutz | SoC ≤ 15 % oder 06:00 Uhr → **AUS** |
| Manuell | Zwangsladung | `input_boolean.zwangsladung_speicher_3` → sofort EIN, Auto-Stopp bei 98 % |

> **Wichtig:** Die Tag-Abschaltung bei Netzbezug wird durch den Zwangsladungs-Schalter blockiert. Erst bei
> 98 % SoC beendet die Automatisierung die Zwangsladung und setzt den Schalter zurück.

```yaml
# automations.yaml
alias: "Solar: Ueberschussladung Speicher 3"
description: "Vollautomatische Tag-Überschussladung & Nacht-Einspeisung für Speicher 3"
mode: single
trigger:
  - platform: numeric_state
    entity_id: sensor.aktueller_pv_ueberschuss
    above: 300
    for:
      minutes: 1
    id: "einschalten_solar"

  - platform: numeric_state
    entity_id: sensor.aktueller_netzbezug
    above: 50
    for:
      minutes: 2
    id: "ausschalten_netzbezug_tag"

  - platform: numeric_state
    entity_id: sensor.speicher_3_battery_state_of_charge
    above: 97
    id: "ausschalten_voll_tag"

  - platform: time
    at: "20:00:00"
    id: "start_nacht_einspeisung"

  - platform: time
    at: "06:00:00"
    id: "ende_nacht_einspeisung"

  - platform: numeric_state
    entity_id: sensor.speicher_3_battery_state_of_charge
    below: 16
    id: "ausschalten_leer_nacht"

  - platform: state
    entity_id: input_boolean.zwangsladung_speicher_3
    to: "on"
    id: "zwangsladung_ein"

  - platform: state
    entity_id: input_boolean.zwangsladung_speicher_3
    to: "off"
    id: "zwangsladung_aus"

action:
  - choose:
      - conditions:
          - condition: trigger
            id: "zwangsladung_ein"
        sequence:
          - action: switch.turn_on
            target:
              entity_id: switch.1_stock_shellyplugsg3_speicher3

      - conditions:
          - condition: trigger
            id: "einschalten_solar"
          - condition: time
            after: "06:00:00"
            before: "20:00:00"
          - condition: numeric_state
            entity_id: sensor.speicher_3_battery_state_of_charge
            below: 98
        sequence:
          - action: switch.turn_on
            target:
              entity_id: switch.1_stock_shellyplugsg3_speicher3

      - conditions:
          - condition: trigger
            id: "ausschalten_netzbezug_tag"
          - condition: time
            after: "06:00:00"
            before: "20:00:00"
          - condition: state
            entity_id: input_boolean.zwangsladung_speicher_3
            state: "off"
        sequence:
          - action: switch.turn_off
            target:
              entity_id: switch.1_stock_shellyplugsg3_speicher3

      - conditions:
          - condition: trigger
            id: "ausschalten_voll_tag"
          - condition: time
            after: "06:00:00"
            before: "20:00:00"
        sequence:
          - action: switch.turn_off
            target:
              entity_id: switch.1_stock_shellyplugsg3_speicher3
          - action: input_boolean.turn_off
            target:
              entity_id: input_boolean.zwangsladung_speicher_3

      - conditions:
          - condition: trigger
            id: "start_nacht_einspeisung"
          - condition: numeric_state
            entity_id: sensor.speicher_3_battery_state_of_charge
            above: 15
        sequence:
          - action: switch.turn_on
            target:
              entity_id: switch.1_stock_shellyplugsg3_speicher3

      - conditions:
          - condition: trigger
            id:
              - "ausschalten_leer_nacht"
              - "ende_nacht_einspeisung"
          - condition: state
            entity_id: input_boolean.zwangsladung_speicher_3
            state: "off"
        sequence:
          - action: switch.turn_off
            target:
              entity_id: switch.1_stock_shellyplugsg3_speicher3

      - conditions:
          - condition: trigger
            id: "zwangsladung_aus"
        sequence:
          - action: switch.turn_off
            target:
              entity_id: switch.1_stock_shellyplugsg3_speicher3
```

---

## 7. Visualisierung & Dashboard-Design

### Ebene 1 – Offizielles Energie-Dashboard

| Feld | Entität |
| :--- | :--- |
| Netzverbrauch | `sensor.netzbezug_gesamt` |
| Netzeinspeisung | `sensor.netzeinspeisung_gesamt` |
| Solarmodule | `sensor.ampere_storagepro_total_pv_generation`, `sensor.turm_today_s_energy` |
| Batterie Dach | `total_charge_battery` / `total_discharge_battery` |
| Batterie Speicher 3 | `speicher_3_battery_charge_energy_today` / `speicher_3_battery_discharge_energy_today` |

### Ebene 2 – Live-Dashboard `/solar` (2-Spalten-Layout)

Karte 1: Multi-Speicher-Energiefluss (Power Flow Card Plus) · Karte 2: 3 Tachometer-Gauges + Wirkleistungen ·
Karte 3: Tageswerte + Steuerungszentrale · Karte 4: 3 getrennte 24h-Verlaufsskalen.

```yaml
# Dashboard (YAML)
# Karte 1: Multi-Speicher Live-Energiefluss
type: custom:power-flow-card-plus
title: "Live-Energiefluss"
entities:
  grid:
    entity: sensor.ampere_storagepro_grid_power
    name: "Stromnetz"
    invert_state: true
  solar:
    entity: sensor.ampere_storagepro_pv_flow
    name: "Dachanlage"
  battery:
    entity: sensor.ampere_storagepro_battery_power
    state_of_charge: sensor.ampere_storagepro_battery_percent
    name: "Dachspeicher"
    color_icon: true
  home:
    name: "Hausverbrauch"
  individual:
    - entity: sensor.turm_battery_power
      name: "Turm"
      icon: mdi:battery-high
      color_icon: true
      display_zero: true
      secondary_info:
        entity: sensor.turm_battery_state_of_charge
        unit_of_measurement: "%"
    - entity: sensor.speicher_3_battery_power
      name: "Speicher 3"
      icon: mdi:battery-medium
      color_icon: true
      display_zero: true
      secondary_info:
        entity: sensor.speicher_3_battery_state_of_charge
        unit_of_measurement: "%"
display_zero_lines:
  mode: grey_out
clickable_entities: true
use_new_flow_rate_model: true
w_decimals: 0
min_flow_rate: 0.75
max_flow_rate: 6

# Karte 2: 3er-Tachometer-Gauges (SoC)
type: horizontal-stack
cards:
  - type: gauge
    entity: sensor.ampere_storagepro_battery_percent
    name: "Dachspeicher"
    min: 0
    max: 100
    needle: true
    severity:
      green: 70
      yellow: 20
      red: 0
  - type: gauge
    entity: sensor.turm_battery_state_of_charge
    name: "Turm (Phase A)"
    min: 0
    max: 100
    needle: true
    severity:
      green: 70
      yellow: 20
      red: 0
  - type: gauge
    entity: sensor.speicher_3_battery_state_of_charge
    name: "Speicher 3"
    min: 0
    max: 100
    needle: true
    severity:
      green: 70
      yellow: 20
      red: 0

# Karte 3: Steuerungs-Zentrale
type: entities
title: "Steuerungs-Zentrale"
show_header_toggle: false
entities:
  - entity: automation.solar_ueberschussladung_speicher_3
    name: "Auto-Überschussladung (Solar)"
    icon: mdi:robot
  - entity: input_boolean.zwangsladung_speicher_3
    name: "⚡ Zwangsladung aus Netz (Forced Charge)"
    icon: mdi:flash
  - entity: switch.1_stock_shellyplugsg3_speicher3
    name: "Shelly Steckdose (Manuell)"
    icon: mdi:power-socket-eu

# Karte 4: 3 getrennte 24h-Verlaufsskalen
type: vertical-stack
title: "Energie- & Akkuverlauf (24h)"
cards:
  - type: history-graph
    title: "Solarerzeugung & Netzfluss"
    hours_to_show: 24
    entities:
      - entity: sensor.ampere_storagepro_pv_flow
        name: "Dach PV Erzeugung"
      - entity: sensor.ampere_storagepro_grid_power
        name: "Netz (Negativ = Einspeisung)"
  - type: history-graph
    title: "Speicher Lade- / Entladeleistungen (Watt)"
    hours_to_show: 24
    entities:
      - entity: sensor.ampere_storagepro_battery_power
        name: "Dachspeicher Leistung (W)"
      - entity: sensor.turm_battery_power
        name: "Turm Leistung (W)"
      - entity: sensor.speicher_3_battery_power
        name: "Speicher 3 Leistung (W)"
  - type: history-graph
    title: "Akkustände (%)"
    hours_to_show: 24
    entities:
      - entity: sensor.ampere_storagepro_battery_percent
        name: "Dachspeicher Akku (%)"
      - entity: sensor.turm_battery_state_of_charge
        name: "Turm Akku (%)"
      - entity: sensor.speicher_3_battery_state_of_charge
        name: "Speicher 3 Akku (%)"
```

---

## 8. Troubleshooting & Checkliste (TL;DR)

| Problem | Ursache / Prüfung | Lösung |
| :--- | :--- | :--- |
| Ampere-Sensoren „Nicht verfügbar“ | IP des `espressif`-Sticks geändert; Port 502 nicht erreichbar | Feste IP vergeben; Port-Check im Terminal; WLAN-Stick neu stecken |
| Hoymiles aktualisiert nicht | S-Miles-Session abgelaufen oder Speicher-WLAN offline | S-Miles-App prüfen; Hoymiles Cloud neu authentifizieren |
| Speicher 3 schaltet nicht ein | Automation deaktiviert, Überschuss < 300 W / < 1 min, SoC ≥ 98 % | Automation aktivieren; Verlauf „Aktueller PV Ueberschuss“ prüfen |
| Shelly offline | WLAN-Empfang am Speicher 3 | Empfang verbessern; Notbetätigung am Hardware-Taster |
| Karte „Entität nicht gefunden“ | Bereich/Name des Shelly geändert | Entwicklerwerkzeuge → Zustände → neue `switch.*`-ID eintragen |

**Betriebs-Checkliste:**

- **Tag:** Überschuss > 300 W (1 min) → Speicher 3 lädt; Netzbezug > 50 W (2 min) → aus; Ladeschluss bei 98 %.
- **Nacht:** 20:00 Uhr Einspeisung freigeben; Tiefentladeschutz bei 15 % SoC; 06:00 Uhr aus.
- **Wartung:** Wöchentliches HA-Backup, Backup vor jedem Core-Update; Shelly-Firmware über *Einstellungen → Updates*.
- **Vorzeichen-Merkregel:** `grid_power` negativ = Einspeisung/Überschuss, positiv = Netzbezug.
