# Plugins

> Les plugins lisent et écrivent des valeurs individuelles via Modbus, HTTP, MQTT ou des scripts et servent de briques de base aux appareils personnalisés.

Les plugins lisent et écrivent des valeurs individuelles sur un appareil, p. ex. une mesure de puissance, un courant cible ou une commande d’activation. Les modèles d’appareils fournis reposent sur eux, et ils sont référencés directement lorsque vous créez un [appareil personnalisé](/fr/user-defined-devices) de `type: custom`.

Les plugins peuvent être utilisés pour les catégories suivantes :

* `meter` : [Solaire, batterie domestique, réseau, compteurs](/fr/meters)
* `charger` : [Chargeurs](/fr/chargers), [Prises commutables](/fr/smartswitches), [Pompes à chaleur, chauffages électriques](/fr/heating)
* `vehicle` : [Véhicules](/fr/vehicles)
* `tariff` : [Tarifs, prévisions](/fr/tariffs)
* `circuit` : [Gestion de la charge](/fr/features/loadmanagement)

Les [notifications](/fr/notifications) utilisent également des plugins pour envoyer des événements de cycle de vie.

### Liste des plugins

* [Plugin GPIO](#gpio) - Plugin d’accès direct aux broches GPIO (Linux uniquement).
* [Plugin Go](#go) - Plugin qui fournit ou reçoit des valeurs via un script Go.
* [Plugin HTTP](#http) - Plugin qui communique avec les appareils via une API HTTP.
* [Plugin JavaScript](#javascript) - Plugin qui fournit ou reçoit des valeurs via un script JavaScript.
* [Plugin Modbus](#modbus) - Plugin de lecture d’un appareil compatible Modbus.
* [Plugin MQTT](#mqtt) - Plugin de communication indirecte avec des appareils compatibles MQTT via MQTT.
* [Plugin Prometheus](#prometheus) - Plugin de lecture de métriques depuis Prometheus à l’aide de PromQL.
* [Plugin Shell](#shell) - Plugin qui peut exécuter un script shell pour extraire des données ou recevoir des données à écrire.
* [Plugin SMA/Speedwire](#speedwire) - Plugin dédié aux appareils SMA qui communiquent avec le protocole Speedwire.
* [Plugin Websocket](#websocket) - Plugin de réception des données d’un appareil via son propre serveur web. Utilisable uniquement pour la lecture de données.

### Liste des plugins auxiliaires

* [Plugin Calc](#calc) - Méta-plugin pour combiner arithmétiquement les sorties d’autres plugins.
* [Plugin Combined](#combined) - Méta-plugin dédié à `charger` pour combiner les valeurs d’état booléennes de connexion (*plugged*) et de charge (*charging*) en un seul statut de charge.
* [Plugin Const](#const) - Plugin spécial qui renvoie simplement une valeur constante.
* [Plugin Error](#error) - Plugin spécial qui renvoie une valeur d’erreur connue.
* [Plugin Convert](#convert) - Méta-plugin de conversion de type de données lors de l’écriture (p. ex. float en int).
* [Plugin Delta](#delta) - Méta-plugin de conversion de valeurs absolues en valeurs delta (incréments) lors de l’écriture.
* [Plugin Ignore](#ignore) - Méta-plugin pour supprimer certains messages d’erreur.
* [Plugin IfElse](#ifelse) - Méta-plugin pour des opérations d’écriture conditionnelles à deux branches (if/else).
* [Plugin Map](#map) - Méta-plugin de traduction de valeurs entières (p. ex. modes propres à l’appareil en modes evcc).
* [Plugin Meter](#meter-plugin) - Plugin pour utiliser un autre compteur comme source de données.
* [Plugin Sequence](#sequence) - Méta-plugin pour l’exécution successive de plusieurs opérations d’écriture.
* [Plugin Sleep](#sleep) - Plugin auxiliaire pour temporiser des actions (généralement utilisé avec Sequence).
* [Plugin Switch](#switch) - Méta-plugin pour des opérations d’écriture conditionnelles selon les valeurs d’entrée (comme switch/case).
* [Plugin Valid](#valid) - Méta-plugin qui ne transmet une valeur que si un indicateur booléen la déclare valide.
* [Plugin Watchdog](#watchdog) - Méta-plugin pour répéter automatiquement des opérations d’écriture à intervalles réguliers.

## Syntaxe

Un plugin se rattache à un attribut d’un appareil. Le nom de l’attribut (p. ex. `power`, `enable`, `soc`) détermine son rôle et `source` choisit le type de plugin. Les autres clés sont des paramètres propres au plugin.

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

Chaque plugin s’utilise soit en contexte de **lecture**, soit en contexte d’**écriture**. Certains paramètres n’ont de sens que dans l’un des deux. Pour la liste complète des attributs des compteurs, chargeurs, véhicules et tarifs, voir [appareils personnalisés](/fr/user-defined-devices).

### Lecture

Lors de la lecture de données via un plugin, il est possible d’utiliser des *pipelines*. Ils permettent d’extraire finement les données de la sortie du plugin. Il est ainsi possible de traiter des structures de données complexes comme JSON ou XML et d’en filtrer les informations nécessaires. Les paramètres possibles pour l’extraction de données sont :

* `regex` : une expression régulière pour extraire des valeurs du texte reçu.
* `jq` : une expression [jq](https://jqlang.github.io/jq/) pour extraire des valeurs de structures JSON. La syntaxe complète et les possibilités sont décrites dans la documentation de jq.
* `quote` : valeur booléenne qui place les données d’entrée entre guillemets avant de les transmettre à jq. jq peut ainsi traiter des chaînes sans guillemets (p. ex. issues de MQTT). Pour une valeur MQTT comme `Charging`, vous pouvez utiliser `quote: true` et `jq: '. == "Charging"'`.
* `unpack` : convertit des valeurs depuis d’autres représentations numériques, p. ex. `hex`.
* `decode` : décode des formats binaires comme `uint32`, `float32`, etc.

#### Valeurs d’erreur connues

Les plugins [HTTP](#http), [MQTT](#mqtt) et [Websocket](#websocket) peuvent renvoyer des chaînes d’erreur spéciales. evcc les reconnaît et les convertit en codes d’erreur internes au lieu de les traiter comme des données. C’est utile p. ex. pour des intégrations de véhicules personnalisées dont la source de données connaît l’état du véhicule.

* `ErrAsleep` : le véhicule est en veille. evcc peut décider de réveiller le véhicule et de réessayer.
* `ErrMustRetry` : l’opération doit être répétée (p. ex. en raison d’une limitation du nombre de requêtes).
* `ErrNotAvailable` : la valeur n’est pas disponible. evcc la traite comme une erreur permanente jusqu’au prochain redémarrage.

Si un plugin renvoie la chaîne `ErrAsleep` comme réponse, evcc génère l’erreur interne correspondante. Le [plugin Error](#error) utilise le même mécanisme pour renvoyer une valeur d’erreur fixe comme constante.

### Écriture

En écriture, les paramètres de configuration peuvent contenir des espaces réservés. Les données sont fournies sous la forme `${var[:format]}`. Si le format n’est pas précisé, les données sont fournies dans le format Go standard %v. Les variables sont remplacées par la valeur correspondante avant l’exécution du plugin. De plus, toutes les fonctions de la bibliothèque Go Template peuvent être utilisées pour des transformations de données plus complexes.

## Plugins

### GPIO lecture écriture

Le plugin `gpio` permet un accès direct aux broches GPIO (General Purpose Input/Output) sur les systèmes Linux. Il convient particulièrement au Raspberry Pi et aux ordinateurs monocartes similaires.

**Paramètres** :

| Paramètre | Type   | Obligatoire | Description                                         |
| --------- | ------ | ----------- | --------------------------------------------------- |
| function  | string | oui         | Mode : `read` (entrée) ou `write` (sortie)          |
| pin       | int    | oui         | Numéro de broche (numérotation BCM, p. ex. GPIO 17) |

**Fonctionnement** :

* **Lecture** (`function: read`) : lit l’état d’une broche GPIO sous forme de valeur booléenne (`true` = HIGH, `false` = LOW)
* **Écriture** (`function: write`) : met une broche GPIO à HIGH (`true`) ou LOW (`false`)

La numérotation des broches suit le schéma BCM (numéros Broadcom, pas la position physique de la broche). Pour le brochage d’un Raspberry Pi, voir p. ex. [pinout.xyz](https://pinout.xyz/).

**Exemple de lecture** :

```yaml
source: gpio
function: read
pin: 17 # numéro de broche BCM
```

**Exemple d’écriture** :

```yaml
source: gpio
function: write
pin: 27 # numéro de broche BCM
```

> **Note**
>
> Ce plugin ne fonctionne que sur les systèmes Linux ayant accès à `/dev/gpiomem`.

### Go lecture écriture

Le plugin `go` utilise l’interpréteur [Yaegi](https://github.com/traefik/yaegi) pour interpréter du code Go à l’exécution. Il est particulièrement utile pour des calculs à typage sûr et une logique de traitement de données complexe.

#### Paquets standard Go disponibles

Les paquets Go suivants sont disponibles automatiquement et n’ont pas besoin d’être importés :

* `fmt` - Entrées/sorties formatées
* `math` - Fonctions mathématiques
* `strings` - Manipulation de chaînes
* `time` - Fonctions de date et d’heure

**Exemple de lecture** :

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

**Exemple avec des fonctions de temps** :

```yaml
source: go
script: |
  hour := time.Now().Hour()
  price := 50.0 // tarif de nuit
  if hour >= 9 && hour < 17 {
    price = 100.0 // prix du tarif pendant les heures de bureau
  }
  price // renvoie 50.0 ou 100.0 selon l’heure de la journée
```

**Exemple avec traitement de chaînes** :

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

Lorsque le plugin `go` est utilisé en écriture, la valeur à écrire est transmise au script sous forme de variable :

**Exemple d’écriture** :

```yaml
maxcurrent:
  source: go
  script: |
    fmt.Printf("Setting charge current: %d A\n", maxcurrent)
    // la variable maxcurrent est automatiquement disponible
```

#### Transformations d’entrée et de sortie

Les plugins `go` et `js` prennent en charge les paramètres `in` et `out` pour utiliser des valeurs d’autres sources comme variables dans le script ou pour transmettre le résultat du script à d’autres plugins.

##### Transformations d’entrée (`in`)

Le paramètre `in` vous permet d’utiliser des valeurs d’autres sources comme variables dans votre script. Chaque entrée nécessite `name` (nom de la variable dans le script), `type` (`bool`, `int`, `float`, `string`) et `config` (configuration du plugin).

Cet exemple montre une logique conditionnelle impossible à réaliser avec de simples opérations Calc :

```yaml
power:
  source: go
  script: |
    // puissance selon le niveau de charge et la veille
    power := 5000.0 // normale
    if standby {
      power = 0.0 // aucune consommation en veille
    } else if soc < 20.0 {
      power = 1000.0 // faible
    }
    power // renvoie 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
```

##### Transformations de sortie (`out`)

Le paramètre `out` transmet le résultat du script à d’autres plugins. C’est particulièrement utile en contexte d’écriture, p. ex. lorsque le résultat du script doit être traité ensuite via MQTT ou HTTP. Chaque élément nécessite `name`, `type` et `config`, comme pour `in`.

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

### HTTP lecture écriture

Le plugin `http` effectue des appels HTTP pour lire ou mettre à jour des données. Il permet aussi de lire des structures de données JSON ou de leur appliquer des transformations simples via des requêtes jq (p. ex. pour des API REST). Toutes les fonctionnalités sont décrites dans la [documentation officielle de jq](https://jqlang.github.io/jq/manual/).

Les méthodes d’authentification sont `basic`, `bearer` et `digest`. Les noms des paramètres correspondants se trouvent [ici](https://github.com/evcc-io/evcc/blob/master/plugin/http_auth.go#L23).

#### Authentification

Plusieurs méthodes d’authentification sont disponibles pour les requêtes HTTP :

**Authentification Basic** :

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

**Jeton Bearer** (p. ex. pour JWT) :

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

**Authentification Digest** :

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

**Authentification personnalisée** :

Pour des scénarios d’authentification plus complexes, il est possible de développer des plugins d’authentification personnalisés. Ils sont intégrés via le paramètre `source` :

```yaml
auth:
  source: <plugin-name>
  user: <username>
  password: <password>
  # autres paramètres spécifiques au plugin
```

Cela permet d’intégrer des appareils ayant des exigences d’authentification particulières sans modifier l’ensemble du code du plugin HTTP.

> **Important**
>
> Les documents XML sont automatiquement convertis en interne au format JSON, qui peut ensuite être filtré avec jq comme une réponse JSON native. Les attributs reçoivent le préfixe `attr`.

> **Astuce**
>
> Pour tester des requêtes jq, des outils en ligne comme [jqplay.org](https://jqplay.org/) sont utiles. Pour tester des expressions régulières, [regex101.com](https://regex101.com/) est une bonne option.

**Exemple de lecture** :

```yaml
source: http
uri: https://volkszaehler/api/data/<uuid>.json?from=now
method: GET # méthode HTTP par défaut
headers:
  - content-type: application/json
auth: # authentification basique
  type: basic
  user: foo
  password: bar
insecure: false # true pour faire confiance aux certificats auto-signés
jq: .data.tuples[0][1] # analyse le JSON de la réponse
scale: 0.001 # facteur appliqué au résultat, p. ex. pour convertir des W en kW
cache: 60s # durée de mise en cache de la réponse
timeout: 10s # délai d’expiration au format de durée Go, voir https://golang.org/pkg/time/#ParseDuration
```

```yaml
source: http
uri: http://charger/status
jq: .total_power > 10 # convertit un entier JSON en valeur booléenne
```

**Exemple d’écriture** :

```yaml
body: %v # uniquement pour les requêtes PUT ou POST
```

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

**URI dynamiques** :

`uri` et `body` sont des modèles Go évalués à chaque requête. En plus des [fonctions sprig](https://masterminds.github.io/sprig/), `addDate` et `timeRound` sont disponibles. Cela permet p. ex. d’interroger une API de prévisions sur une plage de dates glissante :

```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 lecture écriture

evcc intègre un interpréteur JavaScript avec la bibliothèque [Underscore.js](https://underscorejs.org), directement accessible via `_.`, p. ex. `_.random(0,5)`. Le plugin `js` peut exécuter du code JavaScript via le paramètre `script`. C’est très pratique pour le prototypage rapide :

**Exemple de lecture** :

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

Lorsque le plugin `js` est utilisé en écriture, la valeur à écrire est transmise au script sous forme de variable :

**Exemple d’écriture** :

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

Le plugin `js` prend en charge les mêmes [transformations d’entrée et de sortie](#transformations) (`in`/`out`) que le plugin `go`.

### Modbus lecture écriture

Le plugin `modbus` peut lire des données depuis n’importe quel appareil compatible Modbus ou onduleur compatible SunSpec. De nombreux compteurs électriques sont déjà préconfigurés (voir [appareils pris en charge par MBMD](https://github.com/volkszaehler/mbmd#supported-devices)). Il est aussi possible d’écrire dans des registres Modbus pour intégrer d’autres chargeurs.

**Exemple** :

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

Voir la [documentation Modbus](/fr/reference/modbus) pour plus de détails.

### MQTT lecture écriture

Le plugin `mqtt` permet de lire des valeurs via des topics MQTT. C’est particulièrement utile pour les compteurs électriques, p. ex. lorsqu’ils fournissent déjà leurs données via MQTT. Voir la [documentation MBMD](https://github.com/volkszaehler/mbmd) pour un exemple montrant comment envoyer des mesures Modbus vers MQTT.

**Paramètres (lecture)** :

| Paramètre | Type     | Obligatoire | Description                                                                   |
| --------- | -------- | ----------- | ----------------------------------------------------------------------------- |
| topic     | string   | oui         | Topic MQTT à lire                                                             |
| timeout   | duration | non         | Âge maximal des valeurs reçues                                                |
| scale     | float    | non         | Facteur d’échelle du résultat (p. ex. 0,001 pour une conversion de Wh en kWh) |

Le plugin renvoie une erreur si aucune nouvelle valeur n’a été reçue pendant la durée `timeout`. Si `timeout` n’est pas défini, les valeurs de tout âge sont acceptées dès que le premier message a été reçu. Il est recommandé de définir un délai d’expiration pour détecter quand la source ne fournit plus de données à jour.

Pour l’extraction de données, les paramètres de pipeline décrits dans la section [Lecture](#reading) sont disponibles (`regex`, `jq`, `quote`, etc.).

**Exemple de lecture** :

```yaml
source: mqtt
topic: mbmd/sdm1-1/Power
timeout: 30s # refuse les valeurs plus anciennes que timeout
scale: 0.001 # facteur appliqué au résultat, p. ex. pour convertir des Wh en kWh
```

**Paramètres (écriture)** :

| Paramètre | Type   | Obligatoire | Description                                                                   |
| --------- | ------ | ----------- | ----------------------------------------------------------------------------- |
| topic     | string | oui         | Topic MQTT dans lequel écrire                                                 |
| payload   | string | non         | Modèle de charge utile (utilise la valeur au format par défaut si non défini) |

En écriture, le paramètre `payload` définit les données envoyées. Si ce paramètre est absent de la configuration, la valeur est écrite au format par défaut.

**Exemple d’écriture** :

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

### Prometheus lecture

Le plugin `prometheus` lit des métriques depuis une instance Prometheus à l’aide de requêtes PromQL. C’est utile lorsque des données de supervision sont déjà disponibles dans Prometheus et doivent être utilisées dans evcc.

**Paramètres** :

| Paramètre | Type     | Obligatoire | Description                                 |
| --------- | -------- | ----------- | ------------------------------------------- |
| uri       | string   | oui         | URL du serveur Prometheus                   |
| query     | string   | oui         | Requête PromQL                              |
| timeout   | duration | non         | Délai d’expiration (par défaut : 2 minutes) |

**Résultats de requête pris en charge** :

* **Scalaire** : une valeur numérique unique
* **Vecteur** : un vecteur avec exactement une entrée de métrique

**Exemple** :

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

**Exemple avec plage temporelle** :

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

La requête doit renvoyer une valeur numérique unique. Un résultat vectoriel doit contenir exactement une métrique.

### Script shell lecture écriture

Le plugin `script` exécute des scripts externes pour lire ou mettre à jour des données. Il permet d’intégrer toutes sortes de fonctionnalités externes.

**Exemple de lecture** :

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

**Exemple d’écriture** :

```yaml
source: script
cmd: /home/user/my-script.sh ${enable:%b} # formate le booléen enable en 0/1
timeout: 5s
```

### SMA/Speedwire lecture

Le plugin `sma` fournit une interface vers les appareils SMA qui prennent en charge le protocole Speedwire.

**Exemple de lecture** :

```yaml
source: sma
uri: 192.168.4.51 # alternative à serial
serial: 123456 # alternative à uri
value: ActivePowerPlus # ID de la valeur à lire
password: "0000" # facultatif (par défaut : 0000)
interface: eth0 # facultatif
scale: 1 # facteur d’échelle facultatif pour la valeur
```

Les valeurs prises en charge pour `value` figurent dans la sortie de diagnostic de la commande `evcc meter` (avec des appareils SMA configurés comme `meter`).

Toutes les valeurs possibles sont listées comme constantes [ici](https://gitlab.com/bboehmke/sunny/-/blob/master/values.go#L24) (utilisez le nom de la constante pour `value`).

### Websocket lecture

Le plugin `websocket` fournit un écouteur WebSocket. Il permet aussi de lire ou d’analyser des structures de données JSON via des requêtes de type jq. Il peut servir p. ex. à recevoir des données du serveur push de Volkszähler.

Pour l’extraction de données, les paramètres de pipeline décrits dans la section [Lecture](#reading) sont disponibles (`regex`, `jq`, `quote`, etc.).

**Exemple de lecture** :

```yaml
source: http
uri: ws://<volkszaehler host:port>/socket
jq: .data | select(.uuid=="<uuid>") .tuples[0][1] # analyse le JSON du message
scale: 0.001 # facteur appliqué au résultat, p. ex. pour convertir des Wh en kWh
timeout: 30s # erreur si aucune mise à jour n’est reçue pendant 30 secondes
```

## Plugins auxiliaires

### Calc lecture

Le plugin `calc` permet le traitement mathématique de plusieurs valeurs individuelles :

**Exemple de lecture** :

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

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

Les opérations arithmétiques de base addition (add), multiplication (mul), division (div), inversion de signe (sign), valeur absolue (abs), minimum (min) et maximum (max) sont prises en charge comme opérandes.

Avec `scale: -1` sur l’une des valeurs, il est possible d’effectuer une simple soustraction, avec `scale: 0.001` une division, p. ex. pour convertir des Wh en kWh.

Avec `sign:` (tout nombre positif devient +1, tout nombre négatif devient -1, 0 reste 0), les signes peuvent être reportés sur d’autres valeurs (en combinaison avec `mul`). Cela permet p. ex. de reporter le sens de la puissance (export ou import) sur les courants mesurés par les compteurs.

`abs:` calcule la valeur absolue d’un nombre.

`min:` et `max:` renvoient respectivement la plus petite et la plus grande des valeurs.

Le plugin `calc` est utile p. ex. pour :

* Additionner les puissances de différentes chaînes de modules solaires (addition)
* Calculer la puissance apparente à partir de la tension et du courant (multiplication)
* Combiner des puissances séparées d’import et d’export en une seule valeur signée (soustraction).
* Calculer des niveaux de remplissage en pourcentage (division)
* Déterminer le bon sens du flux de courant (sign)
* Éliminer des décalages connus (addition avec le plugin `const`)

> **Astuce**
>
> Des valeurs auxiliaires constantes (p. ex. pour des décalages) peuvent être générées comme opérandes avec le plugin `const`.

### Combined lecture

Le plugin d’état `combined` sert à combiner les états booléens `Plugged` (connecté) et `Charging` (en charge) en un statut de charge (A à F) compatible avec evcc. Il est utilisé p. ex. avec une intégration MQTT OpenWB.

**Exemple de lecture** :

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

### Const lecture

Le plugin `const` renvoie une valeur constante. Il sert p. ex. à appliquer des corrections fixes (décalages) à une valeur variable en combinaison avec le plugin `calc`, ou pour simuler des valeurs de mesure et d’état à des fins de test.

**Exemple de lecture** :

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

### Error lecture

Le plugin `error` renvoie toujours une [valeur d’erreur connue](#known-errors). Il sert à marquer explicitement un attribut non implémenté comme non disponible.

**Paramètres** :

| Paramètre | Type   | Obligatoire | Description                                                        |
| --------- | ------ | ----------- | ------------------------------------------------------------------ |
| error     | string | oui         | Valeur d’erreur (`ErrAsleep`, `ErrMustRetry` ou `ErrNotAvailable`) |

**Exemple** :

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

### Convert écriture

Le plugin `convert` convertit des types de données lors de l’écriture. Il est utilisé lorsqu’un plugin attend un autre type de données que celui fourni par evcc.

**Paramètres** :

| Paramètre | Type   | Obligatoire | Description                           |
| --------- | ------ | ----------- | ------------------------------------- |
| convert   | string | oui         | Type de conversion                    |
| set       | config | oui         | Plugin d’écriture après la conversion |

**Conversions prises en charge** :

| Conversion | Description                                     |
| ---------- | ----------------------------------------------- |
| float2int  | Float64 → Int64 (les décimales sont tronquées)  |
| int2float  | Int64 → Float64                                 |
| int2bytes  | Int64 → tableau d’octets (big-endian, 8 octets) |
| bool2int   | Bool → Int64 (true=1, false=0)                  |

**Exemple** (evcc fournit un float, l’appareil attend un 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
```

Dans cet exemple, evcc convertit une valeur décimale comme `85.5` en `85` avant de l’écrire dans le registre Modbus.

### Delta écriture

Le plugin `delta` convertit des valeurs absolues en valeurs delta (incréments). Il est utilisé pour les appareils qui ajoutent les valeurs écrites à une somme interne au lieu de les appliquer directement.

**Paramètres** :

| Paramètre | Type   | Obligatoire | Description                                     |
| --------- | ------ | ----------- | ----------------------------------------------- |
| get       | config | non         | Plugin de lecture du total actuel de l’appareil |
| set       | config | oui         | Plugin d’écriture du delta calculé              |

**Fonctionnement** :

1. La valeur d’entrée est reçue (p. ex. `1000`)
2. Si `get` est configuré, le total actuel est lu depuis l’appareil
3. Le delta est calculé : `delta = new_value - current_total`
4. Le delta est écrit via le plugin `set`
5. L’état interne est mis à jour

Sans le paramètre `get`, le plugin suit le total en interne (en partant de 0). Au redémarrage de votre instance evcc, la synchronisation avec l’appareil est perdue. Il est donc recommandé de configurer `get` si l’appareil fournit un registre de total lisible.

**Types de données pris en charge** : `int64`, `float64`

**Cas d’usage** :

Certaines pompes à chaleur (p. ex. Ochsner) ont des registres qui attendent des valeurs delta au lieu de valeurs absolues. Lors de l’écriture de `500`, l’appareil ajoute 500 W à la valeur interne au lieu de la fixer à 500 W. Pour passer de 1 000 W à 1 500 W, vous devez écrire `+500`. Pour réduire la valeur, il faut écrire une valeur négative (p. ex. `-300`).

**Exemple d’écriture** :

```yaml
setmaxpower:
  source: delta
  get: # lit le total actuel de l’appareil
    source: modbus
    uri: 192.168.1.50:502
    id: 50
    register:
      address: 2012 # registre de somme interne de l’appareil
      type: input
      decode: uint16
  set: # écrit le delta calculé
    source: modbus
    uri: 192.168.1.50:502
    id: 50
    register:
      address: 2201 # registre delta
      type: writeholding
      decode: uint16
```

**Exemple avec Watchdog** :

Le plugin Delta est souvent combiné avec le [plugin Watchdog](#watchdog) lorsque l’appareil exige des mises à jour périodiques :

```yaml
setmaxpower:
  source: watchdog
  timeout: 60s # renvoi toutes les 30 secondes
  set:
    source: delta # convertit les valeurs absolues en deltas
    get: # lit le total actuel
      source: modbus
      uri: 192.168.1.50:502
      id: 50
      register:
        address: 2012 # registre du total
        type: input
        decode: uint16
    set: # écrit le delta
      source: modbus
      uri: 192.168.1.50:502
      id: 50
      register:
        address: 2201 # registre delta
        type: writeholding
        decode: uint16
```

### Ignore écriture

Le plugin `ignore` supprime certains messages d’erreur lors de l’écriture. Il est utilisé lorsqu’un appareil renvoie des erreurs anodines qui peuvent être ignorées.

**Paramètres** :

| Paramètre | Type   | Obligatoire | Description                         |
| --------- | ------ | ----------- | ----------------------------------- |
| error     | string | oui         | Préfixe du texte d’erreur à ignorer |
| set       | config | oui         | Plugin d’écriture                   |

**Fonctionnement** :

1. Le plugin `set` imbriqué est exécuté
2. Si une erreur survient, le plugin vérifie si le message d’erreur commence par la chaîne `error`
3. Si oui, l’erreur est ignorée et un succès est renvoyé
4. Sinon, l’erreur est transmise normalement

**Types de données pris en charge** : `int64`, `float64`, `bool`, `[]byte`

**Exemple** :

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

Dans cet exemple, l’appareil renvoie une erreur Modbus anodine qui est ignorée.

### IfElse écriture

Le plugin `ifelse` effectue des opérations d’écriture conditionnelles avec deux branches. Selon la valeur d’entrée, c’est le plugin `if` ou le plugin `else` qui est exécuté.

**Paramètres** :

| Paramètre | Type   | Obligatoire | Description                                           |
| --------- | ------ | ----------- | ----------------------------------------------------- |
| if        | config | oui         | Plugin exécuté lorsque la condition est remplie       |
| else      | config | oui         | Plugin exécuté lorsque la condition n’est pas remplie |

**Fonctionnement** :

* Pour les valeurs `bool` : `true` exécute `if`, `false` exécute `else`
* Pour les valeurs `int64` : `> 0` exécute `if`, sinon `else` est exécuté

**Types de données pris en charge** : `int64`, `bool`

**Exemple** (points de terminaison différents pour l’activation et la désactivation) :

```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 lecture écriture

Le plugin `map` traduit des valeurs entières en d’autres valeurs entières à l’aide d’une table de correspondance. Il est souvent utilisé pour convertir des valeurs propres à l’appareil en valeurs standard d’evcc et inversement.

**Paramètres** :

| Paramètre | Type             | Obligatoire | Description                                       |
| --------- | ---------------- | ----------- | ------------------------------------------------- |
| values    | map\[int64]int64 | oui         | Table de correspondance entrée → sortie           |
| get       | config           | non         | Plugin de lecture (requis uniquement en lecture)  |
| set       | config           | non         | Plugin d’écriture (requis uniquement en écriture) |

**Fonctionnement** :

**En lecture** :

1. Le plugin `get` renvoie une valeur (p. ex. `0`)
2. La valeur est recherchée dans la table `values`
3. La valeur correspondante est renvoyée (p. ex. `0` → `2`)

**En écriture** :

1. La valeur d’entrée est reçue (p. ex. `3`)
2. La valeur est recherchée dans la table `values`
3. La valeur correspondante est transmise au plugin `set` (p. ex. `3` → `6`)

Si aucune valeur correspondante n’est trouvée dans la table, une erreur se produit.

**Types de données pris en charge** : `int64` (valeurs entières uniquement)

**Exemple de lecture** (valeur de l’appareil → evcc) :

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

**Exemple d’écriture** (evcc → valeur de l’appareil) :

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

### Meter lecture

Le plugin `meter` permet d’utiliser un autre compteur comme source de données. C’est utile lorsque vous souhaitez utiliser un appareil existant pour plusieurs mesures ou lorsque vous avez besoin de différentes méthodes d’un appareil pour différents attributs.

La section `config` contient la configuration complète du modèle du compteur à intégrer. Le paramètre `method` sélectionne la valeur à lire : `power`, `energy`, `returnenergy` ou `soc`.

**Exemple de lecture** :

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

Dans cet exemple, un appareil Shelly 1PM sert de source de données pour la puissance et l’énergie d’une batterie domestique, tandis que le niveau de charge est récupéré via MQTT.

### Sequence écriture

Le plugin `sequence` exécute plusieurs opérations d’écriture l’une après l’autre. Tous les plugins imbriqués reçoivent la même valeur d’entrée et sont exécutés dans l’ordre défini. L’exécution s’arrête immédiatement en cas d’erreur.

**Paramètres** :

| Paramètre | Type      | Obligatoire | Description                                    |
| --------- | --------- | ----------- | ---------------------------------------------- |
| set       | \[config] | oui         | Tableau de configurations de plugins imbriqués |

**Fonctionnement** :

1. La valeur d’entrée est reçue
2. Chaque plugin de la liste `set` est appelé successivement avec cette valeur
3. En cas d’erreur, la séquence est interrompue et l’erreur est renvoyée
4. La séquence ne réussit que si tous les plugins s’exécutent sans erreur

**Types de données pris en charge** : `int64`, `float64`, `bool`

**Cas d’usage** :

* Exécuter plusieurs appels HTTP l’un après l’autre
* Combiner avec le plugin `sleep` pour des actions temporisées
* Écrire simultanément dans plusieurs registres Modbus
* Propager des valeurs vers plusieurs cibles

**Exemple 1 : plusieurs appels 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"}'
```

**Exemple 2 : combinaison avec 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}"
```

**Déroulement pour `batterymode: 1` (normal)** :

1. La `sequence` externe reçoit la valeur `1`
2. Première étape : `switch` vérifie la valeur `1`
   * Le cas `1` correspond → appel HTTP vers `/api/mode` avec le corps `automatic`
3. Deuxième étape : `mqtt` envoie un message à `home/battery/status` avec la charge utile `mode_1`
4. Terminé

**Déroulement pour `batterymode: 3` (charge)** :

1. La `sequence` externe reçoit la valeur `3`

2. Première étape : `switch` vérifie la valeur `3`

   * Le cas `3` correspond → la `sequence` interne est exécutée :

     * Attente d’une seconde (`sleep`)
     * Appel HTTP vers `/api/charge` avec le corps `5000`

3. Deuxième étape : `mqtt` envoie un message à `home/battery/status` avec la charge utile `mode_3`

4. Terminé

La valeur traverse tous les plugins : `switch` exécute une action différente selon la valeur, puis `mqtt` envoie toujours une notification à la fin.

### Sleep écriture

Le plugin `sleep` ajoute un délai. Il est généralement utilisé dans un plugin `sequence` pour créer des intervalles de temps entre les actions.

**Paramètres** :

| Paramètre | Type     | Obligatoire | Description                                                   |
| --------- | -------- | ----------- | ------------------------------------------------------------- |
| duration  | duration | oui         | Temps d’attente (p. ex. `1s`, `500ms`, `0s` pour aucun délai) |

**Exemple** :

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

Le plugin `switch` effectue des opérations d’écriture conditionnelles, comme une instruction switch/case dans les langages de programmation. Selon la valeur d’entrée, l’action correspondante est exécutée.

**Paramètres** :

| Paramètre | Type    | Obligatoire | Description                                       |
| --------- | ------- | ----------- | ------------------------------------------------- |
| switch    | \[case] | oui         | Tableau de cas avec configuration `case` et `set` |
| default   | config  | non         | Plugin de repli si aucun cas ne correspond        |

**Fonctionnement** :

1. La valeur d’entrée est comparée aux valeurs `case`
2. En cas de correspondance, le plugin `set` correspondant est exécuté
3. Si aucun cas ne correspond et que `default` est défini, celui-ci est exécuté
4. Si aucun cas ne correspond et qu’aucun `default` n’est défini, une erreur se produit

**Types de données pris en charge** : `int64` (valeurs entières uniquement)

**Exemple** :

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

Le plugin `valid` ne transmet une valeur que si un indicateur booléen la déclare valide. Il sépare la validité d’une valeur de son contenu réel. Si la validation renvoie `false`, la valeur est considérée comme non disponible.

C’est particulièrement utile pour les intégrations comme ioBroker qui fournissent la validité et la valeur séparément.

**Exemple de lecture** :

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

Dans cet exemple, la valeur n’est utilisée que lorsque le topic `valid` renvoie `true`. S’il renvoie `false`, la valeur est marquée comme non disponible.

### Watchdog écriture

Le plugin `watchdog` est un plugin d’encapsulation qui répète automatiquement les opérations d’écriture à intervalles réguliers. Certains appareils (p. ex. batteries domestiques, onduleurs) attendent que les commandes de pilotage soient répétées régulièrement pour rester actives. Le plugin Watchdog surveille les opérations d’écriture et les répète automatiquement à la moitié de l’intervalle d’expiration configuré.

**Paramètres** :

| Paramètre | Type                | Obligatoire | Description                                                                                |
| --------- | ------------------- | ----------- | ------------------------------------------------------------------------------------------ |
| timeout   | duration            | oui         | Intervalle de temps des répétitions (la valeur est réécrite à chaque intervalle timeout/2) |
| reset     | string \| \[string] | non         | Valeur(s) pour lesquelles les répétitions sont arrêtées                                    |
| initial   | string              | non         | Valeur écrite une fois au démarrage                                                        |
| defer     | bool                | non         | Diffère les mises à jour au lieu de les exécuter immédiatement (par défaut : false)        |
| set       | config              | oui         | Plugin imbriqué pour l’opération d’écriture proprement dite                                |

**Fonctionnement** :

1. **Démarrage** : si `initial` est configuré, cette valeur est écrite une fois au démarrage
2. **Écriture** : lorsqu’une valeur est écrite, le plugin vérifie si elle figure dans la liste `reset`
3. **Watchdog actif** : si la valeur **ne** figure **pas** dans `reset`, le watchdog démarre et réécrit automatiquement la valeur à la moitié de l’intervalle `timeout`
4. **Arrêt du watchdog** : si la valeur figure dans `reset`, aucune répétition n’est effectuée

**Paramètre reset** :

* Définit les valeurs pour lesquelles le watchdog est arrêté
* Peut être une valeur unique (`reset: 0`) ou plusieurs valeurs (`reset: [0, 1]`)
* Généralement utilisé pour des états « sûrs » ou « par défaut » qui n’ont pas besoin d’être répétés en continu

**Paramètre initial** :

* Valeur facultative écrite une fois au démarrage du plugin
* Utile pour définir un état initial déterminé
* Exécutée avant toutes les autres opérations d’écriture

**Paramètre defer** :

* Garantit le respect des délais entre les mises à jour
* Le watchdog est arrêté pendant le délai et redémarré avec la nouvelle valeur à son expiration
* Utile lorsque les appareils exigent un temps d’attente minimal entre deux changements de mode
* Le délai est calculé à partir du temps écoulé depuis la dernière mise à jour
* Les valeurs de reset sont toujours écrites immédiatement (sans délai)

**Types de données pris en charge** : `int64`, `float64`, `bool`

**Exemple d’écriture** :

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

Dans cet exemple, les valeurs sont automatiquement répétées toutes les 30 s, sauf lorsque la valeur `0` est écrite.

**Exemple de déroulement** avec `timeout: 60s`, `reset: 0` :

* Le plugin démarre
* Écriture de la valeur `100` → le watchdog **tourne**, la valeur est répétée toutes les 30 s
* Après 2 minutes : la valeur `100` a été écrite 5 fois (à 0, 30, 60, 90 et 120 s)
* Écriture de la valeur `200` → le watchdog **continue** de tourner, désormais avec la nouvelle valeur `200` toutes les 30 s
* Écriture de la valeur `0` → le watchdog **s’arrête** (puisque `0` est défini dans `reset`)
* Plus aucune répétition jusqu’à l’écriture d’une nouvelle valeur différente de `0`

**Exemple de pilotage de batterie** (`batterymode` utilise les valeurs 1=normal, 2=hold, 3=charge, 4=holdcharge, 5=discharge) :

```yaml
batterymode:
  source: watchdog
  timeout: 60s
  reset: 1 # arrête les répétitions en fonctionnement normal
  set:
    source: switch
    switch:
      - case: 1 # normal
        set:
          source: modbus
          # ... configuration Modbus pour le fonctionnement normal
      - case: 2 # hold
        set:
          source: modbus
          # ... configuration Modbus pour le mode maintien
      - case: 3 # charge
        set:
          source: modbus
          # ... configuration Modbus pour le mode charge
```