# Plugins

> Plugins lesen und schreiben einzelne Werte per Modbus, HTTP, MQTT, Skript und Co. und bilden damit die Grundlage für benutzerdefinierte Geräte.

Plugins lesen und schreiben einzelne Werte an einem Gerät, z. B. eine Leistungsmessung, einen Zielstrom, einen Enable-Befehl. Sie treiben die mitgelieferten Geräte-Templates und werden direkt referenziert, wenn du ein [eigenes Gerät](/de/user-defined-devices) vom Typ `type: custom` aufbaust.

Plugins können für folgende Kategorien verwendet werden:

* `meter`: [PV, Batterie, Netz, Zähler](/de/meters)
* `charger`: [Wallboxen](/de/chargers), [Smarte Schalter](/de/smartswitches), [Wärmepumpen, Heizstäbe](/de/heating)
* `vehicle`: [Fahrzeuge](/de/vehicles)
* `tariff`: [Tarife, Vorhersagen](/de/tariffs)
* `circuit`: [Lastmanagement](/de/features/loadmanagement)

Zusätzlich können Plugins auch von [Benachrichtigungen](/de/notifications) zum Versenden von Lifecycle-Events genutzt werden.

### Plugin-Liste

* [GPIO Plugin](#gpio) - Plugin zum direkten Zugriff auf GPIO-Pins (nur Linux).
* [Go Plugin](#go) - Plugin, das Werte über ein Go Skript bereitstellt oder entgegennimmt.
* [HTTP Plugin](#http) - Plugin, das über HTTP-API mit Endgeräten spricht.
* [JavaScript Plugin](#javascript) - Plugin, das Werte in über ein JavaScript Skript bereitstellt oder entgegennimmt.
* [Modbus Plugin](#modbus) - Plugin zum Auslesen von einem Modbus-fähigen Gerät.
* [MQTT Plugin](#mqtt) - Plugin um indirekt über MQTT mit den MQTT-fähigen Geräten zu kommunizieren.
* [Prometheus Plugin](#prometheus) - Plugin zum Lesen von Metriken aus Prometheus über PromQL.
* [Shell Plugin](#shell) - Plugin, das ein Shell Skript ausführen kann, um Daten zu extrahieren oder schreibend entgegennimmt.
* [SMA/Speedwire Plugin](#speedwire) - Plugin speziell für SMA Geräte, die mit dem Speedwire Protokoll kommunizieren können.
* [Websocket Plugin](#websocket) - Plugin zum Empfangen von Gerätedaten über einen eigenen Webserver. Kann nur zum Lesen von Daten genutzt werden.

### Helper-Liste

* [Calc Plugin](#calc) - Meta-Plugin um Ausgaben von anderen Plugins arithmetisch zu verknüpfen.
* [Combined Plugin](#combined) - Meta-Plugin speziell für `charger` um die booleschen Status-Werte für den angeschlossenen (*plugged*) und ladenden (*charging*) Zustand zu einem einzigen Ladestatus zu kombinieren.
* [Const Plugin](#const) - Spezielles Plugin das einfach einen konstanten Wert zurückliefert.
* [Error Plugin](#error) - Spezielles Plugin das einen bekannten Fehlerwert zurückgibt.
* [Convert Plugin](#convert) - Meta-Plugin zur Datentyp-Konvertierung beim Schreiben (z. B. float zu int).
* [Delta Plugin](#delta) - Meta-Plugin zur Umwandlung von absoluten Werten in Änderungswerte (Deltas) beim Schreiben.
* [Ignore Plugin](#ignore) - Meta-Plugin zum Unterdrücken spezifischer Fehlermeldungen.
* [IfElse Plugin](#ifelse) - Meta-Plugin für bedingte Schreibvorgänge mit zwei Zweigen (if/else).
* [Map Plugin](#map) - Meta-Plugin zur Übersetzung von Integer-Werten (z. B. gerätespezifische Modi in evcc-Modi).
* [Meter Plugin](#meter-plugin) - Plugin um ein anderes Messgerät als Datenquelle zu verwenden.
* [Sequence Plugin](#sequence) - Meta-Plugin zur sequentiellen Ausführung mehrerer Schreibvorgänge.
* [Sleep Plugin](#sleep) - Hilfsplugin zum Verzögern von Aktionen (wird meist mit Sequence verwendet).
* [Switch Plugin](#switch) - Meta-Plugin für bedingte Schreibvorgänge basierend auf Eingabewerten (wie switch/case).
* [Valid Plugin](#valid) - Meta-Plugin um Plugin-Werte basierend auf einer booleschen Validierung bereitzustellen.
* [Watchdog Plugin](#watchdog) - Meta-Plugin zur automatischen Wiederholung von Schreibvorgängen in regelmäßigen Abständen.

## Syntax

Ein Plugin wird an ein Attribut eines Geräts gehängt. Der Attributname (z. B. `power`, `enable`, `soc`) bestimmt die Rolle; `source` wählt den Plugin-Typ; weitere Schlüssel sind plugin-spezifische Parameter.

```yaml
<attribut>:
  source: <plugin>
  <p-attr1>: ...
  <p-attr2>: ...
```

Jedes Plugin wird entweder in einem **lesenden** oder einem **schreibenden** Kontext verwendet. Einige Parameter sind nur in einem der beiden Modi sinnvoll. Die vollständigen Attribut-Listen für Zähler, Wallboxen, Fahrzeuge und Tarife findest du unter [Eigene Geräte](/de/user-defined-devices).

### Lesen

Beim Lesen von Daten mithilfe eines Plugins können sogenannte *Pipelines* verwendet werden. Damit können Daten aus der Ausgabe des Plugins fein granular extrahiert werden. Dies ermöglicht es, komplexe Datenstrukturen wie JSON oder XML zu verarbeiten und die benötigten Informationen herauszufiltern. Mögliche Parameter für die Datenextraktion sind:

* `regex`: Ein regulärer Ausdruck, um Werte aus dem empfangenen Text zu extrahieren.
* `jq`: Ein [jq](https://jqlang.github.io/jq/)-Ausdruck, um Werte aus JSON-Strukturen zu extrahieren. Die volle Syntax und Möglichkeiten finden sich in der jq-Dokumentation.
* `quote`: Boolean-Wert, der die Eingabedaten in Anführungszeichen einschließt, bevor sie an jq weitergegeben werden. Dies ermöglicht es jq, unquotierte Strings (z. B. von MQTT) zu verarbeiten. Bei einem MQTT-Wert wie `Charging` kann man `quote: true` und `jq: '. == "Charging"'` verwenden.
* `unpack`: Konvertiert Werte aus anderen Zahlenrepräsentationen, z. B. `hex`.
* `decode`: Dekodiert Binärformate wie `uint32`, `float32` etc.

#### Bekannte Fehlerwerte

[HTTP](#http)-, [MQTT](#mqtt)- und [Websocket](#websocket)-Plugins können spezielle Fehlerwerte als String zurückgeben. evcc erkennt diese und wandelt sie in interne Fehlercodes um, anstatt sie als Daten zu behandeln. Das ist z. B. nützlich für eigene Fahrzeugintegrationen, bei denen die Datenquelle den Zustand des Fahrzeugs kennt.

* `ErrAsleep`: Fahrzeug schläft. evcc kann entscheiden, ob das Fahrzeug geweckt werden soll.
* `ErrMustRetry`: Vorgang soll erneut versucht werden (z. B. bei Rate-Limiting).
* `ErrNotAvailable`: Wert ist nicht verfügbar. evcc behandelt dies als permanenten Fehler bis zum nächsten Neustart.

Wenn ein Plugin z. B. den String `ErrAsleep` als Antwort liefert, erzeugt evcc intern den entsprechenden Fehler. Das [Error Plugin](#error) nutzt denselben Mechanismus, um einen festen Fehlerwert als Konstante zurückzugeben.

### Schreiben

Beim Schreiben können Parameter in der Konfiguration durch Platzhalter ersetzt werden. Die Daten werden in Form von `${var[:format]}` zur Verfügung gestellt. Wenn Format nicht angegeben wird, werden die Daten im Standard %v Go-Format bereitgestellt. Die Variablen werden mit dem entsprechenden Wert ersetzt, bevor das Plugin ausgeführt wird. Zusätzlich können sämtliche Funktionen der Go Template Library verwendet werden, um komplexere Datentransformationen durchzuführen.

## Plugins

### GPIO lesen schreiben

Das `gpio` Plugin ermöglicht den direkten Zugriff auf GPIO-Pins (General Purpose Input/Output) unter Linux. Es ist besonders für Raspberry Pi und ähnliche Einplatinencomputer geeignet.

**Parameter**:

| Parameter | Typ    | Erfordert | Beschreibung                                   |
| --------- | ------ | --------- | ---------------------------------------------- |
| function  | string | ja        | Modus: `read` (Eingang) oder `write` (Ausgang) |
| pin       | int    | ja        | Pin-Nummer (BCM-Nummerierung, z. B. GPIO 17)   |

**Funktionsweise**:

* **Lesen** (`function: read`): Liest den Zustand eines GPIO-Pins als Boolean-Wert (`true` = HIGH, `false` = LOW)
* **Schreiben** (`function: write`): Setzt einen GPIO-Pin auf HIGH (`true`) oder LOW (`false`)

Die Pin-Nummerierung folgt dem BCM-Schema (Broadcom-Nummern, nicht die physische Pin-Position). Für ein Raspberry Pi Pinout siehe z. B. [pinout.xyz](https://pinout.xyz/).

**Beispiel Lesen**:

```yaml
source: gpio
function: read
pin: 17 # BCM pin number
```

**Beispiel Schreiben**:

```yaml
source: gpio
function: write
pin: 27 # BCM pin number
```

> **Hinweis**
>
> Dieses Plugin funktioniert nur auf Linux-Systemen mit Zugriff auf `/dev/gpiomem`.

### Go lesen schreiben

Das `go` Plugin verwendet den [Yaegi](https://github.com/traefik/yaegi) Interpreter, um Go Code zur Laufzeit auszuführen. Es ist besonders nützlich für typsichere Berechnungen und komplexe Datenverarbeitungslogik.

#### Verfügbare Go Standardbibliotheken

Folgende Go Pakete stehen automatisch zur Verfügung und müssen nicht importiert werden:

* `fmt` - Formatierte Ein-/Ausgabe
* `math` - Mathematische Funktionen
* `strings` - String-Manipulation
* `time` - Zeit- und Datumsfunktionen

**Beispiel Lesen**:

```yaml
source: go
script: |
  res := 500.0
  res * 2 // returns 1000.0
```

**Beispiel mit Zeitfunktionen**:

```yaml
source: go
script: |
  hour := time.Now().Hour()
  price := 50.0 // Nachttarif
  if hour >= 9 && hour < 17 {
    price = 100.0 // Tagestarif
  }
  price // returns 50.0 oder 100.0 abhängig von der Uhrzeit
```

**Beispiel mit String-Verarbeitung**:

```yaml
source: go
script: |
  text := "hello world"
  strings.ToUpper(text) // returns "HELLO WORLD"
```

Wenn das `go` Plugin zum Schreiben verwendet wird, wird der zu schreibende Wert dem Script als Variable übergeben:

**Beispiel Schreiben**:

```yaml
maxcurrent:
  source: go
  script: |
    fmt.Printf("Setze Ladestrom: %d A\n", maxcurrent)
    // maxcurrent Variable ist automatisch verfügbar
```

#### Input- und Output-Transformationen

Die `go` und `js` Plugins unterstützen `in` und `out` Parameter, um Daten aus anderen Quellen als Variablen im Script zu verwenden bzw. das Ergebnis an andere Plugins weiterzuleiten.

##### Input-Transformationen (`in`)

Mit dem `in` Parameter können Werte aus anderen Quellen als Variablen im Script verwendet werden. Jeder Eintrag benötigt `name` (Variablenname im Script), `type` (`bool`, `int`, `float`, `string`) und `config` (Plugin-Konfiguration).

Dieses Beispiel zeigt eine bedingte Logik, die mit den einfachen Calc-Operationen nicht möglich ist:

```yaml
power:
  source: go
  script: |
    // Leistung abhängig vom SoC und Standby
    power := 5000.0 // normal
    if standby {
      power = 0.0 // im Standby keine Last
    } else if soc < 20.0 {
      power = 1000.0 // niedrig
    }
    power // returns 5000.0 (standby=false, soc>=20)
  in:
    - name: soc
      type: float
      config:
        source: const
        value: 85.0
    - name: standby
      type: bool
      config:
        source: const
        value: false
```

##### Output-Transformationen (`out`)

Mit dem `out` Parameter kann das Ergebnis eines Scripts an andere Plugins weitergeleitet werden. Dies ist besonders nützlich im Schreibkontext, wenn das Script-Ergebnis z. B. per MQTT oder HTTP weiterverarbeitet werden soll. Jeder Eintrag benötigt `name`, `type` und `config`, analog zu `in`.

```yaml
maxcurrent:
  source: go
  script: |
    watts := maxcurrent * 230
    watts
  out:
    - name: watts
      type: float
      config:
        source: mqtt
        topic: heater/target_power
```

### HTTP lesen schreiben

Das `http`-Plugin führt HTTP Aufrufe durch, um Daten zu lesen oder zu aktualisieren. Es beinhaltet auch die Fähigkeit, JSON-Datenstrukturen über jq-Abfragen (z. B. für REST-APIs) zu lesen oder einfache Transformationen durchzuführen. Der volle Funktionsumfang ist in der [offiziellen jq Dokumentation](https://jqlang.github.io/jq/manual/) zu finden.

Methoden der Authentifizierung sind `basic`, `bearer` und `digest`. Die Namen der jeweiligen Parameter finden sich [hier](https://github.com/evcc-io/evcc/blob/master/plugin/http_auth.go#L23).

#### Authentifizierung

Für HTTP-Anfragen stehen verschiedene Authentifizierungsmethoden zur Verfügung:

**Basic Authentication**:

```yaml
auth:
  type: basic
  user: <benutzername>
  password: <passwort>
```

**Bearer Token** (z. B. für JWT):

```yaml
auth:
  type: bearer
  token: <token>
```

**Digest Authentication**:

```yaml
auth:
  type: digest
  user: <benutzername>
  password: <passwort>
```

**Benutzerdefinierte Authentifizierung**:

Für komplexere Authentifizierungsszenarien können benutzerdefinierte Authentifizierungs-Plugins entwickelt werden. Diese werden über den Parameter `source` eingebunden:

```yaml
auth:
  source: <plugin-name>
  user: <benutzername>
  password: <passwort>
  # weitere plugin-spezifische Parameter
```

Dies ermöglicht die Integration von Geräten mit speziellen Authentifizierungsanforderungen, ohne den gesamten HTTP-Plugin-Code anpassen zu müssen.

> **Wichtig**
>
> XML-Dokumente werden intern automatisch in JSON-Form überführt, welche dann mit jq wie eine native JSON-Antwort weiter gefiltert werden können. Attribute bekommen das prefix `attr`.

> **Tipp**
>
> Für den Test von jq-Abfragen bietet sich z. B. das Online-Tool [jqplay.org](https://jqplay.org/) an. Für Regex-Tests eignet sich [regex101.com](https://regex101.com/).

**Beispiel Lesen**:

```yaml
source: http
uri: https://volkszaehler/api/data/<uuid>.json?from=now
method: GET # default HTTP method
headers:
  - content-type: application/json
auth: # basic authentication
  type: basic
  user: foo
  password: bar
insecure: false # set to true to trust self-signed certificates
jq: .data.tuples[0][1] # parse response json
scale: 0.001 # factor applied to result, e.g. for kW to W conversion
cache: 60s # response cache duration
timeout: 10s # timeout in golang duration format, see https://golang.org/pkg/time/#ParseDuration
```

```yaml
source: http
uri: http://charger/status
jq: .total_power > 10 # Converts a json integer to a boolean value
```

**Beispiel Schreiben**:

```yaml
body: %v # only applicable for PUT or POST requests
```

```yaml
enable:
  source: http
  uri: "http://charger/relay/0?turn={{if .enable}}on{{else}}off{{end}}"
```

**Dynamische URIs**:

`uri` und `body` sind Go-Templates, die bei jeder Anfrage neu ausgewertet werden. Neben den [sprig-Funktionen](https://masterminds.github.io/sprig/) stehen `addDate` und `timeRound` zur Verfügung. Damit lässt sich z. B. eine Prognose-API immer für einen mitlaufenden Zeitraum abfragen:

```yaml
forecast:
  source: http
  uri: https://api.example.org/forecast?from={{ now | date "2006-01-02" }}&to={{ addDate now 0 0 2 | date "2006-01-02" }}
```

### JavaScript lesen schreiben

evcc integriert einen JavaScript Interpreter mit der [Underscore.js](https://underscorejs.org) Bibliothek, welche direkt über `_.` zugreifbar ist, z. B. `_.random(0,5)`. Das `js` Plugin kann JavaScript code über den `script` Parameter ausführen. Sehr hilfreich für das schnelle Erstellen von Prototypen:

**Beispiel Lesen**:

```yaml
source: js
script: |
  var res = 500;
  2 * res; // returns 1000
```

Wenn das `js` Plugin zum Schreiben verwendet wird, wird der zu schreibende Wert dem Script als Variable übergeben:

**Beispiel Schreiben**:

```yaml
maxcurrent:
  source: js
  script: |
    console.log(maxcurrent);
```

Das `js` Plugin unterstützt dieselben [Input- und Output-Transformationen](#transformations) (`in`/`out`) wie das `go` Plugin.

### Modbus lesen schreiben

Das `modbus`-Plugin kann Daten von jedem Modbus-fähigen Gerät oder SunSpec-kompatiblen Wechselrichter lesen. Viele Strommessgeräte sind bereits vorkonfiguriert (siehe [MBMD Supported Devices](https://github.com/volkszaehler/mbmd#supported-devices)). Es ist ebenfalls möglich Modbus Register zu Schreiben um weitere Wallboxen zu integrieren.

**Beispiel**:

```yaml
source: modbus
id: 1
uri: 192.168.1.10:502
register:
  address: 300
  type: holding
  decode: uint16
```

Schaue in die [Modbus Dokumentation](/de/reference/modbus) für weitere Details.

### MQTT lesen schreiben

Das `mqtt`-Plugin ermöglicht das Lesen von Werten über MQTT-Topics. Das ist insbesondere für Strommessgeräte nützlich, z. B. wenn diese ihre Daten bereits über MQTT bereitstellen. Schaue in die [MBMD Dokumentation](https://github.com/volkszaehler/mbmd) für ein Beispiel, wie man Modbus Messdaten in MQTT bekommt.

**Parameter (Lesen)**:

| Parameter | Typ      | Erfordert | Beschreibung                                                             |
| --------- | -------- | --------- | ------------------------------------------------------------------------ |
| topic     | string   | ja        | MQTT Topic zum Lesen                                                     |
| timeout   | duration | nein      | Maximales Alter empfangener Werte                                        |
| scale     | float    | nein      | Skalierungsfaktor für Ergebnis (z. B. 0.001 für Wh zu kWh Konvertierung) |

Das Plugin gibt einen Fehler zurück, wenn innerhalb der `timeout` Dauer kein neuer Wert empfangen wurde. Wenn `timeout` nicht gesetzt ist, werden Werte beliebigen Alters akzeptiert, sobald die erste Nachricht empfangen wurde. Es wird empfohlen, einen Timeout zu setzen, um zu erkennen, wenn die Quelle keine aktuellen Daten mehr liefert.

Für die Datenextraktion stehen die unter [Lesen](#reading) beschriebenen Pipeline-Parameter zur Verfügung (`regex`, `jq`, `quote`, etc.).

**Beispiel Lesen**:

```yaml
source: mqtt
topic: mbmd/sdm1-1/Power
timeout: 30s # don't accept values older than timeout
scale: 0.001 # factor applied to result, e.g. for Wh to kWh conversion
```

**Parameter (Schreiben)**:

| Parameter | Typ    | Erfordert | Beschreibung                                                            |
| --------- | ------ | --------- | ----------------------------------------------------------------------- |
| topic     | string | ja        | MQTT Topic zum Schreiben                                                |
| payload   | string | nein      | Payload-Template (verwendet Wert im Standardformat, wenn nicht gesetzt) |

Für den Schreibzugriff werden die Daten mit dem Attribut `payload` bereitgestellt. Falls dieser Parameter in der Konfiguration fehlt, wird der Wert im Standardformat geschrieben.

**Beispiel Schreiben**:

```yaml
source: mqtt
topic: mbmd/charger/maxcurrent
payload: ${var:%d}
```

### Prometheus lesen

Das `prometheus` Plugin liest Metriken aus einer Prometheus-Instanz über PromQL-Abfragen. Dies ist nützlich, wenn bereits Monitoring-Daten in Prometheus vorliegen, die in evcc verwendet werden sollen.

**Parameter**:

| Parameter | Typ      | Erfordert | Beschreibung                  |
| --------- | -------- | --------- | ----------------------------- |
| uri       | string   | ja        | Prometheus Server URL         |
| query     | string   | ja        | PromQL-Abfrage                |
| timeout   | duration | nein      | Timeout (Standard: 2 Minuten) |

**Unterstützte Abfrageergebnisse**:

* **Scalar**: Ein einzelner Zahlenwert
* **Vector**: Ein Vektor mit genau einem Metrik-Eintrag

**Beispiel**:

```yaml
power:
  source: prometheus
  uri: http://prometheus.local:9090
  query: "sum(household_power_watts)"
  timeout: 30s
```

**Beispiel mit Zeitbereich**:

```yaml
energy:
  source: prometheus
  uri: http://prometheus.local:9090
  query: "increase(energy_total_kwh[1h])"
```

Die Abfrage muss einen einzelnen numerischen Wert zurückgeben. Bei Vektor-Ergebnissen muss genau eine Metrik enthalten sein.

### Shell Script lesen schreiben

Das `script` Plugin führt externe Skripte zum Lesen oder Aktualisieren von Daten aus. Das Plugin ist hilfreich um jede Art von externer Funktionalität einzubinden.

**Beispiel Lesen**:

```yaml
source: script
cmd: /bin/bash -c "cat /dev/urandom"
timeout: 5s
```

**Beispiel Schreiben**:

```yaml
source: script
cmd: /home/user/my-script.sh ${enable:%b} # format boolean enable as 0/1
timeout: 5s
```

### SMA/Speedwire lesen

Das `sma` Plugin bietet eine Schnittstelle zu SMA Geräten, welche das Speedwire Protokoll beherrschen.

**Beispiel Lesen**:

```yaml
source: sma
uri: 192.168.4.51 # alternative to serial
serial: 123456 # alternative to uri
value: ActivePowerPlus # ID of value to read
password: "0000" # optional (default: 0000)
interface: eth0 # optional
scale: 1 # optional scale factor for value
```

Unterstützte Werte für `value` können in der Diagnoseausgabe über das Kommando `evcc meter` (mit konfigurierten SMA `meter` Geräten) gefunden werden.

Alle möglichen Werte können als Konstanten [hier](https://gitlab.com/bboehmke/sunny/-/blob/master/values.go#L24) gefunden werden (verwende den Namen der Konstante für `value`).

### Websocket lesen

Das `websocket`-Plugin bietet einen WebSocket-Listener. Es beinhaltet auch die Fähigkeit, JSON-Datenstrukturen über jq-ähnliche Abfragen zu lesen oder zu parsen. Dies kann z. B. verwendet werden, um Daten von Volkszählers Push Server zu empfangen.

Für die Datenextraktion stehen die unter [Lesen](#reading) beschriebenen Pipeline-Parameter zur Verfügung (`regex`, `jq`, `quote`, etc.).

**Beispiel Lesen**:

```yaml
source: http
uri: ws://<volkszaehler host:port>/socket
jq: .data | select(.uuid=="<uuid>") .tuples[0][1] # parse message json
scale: 0.001 # factor applied to result, e.g. for Wh to kWh conversion
timeout: 30s # error if no update received in 30 seconds
```

## Helpers

### Calc lesen

Das `calc` Plugin erlaubt es mehrere Einzelwerte mathematisch weiterzuverarbeiten:

**Beispiel Lesen**:

```yaml
source: calc
add:
- source: ...
  ...
- source: ...
  ...
```

```yaml
source: calc
mul:
- source: calc
  sign:
    source: ... (power)
  ...
- source: ... (current)
  ...
```

Als Operanden werden dabei die Grundrechenarten Addition (`add`), Multiplikation (`mul`) unterstützt, Division (`div`), Vorzeichenumkehr (`sign`), Absolutwert (`abs`), Minimalwert (`min`) und Maximalwert (`max`) unterstützt.

Mit `scale: -1` bei einem der Werte kann eine einfache Subtraktion durchgeführt werden, mit `scale: 0.001` eine Division z. B. zur Konvertierung von kWh in Wh.

Mit `sign:` (jede positive Zahl wird zu +1, jede negative Zahl wird zu -1, 0 bleibt 0) können (in Verbindung mit `mul`) Vorzeichen auf andere Werte übertragen werden. Z. B. um bei Zählern die „Richtung” der Leistung (Einspeisung oder Bezug) auf die gemessenen Ströme zu übertragen.

Mit `abs:` wird der Absolutwert einer Zahl berechnet.

Mit `min:` und `max:` wird der Minimalwert bzw. der Maximalwert berechnet.

Das `calc` Plugin ist hilfreich um z. B.

* Leistungswerte von einzelnen PV-Strings zu summieren (addieren)
* Die Scheinleistung aus Spannung und Strom zu berechnen (multiplizieren)
* Getrennte Leistungswerte für Import und Export zu einem vorzeichenbehafteten Einzelwert zu kombinieren (subtrahieren).
* Prozentuale Füllstände zu berechnen (dividieren)
* Die richtige Richtung des Stromflusses festlegen (sign)
* Bekannte Offsets zu eliminieren (addieren mit `const` Plugin)

> **Tipp**
>
> Konstante Hilfswerte (z. B. für Offsets) lassen sich mithilfe des `const` Plugins als Operand erzeugen.

### Combined lesen

Das `combined` Status Plugin wird verwendet um gemischte Boolean Status Werte von `Plugged` (angeschlossen) / `Charging` (Laden) in einen evcc-kompatiblen Ladestatus von A..F zu konvertieren. Es wird z.b. zusammen mit einer OpenWB MQTT Integration verwendet.

**Beispiel Lesen**:

```yaml
source: combined
plugged:
  source: mqtt
  topic: openWB/lp/1/boolPlugStat
charging:
  source: mqtt
  topic: openWB/lp/1/boolChargeStat
```

### Const lesen

Das `const` Plugin gibt einen konstanten Wert zurück. Es eignet sich z. B. um in Verbindung mit dem `calc` Plugin feste Korrekturwerte (Offset) auf einen variablen Wert anzuwenden oder auch zur Simulation von Mess- und Statuswerten zu Testzwecken.

**Beispiel Lesen**:

```yaml
source: const
value: -16247
```

### Error lesen

Das `error` Plugin gibt immer einen [bekannten Fehlerwert](#known-errors) zurück. Es ist nützlich, um ein nicht implementiertes Attribut als explizit nicht verfügbar zu kennzeichnen.

**Parameter**:

| Parameter | Typ    | Erfordert | Beschreibung                                                    |
| --------- | ------ | --------- | --------------------------------------------------------------- |
| error     | string | ja        | Fehlerwert (`ErrAsleep`, `ErrMustRetry` oder `ErrNotAvailable`) |

**Beispiel**:

```yaml
soc:
  source: error
  error: ErrNotAvailable
```

### Convert schreiben

Das `convert` Plugin konvertiert Datentypen beim Schreiben. Es wird verwendet, wenn ein Plugin einen anderen Datentyp erwartet als evcc liefert.

**Parameter**:

| Parameter | Typ    | Erfordert | Beschreibung                            |
| --------- | ------ | --------- | --------------------------------------- |
| convert   | string | ja        | Konvertierungstyp                       |
| set       | config | ja        | Plugin zum Schreiben nach Konvertierung |

**Unterstützte Konvertierungen**:

| Konvertierung | Beschreibung                                            |
| ------------- | ------------------------------------------------------- |
| float2int     | Float64 → Int64 (Nachkommastellen werden abgeschnitten) |
| int2float     | Int64 → Float64                                         |
| int2bytes     | Int64 → Byte-Array (Big Endian, 8 Bytes)                |
| bool2int      | Bool → Int64 (true=1, false=0)                          |

**Beispiel** (evcc liefert float, Gerät erwartet int):

```yaml
limitsoc:
  source: convert
  convert: float2int
  set:
    source: modbus
    uri: 192.168.1.10:502
    id: 1
    register:
      address: 41009
      type: writesingle
      encoding: uint16
```

In diesem Beispiel konvertiert evcc einen Float-Wert wie `85.5` in `85` bevor er an das Modbus-Register geschrieben wird.

### Delta schreiben

Das `delta` Plugin wandelt absolute Werte in Änderungswerte (Deltas) um. Es wird für Geräte verwendet, die geschriebene Werte zu einem internen Summenwert addieren, anstatt sie direkt zu setzen.

**Parameter**:

| Parameter | Typ    | Erfordert | Beschreibung                                |
| --------- | ------ | --------- | ------------------------------------------- |
| get       | config | nein      | Plugin zum Lesen des aktuellen Gesamtwerts  |
| set       | config | ja        | Plugin zum Schreiben des berechneten Deltas |

**Funktionsweise**:

1. Eingabewert wird empfangen (z. B. `1000`)
2. Falls `get` konfiguriert ist, wird der aktuelle Gesamtwert vom Gerät gelesen
3. Delta wird berechnet: `delta = neuer_wert - aktueller_gesamtwert`
4. Delta wird über das `set` Plugin geschrieben
5. Interner Zustand wird aktualisiert

Ohne `get` Parameter führt das Plugin den Gesamtwert intern (startet bei 0). Bei Neustart von evcc geht die Synchronisation zum Gerät verloren. Daher wird empfohlen, `get` zu konfigurieren, wenn das Gerät einen lesbaren Summenwert bereitstellt.

**Unterstützte Datentypen**: `int64`, `float64`

**Anwendungsfall**:

Einige Wärmepumpen (z. B. Ochsner) haben Register, die Änderungswerte statt absolute Werte erwarten. Beim Schreiben von `500` addiert das Gerät 500 W zum internen Wert, anstatt auf 500 W zu setzen. Um von 1000 W auf 1500 W zu erhöhen, muss `+500` geschrieben werden. Um zu reduzieren, werden negative Werte geschrieben (z. B. `-300`).

**Beispiel Schreiben**:

```yaml
setmaxpower:
  source: delta
  get: # Aktuellen Gesamtwert vom Gerät lesen
    source: modbus
    uri: 192.168.1.50:502
    id: 50
    register:
      address: 2012 # Interner Summenwert des Geräts
      type: input
      decode: uint16
  set: # Berechnetes Delta schreiben
    source: modbus
    uri: 192.168.1.50:502
    id: 50
    register:
      address: 2201 # Delta-Register
      type: writeholding
      decode: uint16
```

**Beispiel mit Watchdog**:

Häufig wird das Delta Plugin mit dem [Watchdog Plugin](#watchdog) kombiniert, wenn das Gerät regelmäßige Updates benötigt:

```yaml
setmaxpower:
  source: watchdog
  timeout: 60s # Erneutes Senden alle 30 Sekunden
  set:
    source: delta # Absolute Werte in Deltas umwandeln
    get: # Aktuellen Gesamtwert lesen
      source: modbus
      uri: 192.168.1.50:502
      id: 50
      register:
        address: 2012 # Summenwert-Register
        type: input
        decode: uint16
    set: # Delta schreiben
      source: modbus
      uri: 192.168.1.50:502
      id: 50
      register:
        address: 2201 # Delta-Register
        type: writeholding
        decode: uint16
```

### Ignore schreiben

Das `ignore` Plugin unterdrückt spezifische Fehlermeldungen beim Schreiben. Es wird verwendet, wenn ein Gerät harmlose Fehler zurückgibt, die ignoriert werden können.

**Parameter**:

| Parameter | Typ    | Erfordert | Beschreibung                                 |
| --------- | ------ | --------- | -------------------------------------------- |
| error     | string | ja        | Fehlertext-Präfix, der ignoriert werden soll |
| set       | config | ja        | Plugin zum Schreiben                         |

**Funktionsweise**:

1. Das verschachtelte `set` Plugin wird ausgeführt
2. Falls ein Fehler auftritt, wird geprüft, ob die Fehlermeldung mit dem `error` String beginnt
3. Wenn ja, wird der Fehler ignoriert und Erfolg zurückgegeben
4. Wenn nein, wird der Fehler normal weitergegeben

**Unterstützte Datentypen**: `int64`, `float64`, `bool`, `[]byte`

**Beispiel**:

```yaml
batterymode:
  source: switch
  switch:
    - case: 1 # normal
      set:
        source: const
        value: 2
        set:
          source: ignore
          error: "modbus: response data size '18' does not match count '4'"
          set:
            source: modbus
            uri: 192.168.1.10:502
            id: 1
            register:
              address: 0x1110
              type: writemultiple
              encoding: int16
```

In diesem Beispiel gibt das Gerät einen harmlosen Modbus-Fehler zurück, der ignoriert wird.

### IfElse schreiben

Das `ifelse` Plugin führt bedingte Schreibvorgänge mit zwei Zweigen aus. Je nach Eingabewert wird entweder das `if` oder das `else` Plugin ausgeführt.

**Parameter**:

| Parameter | Typ    | Erfordert | Beschreibung                                              |
| --------- | ------ | --------- | --------------------------------------------------------- |
| if        | config | ja        | Plugin, das bei erfüllter Bedingung ausgeführt wird       |
| else      | config | ja        | Plugin, das bei nicht erfüllter Bedingung ausgeführt wird |

**Funktionsweise**:

* Bei `bool` Werten: `true` führt `if` aus, `false` führt `else` aus
* Bei `int64` Werten: `> 0` führt `if` aus, sonst `else`

**Unterstützte Datentypen**: `int64`, `bool`

**Beispiel** (unterschiedliche Endpunkte für An- und Ausschalten):

```yaml
enable:
  source: ifelse
  if:
    source: http
    uri: http://device.local/api/on
    method: POST
  else:
    source: http
    uri: http://device.local/api/off
    method: POST
```

### Map lesen schreiben

Das `map` Plugin übersetzt Integer-Werte in andere Integer-Werte mithilfe einer Lookup-Tabelle. Es wird häufig verwendet, um gerätespezifische Werte in evcc-Standardwerte zu konvertieren und umgekehrt.

**Parameter**:

| Parameter | Typ              | Erfordert | Beschreibung                                           |
| --------- | ---------------- | --------- | ------------------------------------------------------ |
| values    | map\[int64]int64 | ja        | Lookup-Tabelle mit Eingabe → Ausgabe Zuordnung         |
| get       | config           | nein      | Plugin zum Lesen (nur beim Lesen erforderlich)         |
| set       | config           | nein      | Plugin zum Schreiben (nur beim Schreiben erforderlich) |

**Funktionsweise**:

**Beim Lesen**:

1. `get` Plugin liefert einen Wert (z. B. `0`)
2. Wert wird in der `values` Tabelle nachgeschlagen
3. Der zugeordnete Wert wird zurückgegeben (z. B. `0` → `2`)

**Beim Schreiben**:

1. Eingabewert wird empfangen (z. B. `3`)
2. Wert wird in der `values` Tabelle nachgeschlagen
3. Der zugeordnete Wert wird an das `set` Plugin übergeben (z. B. `3` → `6`)

Falls kein passender Wert in der Tabelle gefunden wird, gibt es einen Fehler.

**Unterstützte Datentypen**: `int64` (nur Integer-Werte)

**Beispiel Lesen** (Gerätewert → evcc):

```yaml
getmode:
  source: map
  values:
    0: 2 # Gerät "Free" → evcc "normal"
    1: 1 # Gerät "Forced off" → evcc "reduced"
    2: 3 # Gerät "Recommended on" → evcc "boost"
    3: 3 # Gerät "Forced on" → evcc "boost"
  get:
    source: modbus
    uri: 192.168.1.10:502
    id: 1
    register:
      address: 55
      type: holding
      encoding: int16
```

**Beispiel Schreiben** (evcc → Gerätewert):

```yaml
setmode:
  source: map
  values:
    1: 1 # evcc "reduced" → Gerät "Forced off"
    2: 0 # evcc "normal" → Gerät "Free"
    3: 3 # evcc "boost" → Gerät "Forced on"
  set:
    source: modbus
    uri: 192.168.1.10:502
    id: 1
    register:
      address: 55
      type: writeholding
      encoding: int16
```

### Meter lesen

Das `meter` Plugin ermöglicht es, ein anderes Messgerät als Datenquelle zu verwenden. Dies ist nützlich, wenn man ein bestehendes Gerät für mehrere Messwerte verwenden möchte oder wenn man verschiedene Methoden eines Geräts für unterschiedliche Attribute nutzen will.

Die `config` Sektion enthält dabei die vollständige Template-Konfiguration des einzubettenden Messgeräts. Der Parameter `method` bestimmt, welcher Wert gelesen wird: `power`, `energy`, `returnenergy` oder `soc`.

**Beispiel Lesen**:

```yaml
meters:
  - name: battery
    type: custom
    power:
      source: meter
      config:
        type: template
        template: shelly-1pm
        host: 192.168.178.21
        channel: 0
      method: power
      scale: -1
    energy:
      source: meter
      config:
        type: template
        template: shelly-1pm
        host: 192.168.178.21
        channel: 0
      method: energy
    soc:
      source: mqtt
      topic: Haus/Batterie
      jq: .soc
      timeout: 60s
```

In diesem Beispiel wird ein Shelly 1PM Gerät als Datenquelle für Leistung und Energie einer Batterie verwendet, während der Ladestand (SoC) über MQTT abgerufen wird.

### Sequence schreiben

Das `sequence` Plugin führt mehrere Schreibvorgänge nacheinander aus. Alle verschachtelten Plugins erhalten denselben Eingabewert und werden in der definierten Reihenfolge ausgeführt. Bei einem Fehler stoppt die Ausführung sofort.

**Parameter**:

| Parameter | Typ       | Erfordert | Beschreibung                                     |
| --------- | --------- | --------- | ------------------------------------------------ |
| set       | \[config] | ja        | Array von verschachtelten Plugin-Konfigurationen |

**Funktionsweise**:

1. Eingabewert wird empfangen
2. Jedes Plugin in der `set` Liste wird nacheinander mit diesem Wert aufgerufen
3. Bei einem Fehler wird die Sequenz abgebrochen und der Fehler zurückgegeben
4. Erfolgreiche Ausführung bedeutet, dass alle Plugins erfolgreich ausgeführt wurden

**Unterstützte Datentypen**: `int64`, `float64`, `bool`

**Anwendungsfälle**:

* Mehrere HTTP-Aufrufe nacheinander ausführen
* Kombination mit `sleep` Plugin für zeitlich verzögerte Aktionen
* Mehrere Modbus-Register gleichzeitig setzen
* Propagierung von Werten an mehrere Ziele

**Beispiel 1: Mehrere HTTP-Aufrufe**

```yaml
setmode:
  source: sequence
  set:
    - source: http
      uri: http://device.local/api/pin1
      method: POST
      body: '{"value": "on"}'
    - source: http
      uri: http://device.local/api/pin4
      method: POST
      body: '{"value": "off"}'
```

**Beispiel 2: Kombination mit switch**

```yaml
batterymode:
  source: sequence
  set:
    - source: switch
      switch:
        - case: 1 # normal
          set:
            source: http
            uri: http://battery.local/api/mode
            body: "automatic"
        - case: 3 # charge
          set:
            source: sequence
            set:
              - source: sleep
                duration: 1s
              - source: http
                uri: http://battery.local/api/charge
                body: "5000" # 5 kW
    - source: mqtt
      topic: home/battery/status
      payload: "mode_${batterymode}"
```

**Ablauf bei `batterymode: 1` (normal)**:

1. Äußere `sequence` empfängt Wert `1`
2. Erster Schritt: `switch` prüft den Wert `1`
   * Fall `1` trifft zu → HTTP-Aufruf zu `/api/mode` mit Body `automatic`
3. Zweiter Schritt: `mqtt` sendet Nachricht zu `home/battery/status` mit Payload `mode_1`
4. Fertig

**Ablauf bei `batterymode: 3` (charge)**:

1. Äußere `sequence` empfängt Wert `3`

2. Erster Schritt: `switch` prüft den Wert `3`

   * Fall `3` trifft zu → Innere `sequence` wird ausgeführt:

     * Warte 1 Sekunde (`sleep`)
     * HTTP-Aufruf zu `/api/charge` mit Body `5000`

3. Zweiter Schritt: `mqtt` sendet Nachricht zu `home/battery/status` mit Payload `mode_3`

4. Fertig

Der Wert fließt durch alle Plugins, wobei `switch` unterschiedliche Aktionen basierend auf dem Wert ausführt und `mqtt` am Ende immer benachrichtigt wird.

### Sleep schreiben

Das `sleep` Plugin fügt eine Verzögerung ein. Wird typischerweise innerhalb eines `sequence` Plugins verwendet, um zeitliche Abstände zwischen Aktionen zu schaffen.

**Parameter**:

| Parameter | Typ      | Erfordert | Beschreibung                                                |
| --------- | -------- | --------- | ----------------------------------------------------------- |
| duration  | duration | ja        | Wartezeit (z. B. `1s`, `500ms`, `0s` für keine Verzögerung) |

**Beispiel**:

```yaml
setmode:
  source: sequence
  set:
    - source: http
      uri: http://device.local/api/prepare
      method: POST
    - source: sleep
      duration: 500ms
    - source: http
      uri: http://device.local/api/activate
      method: POST
```

### Switch schreiben

Das `switch` Plugin führt bedingte Schreibvorgänge durch, ähnlich einer switch/case-Anweisung in Programmiersprachen. Basierend auf dem Eingabewert wird die entsprechende Aktion ausgeführt.

**Parameter**:

| Parameter | Typ     | Erfordert | Beschreibung                                        |
| --------- | ------- | --------- | --------------------------------------------------- |
| switch    | \[case] | ja        | Array von Fällen mit `case` und `set` Konfiguration |
| default   | config  | nein      | Fallback-Plugin, wenn kein Fall zutrifft            |

**Funktionsweise**:

1. Eingabewert wird mit den `case` Werten verglichen
2. Bei Übereinstimmung wird das entsprechende `set` Plugin ausgeführt
3. Falls kein Fall zutrifft und `default` definiert ist, wird dieses ausgeführt
4. Falls kein Fall zutrifft und kein `default` definiert ist, gibt es einen Fehler

**Unterstützte Datentypen**: `int64` (nur Integer-Werte)

**Beispiel**:

```yaml
setmode:
  source: switch
  switch:
    - case: 1 # reduced
      set:
        source: http
        uri: http://device.local/api/mode
        body: "eco"
    - case: 2 # normal
      set:
        source: http
        uri: http://device.local/api/mode
        body: "normal"
    - case: 3 # boost
      set:
        source: http
        uri: http://device.local/api/mode
        body: "boost"
```

### Valid lesen

Das `valid` Plugin ermöglicht es, Plugin-Werte basierend auf einer booleschen Validierung bereitzustellen. Es trennt die Gültigkeit eines Werts von dessen eigenem Inhalt. Wenn die Validierung `false` zurückgibt, wird der Wert als nicht verfügbar betrachtet.

Dies ist besonders nützlich für Integrationen wie ioBroker, die Gültigkeit und Wert getrennt bereitstellen.

**Beispiel Lesen**:

```yaml
source: valid
valid:
  source: mqtt
  topic: iobroker/wallbox/power/valid
value:
  source: mqtt
  topic: iobroker/wallbox/power/value
```

In diesem Beispiel wird der Wert nur verwendet, wenn das `valid` Topic `true` zurückgibt. Wenn es `false` zurückgibt, wird der Wert als nicht verfügbar markiert.

### Watchdog schreiben

Das `watchdog` Plugin ist ein Wrapper-Plugin, das Schreibvorgänge automatisch in regelmäßigen Abständen wiederholt. Manche Geräte (z. B. Batteriespeicher, Wechselrichter) erwarten, dass Steuerbefehle regelmäßig wiederholt werden, um aktiv zu bleiben. Das Watchdog-Plugin überwacht Schreibvorgänge und wiederholt diese automatisch in der Hälfte des konfigurierten Timeout-Intervalls.

**Parameter**:

| Parameter | Typ                 | Erfordert | Beschreibung                                                                   |
| --------- | ------------------- | --------- | ------------------------------------------------------------------------------ |
| timeout   | duration            | ja        | Zeitintervall für Wiederholungen (Wert wird alle timeout/2 erneut geschrieben) |
| reset     | string \| \[string] | nein      | Wert(e), bei denen Wiederholungen gestoppt werden                              |
| initial   | string              | nein      | Wert, der beim Start einmalig geschrieben wird                                 |
| defer     | bool                | nein      | Verzögert Updates statt sie sofort auszuführen (Standard: false)               |
| set       | config              | ja        | Verschachteltes Plugin für den eigentlichen Schreibvorgang                     |

**Funktionsweise**:

1. **Start**: Falls `initial` konfiguriert ist, wird dieser Wert beim Start einmalig geschrieben
2. **Schreiben**: Wenn ein Wert geschrieben wird, prüft das Plugin, ob dieser in der `reset` Liste steht
3. **Watchdog aktiv**: Falls der Wert **nicht** in `reset` steht, startet der Watchdog und schreibt den Wert automatisch alle `timeout/2` Sekunden erneut
4. **Watchdog stoppt**: Falls der Wert in `reset` steht, werden keine Wiederholungen durchgeführt

**reset Parameter**:

* Definiert Werte, bei denen der Watchdog gestoppt wird
* Kann ein einzelner Wert (`reset: 0`) oder mehrere Werte (`reset: [0, 1]`) sein
* Typischerweise für “sichere” oder “Standard” Zustände verwendet, die keine kontinuierliche Wiederholung benötigen

**initial Parameter**:

* Optionaler Wert, der beim Pluginstart einmalig geschrieben wird
* Nützlich, um einen definierten Startzustand zu setzen
* Wird vor allen anderen Schreibvorgängen ausgeführt

**defer Parameter**:

* Stellt sicher, dass Timeouts zwischen Updates eingehalten werden
* Der Watchdog wird während der Verzögerung gestoppt und nach Ablauf mit dem neuen Wert neu gestartet
* Sinnvoll wenn Geräte eine Mindestwartezeit zwischen Modusänderungen benötigen
* Die Verzögerung wird basierend auf der Zeit seit dem letzten Update berechnet
* Reset-Werte werden immer sofort (ohne Verzögerung) geschrieben

**Unterstützte Datentypen**: `int64`, `float64`, `bool`

**Beispiel Schreiben**:

```yaml
source: watchdog
timeout: 60s
reset: 0
set:
  source: modbus
  uri: 192.168.1.10:502
  id: 1
  register:
    address: 100
    type: writemultiple
    encoding: uint16
```

In diesem Beispiel werden Werte automatisch alle 30 s wiederholt, außer wenn der Wert `0` geschrieben wird.

**Ablaufbeispiel** mit `timeout: 60s`, `reset: 0`:

* Plugin startet
* Schreibe Wert `100` → Watchdog **läuft**, Wert wird alle 30 s wiederholt
* Nach 2 Minuten: Wert `100` wurde bereits 4x geschrieben (0s, 30s, 60s, 90s, 120s)
* Schreibe Wert `200` → Watchdog **läuft** weiter, jetzt mit neuem Wert `200` alle 30 s
* Schreibe Wert `0` → Watchdog **stoppt** (da `0` in `reset` definiert)
* Keine weiteren Wiederholungen, bis ein neuer Wert != `0` geschrieben wird

**Beispiel mit Batteriesteuerung** (`batterymode` verwendet Werte 1=normal, 2=hold, 3=charge, 4=holdcharge, 5=discharge):

```yaml
batterymode:
  source: watchdog
  timeout: 60s
  reset: 1 # Stoppe Wiederholungen im Normalbetrieb
  set:
    source: switch
    switch:
      - case: 1 # normal
        set:
          source: modbus
          # ... Modbus-Konfiguration für Normalbetrieb
      - case: 2 # hold
        set:
          source: modbus
          # ... Modbus-Konfiguration für Haltemodus
      - case: 3 # charge
        set:
          source: modbus
          # ... Modbus-Konfiguration für Lademodus
```