# Pluginy

> Pluginy, které čtou a zapisují jednotlivé hodnoty přes Modbus, HTTP, MQTT, skripty a další rozhraní, tvoří stavební kameny zařízení definovaných uživatelem.

Pluginy čtou a zapisují jednotlivé hodnoty na zařízení, např. jednu hodnotu výkonu, cílový proud nebo příkaz k zapnutí. Využívají je dodávané šablony zařízení a přímo se na ně odkazujete, když vytváříte [zařízení definované uživatelem](/cs/user-defined-devices) s `type: custom`.

Pluginy lze použít pro tyto kategorie:

* `meter`: [Solár, domácí baterie, síť, měřiče](/cs/meters)
* `charger`: [Nabíječky](/cs/chargers), [Chytré spínače](/cs/smartswitches), [Tepelná čerpadla, elektrické ohřívače](/cs/heating)
* `vehicle`: [Vozidla](/cs/vehicles)
* `tariff`: [Tarify, předpovědi](/cs/tariffs)
* `circuit`: [Řízení zátěže](/cs/features/loadmanagement)

Pluginy se používají také u [oznámení](/cs/notifications), kde odesílají události životního cyklu.

### Seznam pluginů

* [Plugin GPIO](#gpio) - Plugin pro přímý přístup k pinům GPIO (pouze Linux).
* [Plugin Go](#go) - Plugin, který poskytuje nebo přijímá hodnoty pomocí skriptu v Go.
* [Plugin HTTP](#http) - Plugin, který komunikuje s koncovými zařízeními přes HTTP API.
* [Plugin JavaScript](#javascript) - Plugin, který poskytuje nebo přijímá hodnoty pomocí skriptu v JavaScriptu.
* [Plugin Modbus](#modbus) - Plugin pro čtení ze zařízení s podporou Modbus.
* [Plugin MQTT](#mqtt) - Plugin pro nepřímou komunikaci se zařízeními s podporou MQTT přes MQTT.
* [Plugin Prometheus](#prometheus) - Plugin pro čtení metrik ze systému Prometheus pomocí PromQL.
* [Plugin Shell](#shell) - Plugin, který spouští shellový skript ke čtení nebo zápisu dat.
* [Plugin SMA/Speedwire](#speedwire) - Plugin speciálně pro zařízení SMA, která umí komunikovat protokolem Speedwire.
* [Plugin Websocket](#websocket) - Plugin pro příjem dat ze zařízení přes vlastní webový server. Lze jej použít pouze ke čtení dat.

### Seznam pomocných pluginů

* [Plugin Calc](#calc) - Metaplugin pro aritmetické propojení výstupů jiných pluginů.
* [Plugin Combined](#combined) - Metaplugin speciálně pro `charger`, který kombinuje logické stavové hodnoty pro připojení (*plugged*) a nabíjení (*charging*) do jediného stavu nabíjení.
* [Plugin Const](#const) - Speciální plugin, který jednoduše vrací konstantní hodnotu.
* [Plugin Error](#error) - Speciální plugin, který vrací známou chybovou hodnotu.
* [Plugin Convert](#convert) - Metaplugin pro převod datových typů při zápisu (např. float na int).
* [Plugin Delta](#delta) - Metaplugin pro převod absolutních hodnot na rozdílové (delta) hodnoty při zápisu.
* [Plugin Ignore](#ignore) - Metaplugin pro potlačení konkrétních chybových hlášení.
* [Plugin IfElse](#ifelse) - Metaplugin pro podmíněné zápisové operace se dvěma větvemi (if/else).
* [Plugin Map](#map) - Metaplugin pro překlad celočíselných hodnot (např. režimů specifických pro zařízení na režimy evcc).
* [Plugin Meter](#meter-plugin) - Plugin, který použije jiný měřič jako zdroj dat.
* [Plugin Sequence](#sequence) - Metaplugin pro postupné provedení více zápisových operací.
* [Plugin Sleep](#sleep) - Pomocný plugin pro zpoždění akcí (obvykle se používá spolu se Sequence).
* [Plugin Switch](#switch) - Metaplugin pro podmíněné zápisové operace podle vstupních hodnot (jako switch/case).
* [Plugin Valid](#valid) - Metaplugin pro poskytování hodnot pluginu na základě logické validace.
* [Plugin Watchdog](#watchdog) - Metaplugin pro automatické opakování zápisových operací v pravidelných intervalech.

## Syntaxe

Plugin se připojuje k atributu zařízení. Název atributu (např. `power`, `enable`, `soc`) určuje roli, `source` vybírá typ pluginu a zbývající klíče jsou parametry specifické pro daný plugin.

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

Každý plugin se používá buď v kontextu **čtení**, nebo v kontextu **zápisu**. Některé parametry mají smysl jen v jednom z nich. Úplné seznamy atributů měřičů, nabíječek, vozidel a tarifů najdete v části [zařízení definovaná uživatelem](/cs/user-defined-devices).

### Čtení

Při čtení dat pomocí pluginu lze použít tzv. *pipeline* (zpracovací řetězec). Umožňuje z výstupu pluginu přesně vybrat potřebná data. Díky tomu lze zpracovat složité datové struktury, jako je JSON nebo XML, a vyfiltrovat z nich potřebné informace. Pro extrakci dat jsou k dispozici tyto parametry:

* `regex`: Regulární výraz pro extrakci hodnot z přijatého textu.
* `jq`: Výraz [jq](https://jqlang.github.io/jq/) pro extrakci hodnot ze struktur JSON. Úplnou syntaxi a možnosti najdete v dokumentaci jq.
* `quote`: Logická hodnota, při jejímž zapnutí se vstupní data před předáním do jq obalí uvozovkami. Díky tomu jq zpracuje i řetězce bez uvozovek (např. z MQTT). Pro hodnotu z MQTT jako `Charging` můžete použít `quote: true` a `jq: '. == "Charging"'`.
* `unpack`: Převádí hodnoty z jiných číselných reprezentací, např. `hex`.
* `decode`: Dekóduje binární formáty jako `uint32`, `float32` atd.

#### Známé chybové hodnoty

Pluginy [HTTP](#http), [MQTT](#mqtt) a [Websocket](#websocket) mohou vracet speciální chybové řetězce. evcc je rozpozná a převede na interní chybové kódy, místo aby s nimi zacházel jako s daty. To se hodí např. pro vlastní integrace vozidel, kde zdroj dat zná stav vozidla.

* `ErrAsleep`: Vozidlo spí. evcc se může rozhodnout vozidlo probudit a akci zopakovat.
* `ErrMustRetry`: Operaci je třeba zopakovat (např. kvůli omezení počtu požadavků).
* `ErrNotAvailable`: Hodnota není k dispozici. evcc ji považuje za trvalou chybu až do příštího restartu.

Pokud plugin vrátí jako odpověď řetězec `ErrAsleep`, evcc vygeneruje odpovídající interní chybu. [Plugin Error](#error) používá stejný mechanismus a vrací pevnou chybovou hodnotu jako konstantu.

### Zápis

Při zápisu lze v parametrech konfigurace použít zástupné symboly. Data se předávají ve tvaru `${var[:format]}`. Pokud formát není uveden, data se předají ve standardním formátu Go %v. Proměnné se před spuštěním pluginu nahradí odpovídající hodnotou. Pro složitější transformace dat lze navíc použít všechny funkce knihovny Go Template.

## Pluginy

### GPIO čtení zápis

Plugin `gpio` umožňuje přímý přístup k pinům GPIO (General Purpose Input/Output) v systémech Linux. Hodí se zejména pro Raspberry Pi a podobné jednodeskové počítače.

**Parametry**:

| Parametr | Typ    | Povinný | Popis                                       |
| -------- | ------ | ------- | ------------------------------------------- |
| function | string | ano     | Režim: `read` (vstup) nebo `write` (výstup) |
| pin      | int    | ano     | Číslo pinu (číslování BCM, např. GPIO 17)   |

**Jak to funguje**:

* **Čtení** (`function: read`): Čte stav pinu GPIO jako logickou hodnotu (`true` = HIGH, `false` = LOW)
* **Zápis** (`function: write`): Nastaví pin GPIO na HIGH (`true`) nebo LOW (`false`)

Číslování pinů se řídí schématem BCM (čísla Broadcom, nikoli fyzická pozice pinu). Rozložení pinů Raspberry Pi najdete např. na [pinout.xyz](https://pinout.xyz/).

**Příklad čtení**:

```yaml
source: gpio
function: read
pin: 17 # číslo pinu BCM
```

**Příklad zápisu**:

```yaml
source: gpio
function: write
pin: 27 # číslo pinu BCM
```

> **Poznámka**
>
> Tento plugin funguje pouze v systémech Linux s přístupem k `/dev/gpiomem`.

### Go čtení zápis

Plugin `go` používá interpret [Yaegi](https://github.com/traefik/yaegi) ke spouštění kódu v Go za běhu. Hodí se zejména pro typově bezpečné výpočty a složitou logiku zpracování dat.

#### Dostupné standardní knihovny Go

Následující balíčky Go jsou automaticky k dispozici a není třeba je importovat:

* `fmt` - Formátovaný vstup a výstup
* `math` - Matematické funkce
* `strings` - Práce s řetězci
* `time` - Funkce pro čas a datum

**Příklad čtení**:

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

**Příklad s časovými funkcemi**:

```yaml
source: go
script: |
  hour := time.Now().Hour()
  price := 50.0 // noční tarif
  if hour >= 9 && hour < 17 {
    price = 100.0 // cena tarifu v pracovní době
  }
  price // vrací 50.0 nebo 100.0 podle denní doby
```

**Příklad se zpracováním řetězců**:

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

Pokud se plugin `go` používá k zápisu, předá se zapisovaná hodnota skriptu jako proměnná:

**Příklad zápisu**:

```yaml
maxcurrent:
  source: go
  script: |
    fmt.Printf("Setting charge current: %d A\n", maxcurrent)
    // proměnná maxcurrent je k dispozici automaticky
```

#### Vstupní a výstupní transformace

Pluginy `go` a `js` podporují parametry `in` a `out`, pomocí kterých lze použít hodnoty z jiných zdrojů jako proměnné ve skriptu nebo předat výsledek skriptu dalším pluginům.

##### Vstupní transformace (`in`)

Parametr `in` vám umožňuje použít ve skriptu hodnoty z jiných zdrojů jako proměnné. Každá položka vyžaduje `name` (název proměnné ve skriptu), `type` (`bool`, `int`, `float`, `string`) a `config` (konfiguraci pluginu).

Tento příklad ukazuje podmíněnou logiku, které nelze dosáhnout jednoduchými operacemi pluginu calc:

```yaml
power:
  source: go
  script: |
    // výkon podle SoC a pohotovostního režimu
    power := 5000.0 // běžný
    if standby {
      power = 0.0 // v pohotovostním režimu bez zátěže
    } else if soc < 20.0 {
      power = 1000.0 // nízký
    }
    power // vrací 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
```

##### Výstupní transformace (`out`)

Parametr `out` předává výsledek skriptu dalším pluginům. Hodí se zejména v kontextu zápisu, např. když se má výsledek skriptu dále zpracovat přes MQTT nebo HTTP. Každá položka vyžaduje `name`, `type` a `config`, obdobně jako u `in`.

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

### HTTP čtení zápis

Plugin `http` provádí volání HTTP ke čtení nebo aktualizaci dat. Umí také číst datové struktury JSON nebo na nich provádět jednoduché transformace pomocí dotazů jq (např. pro REST API). Všechny možnosti najdete v [oficiální dokumentaci jq](https://jqlang.github.io/jq/manual/).

Metody ověřování jsou `basic`, `bearer` a `digest`. Názvy příslušných parametrů najdete [zde](https://github.com/evcc-io/evcc/blob/master/plugin/http_auth.go#L23).

#### Ověřování

Pro požadavky HTTP jsou k dispozici různé metody ověřování:

**Ověřování Basic**:

```yaml
auth:
  type: basic
  user: <username>
  password: <password>
```

**Token Bearer** (např. pro JWT):

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

**Ověřování Digest**:

```yaml
auth:
  type: digest
  user: <username>
  password: <password>
```

**Vlastní ověřování**:

Pro složitější scénáře ověřování lze vyvinout vlastní ověřovací pluginy. Ty se zapojují pomocí parametru `source`:

```yaml
auth:
  source: <plugin-name>
  user: <username>
  password: <password>
  # další parametry specifické pro plugin
```

Díky tomu lze integrovat zařízení se zvláštními požadavky na ověřování, aniž by bylo nutné upravovat celý kód pluginu HTTP.

> **Důležité**
>
> Dokumenty XML se interně automaticky převádějí do formátu JSON, který pak lze filtrovat pomocí jq stejně jako nativní odpověď JSON. Atributy dostanou předponu `attr`.

> **Tip**
>
> K testování dotazů jq se hodí online nástroje jako [jqplay.org](https://jqplay.org/). Pro testy regulárních výrazů je dobrou volbou [regex101.com](https://regex101.com/).

**Příklad čtení**:

```yaml
source: http
uri: https://volkszaehler/api/data/<uuid>.json?from=now
method: GET # výchozí metoda HTTP
headers:
  - content-type: application/json
auth: # ověřování Basic
  type: basic
  user: foo
  password: bar
insecure: false # true = důvěřovat self-signed certifikátům
jq: .data.tuples[0][1] # zpracování odpovědi JSON
scale: 0.001 # faktor použitý na výsledek, např. pro převod W na kW
cache: 60s # doba uchování odpovědi v mezipaměti
timeout: 10s # časový limit ve formátu golang duration, viz https://golang.org/pkg/time/#ParseDuration
```

```yaml
source: http
uri: http://charger/status
jq: .total_power > 10 # převede celé číslo JSON na logickou hodnotu
```

**Příklad zápisu**:

```yaml
body: %v # platí jen pro požadavky PUT nebo POST
```

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

**Dynamické URI**:

`uri` a `body` jsou šablony Go, které se vyhodnocují při každém požadavku. Kromě [funkcí sprig](https://masterminds.github.io/sprig/) jsou k dispozici `addDate` a `timeRound`. Díky tomu lze např. z API předpovědi vždy načíst klouzavé časové okno:

```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 čtení zápis

evcc obsahuje interpret JavaScriptu s knihovnou [Underscore.js](https://underscorejs.org), která je přímo dostupná přes `_.`, např. `_.random(0,5)`. Plugin `js` umí spouštět kód JavaScriptu pomocí parametru `script`. Velmi se hodí pro rychlé prototypování:

**Příklad čtení**:

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

Pokud se plugin `js` používá k zápisu, předá se zapisovaná hodnota skriptu jako proměnná:

**Příklad zápisu**:

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

Plugin `js` podporuje stejné [vstupní a výstupní transformace](#transformations) (`in`/`out`) jako plugin `go`.

### Modbus čtení zápis

Plugin `modbus` umí číst data z libovolného zařízení s podporou Modbus nebo ze střídače kompatibilního se SunSpec. Mnoho měřičů je již předkonfigurováno (viz [zařízení podporovaná MBMD](https://github.com/volkszaehler/mbmd#supported-devices)). Do registrů Modbus lze také zapisovat a integrovat tak další nabíječky.

**Příklad**:

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

Další podrobnosti najdete v [dokumentaci Modbus](/cs/reference/modbus).

### MQTT čtení zápis

Plugin `mqtt` umožňuje číst hodnoty z témat MQTT. Hodí se zejména pro měřiče, např. když už svá data poskytují přes MQTT. Příklad, jak přenést naměřená data z Modbusu do MQTT, najdete v [dokumentaci MBMD](https://github.com/volkszaehler/mbmd).

**Parametry (čtení)**:

| Parametr | Typ      | Povinný | Popis                                                          |
| -------- | -------- | ------- | -------------------------------------------------------------- |
| topic    | string   | ano     | Téma MQTT, ze kterého se čte                                   |
| timeout  | duration | ne      | Maximální stáří přijatých hodnot                               |
| scale    | float    | ne      | Faktor měřítka pro výsledek (např. 0,001 pro převod Wh na kWh) |

Plugin vrátí chybu, pokud během doby `timeout` nepřijde žádná nová hodnota. Pokud `timeout` není nastaven, plugin po první zprávě přijímá hodnoty libovolného stáří. Doporučujeme nastavit časový limit, aby se poznalo, kdy zdroj přestane poskytovat aktuální data.

Pro extrakci dat jsou k dispozici parametry pipeline popsané v části [Čtení](#reading) (`regex`, `jq`, `quote` atd.).

**Příklad čtení**:

```yaml
source: mqtt
topic: mbmd/sdm1-1/Power
timeout: 30s # nepřijímat hodnoty starší než timeout
scale: 0.001 # faktor použitý na výsledek, např. pro převod Wh na kWh
```

**Parametry (zápis)**:

| Parametr | Typ    | Povinný | Popis                                                                                |
| -------- | ------ | ------- | ------------------------------------------------------------------------------------ |
| topic    | string | ano     | Téma MQTT, do kterého se zapisuje                                                    |
| payload  | string | ne      | Šablona obsahu zprávy (pokud není nastavena, použije se hodnota ve výchozím formátu) |

Pro zápis se data předávají pomocí atributu `payload`. Pokud tento parametr v konfiguraci chybí, zapíše se hodnota ve výchozím formátu.

**Příklad zápisu**:

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

### Prometheus čtení

Plugin `prometheus` čte metriky z instance Prometheus pomocí dotazů PromQL. Hodí se, když jsou monitorovací data již k dispozici v Prometheu a mají se použít v evcc.

**Parametry**:

| Parametr | Typ      | Povinný | Popis                            |
| -------- | -------- | ------- | -------------------------------- |
| uri      | string   | ano     | URL serveru Prometheus           |
| query    | string   | ano     | Dotaz PromQL                     |
| timeout  | duration | ne      | Časový limit (výchozí: 2 minuty) |

**Podporované výsledky dotazu**:

* **Skalár**: Jedna číselná hodnota
* **Vektor**: Vektor s právě jednou položkou metriky

**Příklad**:

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

**Příklad s časovým rozsahem**:

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

Dotaz musí vrátit jedinou číselnou hodnotu. U vektorových výsledků musí být obsažena právě jedna metrika.

### Shellový skript čtení zápis

Plugin `script` spouští externí skripty ke čtení nebo aktualizaci dat. Plugin se hodí k napojení libovolných externích funkcí.

**Příklad čtení**:

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

**Příklad zápisu**:

```yaml
source: script
cmd: /home/user/my-script.sh ${enable:%b} # formátovat logickou hodnotu enable jako 0/1
timeout: 5s
```

### SMA/Speedwire čtení

Plugin `sma` poskytuje rozhraní k zařízením SMA, která podporují protokol Speedwire.

**Příklad čtení**:

```yaml
source: sma
uri: 192.168.4.51 # alternativa k serial
serial: 123456 # alternativa k uri
value: ActivePowerPlus # ID čtené hodnoty
password: "0000" # volitelné (výchozí: 0000)
interface: eth0 # volitelné
scale: 1 # volitelný faktor škálování hodnoty
```

Podporované hodnoty pro `value` najdete v diagnostickém výstupu příkazu `evcc meter` (s nakonfigurovanými zařízeními SMA typu `meter`).

Všechny možné hodnoty najdete jako konstanty [zde](https://gitlab.com/bboehmke/sunny/-/blob/master/values.go#L24) (pro `value` použijte název konstanty).

### Websocket čtení

Plugin `websocket` poskytuje naslouchací server WebSocket. Umí také číst nebo parsovat datové struktury JSON pomocí dotazů podobných jq. Lze jej použít např. k příjmu dat z push serveru Volkszähleru.

Pro extrakci dat jsou k dispozici parametry pipeline popsané v části [Čtení](#reading) (`regex`, `jq`, `quote` atd.).

**Příklad čtení**:

```yaml
source: http
uri: ws://<volkszaehler host:port>/socket
jq: .data | select(.uuid=="<uuid>") .tuples[0][1] # zpracování zprávy JSON
scale: 0.001 # faktor použitý na výsledek, např. pro převod Wh na kWh
timeout: 30s # chyba, pokud do 30 sekund nepřijde aktualizace
```

## Pomocné pluginy

### Calc čtení

Plugin `calc` umožňuje matematické zpracování více jednotlivých hodnot:

**Příklad čtení**:

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

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

Podporovány jsou základní aritmetické operace: sčítání (add), násobení (mul), dělení (div), změna znaménka (sign), absolutní hodnota (abs), minimum (min) a maximum (max).

Pomocí `scale: -1` u jedné z hodnot lze provést jednoduché odčítání, pomocí `scale: 0.001` dělení, např. pro převod Wh na kWh.

Pomocí `sign:` (každé kladné číslo se změní na +1, každé záporné na −1, 0 zůstane 0) lze přenést znaménka na jiné hodnoty (ve spojení s `mul`). Lze tak např. přenést „směr“ výkonu (export do sítě nebo odběr ze sítě) na proudy naměřené měřičem.

Pomocí `abs:` se vypočítá absolutní hodnota čísla.

Pomocí `min:` a `max:` se vypočítá minimum, resp. maximum.

Plugin `calc` se hodí např. pro:

* Sčítání hodnot výkonu z jednotlivých solárních stringů (sčítání)
* Výpočet zdánlivého výkonu z napětí a proudu (násobení)
* Spojení oddělených hodnot výkonu pro odběr ze sítě a export do sítě do jediné hodnoty se znaménkem (odčítání)
* Výpočet procentuální úrovně naplnění (dělení)
* Určení správného směru toku proudu (znaménko)
* Odstranění známých posunů (sčítání s pluginem `const`)

> **Tip**
>
> Konstantní pomocné hodnoty (např. pro posuny) lze jako operandy vytvořit pomocí pluginu `const`.

### Combined čtení

Plugin `combined` sloučí dvě logické hodnoty `Plugged` (připojeno) a `Charging` (nabíjí se) do stavu nabíjení A–F, se kterým evcc pracuje. Používá se např. u integrace OpenWB přes MQTT.

**Příklad čtení**:

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

### Const čtení

Plugin `const` vrací konstantní hodnotu. Hodí se např. k přičtení pevné korekce (posunu) k proměnné hodnotě ve spojení s pluginem `calc` nebo k simulaci naměřených a stavových hodnot pro testovací účely.

**Příklad čtení**:

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

### Error čtení

Plugin `error` vždy vrací [známou chybovou hodnotu](#known-errors). Hodí se k výslovnému označení neimplementovaného atributu jako nedostupného.

**Parametry**:

| Parametr | Typ    | Povinný | Popis                                                                |
| -------- | ------ | ------- | -------------------------------------------------------------------- |
| error    | string | ano     | Chybová hodnota (`ErrAsleep`, `ErrMustRetry` nebo `ErrNotAvailable`) |

**Příklad**:

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

### Convert zápis

Plugin `convert` převádí datové typy při zápisu. Používá se, když plugin očekává jiný datový typ, než jaký evcc poskytuje.

**Parametry**:

| Parametr | Typ    | Povinný | Popis                       |
| -------- | ------ | ------- | --------------------------- |
| convert  | string | ano     | Typ převodu                 |
| set      | config | ano     | Plugin pro zápis po převodu |

**Podporované převody**:

| Převod    | Popis                                         |
| --------- | --------------------------------------------- |
| float2int | Float64 → Int64 (desetinná místa se odříznou) |
| int2float | Int64 → Float64                               |
| int2bytes | Int64 → pole bajtů (Big Endian, 8 bajtů)      |
| bool2int  | Bool → Int64 (true=1, false=0)                |

**Příklad** (evcc poskytuje float, zařízení očekává 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
```

V tomto příkladu evcc převede hodnotu float jako `85.5` na `85` a teprve pak ji zapíše do registru Modbus.

### Delta zápis

Plugin `delta` převádí absolutní hodnoty na rozdílové (delta) hodnoty. Používá se u zařízení, která zapsané hodnoty přičítají k internímu součtu, místo aby je přímo nastavila.

**Parametry**:

| Parametr | Typ    | Povinný | Popis                                          |
| -------- | ------ | ------- | ---------------------------------------------- |
| get      | config | ne      | Plugin pro čtení aktuálního součtu ze zařízení |
| set      | config | ano     | Plugin pro zápis vypočteného rozdílu           |

**Jak to funguje**:

1. Přijme se vstupní hodnota (např. `1000`)
2. Pokud je nakonfigurován `get`, přečte se ze zařízení aktuální součet
3. Vypočte se rozdíl: `delta = new_value - current_total`
4. Rozdíl se zapíše pomocí pluginu `set`
5. Aktualizuje se interní stav

Bez parametru `get` plugin sleduje součet interně (začíná na 0). Při restartu evcc se synchronizace se zařízením ztratí. Proto doporučujeme nakonfigurovat `get`, pokud zařízení poskytuje čitelný registr s aktuálním součtem.

**Podporované datové typy**: `int64`, `float64`

**Případ použití**:

Některá tepelná čerpadla (např. Ochsner) mají registry, které místo absolutních hodnot očekávají rozdílové hodnoty. Při zápisu `500` zařízení přičte k interní hodnotě 500 W, místo aby ji nastavilo na 500 W. Pro zvýšení z 1000 W na 1500 W je nutné zapsat `+500`. Pro snížení se zapisují záporné hodnoty (např. `-300`).

**Příklad zápisu**:

```yaml
setmaxpower:
  source: delta
  get: # čtení aktuálního součtu ze zařízení
    source: modbus
    uri: 192.168.1.50:502
    id: 50
    register:
      address: 2012 # interní součtový registr zařízení
      type: input
      decode: uint16
  set: # zápis vypočtené delty
    source: modbus
    uri: 192.168.1.50:502
    id: 50
    register:
      address: 2201 # registr delty
      type: writeholding
      decode: uint16
```

**Příklad s pluginem watchdog**:

Plugin delta se často kombinuje s [pluginem watchdog](#watchdog), pokud zařízení vyžaduje pravidelné aktualizace:

```yaml
setmaxpower:
  source: watchdog
  timeout: 60s # opětovné odeslání každých 30 sekund
  set:
    source: delta # převod absolutních hodnot na delty
    get: # čtení aktuálního součtu
      source: modbus
      uri: 192.168.1.50:502
      id: 50
      register:
        address: 2012 # součtový registr
        type: input
        decode: uint16
    set: # zápis delty
      source: modbus
      uri: 192.168.1.50:502
      id: 50
      register:
        address: 2201 # registr delty
        type: writeholding
        decode: uint16
```

### Ignore zápis

Plugin `ignore` při zápisu potlačuje konkrétní chybová hlášení. Používá se, když zařízení vrací neškodné chyby, které lze ignorovat.

**Parametry**:

| Parametr | Typ    | Povinný | Popis                                      |
| -------- | ------ | ------- | ------------------------------------------ |
| error    | string | ano     | Začátek textu chyby, která se má ignorovat |
| set      | config | ano     | Plugin pro zápis                           |

**Jak to funguje**:

1. Spustí se vnořený plugin `set`
2. Pokud nastane chyba, zkontroluje se, zda chybové hlášení začíná řetězcem `error`
3. Pokud ano, chyba se ignoruje a vrátí se úspěch
4. Pokud ne, chyba se normálně předá dál

**Podporované datové typy**: `int64`, `float64`, `bool`, `[]byte`

**Příklad**:

```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
```

V tomto příkladu zařízení vrací neškodnou chybu Modbus, která se ignoruje.

### IfElse zápis

Plugin `ifelse` provádí podmíněné zápisové operace se dvěma větvemi. Podle vstupní hodnoty se spustí buď plugin `if`, nebo plugin `else`.

**Parametry**:

| Parametr | Typ    | Povinný | Popis                                               |
| -------- | ------ | ------- | --------------------------------------------------- |
| if       | config | ano     | Plugin, který se spustí, když je podmínka splněna   |
| else     | config | ano     | Plugin, který se spustí, když podmínka splněna není |

**Jak to funguje**:

* U hodnot `bool`: `true` spustí `if`, `false` spustí `else`
* U hodnot `int64`: `> 0` spustí `if`, jinak se spustí `else`

**Podporované datové typy**: `int64`, `bool`

**Příklad** (různé koncové body pro zapnutí a vypnutí):

```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 čtení zápis

Plugin `map` překládá celočíselné hodnoty na jiné celočíselné hodnoty pomocí převodní tabulky. Často se používá k převodu hodnot specifických pro zařízení na standardní hodnoty evcc a naopak.

**Parametry**:

| Parametr | Typ              | Povinný | Popis                                        |
| -------- | ---------------- | ------- | -------------------------------------------- |
| values   | map\[int64]int64 | ano     | Převodní tabulka s přiřazením vstup → výstup |
| get      | config           | ne      | Plugin pro čtení (nutný jen při čtení)       |
| set      | config           | ne      | Plugin pro zápis (nutný jen při zápisu)      |

**Jak to funguje**:

**Při čtení**:

1. Plugin `get` vrátí hodnotu (např. `0`)
2. Hodnota se vyhledá v tabulce `values`
3. Vrátí se přiřazená hodnota (např. `0` → `2`)

**Při zápisu**:

1. Přijme se vstupní hodnota (např. `3`)
2. Hodnota se vyhledá v tabulce `values`
3. Přiřazená hodnota se předá pluginu `set` (např. `3` → `6`)

Pokud se v tabulce nenajde odpovídající hodnota, nastane chyba.

**Podporované datové typy**: `int64` (pouze celočíselné hodnoty)

**Příklad čtení** (hodnota zařízení → evcc):

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

**Příklad zápisu** (evcc → hodnota zařízení):

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

### Meter čtení

Plugin `meter` umožňuje použít jiný měřič jako zdroj dat. Hodí se, když chcete jedno existující zařízení použít pro více měření nebo když pro různé atributy potřebujete různé metody zařízení.

Sekce `config` obsahuje úplnou konfiguraci šablony vkládaného měřiče. Parametr `method` určuje, která hodnota se čte: `power`, `energy`, `returnenergy` nebo `soc`.

**Příklad čtení**:

```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
```

V tomto příkladu slouží zařízení Shelly 1PM jako zdroj dat pro výkon a energii domácí baterie, zatímco stav nabití se načítá přes MQTT.

### Sequence zápis

Plugin `sequence` provádí více zápisových operací postupně. Všechny vnořené pluginy dostanou stejnou vstupní hodnotu a spustí se v definovaném pořadí. Při chybě se provádění okamžitě zastaví.

**Parametry**:

| Parametr | Typ       | Povinný | Popis                              |
| -------- | --------- | ------- | ---------------------------------- |
| set      | \[config] | ano     | Pole konfigurací vnořených pluginů |

**Jak to funguje**:

1. Přijme se vstupní hodnota
2. Každý plugin v seznamu `set` se postupně zavolá s touto hodnotou
3. Při chybě se sekvence přeruší a vrátí se chyba
4. Úspěšné provedení znamená, že všechny pluginy proběhly úspěšně

**Podporované datové typy**: `int64`, `float64`, `bool`

**Případy použití**:

* Postupné provedení více volání HTTP
* Kombinace s pluginem `sleep` pro časově zpožděné akce
* Zápis do více registrů Modbus najednou
* Předání hodnot více cílům

**Příklad 1: Více volání HTTP**

```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"}'
```

**Příklad 2: Kombinace s pluginem 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}"
```

**Průběh pro `batterymode: 1` (normal)**:

1. Vnější `sequence` přijme hodnotu `1`
2. První krok: `switch` zkontroluje hodnotu `1`
   * Případ `1` odpovídá → volání HTTP na `/api/mode` s tělem `automatic`
3. Druhý krok: `mqtt` odešle zprávu do `home/battery/status` s obsahem `mode_1`
4. Hotovo

**Průběh pro `batterymode: 3` (charge)**:

1. Vnější `sequence` přijme hodnotu `3`

2. První krok: `switch` zkontroluje hodnotu `3`

   * Případ `3` odpovídá → spustí se vnitřní `sequence`:

     * Čeká se 1 sekundu (`sleep`)
     * Volání HTTP na `/api/charge` s tělem `5000`

3. Druhý krok: `mqtt` odešle zprávu do `home/battery/status` s obsahem `mode_3`

4. Hotovo

Hodnota prochází všemi pluginy, přičemž `switch` podle hodnoty provádí různé akce a `mqtt` na konci vždy odešle oznámení.

### Sleep zápis

Plugin `sleep` přidává zpoždění. Obvykle se používá v rámci pluginu `sequence` k vytvoření časových odstupů mezi akcemi.

**Parametry**:

| Parametr | Typ      | Povinný | Popis                                                |
| -------- | -------- | ------- | ---------------------------------------------------- |
| duration | duration | ano     | Doba čekání (např. `1s`, `500ms`, `0s` bez zpoždění) |

**Příklad**:

```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 zápis

Plugin `switch` provádí podmíněné zápisové operace, podobně jako příkaz switch/case v programovacích jazycích. Podle vstupní hodnoty se provede odpovídající akce.

**Parametry**:

| Parametr | Typ     | Povinný | Popis                                         |
| -------- | ------- | ------- | --------------------------------------------- |
| switch   | \[case] | ano     | Pole případů s konfigurací `case` a `set`     |
| default  | config  | ne      | Záložní plugin, pokud žádný případ neodpovídá |

**Jak to funguje**:

1. Vstupní hodnota se porovná s hodnotami `case`
2. Při shodě se spustí odpovídající plugin `set`
3. Pokud žádný případ neodpovídá a je definován `default`, spustí se plugin default
4. Pokud žádný případ neodpovídá a `default` definován není, nastane chyba

**Podporované datové typy**: `int64` (pouze celočíselné hodnoty)

**Příklad**:

```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 čtení

Plugin `valid` umožňuje poskytovat hodnoty pluginu na základě logické validace. Odděluje platnost hodnoty od jejího skutečného obsahu. Pokud validace vrátí `false`, hodnota se považuje za nedostupnou.

Hodí se zejména pro integrace jako ioBroker, které poskytují platnost a hodnotu odděleně.

**Příklad čtení**:

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

V tomto příkladu se hodnota použije jen tehdy, když téma `valid` vrátí `true`. Pokud vrátí `false`, hodnota se označí jako nedostupná.

### Watchdog zápis

Plugin `watchdog` je obalový plugin, který automaticky opakuje zápisové operace v pravidelných intervalech. Některá zařízení (např. domácí baterie, střídače) očekávají, že se řídicí příkazy budou pravidelně opakovat, aby zůstaly aktivní. Plugin watchdog sleduje zápisové operace a automaticky je opakuje v intervalu rovném polovině nastaveného časového limitu.

**Parametry**:

| Parametr | Typ                 | Povinný | Popis                                                                     |
| -------- | ------------------- | ------- | ------------------------------------------------------------------------- |
| timeout  | duration            | ano     | Časový interval pro opakování (hodnota se znovu zapíše každých timeout/2) |
| reset    | string \| \[string] | ne      | Hodnota či hodnoty, při kterých se opakování zastaví                      |
| initial  | string              | ne      | Hodnota, která se jednou zapíše při startu                                |
| defer    | bool                | ne      | Odkládá aktualizace, místo aby je provedl okamžitě (výchozí: false)       |
| set      | config              | ano     | Vnořený plugin pro samotnou zápisovou operaci                             |

**Jak to funguje**:

1. **Start**: Pokud je nakonfigurován `initial`, zapíše se tato hodnota jednou při startu
2. **Zápis**: Při zápisu hodnoty plugin zkontroluje, zda je v seznamu `reset`
3. **Watchdog aktivní**: Pokud hodnota **není** v `reset`, watchdog se spustí a automaticky hodnotu znovu zapisuje v polovičním intervalu `timeout`
4. **Watchdog se zastaví**: Pokud je hodnota v `reset`, žádná opakování se neprovádějí

**Parametr reset**:

* Definuje hodnoty, při kterých se watchdog zastaví
* Může to být jedna hodnota (`reset: 0`) nebo více hodnot (`reset: [0, 1]`)
* Obvykle se používá pro „bezpečné“ nebo „výchozí“ stavy, které nepotřebují průběžné opakování

**Parametr initial**:

* Volitelná hodnota, která se zapíše jednou při startu pluginu
* Hodí se k nastavení definovaného počátečního stavu
* Provede se před všemi ostatními zápisovými operacemi

**Parametr defer**:

* Zajišťuje dodržení časových odstupů mezi aktualizacemi
* Watchdog se během zpoždění zastaví a po jeho uplynutí se znovu spustí s novou hodnotou
* Hodí se, když zařízení vyžadují minimální dobu čekání mezi změnami režimu
* Zpoždění se počítá podle času od poslední aktualizace
* Hodnoty reset se vždy zapíší okamžitě (bez zpoždění)

**Podporované datové typy**: `int64`, `float64`, `bool`

**Příklad zápisu**:

```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
```

V tomto příkladu se hodnoty automaticky opakují každých 30 s, kromě případu, kdy se zapíše hodnota `0`.

**Příklad průběhu** s `timeout: 60s`, `reset: 0`:

* Plugin se spustí
* Zápis hodnoty `100` → watchdog **běží**, hodnota se opakuje každých 30 s
* Po 2 minutách: hodnota `100` byla zapsána 5krát (v 0, 30, 60, 90 a 120 s)
* Zápis hodnoty `200` → watchdog **dál běží**, nyní s novou hodnotou `200` každých 30 s
* Zápis hodnoty `0` → watchdog se **zastaví** (protože `0` je definována v `reset`)
* Žádná další opakování, dokud se nezapíše nová hodnota jiná než `0`

**Příklad s řízením baterie** (`batterymode` používá hodnoty 1=normal, 2=hold, 3=charge, 4=holdcharge, 5=discharge):

```yaml
batterymode:
  source: watchdog
  timeout: 60s
  reset: 1 # zastavit opakování v běžném provozu
  set:
    source: switch
    switch:
      - case: 1 # normal
        set:
          source: modbus
          # ... konfigurace Modbus pro běžný provoz
      - case: 2 # hold
        set:
          source: modbus
          # ... konfigurace Modbus pro režim držení
      - case: 3 # charge
        set:
          source: modbus
          # ... konfigurace Modbus pro režim nabíjení
```