# Python Skripte schreiben

{% hint style="warning" %}
Mit der Einführung von Eliona Version 12.2 wurden wichtige Änderungen vorgenommen, die es erforderlich machen, Skripte anzupassen, um das neue API-Design zu nutzen.
{% endhint %}

## Skript-Definition

Wenn ein neues Skript im Eliona-System erstellt wird, wird automatisch ein Funktionsheader generiert. Dieser Header enthält zwei Parameter:

* **`id`**: Die eindeutige ID des Skripts.
* **`eliona`**: Ein Objekt, über das der Benutzer mit dem Eliona-System interagieren kann.

Der Funktionsheader sieht wie folgt aus:

```python
# you may add imports only inside the function
# don't change or delete the function definition
def UserFunction(id, eliona):
	# add your code here
```

### Importieren von Modulen

Alle notwendigen Module müssen innerhalb der Funktion importiert werden. Es ist wichtig, dass Module nur innerhalb der Funktion importiert und keine globalen Importe verwendet werden.

Beispiel:

```python
def UserFunction(id, eliona):
    import random
    # Weitere Codezeilen folgen
```

***

## Methoden des Eliona-Objekts

Das **`eliona`**-Objekt stellt eine Reihe von Methoden zur Verfügung, die es Benutzern ermöglichen, mit dem Eliona-System zu interagieren. Hier sind die wichtigsten Methoden zusammen mit ihren Parametern und Beschreibungen:

<table data-full-width="true"><thead><tr><th width="173">Methode</th><th>Parameter</th><th>Beschreibung</th></tr></thead><tbody><tr><td><strong>GetHeap</strong></td><td><code>gai: str</code>, <code>subtype: str</code>, <code>attribute: str</code></td><td>Ruft den aktuellen Wert eines Attributs oder das gesamte Daten-JSON eines Assets aus dem Heap ab.</td></tr><tr><td><strong>SetHeap</strong></td><td><code>gai: str</code>, <code>subtype: str</code>, <code>data: dict</code>, <code>source: str</code></td><td>Sendet Daten an den Calculator, um sie zu verarbeiten und im Heap zu speichern.</td></tr><tr><td><strong>GetAssetIDByGAI</strong></td><td><code>gai: str</code></td><td>Gibt die numerische Asset-ID für die angegebene GAI zurück.</td></tr><tr><td><strong>GetAll</strong></td><td><code>ids: list[intstr], subtype: str</code></td><td>Ruft alle Daten für eine Liste von Assets ab, die durch ihre IDs und einen Subtyp identifiziert werden.</td></tr><tr><td><strong>SQLQuery</strong></td><td><code>query: str</code></td><td>Führt eine beliebige SQL-Abfrage aus und gibt die Ergebnisse als Liste von Tupeln zurück.</td></tr><tr><td><strong>MakeSource</strong></td><td><code>id: int</code></td><td>Erzeugt einen Quell-String für Heap-Operationen auf Basis der Function-ID.</td></tr><tr><td><strong>OpenFile</strong></td><td><code>name: str</code>, <code>mode: str</code></td><td>Öffnet eine Datei im Eliona-Arbeitsverzeichnis (analog zu Python’s <code>open()</code>).</td></tr><tr><td><strong>AddAssetTag</strong></td><td><code>gai: str</code>, <code>tag: str</code></td><td>Fügt einem Asset ein Tag hinzu (falls nicht schon vorhanden).</td></tr><tr><td><strong>RemoveAssetTag</strong></td><td><code>gai: str</code>, <code>tag: str</code></td><td>Entfernt ein Tag von einem Asset (falls vorhanden).</td></tr><tr><td><strong>GetTrendRecords</strong></td><td><code>asset_id: int</code>, <code>subtype: str</code>, <code>attr: str</code>, <code>begin: datetime</code></td><td>Ruft historische Messwerte als Liste von <code>TrendRecord(ts, value)</code> ab dem angegebenen Zeitpunkt ab.</td></tr><tr><td><strong>WriteTrendRecords</strong></td><td><code>asset_id: int</code>, <code>subtype: str</code>, <code>attr: str</code>, <code>`recs: TrendRecord</code></td><td>Schreibt einen oder mehrere <code>TrendRecord</code>-Datensätze ins System.</td></tr><tr><td><strong>GetAggregate</strong></td><td><code>agg: Aggregate</code>, <code>asset_id: int</code>, <code>subtype: str</code>, <code>attr: str</code>,<code>raster: str</code>, <code>start: datetime</code>,<code>end: datetime = now()</code></td><td>Berechnet aggregierte Werte (Avg, Sum, Cusum) in festem Raster über einen Zeitraum.</td></tr><tr><td><strong>GetLastAggregate</strong></td><td><code>agg: Aggregate</code>, <code>asset_id: int</code>, <code>subtype: str</code>, <code>attr: str</code>, <code>raster: str</code></td><td>Liefert das aktuellste kumulierte Aggregate im gewählten Raster (inkl. <code>last_ts</code>).</td></tr></tbody></table>

***

## Beispielskripte

### GetHeap

**Beschreibung:**\
Ruft den aktuellen Wert eines Attributs oder das gesamte JSON eines Assets aus dem Heap ab.

**Parameter:**

* `gai: str` (erforderlich) – GAI des Assets, z. B. `"K86_WP01"`
* `subtype: str` (erforderlich) – Daten-Subtyp, z. B. `"input"`
* `attribute: str` (erforderlich) – Attributname, z. B. `"Aussentemperatur"`

**Code-Beispiel:**

```python
value = eliona.GetHeap("K86_WP01", "input", "Aussentemperatur")
```

**Beispiel-Output:**

```
23.5
```

***

### SetHeap

**Beschreibung:**\
Sendet ein Wörterbuch von Werten an den Calculator, um sie zu verarbeiten und im Heap zu speichern.

**Parameter:**

* `gai: str` (erforderlich)
* `subtype: str` (erforderlich)
* `data: dict` (erforderlich) – z. B. `{"Aussentemperatur": 23.5}`
* `source: str` (erforderlich) – typischerweise `eliona.MakeSource(id)`

**Code-Beispiel:**

```python
data   = {"Aussentemperatur": 23.5}
source = eliona.MakeSource(id)
eliona.SetHeap("K86_WP01", "input", data, source)
```

***

### GetAssetIDByGAI

**Beschreibung:**\
Gibt die numerische Asset-ID für die angegebene GAI zurück.

**Parameter:**

* `gai: str` (erforderlich)

**Code-Beispiel:**

```python
asset_id = eliona.GetAssetIDByGAI("K86_WP01")
```

***

### GetAll

**Beschreibung:**\
Ruft alle Datenpunkte eines bestimmten Subtyps für eine Liste von Assets ab und gibt diese als Dictionary zurück (GAI → Daten).

**Parameter:**

* `ids: list[int|str]` (erforderlich) – z. B. `["K86_WP01", 1073]`
* `subtype: str` (erforderlich)

**Code-Beispiel:**

```python
all_data = eliona.GetAll(["K86_WP01"], "input")
```

***

### SQLQuery

**Beschreibung:**\
Führt eine beliebige SQL-Abfrage aus und gibt die Ergebnisse als Liste von Tupeln zurück.

**Parameter:**

* `query: str` (erforderlich)

**Code-Beispiel:**

```python
res = eliona.SQLQuery(
    "SELECT data->>'Aussentemperatur' "
    "FROM public.heap "
    "WHERE gai='K86_WP01' AND subtype='input' "
    "ORDER BY timestamp DESC LIMIT 1;"
)
```

**Beispiel-Output:**

```css
[('23.5',)]
```

***

### MakeSource

**Beschreibung:**\
Erzeugt einen Quell-String (z. B. `"ssr:123"`) für Heap-Operationen basierend auf der Function-ID.

**Parameter:**

* `id: int` (erforderlich)

**Code-Beispiel:**

```python
pythonKopierenBearbeitensrc = eliona.MakeSource(id)
```

***

### OpenFile

**Beschreibung:**\
Öffnet eine Datei im Eliona-Arbeitsverzeichnis (analog zu Python’s `open()`).

**Parameter:**

* `name: str` (erforderlich) – Dateiname, z. B. `"log.txt"`
* `mode: str` (erforderlich) – z. B. `"w"`, `"r"`

**Code-Beispiel:**

```python
pythonKopierenBearbeitenf = eliona.OpenFile("aussentemp_log.txt", "w")
f.write("Temperatur: 23.5\n")
f.close()
```

***

### AddAssetTag

**Beschreibung:**\
Fügt einem Asset ein Tag hinzu, wenn es noch nicht zugewiesen ist.

**Parameter:**

* `gai: str` (erforderlich)
* `tag: str` (erforderlich)

**Code-Beispiel:**

```python
eliona.AddAssetTag("K86_WP01", "has-aussentemp")
```

***

### RemoveAssetTag

**Beschreibung:**\
Entfernt ein Tag von einem Asset, falls vorhanden.

**Parameter:**

* `gai: str` (erforderlich)
* `tag: str` (erforderlich)

**Code-Beispiel:**

```python
eliona.RemoveAssetTag("K86_WP01", "has-aussentemp")
```

***

### GetTrendRecords

**Beschreibung:**\
Lädt eine Liste historischer Messwerte (`TrendRecord(ts, value)`) ab dem angegebenen Start-Zeitpunkt.

**Parameter:**

* `asset_id: int` (erforderlich)
* `subtype: str` (erforderlich)
* `attr: str` (erforderlich)
* `begin: datetime` (erforderlich)

**Code-Beispiel:**

```python
from datetime import datetime, timedelta

aid   = eliona.GetAssetIDByGAI("K86_WP01")
begin = datetime.now() - timedelta(hours=1)
records = eliona.GetTrendRecords(aid, "input", "Aussentemperatur", begin)
```

***

### WriteTrendRecords

**Beschreibung:**\
Schreibt einen oder mehrere `TrendRecord`-Datensätze ins System.

**Parameter:**

* `asset_id: int` (erforderlich)
* `subtype: str` (erforderlich)
* `attr: str` (erforderlich)
* `recs: TrendRecord | list[TrendRecord]` (erforderlich)

**Code-Beispiel:**

```python
from datetime import datetime, timedelta
import eliona_types

aid = eliona.GetAssetIDByGAI("K86_WP01")
now = datetime.now()
r1  = eliona_types.TrendRecord(now, 18.7)
r2  = eliona_types.TrendRecord(now + timedelta(minutes=30), 19.2)
eliona.WriteTrendRecords(aid, "input", "Aussentemperatur", [r1, r2])
```

***

### GetAggregate

**Beschreibung:**\
Berechnet aggregierte Werte (Durchschnitt, Summe, kumulative Summe) in festem Raster über einen Zeitraum.

**Parameter:**

* `agg: Aggregate` (erforderlich) – z. B. `eliona_types.Aggregate.Avg`
* `asset_id: int` (erforderlich)
* `subtype: str` (erforderlich)
* `attr: str` (erforderlich)
* `raster: str` (erforderlich) – ISO 8601-Duration, z. B. `"PT1H"` für stündlich
* `start: datetime` (erforderlich)
* `end: datetime` (optional, default: `now()`)

**Code-Beispiel:**

```python
from datetime import datetime, timedelta
import eliona_types

aid   = eliona.GetAssetIDByGAI("K86_WP01")
end   = datetime.now()
start = end - timedelta(days=1)
aggs  = eliona.GetAggregate(
    eliona_types.Aggregate.Avg,
    aid, "input", "Aussentemperatur",
    "PT1H", start, end
)
```

**Beispiel-Output-Struktur:**

```python
[
  TrendAggregate(ts=<datetime>, cnt=<int>, avg=<float>,
                 sum=<float>, first=<float>,
                 min=<float>, max=<float>, last=<float>),
  ...
]
```

***

### GetLastAggregate

**Beschreibung:**\
Gibt das aktuellste kumulierte Aggregate im gewählten Raster zurück (inklusive `last_ts`).

**Parameter:**

* `agg: Aggregate` (erforderlich)
* `asset_id: int` (erforderlich)
* `subtype: str` (erforderlich)
* `attr: str` (erforderlich)
* `raster: str` (erforderlich)

**Code-Beispiel:**

```python
import eliona_types

aid  = eliona.GetAssetIDByGAI("K86_WP01")
last = eliona.GetLastAggregate(
    eliona_types.Aggregate.Cusum,
    aid, "input", "Aussentemperatur", "P1D"
)
```

**Beispiel-Output-Struktur:**

```python
RunningAggregate(
  ts=<datetime>, cnt=<int>, avg=<float>,
  sum=<float>, first=<float>,
  min=<float>, max=<float>, last=<float>,
  last_ts=<datetime>
)
```

***

### Einfaches Skript zur Manipulation von Daten

Dieses Beispiel zeigt, wie ein Skript einen zufälligen Wert generiert und diesen in den Heap eines Assets schreibt:

```python
def UserFunction(id, eliona):
    import random

    data = {
        "power_val": random.randint(10, 100)
    }

    eliona.SetHeap("TestInactIn", "input", data, eliona.MakeSource(id))
```

**Beschreibung:**

* Das Skript importiert das Modul `random`.
* Es wird ein Wörterbuch `data` erstellt, das einen Schlüssel `power_val` enthält, dem ein zufälliger Wert zwischen 10 und 100 zugewiesen wird.
* Der generierte Wert wird mit der Methode `SetHeap` in den Heap des Assets `TestInactIn` geschrieben.

***

### Daten abrufen und modifizieren

In diesem Beispiel wird der Wert eines Attributs von einem Asset abgerufen, modifiziert und in einem anderen Asset gespeichert:

```python
def UserFunction(id, eliona):
    from datetime import datetime

    heap = eliona.GetHeap("TestInactIn", "input")

    data = {
        "energy_val": heap["power_val"] * 3
    }

    eliona.SetHeap("TestInactIn", "input", data, eliona.MakeSource(id))
```

**Beschreibung:**

* Das Skript importiert das Modul `datetime`.
* Der aktuelle Wert des Attributs `power_val` wird aus dem Heap des Assets `TestInactIn` abgerufen.
* Dieser Wert wird verdreifacht und als `energy_val` in dasselbe Asset geschrieben.

***

### Erweiterte Funktionen und SQL-Abfragen

Mit der Methode `SQLQuery` können beliebige SQL-Abfragen direkt aus dem Skript heraus ausgeführt werden:

```python
def UserFunction(id, eliona):
    result = eliona.SQLQuery("SELECT * FROM public.asset WHERE gai = 'TestInactIn';")
    # Weitere Verarbeitung des Ergebnisses
```

**Beschreibung:**

* Die SQL-Abfrage ruft alle Informationen zu dem Asset mit der GAI `TestInactIn` ab.
* Das Ergebnis kann anschließend weiterverarbeitet werden.
