# Writing Python Scripts

## Writing Python Scripts

{% hint style="warning" %}
With the introduction of BuildingPro Suites Version 12.2, important changes were made that require scripts to be adapted to use the new API design.
{% endhint %}

## Script Definition

When a new script is created in the BuildingPro Suites system, a function header is automatically generated. This header contains two parameters:

* **`id`**: The unique ID of the script.
* **`eliona`**: An object through which the user can interact with the BuildingPro Suites system.

The function header looks like this:

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

### Importing Modules

All necessary modules must be imported within the function. It is important that modules are only imported within the function and no global imports are used.

Example:

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

***

## Methods of the BuildingPro Suites Object

The **`eliona`** object provides a set of methods that allow users to interact with the BuildingPro Suites system. Here are the most important methods along with their parameters and descriptions:

<table data-full-width="true"><thead><tr><th width="173">Methode</th><th>Parameter</th><th>Beschreibung</th><th></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>Retrieves the current value of an attribute or the entire data JSON of an asset from the heap.</td><td></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>Sends data to the calculator to process it and store it in the heap.</td><td></td></tr><tr><td><strong>GetAssetIDByGAI</strong></td><td><code>gai: str</code></td><td>Returns the numerical asset ID for the specified GAI.</td><td></td></tr><tr><td><strong>GetAll</strong></td><td>`ids: list[int</td><td>str]<code>,</code> subtype: str`</td><td>Retrieves all data points of a specific subtype for a list of assets and returns them as a dictionary (GAI → data).</td></tr><tr><td><strong>SQLQuery</strong></td><td><code>query: str</code></td><td>Executes an arbitrary SQL query and returns the results as a list of tuples.</td><td></td></tr><tr><td><strong>MakeSource</strong></td><td><code>id: int</code></td><td>Generates a source string for heap operations based on the function ID.</td><td></td></tr><tr><td><strong>OpenFile</strong></td><td><code>name: str</code>, <code>mode: str</code></td><td>Opens a file in the BuildingPro Suites working directory (analogous to Python’s <code>open()</code>).</td><td></td></tr><tr><td><strong>AddAssetTag</strong></td><td><code>gai: str</code>, <code>tag: str</code></td><td>Adds a tag to an asset (if not already present).</td><td></td></tr><tr><td><strong>RemoveAssetTag</strong></td><td><code>gai: str</code>, <code>tag: str</code></td><td>Removes a tag from an asset (if present).</td><td></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>Retrieves historical measured values as a list of <code>TrendRecord(ts, value)</code> from the specified time.</td><td></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>Writes one or more <code>TrendRecord</code> data sets to the system.</td><td></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>Calculates aggregated values (Avg, Sum, Cusum) in a fixed raster over a period.</td><td></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>Returns the latest cumulative aggregate in the selected raster (incl. <code>last_ts</code>).</td><td></td></tr></tbody></table>

***

## Example Scripts

### GetHeap

**Description:** Retrieves the current value of an attribute or the entire JSON of an asset from the heap.

**Parameter:**

* `gai: str` (required) – GAI of the asset, e.g., `"K86_WP01"`
* `subtype: str` (required) – Data subtype, e.g., `"input"`
* `attribute: str` (required) – Attributename, e.g., `"Aussentemperatur"`

#### Example-Code:

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

**Example-Output:**

```python
23.5
```

***

### SetHeap

**Description:** Sends a dictionary of values to the calculator to process and store in the heap.

**Parameter:**

* `gai: str` (required)
* `subtype: str` (required)
* `data: dict` (required) – e.g., `{"Aussentemperatur": 23.5}`
* `source: str` (required) – typically `eliona.MakeSource(id)`

**Code-Example:**

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

***

### GetAssetIDByGAI

**Description:** Returns the numerical asset ID for the specified GAI.

**Parameter:**

* `gai: str` (required)

**Code-Example:**

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

***

### GetAll

**Description:** Retrieves all data points of a specific subtype for a list of assets and returns them as a dictionary (GAI → data).

**Parameter:**

* `ids: list[int|str]` (required) – e.g., `["K86_WP01", 1073]`
* `subtype: str` (required)

**Code-Example:**

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

```

***

### SQLQuery

**Description:** Executes an arbitrary SQL query and returns the results as a list of tuples.

**Parameter:**

* `query: str` (required)

**Code-Example:**

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

**Example-Output:**

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

***

### MakeSource

**Description:** Generates a source string (z. B. `"ssr:123"`) for heap operations based on the function ID.

**Parameter:**

* `id: int` (required)

**Code-Example:**

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

***

### OpenFile

**Description:** Opens a file in the BuildingPro Suites working directory (analogous to Python’s `open()`).

**Parameter:**

* `name: str` (required) – Filename, e.g., `"log.txt"`
* `mode: str` (required) – e.g., `"w"`, `"r"`

**Code-Example:**

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

***

### AddAssetTag

**Description:** Adds a tag to an asset if it is not yet assigned.

**Parameter:**

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

**Code-Example:**

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

```

***

### RemoveAssetTag

**Description:** Removes a tag from an asset, if present.

**Parameter:**

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

**Code-Example:**

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

***

### GetTrendRecords

**Description:** Loads a list of historical measured values (`TrendRecord(ts, value)`) from the specified start time.

**Parameter:**

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

**Code-Example:**

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

**Description:** Writes one or more `TrendRecord` data sets to the system.

**Parameter:**

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

**Code-Example:**

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

**Description:** Calculates aggregated values (Average, Sum, cumulative sum) in a fixed raster over a period.

**Parameter:**

* `agg: Aggregate` (required) – e.g., `eliona_types.Aggregate.Avg`
* `asset_id: int` (required)
* `subtype: str` (required)
* `attr: str` (required)
* `raster: str` (required) – ISO 8601-Duration, e.g., `"PT1H"` for hourly
* `start: datetime` (required)
* `end: datetime` (optional, default: `now()`)
* `recs: TrendRecord | list[TrendRecord]` (required)

**Code-Example:**

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

**Example-Output-Struktur:**

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

***

### GetLastAggregate

**Description:** Returns the latest cumulative aggregate in the selected raster (including `last_ts`).

**Parameter:**

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

**Code-Example:**

```python
import eliona_types

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

**Example-Output-Struktur:**

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

***

### Simple Script for Data Manipulation

This example shows how a script generates a random value and writes it to the heap of an asset:

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

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

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

**Description:**

* The script imports the `random` module.
* A dictionary `data` is created that contains a key `power_val`, to which a random value between 10 and 100 is assigned.
* The generated value is written to the heap of the `TestInactIn` asset using the `SetHeap` method.

***

## Get and Modify Data

In this example, the value of an attribute is retrieved from one asset, modified, and saved in another asset:

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

**Description:**

* The script imports the `datetime` module.
* The current value of the `power_val` attribute is retrieved from the heap of the `TestInactIn` asset.
* This value is tripled and written to the same asset as `energy_val`.

***

## Advanced Functions and SQL Queries

With the `SQLQuery` method, any SQL queries can be executed directly from the script:

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

**Description:**

* The SQL query retrieves all information about the asset with the GAI `TestInactIn`.
* The result can then be processed further.
