# Logic Block Scripting

&#x20;

{% hint style="info" %}
For general scripting concepts refer to this page: [scripting](https://docs.akenza.io/akenza.io/get-started/reference/scripting "mention")
{% endhint %}

### Rule logic script

The Rule logic script defines the custom logic that is evaluated during rule runs.

```javascript
function consume(event) {
  var temperature = event.inputs.temperature;
  var threshold = event.properties.tempThreshold;

  if (temperature < threshold) {
    emit("action", {
      message: `it is too cold: the temperature is ${temperature}°C`,
    });
  }
}
```

Once a Custom Logic Block is saved, it can be selected as a logic block when creating a rule. Inputs need to be linked to an input device or tag and parameters need to be set.&#x20;

Further, one or more actions need to be defined. The templating syntax can be used to access the results of a Custom Logic Block {{result.\*}}. For example, {{result.message}} for the script shown above.

#### Custom logic timer

There is also the possibility to emit a timer inside the custom logic block.

When emitting the timer event, two params can be specified

* `runAt` required, a date which needs to be at least 15 seconds in the future
* `meta` optional, object which can contain any values

The meta object will be available when the rule is triggered by that timer and can be accessed at `timer.meta` during script run. This can provide useful information for the next timed run or help uniquely identify timers when running them.

```javascript
function consume(event) { 
  let time = new Date();
  time = new Date(time.setDate(time.getDate() + 1));
  time = time.setHours(0,0,0,0);
  emit("timer", {runAt: new Date(time), meta: {"foo": "bar"}});
}
```

{% hint style="info" %}
Refer to Stateful Operations for more information on how to share data between runs of a rule.
{% endhint %}

## Event Object

The Custom Logic Block script is invoked with the event object as param. It contains the following properties & strucutre:

* `inputs` object, contains the values of the specified variables
* `state` object, contains the user defined rule state
* `type` string, indicates how the rule was invoked (either `timer` or `uplink`)
* `dataSources` object, see data source properties
* `device` object, see device properties
* `properties` object, contains the user defined properties of the custom logic block

**Note:** if `type` has the value of `timer`  certain properties will behave differently:

* the variable values of the `inputs` object will be null
* `dataSources` will be an empty object
* `device` will be undefined

#### Device Properties

The following properties of a device can be accessed when using template syntax.

**Note**: The prefix `device.` is always required in order to access sub properties.

* `name` the device name
* `description` the device description
* `integrationId` the integrationId of the device (only for LoRaWAN devices)
* `workspaceId` the workspaceId of the device
* `dataFlowId` the data flow id of the device
* `connectivity` the connectivity of the device
* `id` the akenza device id
* `deviceId` the unique device id

#### Data Source Properties

The following properties of a data source can be accessed when using template syntax.

To access a specific data source, use `dataSources.X` where `X` is the number of the data source as specified in the rule (starting at 1).

Some properties can be null in certain circumstances (e.g. the `correlationId` or the `device`) if e.g. the rule was triggered by a timer event or the data source is set to access the last sample.

**Note**: The prefix `dataSources.X.` is always required in order to access sub properties.

* `correlationId` the correlation id of the data source (only available if the data source was triggering the flow)
* `device` the complete device object. See Device Properties for sub properties
* `deviceId` the unique device id
* `akenzaDeviceId` the akenza device id
* `topic` the topic of the sample
* `timestamp` the timestamp of the sample
* `data.*` access any values of the sample.
* `meta.*` access any values of the meta object
* `uplinkMeta` the complete uplink meta object
* `trigger` boolean, indicates whether the data source has triggered the uplink
* `deviceInput` boolean, indicates if the data source is a device
* `tagInput` boolean, indicates if the data source is a tag

The values related to the sample (namely `correlationId`, `topic`, `timestamp`, `data`, `meta` and `uplinkMeta`) are resolved based on which data source triggered the rule evalution. If the data source is triggering (`trigger = true`) the rule, the values will be the one from the triggering uplink. Otherwise the values will correlate the most recent stored sample.

#### Uplink Meta Properties

The following properties of uplink meta data can be accessed when using template syntax

**Note**: The prefix `uplinkMeta.` is always required in order to access sub properties.

* `dataReceived` the ISO-8601 timestamp when the data was recieved
* `bytesReceived` the number of bytes received in the uplink request
* `processingStart` the ISO-8601 timestamp when the processing was started
* `scriptRunUplinkStart` the ISO-8601 timestamp when script run was started
* `scriptRunUplinkEnd` the ISO-8601 timestamp when script run ended
* `processingEnd` the ISO-8601 timestamp when the processing ended
* `outputProduced` the ISO-8601 timestamp when all output were produced
* `uplinkDuration` the ISO-8601 duration of the whole uplink flow
* `processingDuration` the IOS-8601 duration of the processing
* `scriptRunningDuration` the ISO-8601 duration of the script run

## Emitting actions

If a condition is met, an action has to be emitted that will invoke the consecutive rule actions.

```javascript
if (currentTemperature > threshold) {
  emit("action", {
    message: `temperature (${currentTemperature} °C) is above threshold (${threshold} °C)`,
  });
}
```

### Re-invoking the rule engine

It is possible to re-invoke the rule engine by enabling the setting in the rule action. This is useful, to apply another rule to the output of a custom logic block.

Note that the emit action is expected to have a specific format (same structure as the sample output in device type scripts), see [Emit Function](https://docs.akenza.io/akenza.io/reference/scripting#emit-function) for details. This means that properties like `topic`, `timestamp (optional)` and `data` should be provided and will be processed in subsequent rules.

```javascript
if (currentTemperature > threshold) {
  emit("action", {
    data: { temperature, threshold, message: `temperature (${currentTemperature} °C) is above threshold (${threshold} °C)` },
    topic: "alert"
  });
}java
```

If the `topic` is specified in the emit action, it will override the one specified in the custom akenza db rule action.

### Rule Action - Custom Payload&#x20;

When using a [custom payload template](https://docs.akenza.io/akenza.io/get-started/reference/message-templating) in the rule action, make sure to use `{{result.*}}` to access data that has been emitted as part of the rule action. For example, to store the result of a rule action that has been used to re-invoke the rule engine, use `{{result.data}}`. Otherwise the whole result object will be stored as a sample meaning `topic`, `timestamp`, `data` and `meta` (if specified) will be contained in the data object.

## Emitting a timer

By emitting a timer, you can schedule a rule to run be invoked again in the future.

```javascript
emit("timer", {
    runAt: new Date(Date.now() + 60 * 60 * 1000), //the timestamp of when the rule should be invoked
    meta: {} //metadata that will be available during the invocation of the time
  });
```

`meta` can then be accessed using `event.timer.meta`.

## Event examples

### Logic Block uplink event

A decoded uplink that triggers a custom logic block, looks as follows. Note that the state is controlled by the script author (see [stateful-operations](https://docs.akenza.io/akenza.io/get-started/reference/scripting/stateful-operations "mention")):

```json
{
  "inputs": {
    "occupied": false
  },
  "state": {
    "devices": {
      "00000000000000ff": {
        "lastOccupiedTimestamp": 1716553293784,
        "currentlyOccupied": false,
        "lastMessageTimestamp": 1716553385376,
        "sent": true
      }
    }
  },
  "type": "uplink",
  "dataSources": {
    "1": {
      "correlationId": "77777777-7777-7777-7777-77777777",
      "device": {
        "connectivity": "LORA",
        "customFields": {
          "FloorNumber": 1,
          "SiteId": "ZURICH",
          "IsWarmDeskEnabled": "true",
          "SpaceId": "1OGR35"
        },
        "name": "Meeting Room R35",
        "description": "",
        "id": "00000000000000ff",
        "deviceId": "1234567891012131",
        "workspaceId": "2900000000000000",
        "tags": [
          "occupancy",
          "climate"
        ]
      },
      "deviceId": "1234567891012131",
      "akenzaDeviceId": "00000000000000ff",
      "topic": "occupancy",
      "timestamp": "2024-05-24T12:38:06.122Z",
      "data": {
        "occupied": false,
        "motionCount": 0
      },
      "trigger": true,
      "deviceInput": false,
      "tagInput": true,
      "tagCombinationInput": false,
      "triggerOnUplink": true,
      "lastSample": false
    }
  },
  "device": {
    "connectivity": "LORA",
    "customFields": {
      "FloorNumber": 1,
      "SiteId": "ZURICH",
      "SpaceId": "1OGR35"
    },
    "name": "Meeting Room R35",
    "description": "",
    "id": "00000000000000ff",
    "deviceId": "1234567891012131",
    "workspaceId": "2900000000000000",
    "tags": [
      "occupancy",
      "climate"
    ]
  },
  "numberOfInvocations": 0,
  "properties": {}
}
```

### Logic Block timer event

A time-based event that triggers a custom logic block, looks as follows. Note that the state is controlled by the script author (see [stateful-operations](https://docs.akenza.io/akenza.io/get-started/reference/scripting/stateful-operations "mention")):

```json
{
  "inputs": {
    "occupied": null
  },
  "state": {
    "devices": {
      "00000000000fffff": {
        "lastOccupiedTimestamp": 1716553293784,
        "currentlyOccupied": false,
        "lastMessageTimestamp": 1716560583110,
        "sent": false
      }
    }
  },
  "type": "timer",
  "dataSources": {},
  "numberOfInvocations": 0,
  "properties": {}
}
```

### Logic Block last sample timer event

A time-based event that triggers a custom logic block, looks as follows.&#x20;

Note that the state is controlled by the script author (see [stateful-operations](https://docs.akenza.io/akenza.io/get-started/reference/scripting/stateful-operations "mention")):

```json
{
  "inputs": {
    "temperature": 23.84
  },
  "state": {},
  "type": "timer",
  "dataSources": {
    "1": {
      "correlationId": "77777777-7777-7777-7777-777777777777",
      "device": {
        "connectivity": "LORA",
        "customFields": {},
        "name": "Watteco Senso",
        "description": null,
        "id": "1998877667788991",
        "deviceId": "000000000000ffff",
        "workspaceId": "2929292929292929",
        "tags": []
      },
      "deviceId": "000000000000ffff",
      "akenzaDeviceId": "1998877667788991",
      "topic": "default",
      "timestamp": "2024-05-24T13:20:14.869Z",
      "data": {
        "temperature": 23.84,
        "humidity": 51.3,
        "voc": 553,
        "co2": 518
      },
      "meta": null,
      "uplinkMeta": {
        "dataReceived": "2024-05-24T13:20:14.150224453Z",
        "bytesReceived": 952,
        "processingStart": "2024-05-24T13:20:14.220414139Z",
        "scriptRunUplinkStart": null,
        "scriptRunUplinkEnd": null,
        "processingEnd": "2024-05-24T13:20:14.259641209Z",
        "outputProduced": null,
        "ruleResolutionStart": "2024-05-24T13:20:14.805071863Z",
        "ruleResolutionEnd": null,
        "ruleExecutionStart": null,
        "ruleExecutionEnd": null,
        "uplinkDuration": "PT0S",
        "processingDuration": "PT0.03922707S",
        "ruleResolutionDuration": "PT0S",
        "ruleExecutionDuration": "PT0S",
        "scriptRunningDuration": "PT0S"
      },
      "trigger": false,
      "deviceInput": true,
      "tagInput": false,
      "tagCombinationInput": false,
      "triggerOnUplink": false,
      "lastSample": true
    }
  },
  "numberOfInvocations": 0,
  "properties": {}
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.akenza.io/akenza.io/get-started/rule-engine/logic-blocks/custom-logic/logic-block-scripting.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
