# MOOST User Manual

Welcome Visitor!

This documentation guides you through all the core features and contains relevant information for the integration with your systems.

**Click the card below to start exploring our documentation.**


# Login

## New Users

To be able to login to the Recommender Platform you need a login. The User Management is currently done by MOOST. If you need a new user to be added to an existing customer please fill out the ticket here and the MOOST Support will get back to you asap.

{% embed url="<https://moost.atlassian.net/servicedesk/customer/portal/2/group/8/create/30>" %}
Onboard new employee
{% endembed %}

## Existing Users

When you already have an account for the Recommender Platform fill in the form on the right of the screen using the email address which was provided to MOOST during the onboarding process and the corresponding password.

<figure><img src="/files/YoQvzmr4lgXH74FrhM8q" alt=""><figcaption><p>Login</p></figcaption></figure>


# Forgot Password

If you have forgotten your password please use the forgot password flow to set a new password. The forgot password process can be started with a click on "[Forgot password?](https://admin.moost.io/user/password/forgot)" on the Login Screen.

<figure><img src="/files/7ArKkcg9lqM2YgaXZM2F" alt=""><figcaption><p>Forgot Password - Link</p></figcaption></figure>

In the next step you need to enter the email address which was used during the onboarding for which the password should be reset.

<figure><img src="/files/uZv5Ci1z8c8UuUNDxFMP" alt=""><figcaption><p>Forgot Password - Email</p></figcaption></figure>

If the entered email is registered on the Recommender Platform you will receive an email with a link to reset your password, after clicking on the "Continue" Button.


# Notifications

The Notification Overview is a powerful tool to keep track of recent notifications and analyze data over a certain period. The page's features, such as search functionality, column selection, time frame adjustment, and pagination, make it easy for users to customize the view according to their needs.&#x20;

The dashboard provides users with additional insight into their notification strategy, allowing them to quickly identify which rules are generating the most notifications, how users are engaging with notifications, and how effective the notification delivery process is.

## Charts

<figure><img src="/files/tTZLmj3SBvTJ8SRP0Gqm" alt=""><figcaption></figcaption></figure>

The Dashboard on the Charts Tab provides users with additional insight into their notification strategy.&#x20;

* The "Rules" chart shows how many notifications of a specific rule were sent in percent, allowing users to quickly identify which rules are generating the most notifications.
* The "Interactions" chart shows the interaction rate of the sent notifications in percent, providing insight into how users are engaging with notifications.&#x20;
* The "Delivery Status" chart shows the rate in percent if notifications were actually delivered or dropped, providing insight into how effective the notification delivery process is.
* The "Delivered Notifications" heatmp gives you insights which rules are sending most recommendations to endusers on a daily basis. It helps to identify if there is an imbalance in one of the rules configured on your tenant.

## Data

<figure><img src="/files/Td1F5q3nvrbGffa4Q9dm" alt=""><figcaption></figcaption></figure>

The table on the Data Tab displays detail information about the latest notifications sent. The table includes columns such as the notification name, date and time, recipient, and other relevant information. Each row in the table represents a single notification, and users can adjust how many notifications are shown at once by adjusting the page limit.

## Toolbar

Located on top of the page is the toolbar which allows you to adjust which information is shown in the charts and the data table.

## Filter

The filter functionality on the top of the page allows users to filter for specific notifications by building, rule, delivery status, interactions and time range.

<figure><img src="/files/6AyfxU0Wt5troXOCuOqB" alt=""><figcaption></figcaption></figure>


# Rules

The core of the platform are the rules. A rule is a combination of datasets, conditions, settings and notification which should be sent to the household.

When navigating to the Rules Menu the first screen that is presented is the Rules Overview. It displays all rules that are currently activated for your customer.

MOOST has a pre-defined set of rules that can be activated for a customer. Each customer has the possibility to add custom new rules to the platform by clicking "Add Rule".

<figure><img src="/files/aeQwPevPj3tCZyImPnOQ" alt=""><figcaption></figcaption></figure>

You can edit each rule in the rule configurator by clicking on the "Edit" Button at the bottom left corner of the card.


# Rule Configurator

The core of the platform are the rules and the rule configurator. The rule configurator allows a customer to edit the pre-defined rules that are available or to add and edit new rules.

The editor consists of six main components:

1. [Data graph](/platform-manual/rules/rule-configurator/data-graph)
2. [Datasets](/platform-manual/rules/rule-configurator/datasets)
3. [Condition](/platform-manual/rules/rule-configurator/condition)
4. [Notification Templates](/platform-manual/rules/rule-configurator/message)
5. [Settings](/platform-manual/rules/rule-configurator/settings)
6. [Command](/platform-manual/rules/rule-configurator/command)
7. [Rule Simulator](/platform-manual/rules/rule-configurator/rule-simulator)

<figure><img src="/files/VX4Mvalz8JAivm5WldYl" alt=""><figcaption></figcaption></figure>


# Data graph

This page describes what is displayed in the rule configurator graph and how you can interact with it.

The part on top of the rule configurator is called the *data graph*. It visualizes the data which is defined by the datasets of the rule for a certain building.

<figure><img src="/files/U9e8vW9ZiSiiRt7giLyG" alt=""><figcaption><p>Data graph which shows the data-sets related event data of a building, a help line, and the produced notifications.</p></figcaption></figure>

## Action Bar

The bar at the top of the data graph is called the action bar. It features settings, like&#x20;

* tags to categorize the rule
* the building and time range selector, which define which data is visualized within the datagraph
* the menu, which contains the History, Delete, Export and Import functionality.

<figure><img src="/files/TyJkO57CeOswo6Z9JUR5" alt=""><figcaption></figcaption></figure>

### Filter for Building Selector

The filter is able to reduce the displayed buildings of the building selector. By default the "Dataset scoped buildings" option is turned on, so that the building only displays buildings which match to the dataset of the rule.

### Building Selector

On top of the data graph is the building selector. The building selector allows you to visualize the datasets of a rule for a specific building.&#x20;

### Date Range Selector

Next to the building selector is the date range selector. The date range selector defines for which date range the datasets will be visualized. It also defines for which date range a simulation run will be executed when clicking on "RUN SIMULATION" in the action bar.

## Graph

On top of the graph are the notifications as a envelop icon, which were generated by the rule. Below the notifications, the datasets that have been defined in the rule are displayed as a time series.

### Datapoints

Each line is a the representation of a set of datapoints which we have in the system for the selected building. By hovering over a data point of the line the value of the respective datapoint is displayed.

<figure><img src="/files/pYqGBdhgRp7tFuEgtG0L" alt=""><figcaption><p>Visualization of a single datapoint</p></figcaption></figure>

### Datasets

Datasets are the basis of each rule. You may attach one or multiple event types, and optionally a set of event sources to a dataset.

The created datasets are rendered in the graph as a single line per event and source type. The displayed datasets can be found in the legend on top of the graph. By clicking on the name of a dataset in the legend the line for the dataset can be hidden or displayed.

<figure><img src="/files/sqdRcInySC3LCiaM1Y8m" alt=""><figcaption></figcaption></figure>

&#x20;They y-axis is dynamically changed according to the types which are defined on the datasets.

#### Y-Axis

The y-axis differ in the scale that they provide, and it is able to display data types, even when having a mixed dataset of different unit types. For example if a dataset is temperature based, and another one is power based, the y-axis renders both a scale for temperature in *°C,* and for power in *W*.&#x20;

<div align="center" data-full-width="false"><figure><img src="/files/asrhmZlAS6UQbq7Jjyzl" alt=""><figcaption><p>Different Y Axis for the configured Datasets</p></figcaption></figure></div>

### Notifications

At the top of the graph you may see notifications that were generated by the rule. They are represented by small envelops. The color of the envelops represent the status of the notification:

<figure><img src="/files/W0mgmrSCrCXvvnCtGyUB" alt=""><figcaption></figcaption></figure>

<table><thead><tr><th width="163">Color</th><th>Description</th></tr></thead><tbody><tr><td>Green</td><td><p><strong>Delivered Notifications</strong></p><p>Notifications which were actually sent to the customers endpoint and marked for delivery to the building.</p></td></tr><tr><td>Gray</td><td><p><strong>Dropped Notifications</strong></p><p>Notifications which were generated by the rule but were dropped by the message queue for various reasons (e.g. hitting delivery threshold).</p></td></tr><tr><td>Light Green</td><td><p><strong>Simulated Delivered Notifications</strong></p><p>Notifications which were generated by the rule simulator and were neither sent to a building nor to the message queue.</p></td></tr><tr><td>Light Gray</td><td><p><strong>Simulated Dropped Notifications</strong></p><p>Notifications which were generated by the rule simulator and would have been dropped by the message queue for various reasons (e.g. hitting delivery threshold).</p></td></tr></tbody></table>

#### Visualization

Hover over the notification icon to see the notification message which was generated. This is the same content that users saw on their device, if the notification was delivered.

<figure><img src="/files/1HyTLWFv8f8RPxjFosVP" alt=""><figcaption><p>Simulated Notification visualization</p></figcaption></figure>


# Tags

Tags are a powerful feature that allows you to categorize, organize, and group related rules within the system. This functionality simplifies the process of managing large numbers of rules and provides a streamlined way to retrieve specific subsets of rules using the REST API.

<figure><img src="/files/B1qO4UlbI6QpqEGST68y" alt=""><figcaption></figcaption></figure>

## **Purpose of Tags**

Tags serve as simple, user-defined labels that can be associated with rules. Their primary use cases include:

* Grouping rules by purpose (e.g., `security`, `compliance`, `marketing`)
* Filtering rules for reporting or processing
* Selectively retrieving rules via the REST API

## **Adding Tags to Rules**

When creating or editing a rule, you can attach one or more tags to it.

### **How to Add Tags**

1. Navigate to the rule configuration page of the rule you want to enrich with tags.
2. In the upper left click on "Add Tag"
3. Enter the name of the tag. Tags should be simple strings (e.g., `highpriority`, `beta`, `finance`).
4. Appply the tag.
5. Save the rule.

## **REST API**

## GET /rules/tags/v1

> Get all distinct tags used on any rules

```json
{"openapi":"3.0.3","info":{"title":"MOOST Public API","version":"latest"},"servers":[{"url":"https://api.moost.io"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","name":"bearerAuth","scheme":"bearer","bearerFormat":"JWT"}}},"paths":{"/rules/tags/v1":{"get":{"tags":["PublicAPI"],"summary":"Get all distinct tags used on any rules","operationId":"getTagsV1","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"type":"string"}}}}},"404":{"description":"Not Found","content":{"*/*":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}}}}
```

#### **REST API Endpoint**

## GET /rules/v1

> Get Rules

```json
{"openapi":"3.0.3","info":{"title":"MOOST Public API","version":"latest"},"servers":[{"url":"https://api.moost.io"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","name":"bearerAuth","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"Rule":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"integer","format":"int64"},"customerId":{"type":"string","pattern":"^[a-zA-Z0-9]{24}$"},"name":{"type":"string","pattern":"^.{1,100}$"},"description":{"type":"string","pattern":"^[\\s\\S]{0,10000}$"},"ruleState":{"type":"string","enum":["ACTIVE","PAUSE"]},"notification":{"$ref":"#/components/schemas/Notification"},"notificationCases":{"type":"array","items":{"$ref":"#/components/schemas/NotificationCase"}},"match_threshold":{"type":"integer","format":"int32"},"time_between_triggers_seconds":{"type":"integer","format":"int64"},"resetStateWhenMatched":{"type":"boolean"},"condition":{"type":"string","pattern":"^[\\s\\S]{0,10000}$"},"isStreak":{"type":"boolean"},"streakCondition":{"type":"string","pattern":"^[\\s\\S]{0,10000}$"},"isRestrictedToEarlyAdopters":{"type":"boolean"},"isTimeBased":{"type":"boolean"},"timeBasedCron":{"type":"string"},"messageQueueSetting":{"$ref":"#/components/schemas/MessageQueueSetting"},"datasets":{"type":"array","items":{"$ref":"#/components/schemas/Dataset"}},"topicIcon":{"type":"string","pattern":"^[a-zA-Z0-9_]{1,50}$"},"impactIcon":{"type":"string","pattern":"^[a-zA-Z0-9_]{1,50}$"},"templateRuleId":{"type":"string","pattern":"^[a-zA-Z0-9]{24}$"},"tags":{"type":"array","items":{"type":"string"}},"motivationScoreWeighting":{"$ref":"#/components/schemas/MotivationScoreWeighting"},"goalAchievedCondition":{"type":"string","pattern":"^[\\s\\S]{0,10000}$"}},"required":["condition","customerId","name"]},"Notification":{"type":"object","properties":{"actionQualifier":{"type":"object","additionalProperties":{"type":"string","enum":["DISMISS","OPENAPP","STOPDELIVERY","OPENWEB"]}},"texts":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/NotificationTexts"}},"command":{"type":"string","pattern":"^[\\s\\S]{0,10000}$"}}},"NotificationTexts":{"type":"object","properties":{"title":{"type":"string","pattern":"^[\\s\\S]{1,10000}$"},"message":{"type":"string","pattern":"^[\\s\\S]{1,10000}$"},"actions":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/NotificationAction"}}}},"NotificationAction":{"type":"object","properties":{"text":{"type":"string","pattern":"^[\\s\\S]{1,10000}$"},"parameter":{"type":"string","pattern":"^[\\s\\S]{0,10000}$"}}},"NotificationCase":{"type":"object","properties":{"label":{"type":"string","pattern":"^.{0,100}$"},"caseCondition":{"type":"string","pattern":"^[\\s\\S]{1,10000}$"},"notification":{"$ref":"#/components/schemas/Notification"}},"required":["caseCondition"]},"MessageQueueSetting":{"type":"object","properties":{"isOverflowProtectionActive":{"type":"boolean"}}},"Dataset":{"type":"object","properties":{"name":{"type":"string","pattern":"^[a-zA-Z0-9_]{1,100}$"},"description":{"type":"string","pattern":"^.{0,100}$"},"type":{"type":"string","enum":["SINGLEVALUE","TIMESERIES"]},"source_types":{"type":"array","items":{"type":"string","enum":["APPLIANCE","BATTERY","CAR","CAR_CHARGER","ENERGY_MEASUREMENT","GATEWAY","HEAT_PUMP","INPUT_DEVICE","INVERTER","MOOST","SMART_METER","SMART_PLUG","SWITCH","THERMOSTAT","THERMAL_ZONE","THERMAL_STORAGE","WATER_HEATER","WALL_TABLET","SOLAR_PANEL","DISHWASHER","DRYER","ENTERTAINMENT","LIGHTING","OVEN","REFRIGERATION","WASHING_MACHINE"]}},"event_types":{"type":"array","items":{"type":"string","enum":["CHARGING_MODE","DEVICE_STATUS","ENERGY_CONSUMPTION","ENERGY_CONSUMPTION_LAST_24H","ENERGY_CONSUMPTION_YESTERDAY","ENERGY_EXCESS_LAST_24H","ENERGY_EXCESS_YESTERDAY","ENERGY_GENERATION_LAST_24H","ENERGY_GENERATION_YESTERDAY","ENERGY_IMPORT","ENERGY_IMPORT_YESTERDAY","ENERGY_EXPORT","ENERGY_EXPORT_YESTERDAY","EXPECTED_OUTSIDE_TEMPERATURE","EXPECTED_OUTSIDE_TEMPERATURE_4DAYS","GRID_POWER_CONSUMPTION","GRID_POWER_CONSUMPTION_ANOMALY_SCORE","IS_LOW_TARIFF_HOURS","POWER_CONSUMPTION","POWER_CONSUMPTION_FORECAST_1H","POWER_CONSUMPTION_FORECAST_24H","POWER_EXCESS","POWER_GENERATION","POWER_GENERATION_FORECAST_1H","POWER_GENERATION_FORECAST_1H_MIN","POWER_GENERATION_FORECAST_24H","POWER_GENERATION_FORECAST_48H","POWER_GENERATION_FORECAST_DAY_AFTER_TOMORROW","POWER_GENERATION_FORECAST_TOMORROW","SELF_CONSUMPTION_RATE","SELF_CONSUMPTION_RATE_YESTERDAY","SELF_SUFFICIENCY_RATE","SELF_SUFFICIENCY_RATE_YESTERDAY","STATE_OF_CHARGE_FORECAST_RATE","STATE_OF_CHARGE_RATE","SWITCH_STATE","TEMPERATURE","WATER_TEMPERATURE","POWER_CONSUMPTION_BASE_LOAD","DYNAMIC_TARIFF_PRICE","DYNAMIC_TARIFF_PRICE_FORECAST_1H","DYNAMIC_TARIFF_LOWEST_PRICE_FORECAST_TOMORROW","IS_HIGH_TARIFF_HOURS","ENERGY_EXCESS","ENERGY_BASE_CONSUMPTION","ENERGY_CONSUMPTION_FORECAST_1H","ENERGY_CONSUMPTION_FORECAST_24H","ENERGY_GENERATION","ENERGY_GENERATION_FORECAST_1H","ENERGY_GENERATION_FORECAST_24H","GRID_ENERGY_CONSUMPTION","GRID_ENERGY_CONSUMPTION_YESTERDAY","GRID_ENERGY_CONSUMPTION_ANOMALY_SCORE","GRID_ENERGY_BASE_CONSUMPTION","ENERGY_GENERATION_FORECAST_DAY_AFTER_TOMORROW","ENERGY_GENERATION_FORECAST_TOMORROW","GLOBAL_HORIZONTAL_IRRADIATION_FORECAST_TOMORROW_HOURLY","GLOBAL_HORIZONTAL_IRRADIATION_FORECAST_TOMORROW","ENERGY_CONSUMPTION_FORECAST_TOMORROW","ENERGY_CONSUMPTION_FORECAST_DAY_AFTER_TOMORROW","GRID_BASE_LOAD_CONSUMPTION","DYNAMIC_TARIFF_PRICE_FORECAST_24H","GRID_POWER_CONSUMPTION_YESTERDAY"]}},"timeframe":{"type":"integer","format":"int64"}},"required":["event_types","name","source_types","timeframe","type"]},"MotivationScoreWeighting":{"type":"object","properties":{"deliveredEconomical":{"type":"integer","format":"int32","maximum":10,"minimum":-10},"deliveredEcological":{"type":"integer","format":"int32","maximum":10,"minimum":-10},"deliveredAutarky":{"type":"integer","format":"int32","maximum":10,"minimum":-10},"actionEconomical":{"type":"integer","format":"int32","maximum":10,"minimum":-10},"actionEcological":{"type":"integer","format":"int32","maximum":10,"minimum":-10},"actionAutarky":{"type":"integer","format":"int32","maximum":10,"minimum":-10},"expectedSetting":{"type":"array","items":{"type":"number","format":"float"}},"expectedAction":{"type":"string","pattern":"^(DECREASE|INCREASE|CHANGE)$"},"expectedActionEventType":{"type":"string","enum":["CHARGING_MODE","DEVICE_STATUS","ENERGY_CONSUMPTION","ENERGY_CONSUMPTION_LAST_24H","ENERGY_CONSUMPTION_YESTERDAY","ENERGY_EXCESS_LAST_24H","ENERGY_EXCESS_YESTERDAY","ENERGY_GENERATION_LAST_24H","ENERGY_GENERATION_YESTERDAY","ENERGY_IMPORT","ENERGY_IMPORT_YESTERDAY","ENERGY_EXPORT","ENERGY_EXPORT_YESTERDAY","EXPECTED_OUTSIDE_TEMPERATURE","EXPECTED_OUTSIDE_TEMPERATURE_4DAYS","GRID_POWER_CONSUMPTION","GRID_POWER_CONSUMPTION_ANOMALY_SCORE","IS_LOW_TARIFF_HOURS","POWER_CONSUMPTION","POWER_CONSUMPTION_FORECAST_1H","POWER_CONSUMPTION_FORECAST_24H","POWER_EXCESS","POWER_GENERATION","POWER_GENERATION_FORECAST_1H","POWER_GENERATION_FORECAST_1H_MIN","POWER_GENERATION_FORECAST_24H","POWER_GENERATION_FORECAST_48H","POWER_GENERATION_FORECAST_DAY_AFTER_TOMORROW","POWER_GENERATION_FORECAST_TOMORROW","SELF_CONSUMPTION_RATE","SELF_CONSUMPTION_RATE_YESTERDAY","SELF_SUFFICIENCY_RATE","SELF_SUFFICIENCY_RATE_YESTERDAY","STATE_OF_CHARGE_FORECAST_RATE","STATE_OF_CHARGE_RATE","SWITCH_STATE","TEMPERATURE","WATER_TEMPERATURE","POWER_CONSUMPTION_BASE_LOAD","DYNAMIC_TARIFF_PRICE","DYNAMIC_TARIFF_PRICE_FORECAST_1H","DYNAMIC_TARIFF_LOWEST_PRICE_FORECAST_TOMORROW","IS_HIGH_TARIFF_HOURS","ENERGY_EXCESS","ENERGY_BASE_CONSUMPTION","ENERGY_CONSUMPTION_FORECAST_1H","ENERGY_CONSUMPTION_FORECAST_24H","ENERGY_GENERATION","ENERGY_GENERATION_FORECAST_1H","ENERGY_GENERATION_FORECAST_24H","GRID_ENERGY_CONSUMPTION","GRID_ENERGY_CONSUMPTION_YESTERDAY","GRID_ENERGY_CONSUMPTION_ANOMALY_SCORE","GRID_ENERGY_BASE_CONSUMPTION","ENERGY_GENERATION_FORECAST_DAY_AFTER_TOMORROW","ENERGY_GENERATION_FORECAST_TOMORROW","GLOBAL_HORIZONTAL_IRRADIATION_FORECAST_TOMORROW_HOURLY","GLOBAL_HORIZONTAL_IRRADIATION_FORECAST_TOMORROW","ENERGY_CONSUMPTION_FORECAST_TOMORROW","ENERGY_CONSUMPTION_FORECAST_DAY_AFTER_TOMORROW","GRID_BASE_LOAD_CONSUMPTION","DYNAMIC_TARIFF_PRICE_FORECAST_24H","GRID_POWER_CONSUMPTION_YESTERDAY"]},"expectedActionSourceType":{"type":"string","enum":["APPLIANCE","BATTERY","CAR","CAR_CHARGER","ENERGY_MEASUREMENT","GATEWAY","HEAT_PUMP","INPUT_DEVICE","INVERTER","MOOST","SMART_METER","SMART_PLUG","SWITCH","THERMOSTAT","THERMAL_ZONE","THERMAL_STORAGE","WATER_HEATER","WALL_TABLET","SOLAR_PANEL","DISHWASHER","DRYER","ENTERTAINMENT","LIGHTING","OVEN","REFRIGERATION","WASHING_MACHINE"]}},"required":["expectedAction","expectedActionEventType","expectedActionSourceType"]}}},"paths":{"/rules/v1":{"get":{"tags":["PublicAPI"],"summary":"Get Rules","operationId":"getRulesV1","parameters":[{"name":"tags","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Rule"}}}}},"404":{"description":"Not Found","content":{"*/*":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}}}}}}}
```

{% hint style="warning" %}
A comma-separated list can be used to query rules with multiple tags (e.g. security,network). *This would return rules tagged with `security`* ***AND*** *`network`.*
{% endhint %}


# Datasets

The list below the [Data graph](/platform-manual/rules/rule-configurator/data-graph) represents the datasets that are configured for the rule. Each dataset has a color assigned so that it is clear which line in the Data graph belongs to which dataset.

<figure><img src="/files/8e89Kc5lxtxhKNEsiK1t" alt=""><figcaption></figcaption></figure>

## Actions

* Delete a dataset by clicking on the "x"-cross icon on the right side of a dataset.
* Change a dataset by clicking on the dataset name, which opens the [dataset configuration form.](#dataset-configuration-form)
* Create a dataset by clicking on "Add Dataset", which opens the [dataset configuration form](#dataset-configuration-form).

## Dataset Configuration Form

A dataset defines a set of data points which can be included in the rule's *condition* and the *notification message*.&#x20;

<figure><img src="/files/gEDAm3dVRhRvOqB1FZPo" alt="" width="386"><figcaption></figcaption></figure>

A dataset consists of the following properties:

### Name

The name of the dataset. It is recommended to use a self-explaining name. This name can be used as variable in rule condition and rule message.

### Description

This field may be used to describe the dataset in more detail.  This field is optional

### Type

This field defines whether we are interested only in a single value, or a whole time series:

* *Single Value*: only the last matching event is relevant
* *Time Series*: all matching events of specified timeframe are relevant&#x20;

### *Event Types*&#x20;

This field defines which event type(s) shall be matched.

### *Source Types*

This field defines which source(s) shall be matched. This field is optional

### *Validity Time Frame*&#x20;

This field is only visible if type *Time Series* is selected, and it defines the size of the timeframe for the time series.


# Condition

A condition defines a boolean expression. If this condition is fulfilled (*true*), then a notification is created which eventually then is going to be delivered to the customer.

<figure><img src="/files/v3QkHIDZf1N32t1JnRWW" alt=""><figcaption><p>This condition shall trigger when the predicted power generation for tomorrow is lower then yesterday's power consumption, and car charges only with photovoltaics.</p></figcaption></figure>

The condition can be expressed as a mathematical expression producing a boolean value, which consists of:

* dataset references \
  E.g. `$MyConsumption` refers to the dataset with name *MyConsumption*
* values \
  E.g. number `1000`
* operators \
  E.g. plus operator `+`, or comparison operator `=`
* functions \
  E.g. average function `AVG(...)`, which calculates the average of specified time series

Have a look at [Rule Language](/platform-manual/rules/rule-language) to see the full feature set of the expression language.


# Streak

A rule can be used for Streaks. Therefore you can turn on the "Streak" feature, and define a streak condition.

<figure><img src="/files/VhQzql2FO2SEN1MQTpgc" alt=""><figcaption><p>Streak Rule</p></figcaption></figure>

The streak condition can be expressed as a mathematical expression producing a boolean value. It works exactly the same as the "Condition" field.&#x20;

Have a look at [Rule Language](/platform-manual/rules/rule-language) to see the full feature set of the expression language.

A notification is sent if both the "Condition" *and* the "Streak Condition" expression is fulfilled. If the "Streak Condition is fulfilled, the streak counter is incremented, otherwise the streak counter is reseted.

The variable StreakCounter can also be included in the message text or the title of a notification. It represents how many times the streak was already matched.

<figure><img src="/files/lCM38SPWZiveLdLDjIru" alt=""><figcaption></figcaption></figure>


# Goal-Achieved Condition

A rule can contain a "Goal-achieved condition". When this feature is enabled, then you can specify a condition so that you are able to measure the success when delivering a notification.

<figure><img src="/files/A1fu8mVKQM2xu2jPo3kY" alt=""><figcaption></figcaption></figure>

The "Goal-achieved condition" can be expressed as a mathematical expression producing a boolean value. It works exactly the same as the "Condition" field.&#x20;

Have a look at [Rule Language](/platform-manual/rules/rule-language) to see the full feature set of the expression language.

When a "Goal-achieved condition" has been specified, then whenever a notification was delivered, then it evaluates periodically this condition until it either becomes 'true' (i.e. measures *goal achieved*), or until it 7 days have passed.


# Notification Templates

When a rule's condition matches, then a notification is created and possibly sent to the customer. A notification consists of a message title, a message text, and actions (primary & secondary). It needs to be defined for all languages, which have been enabled for your company.\
Additionally a notification may also contain a command, which contains an instruction for a machine-to-machine interface.\ <br>

<figure><img src="/files/R2oRM2T6BJFxLQWbpqWS" alt=""><figcaption></figcaption></figure>

## Multiple Notification Cases

In case the *Multiple Notification Cases* toggle is active, you are able to specify a set of notification templates.&#x20;

![](/files/8Wbu7dCOFF6x63gDDWCK)

<figure><img src="/files/sbYxxgRhNfRAqoaGwyIC" alt=""><figcaption></figcaption></figure>

Each template has an additional "*Label*" field, which describes what this case is about, and an additional "*Case Condition*" field, which is used to decide which notification case is to be chosen. \
This is done by going through the cases list from the top until the first case condition matches. If no case condition matches, then no notification template is selected (i.e. no notification would be sent).

<figure><img src="/files/R7Ya8z4JFHD8KKefh5cp" alt=""><figcaption></figcaption></figure>

Hint: in case you want to specifiy a "default" case, add this case to the bottom of the list, and set "*Case Condition*" value to "true".

## Message

### Message Title

This field contains the content for the message title.

### Message Text

This field contains the content for the message text, and may contain dynamic data, so that e.g. the effective grid power consumption of the last week may be embedded into the message, with a suitable formatting.

IMPORTANT: this field does not expect a static text, like in the message title, but same syntax as expected in the [Condition](/platform-manual/rules/rule-configurator/condition) field.  I.e. it has to be expressed as a mathematical expression which produces a *text*. It consists of:

* dataset references\
  E.g. `$MyConsumption` refers to the dataset with name *MyConsumption*
* values \
  E.g. text `"Excpected consumption is..."` (text can be wrapped in single or double quotes)&#x20;
* operators \
  E.g. plus operator `+` (for text concatination, or number addition), or format operator `|` to format numbers, etc
* functions \
  E.g. average function `MAX(...)`, which finds the maximum value of specified time series

Have a look at [Rule Language](https://app.gitbook.com/o/mHVsgmp8VieATykD486b/s/kz3AVy8mukfhOsBy0Lvo/~/changes/94/platform-manual/rules/rule-configurator/moost-rule-language) to see the full feature set of the expression language.

### Primary and Secondary Action Text

This field contains the text which should be displayed for the primary and secondary action.

### Primary and Secondary Action

This field defines the action category of the primary and secondary action.

## Command

Optionally, you may specify a command containing an instruction for a machine-to-machine interface so that some actions could be triggered. The command is expected to be a [Rule Language](https://app.gitbook.com/o/mHVsgmp8VieATykD486b/s/kz3AVy8mukfhOsBy0Lvo/~/changes/94/platform-manual/rules/rule-configurator/moost-rule-language) expression.

When a rule's condition matches, then the notification will not only contain a message but the evaluated expression.

**Example**

<figure><img src="https://doc.moost.io/~gitbook/image?url=https%3A%2F%2F2791588365-files.gitbook.io%2F%7E%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252Fkz3AVy8mukfhOsBy0Lvo%252Fuploads%252FgGZdp7bZ2PurusMjeid7%252Fimage.png%3Falt%3Dmedia%26token%3Dd2d7e38a-3c90-4ef8-86b8-c1adc9109b67&#x26;width=768&#x26;dpr=4&#x26;quality=100&#x26;sign=cef72d7f&#x26;sv=2" alt=""><figcaption></figcaption></figure>

Have a look at [Rule Language](https://app.gitbook.com/o/mHVsgmp8VieATykD486b/s/kz3AVy8mukfhOsBy0Lvo/~/changes/94/platform-manual/rules/rule-configurator/moost-rule-language) to see the full feature set of the expression language.


# Settings

Besides datasets, condition and message, the rule has a couple of additional settings which can be set.

<figure><img src="/files/dKIV7bmNuaasSq2E2bAl" alt=""><figcaption></figcaption></figure>

## Time-based rule

A standard rule is evaluated each time an event has entered the platform for which the rule has a dataset configured. This can be changed by activating the toggle labeled as "Time-based rule". By activating this toggle, the input field "Time-based trigger" will be enabled and is then required.

<figure><img src="/files/bSC0AkXsIkmzXLSCUZdx" alt=""><figcaption></figcaption></figure>

A rule with "Time-based trigger" activated will no longer be evaluated each time an event enters the platform for which a dataset is configured. The rule will only be evaluated based on the point in time that is defined via the input field "Time-based trigger".

The field accepts a time definition in [cron notation](https://crontab.guru/) which deines when the rule should be evaluated.

## Reset state when matched

On a standard Rule, a configured dataset is a First-in-First-Out Stack. Which means, when the total amount of events is reached (defined by the timespan of the dataset), when a new event enters the platform the oldest event will be thrown off the stack. The Dataset will never be cleared. Even, when a notification is sent the dataset is not emptied.

The toggle "Reset state when matched" forces the dataset to be emptied for the according household each time a Notification is generated. This can be useful, e.g. when having datasets that fill over a certain period of time and should earliest generate another notification after this timeframe has passed again.

## Name

The name of the rule. The name is for internal use only (i.e. is not customer-facing), has to be unique, and should give a good understanding what the rule is about.

## Description

This field may be used to describe the rule in more detail. This is for internal use only (i.e. is not customer-facing).

## Match Threshold

This field defines, how often a condition has to be met, before the message is delivered.

If this value is set to "0" (default), then it will always be delivered.

## Time between Notifications

When a message has been delivered, we might want to make sure that the same rule does not trigger another message for the same building for some time. This can be defined in this field.

Messages which would have been sent, but were within this time range, can be seen in the [data graph](https://app.gitbook.com/o/mHVsgmp8VieATykD486b/s/kz3AVy8mukfhOsBy0Lvo/~/changes/94/platform-manual/rules/rule-configurator/data-graph) as *Dropped Notifications*.


# Rule Simulator

The Rule Simulator is a logical component which allows us to simulate changes or new rules based on existing data of existing buildings.

The simulator runs the current rule configuration against the datasets and the time rang which is selected. The notifications which would have been generated and sent to the message queue and eventually to the user are shown as green letters on top.

<figure><img src="/files/CMAD6y1E59orYqwZrZSW" alt=""><figcaption><p>Datagraph after Simulation run</p></figcaption></figure>

When hovering over the green notification icons, the text is shown, which would have been transmitted to the customer and eventually to the end user. Even placeholders are replaced with the actual text so that logic components within the message can be tested, before a new rule or changes to an existing rule go live.

<figure><img src="/files/wpinb1GqIbSd23HJGTok" alt=""><figcaption><p>Simulated Notification with replaced placeholders</p></figcaption></figure>


# Early Adopter

When writing a new rule, you may turn on the rule only for *early adopter* buildings. This is great to gain in-sight and to collect feedback from early adopters, before turning on this rule for all buildings.

<figure><img src="/files/DC5zBse9Bmv1CJXAjV2N" alt=""><figcaption></figcaption></figure>

Activating the toggle will only send messages to buildings flagged as Early Adopters. You can set buildings as Early Adopters on the Detail Page of the respective building.


# Command

Optionally, you may specify a command containing an instruction for a machine-to-machine interface so that some actions could be triggered. The command is expected to be a [Rule Language](https://app.gitbook.com/o/mHVsgmp8VieATykD486b/s/kz3AVy8mukfhOsBy0Lvo/~/changes/94/platform-manual/rules/rule-configurator/moost-rule-language) expression.

When a rule's condition matches, then the notification will not only contain a message but the  evaluated expression.

**Example**

<figure><img src="/files/2FZH5BqAO9GgNTF3kufH" alt=""><figcaption></figcaption></figure>

Have a look at [Rule Language](https://app.gitbook.com/o/mHVsgmp8VieATykD486b/s/kz3AVy8mukfhOsBy0Lvo/~/changes/94/platform-manual/rules/rule-configurator/moost-rule-language) to see the full feature set of the expression language.


# Rule Language

The *Rule Language* is a powerful feature which we use in the *Recommender Platform* to process event streams, test condition rules, and produce relevant and precise recommendation messages.

Expressions in *Rule Language* look similar to expressions in Excel or other comparable tools or script/program languages.

To give a first impression, have a look at the following examples, which give an idea how the language can be used:

## Examples

Example of a boolean expression which compares multiple values and uses AND/OR operators:

<pre class="language-java" data-full-width="false"><code class="lang-java"><strong>$GridPowerConsumption > 500 AND ($CarChargingMode = 0 OR $CarChargingMode = 4) 
</strong>AND $IsLowTariffHours = 0
</code></pre>

Example of a text expression, which concatenates static text and dynamic data:

```java
"Heat: " + $HeatCelsius::Value + " °C / " + ($HeatCelsius::Value - 273.15) + " K"
```

## Details

Read in the next sub-chapters about the language [syntax](/platform-manual/rules/rule-language/syntax), the [data types](/platform-manual/rules/rule-language/data-types) and [structures](/platform-manual/rules/rule-language/data-structures), and how to process these with the help of [functions](/platform-manual/rules/rule-language/functions) and [operations](/platform-manual/rules/rule-language/operations).&#x20;


# Syntax

In this example we see already all core elements of the language.&#x20;

In the following chapters you see many more examples, and all details about what kind of functions and operations exist, and how you can compose the data.

Example of a condition term:

<pre class="language-java" data-overflow="wrap" data-full-width="false"><code class="lang-java"><strong>$GridPowerConsumption > 500 AND ($CarChargingMode = 0 OR $CarChargingMode = 4) AND $IsLowTariffHours = 0
</strong></code></pre>

Example of a message term:

{% code overflow="wrap" fullWidth="false" %}

```java
"Sie beziehen aktuell " + ($GridPowerConsumption / 1000) + "kW vom Netz."
```

{% endcode %}

Following syntax base components exist:

## Literals

### Variables

#### Example

```java
$GridPowerConsumption
```

Example of a variable referring to the dataset with name "PowerConsumption"

### Values

#### Example

```java
1.5
```

Example of a *number* value&#x20;

```java
"it is cold"
```

Example of a *text* value&#x20;

```java
30min
```

Example of a *timespan* value

```java
now
```

Example of a time value

## Functions

#### Example

```java
AVG($PowerConsumption)
```

Example for calculating the average

## Operations

#### Example

```java
1.2 * 42
```

Example for multiplying two numbers

## Brackets

Brackets may be needed to control the resolution order of the terms in the expression. \
This is very familiar to us of course in mathematical terms:

#### Example

```java
((4 + 0.5) / (4 - 0.5)) * 2
```


# Data Types

In the [Values section](/platform-manual/rules/rule-language/syntax#values) featured already some data types. Each literal has a specific data type. Here you see the full list:

## Boolean

<table><thead><tr><th width="176">Literal</th><th>Description</th></tr></thead><tbody><tr><td>true</td><td>Value for true / fulfilled</td></tr><tr><td>false</td><td>Value for false / not fulfilled</td></tr></tbody></table>

### Examples

```java
true
```

```java
AVG($PowerConsumption) > 1000
```

## Number

A decimal number

### Examples

```java
42.0
```

```java
$Event::Value * 1.5
```

## Time

A time which consists of date and day time.

Besides of event times, we can also use one of following supported literals:

<table><thead><tr><th width="245">Literal</th><th>Description</th></tr></thead><tbody><tr><td>now</td><td>The current time. This produces e.g. <em>2023-09-25 23:26</em></td></tr><tr><td>lastHour</td><td>The last full hour.  E.g. <em>2023-09-25 23:00</em></td></tr><tr><td>lastZeroHour</td><td>The last zero hour (midnight). E.g. 2023-09-25 00:00</td></tr><tr><td>startOfLastMonth</td><td>The first day of the previous month. E.g. 2024-05-01 00:00</td></tr><tr><td>endOfLastMonth</td><td>The last day of the previous month. E.g. 2024-05-31 00:00</td></tr><tr><td>startOfMonthBeforeLast</td><td>The first day of the penultimate month E.g. 2024-04-01 00:00</td></tr><tr><td>endOfMonthBeforeLast</td><td>The last day of the penultimate month E.g. 2024-04-30 00:00</td></tr><tr><td>startOfCurrentMonth</td><td>The first day of the current month. E.g. 2024-06-01 00:00</td></tr><tr><td>endOfCurrentMonth</td><td>The last day of the current month. E.g. 2024-06-30 00:00</td></tr></tbody></table>

### Examples

```java
$Event::Timestamp
```

```java
now - 24h
```

## Timespan

A timespan.

A timespan literal is a combination of a number and a time unit. Following time units are supported:

<table><thead><tr><th width="180">Literal</th><th>Description</th></tr></thead><tbody><tr><td>d</td><td>Days</td></tr><tr><td>h</td><td>Hours</td></tr><tr><td>min</td><td>Minutes</td></tr><tr><td>s</td><td>Seconds</td></tr></tbody></table>

### Examples

```java
7d
```

```java
24h
```

```java
30min
```

## Text

A text element

Everything that begins and ends with a single or double quotation mark is considered a text element. This also includes elements that cannot be assigned to one of the other data types. For example, if $Event was used but no such variable was defined, it would also be considered a text element.

Remark: we recommend to use one of the quoted forms, which makes it crystal clear for the readers that this is a text.

### Examples

```java
'foo'
```

```java
"bar"
```

```java
foo
```

## Event

An event, which consists of:

<table><thead><tr><th width="179">Attributes</th><th>Description</th></tr></thead><tbody><tr><td><code>Value</code></td><td>The value of the event, which is of type <a href="#number"><em>Number</em></a></td></tr><tr><td><code>Timestamp</code></td><td>The timestamp of the event, which is of type <a href="#time"><em>Time</em></a></td></tr><tr><td><code>DeviceName</code></td><td>The name of the device which is the source of this event. It is of the of type <a href="#text"><em>Text</em></a></td></tr><tr><td><code>DeviceId</code></td><td>The ID of the device, which is the source of this event. It is of the of type <a href="#text"><em>Text</em></a></td></tr></tbody></table>

### Examples:

```java
$Event::DeviceName
```

## Building

The building related to the event, so that building related data can be accessed via *Attribute Accessor*, see:[Attribute Accessor](/platform-manual/rules/rule-language/operations/attribute-accessor#accessor-on-building).

<table><thead><tr><th width="268">Attributes</th><th>Description</th></tr></thead><tbody><tr><td><code>Id</code></td><td>The value of the event, which is of type <a href="#text">Text</a></td></tr><tr><td><code>RegistrationTimestamp</code></td><td>The timestamp when the building was registered, i.e. the on-boarding timestamp on the customer side. It is of type <a href="#time"><em>Time</em></a></td></tr><tr><td><code>DeviceTypes</code></td><td>The set of device types (which relates to the "source" entries in the events). It is a Vector of type <a href="#text">Text</a>.</td></tr></tbody></table>

### Examples:

```java
Building::DeviceTypes
```


# Data Structures

When having a look at some previous examples, it might not be directly obvious, but we are actually not always working just with single values, such as a number. We also had a look at examples which are actually working with a multi-value set, such as when calculating an *average*.&#x20;

So each literal has not only a [Data Type](/platform-manual/rules/rule-language/data-types), but also a [Data Structure](/platform-manual/rules/rule-language/data-structures). Here you see the full list:

## Scalar

A single value is stored in a *Scalar* data structure.&#x20;

Generally all [values](/platform-manual/rules/rule-language/syntax#values) are Scalars, but you typically also get Scalars when evaluating boolean or arithmetic operations.

### Examples

```java
99.5
```

Example with `$PowerConsumption` with dataset of name *PowerConsumption* of type Single Value:

```java
AVG($PowerConsumption)
```

## **Vector**

&#x20;A set of values is stored in a Vector data structure.

Generally only [variables](/platform-manual/rules/rule-language/syntax#variables), or operations and functions on top of [variables](/platform-manual/rules/rule-language/syntax#variables), may produce Vectors.

### Examples:

Example with `$PowerConsumption` with dataset of name *PowerConsumption* of type Time Series:

```java
SUBSET($PowerConsumption, now-24h)
```

## **GroupedScalar**

A list of single values, grouped by device or time.

Generally only [variables](/platform-manual/rules/rule-language/syntax#variables) in combination with a `GROUP_BY_DEVICE` or `GROUP_BY_TIME` function may produce a GroupedScalar.

### Examples

```java
AVG(GROUP_BY_DEVICE($PowerConsumption)) with Dataset of name PowerConsumption with type Time Series
```

## **GroupedVector**

A list of list of value sets, grouped by device or time.

Generally only [variables](/platform-manual/rules/rule-language/syntax#variables) in combination with a `GROUP_BY_DEVICE` or `GROUP_BY_TIME` function may produce a GroupedVector.

### Examples

```java
GROUP_BY_DEVICE($PowerConsumption) with Dataset of name PowerConsumption with type Time Series
```


# Literals

## Value

A literal may consist of a fix value, such as a number, a text block, a boolean value, a time span or a time.

### Examples

```
1.5
"it is cold"
true
30min
now
```

## Variables

Literals starting with a `$` sign are variables. The refer the data set with the same name.

### Examples

```
$PowerConsumption
```

## Building

The building related to the event, so that building related data can be accessed via *Attribute Accessor*, see:[Attribute Accessor](/platform-manual/rules/rule-language/operations/attribute-accessor#accessor-on-building).

### Examples:

```java
Building::Id
Building::DeviceTypes
```

## DeliveredNotificationCounter

When the rule uses this literal then returns the number of notifications that have been delivered since the creation of the rule. It is of type [*Number*](#number).&#x20;

{% hint style="warning" %}
The DeliveredNotificationCounter does not count Test Notifications that were delivered through "Send Test Notification" Feature.
{% endhint %}

### Examples

```java
DeliveredNotificationCounter % 3
```

## DeliveredNotification

This liberal can be used only in "Goal-achieved condition". It refers to the notification which has been delivered, so that you may be able to check the time when the notification was created, or when the possible interaction happened.&#x20;

{% hint style="warning" %}
The DeliveredNotification can only be used in "Goal-achieved condition" (see [Goal-Achieved Condition](/platform-manual/rules/rule-configurator/streak-1)).
{% endhint %}

### Examples

```java
DeliveredNotificationCounter % 3
```

## StreakCounter

When the rule has the "Streak" feature enabled, then the literal "StreakCounter" returns the current streak of this rule. It is of type [*Number*](#number).

See [Streak](/platform-manual/rules/rule-configurator/streak)for more details about the *Streak* feature.

### Examples

```java
StreakCounter
```

## StreakHasBeenReset

When the rule has the "Streak" feature enabled, then the literal "StreakHasBeenReset" returns the current streak of this rule. It is of type [Data Types](/platform-manual/rules/rule-language/data-types#boolean)

See [Streak](/platform-manual/rules/rule-configurator/streak)for more details about the *Streak* feature.

### Examples

```java
StreakHasBeenReset
```


# Functions

Every function is of form `FCT-NAME(argument1, ...)`, expects one or more arguments, and returns a value.

In the following list, each function is listed, including the [data structure](/platform-manual/rules/rule-language/data-structures) and [data type](/platform-manual/rules/rule-language/data-types) of each argument, and the return value. This is done by using form *`DataStructure<DataType>`*.

## Overview

Following functions are provided:

[AVG](/platform-manual/rules/rule-language/functions/avg)

[COUNT](/platform-manual/rules/rule-language/functions/count)

[DISTINCT](/platform-manual/rules/rule-language/functions/distinct)

[EVAL](/platform-manual/rules/rule-language/functions/avg-1)

[FILTER](/platform-manual/rules/rule-language/functions/filter)

[GROUP\_BY\_DEVICE](/platform-manual/rules/rule-language/functions/group_by_device)

[GROUP\_BY\_TIME](/platform-manual/rules/rule-language/functions/group_by_time)

[MAX](/platform-manual/rules/rule-language/functions/max)

[MIN](/platform-manual/rules/rule-language/functions/min)

[POSITION](/platform-manual/rules/rule-language/functions/avg-2)

[REVERSE](/platform-manual/rules/rule-language/functions/reverse)

[SORT](/platform-manual/rules/rule-language/functions/sort)

[SUBSET](/platform-manual/rules/rule-language/functions/subset)

[SUM](/platform-manual/rules/rule-language/functions/sum)


# AVG

### Description

Calculates the average on a set of numbers or event values

### AVG(Vector\<Number/Event>)

```java
AVG(vector: Vector<Number/Event>): Scalar<Number>
```

Calculate the average as Scalar\<any> from the given parameter.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type &#x3C;Number/Event></td></tr></tbody></table>

#### Returns

Returns the average as Scalar\<Number>

***

### AVG(GroupedScalar\<any, Number/Event>)

```java
AVG(group: GroupedScalar<any, Number/Event>): Scalar<Number>
```

Calculate the average as Scalar\<any> from the given parameter.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedScalar of type &#x3C;any, Number/Event></td></tr></tbody></table>

#### Returns

Returns the average as Scalar\<Number>

***

### AVG(GroupedVector\<any, Number/Event>)

```java
AVG(group: GroupedVector<any, Number/Event>): GroupedScalar<Number>
```

Calculate the average as GroupedScalar\<any> from the given parameter.

#### Parameters

<table><thead><tr><th width="219">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedVector of type &#x3C;any, Number/Event></td></tr></tbody></table>

#### Returns

Returns the average as Scalar\<Number>

***

### Examples

Retrieve the average of the GridPowerConsumption time series.

```java
AVG($GridPowerConsumption)
```


# COUNT

### Description

Counts the number of entries on a *vector, grouped scalar,* or *grouped vector.*

### COUNT(Vector\<any>)

```java
COUNT(vector: Vector<any>): Scalar<Number>
```

Count the entries of the given Vector\<any>.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type &#x3C;any></td></tr></tbody></table>

#### Returns

Returns how many elements are within the given vector as Scalar\<Number>

***

### COUNT(GroupedScalar\<any, any>)

```java
COUNT(group: GroupedScalar<any, any>): Scalar<Number>
```

Count the entries of the given GroupedScalar\<any, any>.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedScalar of type &#x3C;any, any></td></tr></tbody></table>

#### Returns

Returns how many elements are within the given group as Scalar\<Number>

***

### COUNT(GroupedVector\<any, any>)

```java
COUNT(group: GroupedVector<any, any>): Scalar<Number>
```

Count the entries of the given GroupedVector\<any, any>.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedVector of type &#x3C;any, any></td></tr></tbody></table>

#### Returns

Returns how many elements are within the given group as Scalar\<Number>

***

### Examples

Count the elements within the GridPowerConsumption time series.

```java
COUNT($GridPowerConsumption)
```


# DISTINCT

### Description

Removes duplicate entries. So if applying on ascending ordered data, you would get a descending ordered data set.

### DISTINCT(Scalar\<any>)

```java
DISTINCT(scalar: Scalar<any>): Scalar<any>
```

Returns the same scalar (because a scalar cannot have any duplicates).

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>scalar</td><td>A scalar of type any</td></tr></tbody></table>

### DISTINCT(Vector\<any>)

```java
DISTINCT(vector: Vector<any>): Vector<any>
```

Returns the same elements, but without any duplicates.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type any</td></tr></tbody></table>

***

### DISTINCT(GroupedScalar\<any, any>)

```java
DISTINCT(group: GroupedScalar<any,any>): GroupedScalar<any,any>
```

Returns the same GroupedScalar (because a scalar cannot have any duplicates).

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedScalar of type &#x3C;any, any></td></tr></tbody></table>

***

### DISTINCT(GroupedVector\<any, any>)

```java
DISTINCT(group: GroupedVector<any,any>): GroupedVector<any,any>
```

Returns the same GroupedVector, but removes all duplicates in the values of each group.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedVector of type &#x3C;any, any></td></tr></tbody></table>

### Examples

Removes duplicated entries in the building's device types Vector.

```java
DISTINCT(Building::DeviceTypes)
```


# EVAL

### Description

Evaluates the `buildingSelector` for the given `datasetName` and returns a `GroupedVector`, where:

* The **key** is the `customerBuildingId`.
* The **value** is a `Vector` containing all event values from the dataset, grouped by `customerBuildingId`.

### EVAL(Text, AttributeOperator)

```java
POSITION(datasetName: Text, buildingSelector: AttributeOperator): GroupedVector<Text, Vector<Number>>
```

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>datasetName</td><td>The datasetName for which we want to load all events for the buildings specified by the buildingSelector.</td></tr><tr><td>buildingSelector</td><td><p>The buildingSelector as AttributeOperator which specifies for which buildings we want to load the events specified by datasetName. Valid values are:<br></p><p><em>Building::IsSelfSufficiencyMotivated</em><br><em>Building::IsEcologicalMotivated</em><br><em>Building::IsSelfSufficiencyMotivated</em><br><em>Building::IsMultiPerson</em><br><em>Building::IsSinglePerson</em><br><em>Building::IsResidential</em><br><em>Building::IsCommercial</em><br><em>Building::IsInSameCountry</em><br><em>Building::IsInSameConsumptionCategory</em><br><em>Building::All</em></p></td></tr></tbody></table>

#### Returns

Returns a `GroupedVector` where:

* The **key** is the `customerBuildingId`.
* The **values** are vectors containing event values, grouped by household as specified in `datasetName`.


# FILTER

### Description

Filter data on a *vector,* *grouped scalar* or *grouped vector*, by comparing with a threshold. Only data which fulfills that boolean condition is kept.

### Filter(Vector\<any>, Scalar\<Text>, Scalar\<any>)

```java
FILTER(vector: Vector<any>, 
       comparisonSymbol: Scalar<Text>, 
       threshold: Scalar<any>): Vector<any>
```

Filter data in the given *vector* by comparing with the threshold.

#### Parameters

<table><thead><tr><th width="203">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A <em>vector</em> whose data is to be filtered</td></tr><tr><td>comparisonSymbol</td><td>The comparison symbol. One of: <code>&#x3C;</code>, <code>&#x3C;=</code>, <code>=</code>, <code>!=</code>, <code>>=</code>, <code>></code></td></tr><tr><td>threshold</td><td>A <em>scalar</em> which is used to filter data.</td></tr></tbody></table>

#### Returns

*Vector* of same type as from the first function parameter, with filtered data.

### Examples

Filter events, so that only values > 1000 are kept in the vector.

```java
FILTER($GridPowerConsumption, '>', 1000)
```

***

### Filter(GroupedScalar\<any, any>, Scalar\<Text>, Scalar\<any>)

```java
FILTER(groupedScalar: GroupedScalar<any, any>, 
       comparisonSymbol: Scalar<Text>, 
       threshold: Scalar<any>): GroupedScalar<any, any>
```

Filter data in the given *grouped scalar* by comparing with the threshold. Empty groups are removed from the *grouped scalar.*

#### Parameters

<table><thead><tr><th width="203">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>groupedScalar</td><td>A <em>vector</em> whose data is to be filtered.</td></tr><tr><td>comparisonSymbol</td><td>The comparison symbol. One of: <code>&#x3C;</code>, <code>&#x3C;=</code>, <code>=</code>, <code>!=</code>, <code>>=</code>, <code>></code></td></tr><tr><td>threshold</td><td>A <em>scalar</em> which is used to filter data.</td></tr></tbody></table>

#### Returns

*Grouped scalar* of same type as from the first function parameter, with filtered data.

### Examples

Filter events in the *grouped scalar*, so that only values > 1000 are kept in the vector.

```java
FILTER(MIN(GROUP_BY_DEVICE($PowerConsumption)), '!=', 0)
```

***

### Filter(GroupedVector\<any, any>, Scalar\<Text>, Scalar\<any>)

```java
FILTER(groupedVector: GroupedVector<any, any>, 
       comparisonSymbol: Scalar<Text>, 
       threshold: Scalar<any>): GroupedVector<any, any>
```

Filter data in the given *grouped vector* by comparing with the threshold. Empty groups are removed from the *grouped vector.*

#### Parameters

<table><thead><tr><th width="203">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>groupedVector</td><td>A <em>vector</em> whose data is to be filtered.</td></tr><tr><td>comparisonSymbol</td><td>The comparison symbol. One of: <code>&#x3C;</code>, <code>&#x3C;=</code>, <code>=</code>, <code>!=</code>, <code>>=</code>, <code>></code></td></tr><tr><td>threshold</td><td>A <em>scalar</em> which is used to filter data.</td></tr></tbody></table>

#### Returns

*Grouped vector* of same type as from the first function parameter, with filtered data.

### Examples

Filter events, so that only values > 1000 are kept in the *grouped vector*.

```java
FILTER(GROUP_BY_DEVICE($PowerConsumption), '<=', 200)
```


# GROUP\_BY\_DEVICE

### Description

Converts *vector* to a *grouped vector* by grouping by devices. I.e. each device has its own list of events.

### GROUP\_BY\_DEVICE(Vector\<Event>)

```java
GROUP_BY_DEVICE(vector: Vector<Event>): GroupedVector<Device,Event>
```

Groups the events in the given vector by deviceId.

#### Parameters

<table><thead><tr><th width="138">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type Event</td></tr></tbody></table>

#### Returns

A GroupedVector of type \<Device,Event>.

### Examples

Group the events in the GridPowerConsumption vector by device id.

```java
GROUP_BY_DEVICE($GridPowerConsumption)
```


# GROUP\_BY\_TIME

### Description

Converts *vector* to a *grouped vector*, by gouping by timespans, so that we have a list of events per timespan. The timespan either is fixed on the latest event, or can be fixed on another time if specified.

### GROUP\_BY\_TIME(Vector\<Event>, Scalar\<Timespan>)

```java
GROUP_BY_TIME(vector: Vector<Event>, timespan: Scalar<Timespan>): GroupedVector<Time,Event>
```

Groups the events in the given vector in spans defined by timespan, starting from the latest event.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type Event</td></tr><tr><td>timespan</td><td>A timespan in which the events should be grouped</td></tr></tbody></table>

#### Returns

A GroupedVector of type \<Time, Event>.

### GROUP\_BY\_TIME(Vector\<Event>, Scalar\<Timespan>, Scalar\<Time>)

```java
GROUP_BY_TIME(vector: Vector<Event>, timespan: Scalar<Timespan>, time: Scalar<Time>): GroupedVector<Time,Event>
```

Group the vector in the given timespans starting at the given time.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type Event</td></tr><tr><td>timespan</td><td>A timespan in which the events should be grouped</td></tr><tr><td>time</td><td> The time form which we want to start grouping.</td></tr></tbody></table>

#### Returns

A GroupedVector of type \<Time, Event>.

### Examples

Group events in 1h spans starting from latest event

```java
GROUP_BY_TIME($GridPowerConsumption, 1h)
```

Group events in 1d spans on full day spans (i.e. 00:00-23:59)

```java
GROUP_BY_TIME($Produced, 1d, lastZeroHour)
```


# MAX

### Description

Extracts the maximum on a set of numbers or event values.

### MAX(Vector\<Number/Event>)

```java
MAX(vector: Vector<Number/Event>): Scalar<Number>
```

Extract the highest value as Scalar\<any> from the given parameter.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type &#x3C;Number/Event></td></tr></tbody></table>

#### Returns

Returns the highest value as Scalar\<Number>

***

### MAX(GroupedScalar\<any, Number/Event>)

```java
MAX(group: GroupedScalar<any, Number/Event>): Scalar<Number>
```

Extract the highest value as Scalar\<any> from the given parameter.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedScalar of type &#x3C;any, Number/Event></td></tr></tbody></table>

#### Returns

Returns the highest value as Scalar\<Number>

***

### MAX(GroupedVector\<any, Number/Event>)

```java
MAX(group: GroupedVector<any, Number/Event>): Scalar<Number>
```

Extract the highest value as Scalar\<any> from the given parameter.

#### Parameters

<table><thead><tr><th width="219">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedVector of type &#x3C;any, Number/Event></td></tr></tbody></table>

#### Returns

Returns the highest value as Scalar\<Number>

***

### Examples

Retrieve the maxiumum of the GridPowerConsumption time series.

```java
MAX($GridPowerConsumption)
```


# MIN

### Description

Extracts the minimum on a set of numbers or event values

### MIN(Vector\<Number/Event>)

```java
MIN(vector: Vector<Number/Event>): Scalar<Number>
```

Extract the lowest value as Scalar\<any> from the given parameter.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type &#x3C;Number/Event></td></tr></tbody></table>

#### Returns

Returns the lowest value as Scalar\<Number>

***

### MIN(GroupedScalar\<any, Number/Event>)

```java
MIN(group: GroupedScalar<any, Number/Event>): Scalar<Number>
```

Extract the lowest value as Scalar\<any> from the given parameter.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedScalar of type &#x3C;any, Number/Event></td></tr></tbody></table>

#### Returns

Returns the lowest value as Scalar\<Number>

***

### MIN(GroupedVector\<any, Number/Event>)

```java
MIN(group: GroupedVector<any, Number/Event>): Scalar<Number>
```

Extract the lowest value as Scalar\<any> from the given parameter.

#### Parameters

<table><thead><tr><th width="219">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedVector of type &#x3C;any, Number/Event></td></tr></tbody></table>

#### Returns

Returns the lowest value as Scalar\<Number>

***

### Examples

Retrieve the minimum of the GridPowerConsumption time series.

```java
MIN($GridPowerConsumption)
```


# POSITION

### Description

Returns the Position of an element within a vector as Scalar\<Number>. The position of the element within the vector is the index of the element within the vector plus 1.

### POSITION(Vector\<Number>)

```java
POSITION(vector: Vector<Number>, targetElement: Scalar<Number>): Scalar<Number>
```

Returns the position of the given targetElement within the given Vector.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type &#x3C;Number> within we search the position of targetElement.</td></tr><tr><td>targetElement</td><td>The targetElement of type &#x3C;Number> we are searching within given vector.</td></tr></tbody></table>

#### Returns

Returns the position of the searched element within the vector as Scalar\<Number>.


# QUANTILE

### Description

Calculates the quantile value on a set of numbers or event values, based on specified quantile.

Remark: to obtain the median value on an array, you can use the `QUANTILE` function with parameter `0.5` (i. e. the 50% quantile).

### QUANTILE(Vector\<Number/Event>, Scalar\<Number>)

```java
QUANTILE(vector: Vector<Number/Event>, Scalar<Number>): Scalar<Number>
```

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type &#x3C;Number/Event></td></tr><tr><td>quantile</td><td>Quantile of type &#x3C;Number>, with value range [0, 1]</td></tr></tbody></table>

#### Returns

Returns the quantile value as Scalar\<Number>

***

### QUANTILE(GroupedScalar\<any, Number/Event>, Scalar\<Number>)

```java
QUANTILE(group: GroupedScalar<any, Number/Event>): Scalar<Number>
```

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedScalar of type &#x3C;any, Number/Event></td></tr><tr><td>quantile</td><td>Quantile of type &#x3C;Number>, with value range [0, 1]</td></tr></tbody></table>

#### Returns

Returns the quantile value as Scalar\<Number>

***

### QUANTILE(GroupedVector\<any, Number/Event>, Scalar\<Number>)

```java
QUANTILE(group: GroupedVector<any, Number/Event>): GroupedScalar<Number>
```

#### Parameters

<table><thead><tr><th width="219">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedVector of type &#x3C;any, Number/Event></td></tr><tr><td>quantile</td><td>Quantile of type &#x3C;Number>, with value range [0, 1]</td></tr></tbody></table>

#### Returns

Returns the quantile value as GroupedScalar\<any, Number>

***

### Examples

Retrieve the 50% percentile of the GridPowerConsumption time series.

```java
QUANTILE($GridPowerConsumption, 0.5)
```


# REVERSE

### Description

Reverses the entries. So if applying on ascending ordered data, you would get a descending ordered data set.

### REVERSE(Scalar\<any>)

```java
REVERSE(scalar: Scalar<any>): Scalar<any>
```

Reverse the order of the given Scalar\<any>

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>scalar</td><td>A scalar of type any</td></tr></tbody></table>

#### Returns

Returns the given scalar in reversed order with type Scalar\<any>

### REVERSE(Vector\<any>)

```java
REVERSE(vector: Vector<any>): Vector<any>
```

Reverse the order of the elements in the given Vector\<any>

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type any</td></tr></tbody></table>

#### Returns

Returns the given vector in reversed order with type Vector\<any>

***

### REVERSE(GroupedScalar\<any, any>)

```java
REVERSE(group: GroupedScalar<any,any>): GroupedScalar<any,any>
```

Reverse the order of the elements in the given GroupedScalar\<any, any>

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedScalar of type &#x3C;any, any></td></tr></tbody></table>

#### Returns

Returns the given group in reversed order with type GroupedScalar\<any, any>.

***

### REVERSE(GroupedVector\<any, any>)

```java
REVERSE(group: GroupedVector<any,any>): GroupedVector<any,any>
```

Reverse the order of the elements in the given GroupedVector\<any, any>

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedVector of type &#x3C;any, any></td></tr></tbody></table>

#### Returns

Returns the given group in reversed order with type GroupedVector\<any, any>.

### Examples

Reverse the elements in the GridPowerConsumption Vector.

```java
REVERSE($GridPowerConsumption)
```


# SORT

### Description

Sorts the entries of the given parameter in ascending order.

### SORT(Scalar\<any>)

```java
SORT(scalar: Scalar<any>): Scalar<any>
```

Sort the entries of the given Scalar\<any> in ascending order.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>scalar</td><td>A scalar of type any</td></tr></tbody></table>

#### Returns

Returns the given scalar sorted in ascending order as type Scalar\<any>.

***

### SORT(Vector\<any>)

```java
SORT(vector: Vector<any>): Vector<any>
```

Sort the entries of the given Vector\<any> in ascending order.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type any</td></tr></tbody></table>

#### Returns

Returns the given vector sorted in ascending order as type Vector\<any>.

***

### SORT(GroupedScalar\<any, any> \[, Scalar\<Text>])

```java
SORT(group: GroupedScalar<any, any> [, by: Scalar<Text>]): GroupedScalar<any, any>
```

Sort the entries of the given GroupedScalar\<any, any> in ascending order by values.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedScalar of type &#x3C;any, any></td></tr><tr><td>by</td><td><p>The by parameter is optional.</p><p></p><p>Specifies by which attribute of the vector it should be sorted.Allowed values are <em>byGroupKey</em> and <em>byGroupValue</em>. </p><p></p><p>Default is byGroupValue.</p></td></tr></tbody></table>

#### Returns

Returns the given group sorted by the specified property in ascending order as type GroupedScalar\<any, any>.

***

### SORT(GroupedVector\<any, any> \[, Scalar\<Text>])

```java
SORT(group: GroupedVector<any, any> [, by: Scalar<Text>]): GroupedVector<any, any>
```

Sort the entries of the given GroupedVector\<any, any> in ascending order by values.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedVector of type &#x3C;any, any></td></tr><tr><td>by</td><td><p>The by parameter is optional.</p><p></p><p>Specifies by which attribute of the vector it should be sorted.Allowed values are <em>byGroupKey</em> and <em>byGroupValue</em>. </p><p></p><p>Default is byGroupKey.</p><p></p><p>If sorting by value, then it does this by comparing the number of values.</p></td></tr></tbody></table>

#### Returns

Returns the given group sorted by the specified property in ascending order as type GroupedScalar\<any, any>.

### Examples

Sort GridPowerConsumption in ascending order.

```java
SORT($GridPowerConsumption)
```

Sort GroupedByDevicePowerConsumption in ascending order by event

```java
SORT($GroupedByDevicePowerConsumption, byGroupKey)
```


# SUBSET

### Description

Extracts a subset of elements from a given *Vector.*&#x20;

### SUBSET(Vector\<Any>, Scalar\<Number>)

Extract the first or last x entries.

<pre class="language-java"><code class="lang-java"><strong>SUBSET(vector: Vector&#x3C;Any>, x: Scalar&#x3C;Number>): Vector&#x3C;Any>
</strong></code></pre>

#### Parameters

<table><thead><tr><th width="163">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type any.</td></tr><tr><td>x</td><td>A positive or negative number. When x is a positive number it starts to extract the number of entries stated in param2 from the beginning, when param2 is a negative number it starts to extract the number of entries stated in param2 from the end of the vector.</td></tr></tbody></table>

#### Returns

Returns a Vector of type any.

***

### SUBSET(Vector\<any>, Scalar\<Number>, Scalar\<Number>)

```java
SUBSET(vector: Vector<Any>, x: Scalar<Number>, y: Scalar<Number>): Vector<Any>
```

#### Parameters

<table><thead><tr><th width="179">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type any.</td></tr><tr><td>x</td><td>Position from which to start extracting elements. If negative, then position is counted from the end of the vector.</td></tr><tr><td>y</td><td>Position until which to extract elements.  If negative, then position is counted from the end of the vector.</td></tr></tbody></table>

### SUBSET(Vector\<Event>, Scalar\<Time>)

```java
SUBSET(vector: Vector<Event>, start: Scalar<Time>): Vector<Event>
```

Extract entries from time *start* until the latest event (left argument is excluding, right is including)

#### Parameters

<table><thead><tr><th width="179">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type Event.</td></tr><tr><td>start</td><td>The start time from which to start extracting entries until the latest.</td></tr></tbody></table>

#### Returns

A Vector of type Event.

***

### SUBSET(Vector\<Event>, Scalar\<Time>, Scalar\<Time>)

```java
SUBSET(vector: Vector<Event>, start: Scalar<Time>, end: Scalar<Time>): Vector<Event>
```

Extract entries from time *start* until time *end* (left argument is excluding, right is including the time border)

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type Event</td></tr><tr><td>start</td><td>The start time from which to start extracting entries. (Excluded)</td></tr><tr><td>end</td><td>The end time until which to extract entries. (Included)</td></tr></tbody></table>

#### Returns

A Vector of type Event.

### Examples

&#x20;Extract the last entry from the vector *GridPowerConsumption*.

```java
SUBSET($GridPowerConsumption, -1)
```

Extract all events from zero hour (midnight) until now.

```java
SUBSET($GridPowerConsumption, lastZeroHour)
```

Extract all events 2 days ago until 1 day ago.

```java
SUBSET($GridPowerConsumption, now-2d, now-1d)
```


# SUM

### Description

Calculates the sum on a set of numbers or event values

### SUM(Vector\<Number/Event>)

```java
SUM(vector: Vector<Number/Event>): Scalar<Number>
```

Calculate the sum as Scalar\<any> from the given parameter.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>vector</td><td>A vector of type &#x3C;Number/Event></td></tr></tbody></table>

#### Returns

Returns the sum as Scalar\<Number>

***

### SUM(GroupedScalar\<any, Number/Event>)

```java
SUM(group: GroupedScalar<any, Number/Event>): Scalar<Number>
```

Calculate the sum as Scalar\<any> from the given parameter.

#### Parameters

<table><thead><tr><th width="172">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedScalar of type &#x3C;any, Number/Event></td></tr></tbody></table>

#### Returns

Returns the sum as Scalar\<Number>

***

### SUM(GroupedVector\<any, Number/Event>)

```java
SUM(group: GroupedVector<any, Number/Event>): GroupedScalar<Number>
```

Calculate the sum as Scalar\<any> from the given parameter.

#### Parameters

<table><thead><tr><th width="219">Parameter</th><th>Description</th></tr></thead><tbody><tr><td>group</td><td>A GroupedVector of type &#x3C;any, Number/Event></td></tr></tbody></table>

#### Returns

Returns the sum as Scalar\<Number>

***

### Examples

Retrieve the sum of the GridPowerConsumption time series.

```java
SUM($GridPowerConsumption)
```


# Operations

Every operation is of form: `OP argument1` , or `argument1 OP argument2` and returns a value.

In the following list, each operation is listed, including the [data structure](/platform-manual/rules/rule-language/data-structures) and [data type](/platform-manual/rules/rule-language/data-types) of each argument, and the return value. This is done by using form *`DataStructure<DataType>`*.

## Overview

Following operations are provided:

[Plus](/platform-manual/rules/rule-language/operations/plus)

[Minus](/platform-manual/rules/rule-language/operations/minus)

[Multiply](/platform-manual/rules/rule-language/operations/multiply)

[Divide](/platform-manual/rules/rule-language/operations/divide)

[Negate](/platform-manual/rules/rule-language/operations/negate)

[Less Than](/platform-manual/rules/rule-language/operations/less-than)

[Less or Equal](/platform-manual/rules/rule-language/operations/less-or-equal)

[Equal](/platform-manual/rules/rule-language/operations/equal)

[Not Equal](/platform-manual/rules/rule-language/operations/not-equal)

[Greater or Equal](/platform-manual/rules/rule-language/operations/greater-or-equal)

[Greater](/platform-manual/rules/rule-language/operations/greater)

[AND](/platform-manual/rules/rule-language/operations/and)

[OR](/platform-manual/rules/rule-language/operations/or)

[NOT](/platform-manual/rules/rule-language/operations/not)

[Format](/platform-manual/rules/rule-language/operations/format)

[Attribute Accessor](/platform-manual/rules/rule-language/operations/attribute-accessor)


# Plus

### Description

Adding or concat two values with each other.

### Number|Event + Number|Event

<pre class="language-java"><code class="lang-java">Scalar&#x3C;Number|Event> + Scalar&#x3C;Number|Event> : Scalar&#x3C;Number>
Scalar&#x3C;Number|Event> + GroupedScalar&#x3C;Number|Event> : GroupedScalar&#x3C;Number>
GroupedScalar&#x3C;Number|Event> + Scalar&#x3C;Number|Event> : GroupedScalar&#x3C;Number>
<strong>GroupedScalar&#x3C;Number|Event> + GroupedScalar&#x3C;Number|Event> : GroupedScalar&#x3C;Number>
</strong></code></pre>

Two arguments of type Scalar\<Numbers> added to each other result in a Scalar\<Number> which is the sum of both arguments.

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

***

### Time + Timespan

```java
Scalar<Time> + Scalar<Timespan> : Scalar<Time>
Scalar<Time> + GroupedScalar<Timespan> : GroupedScalar<Time>
GroupedScalar<Time> + Scalar<Timespan> : GroupedScalar<Time>
GroupedScalar<Time> + GroupedScalar<Timespan> : GroupedScalar<Time>
```

Adding a Scalar\<Time> with a Scalar\<Timespan> results in a Scalar\<Time> which is a point in time after the Scalar\<Time>.

***

### Timespan + Timespan&#x20;

```java
Scalar<Timespan> + Scalar<Timespan> : Scalar<Time>
Scalar<Timespan> + GroupedScalar<Timespan> : GroupedScalar<Time>
GroupedScalar<Timespan> + Scalar<Timespan> : GroupedScalar<Time>
GroupedScalar<Timespan> + GroupedScalar<Timespan> : GroupedScalar<Time>
```

Adding a Scalar\<Timespan> to a Scalar\<Time> results in a Scalar\<Time> which is far more in the future as stated by the Scalar\<Timespan>

***

### Text + any

```java
Scalar<Text> + Scalar<any> : Scalar<Text>
Scalar<Text> + Vector<any> : Scalar<Text>
Scalar<Text> + GroupedScalar<any> : Scalar<Text>
Scalar<Text> + GroupedVector<any> : Scalar<Text>
Vector<Text> + Scalar<any> : Scalar<Text>
Vector<Text> + Vector<any> : Scalar<Text>
Vector<Text> + GroupedScalar<any> : Scalar<Text>
Vector<Text> + GroupedVector<any> : Scalar<Text>
GroupedScalar<Text> + Scalar<any> : Scalar<Text>
GroupedScalar<Text> + Vector<any> : Scalar<Text>
GroupedScalar<Text> + GroupedScalar<any> : Scalar<Text>
GroupedScalar<Text> + GroupedVector<any> : Scalar<Text>
GroupedVector<Text> + Scalar<any> : Scalar<Text>
GroupedVector<Text> + Vector<any> : Scalar<Text>
GroupedVector<Text> + GroupedScalar<any> : Scalar<Text>
GroupedVector<Text> + GroupedVector<any> : Scala
```

Adding a *scalar* or *vector* of type Boolean, Number, Time, Timespan, Text to a *scalar* or *vector* of type Text results in a new Scalar\<Text with concatenated text elements. Non-text elements are implicitly formatted (see [#format-or](#format-or "mention")). Vector elements are joined with commas.

### Examples

Calculating 1 + 2 equals 3

```java
1+2
```

Adding 8 hours to the last full hour results in 13:00 o'clock, when the lastZeroHour was 5:00 o'clock.

```java
lastZeroHour + 8h
```

Adding 30min to 1h results in 1h30m

```java
1h + 30min
```

Concat Text with the result of a function

```java
'Consumption' + SUM($PowerConsumption) + ' W'
```


# Minus

### Description

Subtracting two values from each other.

### Number/Event - Number/Event

```java
Scalar<Number|Event> - Scalar<Number|Event> : Scalar<Number>
Scalar<Number|Event> - GroupedScalar<Number|Event> : GroupedScalar<Number>
GroupedScalar<Number|Event> - Scalar<Number|Event> : GroupedScalar<Number>
GroupedScalar<Number|Event> - GroupedScalar<Number|Event> : GroupedScalar<Number>
```

Two arguments of type Scalar\<Numbers> subtracted from each other result in a Scalar\<Number> which is the result of the division.

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

***

### Time - Time

```java
Scalar<Time> - Scalar<Time> : Scalar<Time>
Scalar<Time> - GroupedScalar<Time> : GroupedScalar<Time>
GroupedScalar<Time> - Scalar<Time> : GroupedScalar<Time>
GroupedScalar<Time> - GroupedScalar<Time> : GroupedScalar<Time>
```

Subtracting a Scalar\<Time> from another Scalar\<Time> results in a Scalar\<Time> which is a point in time before the first operator.

***

### Time -Timespan

```java
Scalar<Time> - Scalar<Timespan> : Scalar<Time>
Scalar<Time> - GroupedScalar<Timespan> : GroupedScalar<Time>
GroupedScalar<Time> - Scalar<Timespan> : GroupedScalar<Time>
GroupedScalar<Time> - GroupedScalar<Timespan> : GroupedScalar<Time>
```

Subtracting a Scalar\<Timespan> from a Scalar\<Time> results in a Scalar\<Time> which is a point in time before the Scalar\<Time>.

***

### Timespan - Timespan&#x20;

```java
Scalar<Timespan> - Scalar<Timespan> : Scalar<Time>
Scalar<Timespan> - GroupedScalar<Timespan> : GroupedScalar<Time>
GroupedScalar<Timespan> - Scalar<Timespan> : GroupedScalar<Time>
GroupedScalar<Timespan> - GroupedScalar<Timespan> : GroupedScalar<Time>
```

Subtracting a Scalar\<Timespan> from a Scalar\<Timespan> results in a Scalar\<Time> which is further in the past as stated by the Scalar\<Timespan>

### Examples

Calculating 1 - 2 equals -1

```java
1-2
```

Subtracting 8 hours to the last full hour results in 00:00 o'clock, when the lastZeroHour was 8:00 o'clock.

```java
lastZeroHour - 8h
```

Subtracting 30min from 1h results in 30m.

```java
1h - 30min
```


# Multiply

### Description

Multiply two values with each other.

### Number|Event \* Number|Event

```java
Scalar<Number|Event> * Scalar<Number|Event> : Scalar<Number>
Scalar<Number|Event> * GroupedScalar<Number|Event> : GroupedScalar<Number>
GroupedScalar<Number|Event> * Scalar<Number|Event> : GroupedScalar<Number>
GroupedScalar<Number|Event> * GroupedScalar<Number|Event> : GroupedScalar<Number>
```

Two arguments of type Scalar\<Number> multiplied with each other result in a Scalar\<Number> which is the result of the multiplication.

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

### Examples

Calculating 1.5 multiplied by the SUM of the PowerConsumption

```java
1.5 * SUM($PowerConsumption)
```


# Divide

### Description

Divide two values by each other.

### Number|Event / Number|Event

```java
Scalar<Number|Event> / Scalar<Number|Event> : Scalar<Number>
Scalar<Number|Event> / GroupedScalar<Number|Event> : GroupedScalar<Number>
GroupedScalar<Number|Event> / Scalar<Number|Event> : GroupedScalar<Number>
GroupedScalar<Number|Event> / GroupedScalar<Number|Event> : GroupedScalar<Number>
```

Two arguments of type Scalar\<Number> divded by each other result in a Scalar\<Number> which is the result of the division.

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

### Examples

Calculating 3 divided by 2 results in 1.5

```java
3 / 2
```


# Modulo

### Description

Calculates the remainder of a division, after one number is divided by another

### Number|Event / Number|Event

```java
Scalar<Number|Event> % Scalar<Number|Event> : Scalar<Number>
Scalar<Number|Event> % GroupedScalar<Number|Event> : GroupedScalar<Number>
GroupedScalar<Number|Event> % Scalar<Number|Event> : GroupedScalar<Number>
GroupedScalar<Number|Event> % GroupedScalar<Number|Event> : GroupedScalar<Number>
```

Modulo of two arguments of type Scalar\<Number> result in a Scalar\<Number> .

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

### Examples

Calculating modulo of 3 divided by 2 results in 1

```java
3 % 2
```


# Negate

### Description

Negate a Scalar\<Event>

### -Number|Event

```java
-Scalar<Number|Event> : Scalar<Number>
-Vector<Number|Event> : Vector<Number>
-GroupedScalar<Number|Event> : GroupedScalar<Number>
-GroupedVector<Number|Event> : GroupedVector<Number>
```

An argument of type Scalar\<Event> prefixed by a - negates the value of the Event.

If it is a Vector, GroupedScalar or GroupedVector, then the operation is performed on each value, and results in another Vector, GroupedScalar or GroupedVector.

### Examples

Calculating 3 divided by 2 results in 1.5

```java
-3
```


# Less Than

### Description

Checks whether first argument is less than the second argument.

### any < any

```java
Scalar<any> < Scalar<any> : Scalar<Boolean>
Scalar<any> < GroupedScalar<any> : GroupedScalar<Boolean>
GroupedScalar<any> < Scalar<any> : GroupedScalar<Boolean>
GroupedScalar<any> < GroupedScalar<any> : GroupedScalar<Boolean>
```

Comparing if a Scalar\<any> is less than another Scalar\<any>. Returns a Scalar\<Boolean> either true oif the first operator is less than the second otherwise false.

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

### Examples

Check if the PowerConsumption is less than the PowerProduction

```java
$PowerConsumption < $PowerProduction
```


# Less or Equal

### Description

Checks whether first argument is less or equal than the second argument.

### any <= any

```java
Scalar<any> <= Scalar<any> : Scalar<Boolean>
Scalar<any> <= GroupedScalar<any> : GroupedScalar<Boolean>
GroupedScalar<any> <= Scalar<any> : GroupedScalar<Boolean>
GroupedScalar<any> <= GroupedScalar<any> : GroupedScalar<Boolean>
```

Comparing if a Scalar\<any> is less or equal than another Scalar\<any>. Returns a Scalar\<Boolean> either true if the first operator is less or equal than the second otherwise false.

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

### Examples

Check if the PowerConsumption is less or equals than the PowerProduction

```java
$PowerConsumption <= $PowerProduction
```


# Equal

### Description

Checks whether first argument is equal to the second argument.

### any = any

```java
Scalar<any> = Scalar<any> : Scalar<Boolean>
Scalar<any> = GroupedScalar<any> : GroupedScalar<Boolean>
GroupedScalar<any> = Scalar<any> : GroupedScalar<Boolean>
GroupedScalar<any> = GroupedScalar<any> : GroupedScalar<Boolean>
```

Comparing if a Scalar\<any> is equal to another Scalar\<any>. Returns a Scalar\<Boolean> either true if the first operator equals to the second otherwise false.

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

### Examples

Check if the PowerConsumption is equals to the PowerProduction

```java
$ChargeMode1 = $ChargeMode2
```


# Not Equal

### Description

Checks whether first argument is not equal to the second argument.

### any != any

```java
Scalar<any> != Scalar<any> : Scalar<Boolean>
Scalar<any> != GroupedScalar<any> : GroupedScalar<Boolean>
GroupedScalar<any> != Scalar<any> : GroupedScalar<Boolean>
GroupedScalar<any> != GroupedScalar<any> : GroupedScalar<Boolean>
```

Comparing if a Scalar\<any> is not equal to another Scalar\<any>. Returns a Scalar\<Boolean> either true if the first operator equals to the second otherwise false.

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

### Examples

Check if the PowerConsumption is equals to the PowerProduction

```java
$ChargeMode1 != $ChargeMode2
```


# Greater or Equal

### Description

Checks whether first argument is greater or equal than the second argument.

### any >= any

```java
Scalar<any> >= Scalar<any> : Scalar<Boolean>
Scalar<any> >= GroupedScalar<any> : GroupedScalar<Boolean>
GroupedScalar<any> >= Scalar<any> : GroupedScalar<Boolean>
GroupedScalar<any> >= GroupedScalar<any> : GroupedScalar<Boolean>
```

Comparing if a Scalar\<any> is greater or equal than another Scalar\<any>. Returns a Scalar\<Boolean> either true if the first operator is greater or equals to the second otherwise false.

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

### Examples

Check if the PowerConsumption is greater or equal than the PowerProduction

```java
$PowerConsumption >= $PowerProduction
```


# Greater

### Description

Checks whether first argument is greater than the second argument.

### any > any

```java
Scalar<any> > Scalar<any> : Scalar<Boolean>
Scalar<any> > GroupedScalar<any> : GroupedScalar<Boolean>
GroupedScalar<any> > Scalar<any> : GroupedScalar<Boolean>
GroupedScalar<any> > GroupedScalar<any> : GroupedScalar<Boolean>
```

Comparing if a Scalar\<any> is greater than another Scalar\<any>. Returns a Scalar\<Boolean> either true if the first operator is greater as the second otherwise false.

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

### Examples

Check if the PowerConsumption is greater than the PowerProduction

```java
$PowerConsumption > $PowerProduction
```


# AND

### Description

Checks whether first argument and the second argument are *true*.

### Boolean AND Boolean

```java
Scalar<Boolean> AND Scalar<Boolean> : Scalar<Boolean>
Scalar<Boolean> AND GroupedScalar<Boolean> : GroupedScalar<Boolean>
GroupedScalar<Boolean> AND Scalar<Boolean> : GroupedScalar<Boolean>
GroupedScalar<Boolean> AND GroupedScalar<Boolean> : GroupedScalar<Boolean>
```

Comparing if a Scalar\<Boolean> and Scalar\<Boolean> are both true. Returns a Scalar\<Boolean> with true when both terms result in true otherwise false.

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

### Examples

Check if the PowerConsumption is greater 0 AND the PowerProduction is > 0. Only returns true if both are valid.

```java
$PowerConsumption > 0 AND $PowerProduction > 0
```


# OR

### Description

Checks whether first argument or the second argument is *true*.

### Boolean OR Boolean

```java
Scalar<Boolean> OR Scalar<Boolean> : Scalar<Boolean>
Scalar<Boolean> OR GroupedScalar<Boolean> : GroupedScalar<Boolean>
GroupedScalar<Boolean> OR Scalar<Boolean> : GroupedScalar<Boolean>
GroupedScalar<Boolean> OR GroupedScalar<Boolean> : GroupedScalar<Boolean>
```

Comparing if one of Scalar\<Boolean> OR Scalar\<Boolean> is true. Returns a Scalar\<Boolean> with true when one of the terms result in true. Returns false when both terms are false.

If one argument is a Scalar, and the other one a GroupedScalar, then the operation is performed by taking the Scalar value and the value of each group value, which results another GroupedScalar (it has same group size, as the one from the input argument).

If both arguments are GroupedScalar, then the operation is performed by taking values with same group key, which  results in another Grouped Scalar (the group size is equal or smaller than the ones of the input arguments).

### Examples

Check if the PowerConsumption is greater 0 OR the PowerProduction is > 0. Only returns false if both are false.

```java
$Consumption > 0 OR $Production > 0
```


# NOT

### Description

Checks whether argument is not *true*.

### NOT Boolean

```java
NOT Scalar<Boolean> : Scalar<Boolean>
NOT Vector<Boolean> : Vector<Boolean>
NOT GroupedScalar<Boolean> : GroupedScalar<Boolean>
NOT GroupedVector<Boolean> : GroupedVector<Boolean>
```

Negate the Scalar\<Boolean> after the NOT keyword. This means a Scalar\<Boolean> with value true will return false and vise versa.

If it is a Vector, GroupedScalar or GroupedVector, then the operation is performed on each value, and results in another Vector, GroupedScalar or GroupedVector.

### Examples

Check if the PowerConsumption is NOT greater than the PowerProduction.

```java
NOT ($PowerConsumption > $PowerProduction)
```


# Format

### Description

Formats numbers, time and timestamp.

Format on Vector will result in a comma-separated list of formatted values. \
E.g.: `Device Link Car, Fronius Symo`

Format on GroupedScalar will result in a  comma-separated list of group identifier, double-quote and formatted value; the group identifier is rendered as *device name*, if grouped by device, else the time span index is taken.\
E.g.: `'Device Link Car': 1035, 'Fronius Symo': 8012`

Format on GroupedVector will result in a  slash-separated list of group identifier, double-quote and comma-separated formatted values; the group identifier is rendered as *device name*, if grouped by device, else the time span index is taken.\
E.g.: `'Device Link Car': 1035, 2012, 3097 / 'Fronius Symo': 8012`

### Number/Event | Text

```java
Scalar<Number/Event> | Scalar<Text> → Scalar<Text>
Vector<Number/Event> | Scalar<Text> → Scalar<Text>
GroupedScalar<Number/Event> | Scalar<Text> → Scalar<Text>
GroupedVector<Number/Event> | Scalar<Text> → Scalar<Text>
```

Format pattern semantic, see [Java's DecimalFormat](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/text/DecimalFormat.html)

***

### Time | Text

```java
Scalar<Time> | Scalar<Text> →  Scalar<Text>
Vector<Time> | Scalar<Text> →  Scalar<Text>
GroupedScalar<Time> | Scalar<Text> →  Scalar<Text>
GroupedVector<Time> | Scalar<Text> →  Scalar<Text>
```

Format pattern semantic, see [Java's SimpleDateFormat](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/text/SimpleDateFormat.html)

***

### Timespan | Text

```java
Scalar<Time> | Scalar<Text> →  Scalar<Text>
Vector<Time> | Scalar<Text> →  Scalar<Text>
GroupedScalar<Time> | Scalar<Text> →  Scalar<Text>
GroupedVector<Time> | Scalar<Text> →  Scalar<Text>
```

Format pattern semantic, see [Java's SimpleDateFormat](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/text/SimpleDateFormat.html)

***

### Timespan | '(ADAPTIVE|SECONDS|MINUTES|HOURS|DAYS):(WORD|SYMBOL)'

```java
Scalar<Time> | '(ADAPTIVE|SECONDS|MINUTES|HOURS|DAYS):(WORD|SYMBOL)' →  Scalar<Text>
Vector<Time> | '(ADAPTIVE|SECONDS|MINUTES|HOURS|DAYS):(WORD|SYMBOL)' →  Scalar<Text>
GroupedScalar<Time> | '(ADAPTIVE|SECONDS|MINUTES|HOURS|DAYS):(WORD|SYMBOL)' →  Scalar<Text>
GroupedVector<Time> | '(ADAPTIVE|SECONDS|MINUTES|HOURS|DAYS):(WORD|SYMBOL)' →  Scalar<Text>
```

Format pattern semantic

<table><thead><tr><th width="249">Value</th><th>Description</th></tr></thead><tbody><tr><td>ADAPATIVE</td><td>Means that it chooses the time unit automatically depending on the value size</td></tr><tr><td>SECONDS</td><td>Redners the value in seconds.</td></tr><tr><td>MINUTES</td><td>Renders the value in minutes.</td></tr><tr><td>HOURS</td><td>Renders the value in hours.</td></tr><tr><td>DAYS</td><td>Renders the value days.</td></tr><tr><td>WORD</td><td>Is in the word form (e.g. Hours)</td></tr><tr><td>SYMBOL</td><td>Is in the abbreviation of the unit (e.g. h)</td></tr></tbody></table>

### Examples

Format the Result of the AVG($Consumption) function (which is a Scalar\<Number) in format '#,##0'.

<pre class="language-java"><code class="lang-java"><strong>AVG($Consumption) | '#.##0'
</strong></code></pre>

Format the Timestamp of the $Event (which is Scalar\<Event>) as hours and minutes.

```java
$Event::Timestamp | 'HH:mm'
```

Fromat the resulting Timestamp as Hours suffixed with the word Hours.

```java
(now - $Event::Timestamp) | 'HOURS:WORD'
```


# Attribute Accessor

### Description

Access sub-data of the argument.

### Accessor on Scalar\<Event>

```java
Scalar<Event>::Value             : Scalar<Number>
Scalar<Event>::DeviceId          : Scalar<Text>
Scalar<Event>::DeviceName        : Scalar<Text>
Scalar<Event>::Timestamp         : Scalar<Time>
Scalar<Event>::ForecastTimestamp : Scalar<Time>
```

#### Returns

Returns the event value, device id, device name, timestamp or forecast timestamp.

Remark: if there is no forecast timestamp, "0" is returned

#### Examples

Get the value of the event stored in the PowerConsumption Variable.

```java
$PowerConsumption::Value
$PowerConsumption::Timestamp
```

***

### Accessor on Vector\<any>

<pre class="language-java"><code class="lang-java"><strong>Vector&#x3C;any>::First : Scalar&#x3C;any>
</strong>Vector&#x3C;any>::Last  : Scalar&#x3C;any>
</code></pre>

#### Returns

&#x20;Returns the first or last element of the Vector as Scalar\<any>

#### Examples

Get the first Event from the $PowerGeneration vector.

```java
$PowerConsumption::First
```

***

### Accessor on Vector\<Event>

<pre class="language-java"><code class="lang-java"><strong>Vector&#x3C;Event>::Value             : Vector&#x3C;Number>
</strong>Vector&#x3C;Event>::DeviceId          : Vector&#x3C;Text>
Vector&#x3C;Event>::DeviceName        : Vector&#x3C;Text>
Vector&#x3C;Event>::Timestamp         : Vector&#x3C;Time>
Vector&#x3C;Event>::ForecastTimestamp : Vector&#x3C;Time>
</code></pre>

#### Returns

Returns the set of event values, device ids, device names, timestamps or forecast timestamps.

Remark: if there is no forecast timestamp, "0" is returned

#### Examples

Get the values of all events in the vector $PowerConsumption as new Vector\<Number>

```java
$PowerConsumption::Value
```

***

### Accessor on GroupedScalar\<any, Event>

<pre class="language-java"><code class="lang-java"><strong>GroupedScalar&#x3C;any, Event>::Value             : Vector&#x3C;Number>
</strong>GroupedScalar&#x3C;any, Event>::DeviceId          : Vector&#x3C;Text>
GroupedScalar&#x3C;any, Event>::DeviceName        : Vector&#x3C;Text>
GroupedScalar&#x3C;any, Event>::Timestamp         : Vector&#x3C;Time>
GroupedScalar&#x3C;any, Event>::ForecastTimestamp : Vector&#x3C;Time>
</code></pre>

#### Returns

Returns the set of event values, device ids, device names, timestamps or forecast timestamps, grouped by device or time spans.

#### Examples

Get the values of all events in the group $PowerConsumption as new Vector\<Number>

```java
GROUP_BY_DEVICE($PowerConsumption)::Value
```

***

### Accessor on GroupedScalar\<Device, any>

<pre class="language-java"><code class="lang-java">GroupedScalar&#x3C;Device, any>::GroupKeyDeviceId   : Vector&#x3C;Text>
<strong>GroupedScalar&#x3C;Device, any>::GroupKeyDeviceName : Vector&#x3C;Text>
</strong></code></pre>

#### Returns

Returns the device id or name of the device which is part of the GroupedScalar key.

#### Examples

Get all device names from all the devices in the $PowerConsumption group.

```java
GROUP_BY_DEVICE($PowerConsumption)::GroupKeyDeviceName
```

***

### Accessor on GroupedScalar\<any, any>

<pre class="language-java"><code class="lang-java"><strong>GroupedScalar&#x3C;any, any>::GroupKeyValue : Vector&#x3C;any>
</strong></code></pre>

#### Returns

Returns the value of the group as new Vector\<any>.

#### Examples

Get all Values from the $PowerConsumption group.

```java
GROUP_BY_DEVICE($PowerConsumption)::GroupValue
```

***

### Accessor on GroupedVector\<any, any>

<pre class="language-java"><code class="lang-java"><strong>GroupedVector&#x3C;any, any>::First : GroupedScalar&#x3C;any, any>
</strong>GroupedVector&#x3C;any, any>::Last  : GroupedScalar&#x3C;any, any>
</code></pre>

#### Returns

Returns a GroupedScalar, which holds the first or last value of each group.

#### Examples

Get all Values from the $PowerConsumption group.

```java
GROUP_BY_DEVICE($PowerConsumption)::First
```

***

### Accessor on GroupedVector\<any, Event>

<pre class="language-java"><code class="lang-java"><strong>GroupedVector&#x3C;any, Event>::Value             : GroupedVector&#x3C;Number>
</strong>GroupedVector&#x3C;any, Event>::DeviceId          : GroupedVector&#x3C;Text>
GroupedVector&#x3C;any, Event>::DeviceName        : GroupedVector&#x3C;Text>
GroupedVector&#x3C;any, Event>::Timestamp         : GroupedVector&#x3C;Time>
GroupedVector&#x3C;any, Event>::ForecastTimestamp : GroupedVector&#x3C;Time>
</code></pre>

#### Returns

Returns the values, device ids, device names, timestamps or forecast timestamps of all events in the group and return as new GroupedVector\<Number>.

#### Examples

Get the values of all events in the group $PowerConsumption as new Vector\<Number>

```java
GROUP_BY_DEVICE($PowerConsumption)::Value
```

***

### Accessor on GroupedVector\<Device, any>

```java
GroupedVector<Device, any>::GroupKeyDeviceId   : Vector<Text>
GroupedVector<Device, any>::GroupKeyDeviceName : Vector<Text>
```

#### Returns

Returns the device ids or device names of all devices which are the groups key, and return as new Vector\<Text>.

#### Examples

Get all device names from all the devices in the $PowerConsumption group.

```java
GROUP_BY_DEVICE($PowerConsumption)::GroupKeyDeviceName
```

***

### Accessor on Scalar\<Building>

```java
Building::Id
Building::RegistrationTimestamp
Building::ActivationTimestamp
Building::DaysSinceActivation
Building::DeviceTypes
Building::EconomicalMotivationScore
Building::EcologicalMotivationScore
Building::SelfSufficiencyMotivationScore
Building::MultiPersonScore
Building::CommercialBuildingScore
Building::IsSelfSufficiencyMotivated
Building::IsEcologicalMotivated
Building::IsSelfSufficiencyMotivated
Building::IsMultiPerson
Building::IsSinglePerson
Building::IsResidential
Building::IsCommercial
Building::IsInSameCountry
Building::IsInSameConsumptionCategory
Building::All
```

#### Returns

Selects the event related building, and returns the specified building data, such as the building id, the registration date when the building was registered, the activation date when the building was activated on MOOST side, or the types of all devices.

See [Data Types](/platform-manual/rules/rule-language/data-types#building)

#### Examples

Get all device types from the event related device.

```java
Building::DeviceTypes
```

### Properties Accessor on Scalar\<Building>

A special case is the Properties Accessor on the ScalayBuilding>. It allows to select the value that is defined in a customer property on a building and use it in the rule language.

```java
Building::Properties[energyThreshold]
```

#### Returns

Returns the value that is defined in the according properties entry on the building. When used in a rule condition the rule can only be trigger for buildings that have the according property set.

#### Examples

Use the value of the Property "energyThreshold" defined on the buildings to compare it against a dataset of type EnergyConsumption.

```java
$EnergyConsumption > Building::Properties[energyThreshold]
```

### Accessor on Scalar\<DeliveredNotification>

```java
DeliveredNotification::DeliveryTimestamp
DeliveredNotification::InteractionTimestamp
DeliveredNotification::InteractionType = ( 'OPENAPP' | 'OPENWEB' | 'DISMISS' | 'STOPDELIVERY' )
```

#### Returns

When used in Goal-achieved condition, selects the delivered notification, and returns

* the time when the notification was delivered
* the time when the interaction happened
* the type of the interaction (e.g. `OPENAPP`)

See [Literals](/platform-manual/rules/rule-language/literals#deliverednotification)

#### Examples

Get all device types from the event related device.

```java
DeliveredNotification::InteractionType = 'OPENAPP'
DeliveredNotification::InteractionTimestamp - DeliveredNotification::DeliveryTimestamp < 8h
```


# Examples

Our rule configurator often provides the possibility to get to your goal in different ways. This page shows examples on how you can combine the different functions and operations to write a rule.

## SUBSETS

Subsets are often used to select a certain time period from a  timeseries.

For example, we want to send a message if the <mark style="background-color:purple;">minimum</mark> of the <mark style="background-color:blue;">newest two entries</mark> of a <mark style="background-color:orange;">time series of 5 days in Grid Power Consumption</mark> is greater than the <mark style="background-color:purple;">minimum</mark> of the <mark style="background-color:blue;">remaining time series</mark>.

[As described on the SUBSET chapter of the Functions page](/platform-manual/rules/rule-language/functions#subset-vector-less-than-any-greater-than-scalar-less-than-number-greater-than), we simply can add the <mark style="background-color:blue;">number of entries</mark> we want to extract from a vector, as long as they are at the beginning or end of the vector. As we want the <mark style="background-color:blue;">most recent entries</mark>, we must precede that value with a <mark style="background-color:blue;">minus</mark> sign.

```java
MIN(SUBSET($GridPowerConsumption5d, -2))
```

We now have a snippet of the two newest entries from the original time series. By adding the [<mark style="background-color:purple;">MIN</mark> function](/platform-manual/rules/rule-language/functions#min-vector-less-than-number-event-greater-than), we end up with the lower value of those two.

Alternatively, we can also take the newest entries in a certain time period. Here we want to get the minimum of all registered Grid Power Consumption events from the last 30 minutes until now.

[Now is always defined as the newest entry and is included in the subset, but the start time is excluded.](/platform-manual/rules/rule-language/functions#subset-vector-less-than-event-greater-than-scalar-less-than-time-greater-than-scalar-less-than-time) So we would need to add an additional minute (or at least some seconds) to include the event that occurred exactly 30 minutes ago.&#x20;

```java
MIN(SUBSET($GridPowerConsumption5d, now-31min, now)) 
```

Let us continue with the first version. We now want to compare this minimum value with the <mark style="background-color:purple;">minimum</mark> of the rest of the <mark style="background-color:orange;">time series</mark>.

The beginning of this snippet is therefore the <mark style="background-color:blue;">first value of the vector at position 0</mark> up to and <mark style="background-color:blue;">including the third most recent event</mark>. As before, we can get the <mark style="background-color:purple;">minimum</mark> by applying a <mark style="background-color:purple;">MIN</mark> function to the resulting subset.

```java
MIN(SUBSET($GridPowerConsumption5d, 0, -3))
```

Finally, we simply have to combine the two parts for our condition by adding a [Greater operator](/platform-manual/rules/rule-language/operations#greater-greater-than) between them:

```java
MIN(SUBSET($GridPowerConsumption5d, -2)) > MIN(SUBSET($GridPowerConsumption5d, 0, -3))
```


# Events

The Events Overview page is a powerful tool for users who need to keep track of recent events, monitor changes in real-time, and analyze data over a certain period. The page's features, such as search functionality, column selection, time frame adjustment, and pagination, make it easy for users to customize the view according to their needs.&#x20;

The page provides a comprehensive view of the latest events received and allows users to quickly and efficiently navigate through the information.

## Charts

<figure><img src="/files/tLPWIHqDbUdzRSCe0xYP" alt=""><figcaption></figcaption></figure>

On the Charts Tab is a Dashboard which gives an overview over statistics of the events that are sent to the Platform. The dashboard is always limited to a single household, which can be selecte in the Filter above.

## Data

<figure><img src="/files/iXHF47r6z6iiSLZaofI1" alt=""><figcaption></figcaption></figure>

The table on the Data Tab displays information about the latest events received. The table includes columns such as the event name, date and time, location, and other relevant information. Each row in the table represents a single event, and users can view multiple events at once by adjusting the pagination.

## Filter

The filter functionality on the Events Overview page allows users to search for specific events by filtering by building, event type, event source, device name and date range.

<figure><img src="/files/GsNB0FU6k9J2JCNoVhJf" alt=""><figcaption></figcaption></figure>

## Pagination

The table has pagination enabled, which means that users can navigate through the table by clicking the page numbers or using the previous and next buttons. Users can also select how many elements per page they want to display. This feature can be accessed by clicking the "Page Size" button located above the table. A drop-down menu will appear, and users can select the number of elements they want to be displayed per page.


# Households

The Households section provides an overview of all households within the system. From here, you can browse, search, and manage households, as well as drill into individual household details to inspect energy data, device profiles, notifications, events, settings, and tariffs.

<figure><img src="/files/dXXcPqvrrLhb8Jwgd826" alt=""><figcaption></figcaption></figure>

### Filters and Search

At the top of the page, you will find several filters and a search bar to help you find specific households quickly.

* **ID** — Search or filter households by their unique Household ID.
* **Location** — Filter households based on their location.
* **Status** — Filter households by their status (e.g., Active, Inactive).
* **Event Types** — Filter households by the types of events associated with them.
* **Event Sources** — Filter households based on the sources of their events.

### Household List

Below the filters, you will see a table of households with the following columns:

* **Household ID** - The unique identifier for each household.
* **Location** - The location of the household, including city and country code.
* **Activated** - Date and Time when the household was activated on the platform.
* **Deactivated** - Date and Time when the household was deactivated on the platform.
* **Status** - The current status of the household (e.g., Active).
* **Early Adopter** - The first icon indicates if the household is in the early adopters group.
* **Favorites** - The star icon indicates if the household is in the favorites list in the shortcut menu.

#### Actions

For each household listed, there are action buttons to perform various tasks:

* **View (Circle Icon)** — Click to open the Household Detail page.
* **Edit (Pencil Icon)** — Click to edit the household's information.
* **Early Adopter (Hat Icon)** — Indicates if the household is in the list of early adopters.
* **Star (Star Icon)** — Click to mark the household as a favourite for quick access.

#### Pagination

* **Page Navigation** — Use the arrows to navigate between pages.


# Detail

The **Household Detail** page is the main view for a single household. It combines household metadata, page-level filters, and access to the tabs used to inspect energy data, devices, notifications, events, settings, and tariffs.

<figure><img src="/files/si4wbfF5hd3rJsIQMnqZ" alt="Household Detail"><figcaption></figcaption></figure>

## Page header

The header shows the selected household and the main controls for the page.

| Item                | Description                                                           |
| ------------------- | --------------------------------------------------------------------- |
| **Household ID**    | The unique identifier of the household, such as `99900000C048ADA6`.   |
| **Status**          | The current household status, such as `Active`.                       |
| **Actions menu**    | Opens the actions available for this household.                       |
| **Delivery Status** | Filters supported data views by delivery status, such as `DELIVERED`. |
| **Date Range**      | Limits supported charts and summary cards to a specific time window.  |

## Household profile

The left sidebar summarizes the key metadata for the household.

| Field                       | Description                                                                                          |
| --------------------------- | ---------------------------------------------------------------------------------------------------- |
| **Household Image**         | An uploaded or auto-generated image for the household.                                               |
| **Tags**                    | Labels assigned to the household, such as `Early Adopter` or `Dual Tariff`.                          |
| **Location**                | The postal code and city, such as `DE-01067 Dresden`.                                                |
| **Time Zone**               | The household time zone in IANA format, such as `Europe/Berlin`.                                     |
| **Has Energy Generation**   | Indicates whether the household has solar or another energy generation source.                       |
| **Effective Capacity (kW)** | The rated output of the household's energy generation system, such as `16.71 kW`.                    |
| **ELCOM Profile**           | The assigned ELCOM tariff profile, including the confidence score, such as `H6 with 44% Confidence`. |
| **Consumption Category**    | The platform's classification of the household's overall energy consumption.                         |

## Tabs

The detail page is split into the following tabs.

| Tab                   | Description                                                                                               |
| --------------------- | --------------------------------------------------------------------------------------------------------- |
| **Dashboard**         | Energy KPI cards and charts for the selected date range.                                                  |
| **Profile & Devices** | Inferred behavioural profile, including motivations, household type, building type, and device inventory. |
| **Notifications**     | Distribution charts, daily heatmap, and interaction tracking for notifications sent to this household.    |
| **Events**            | Interactive time-series graph of raw event data with configurable event type selection.                   |
| **Settings**          | User management, disabled rules, early adopter toggle, and custom properties.                             |
| **Tariffs**           | Weekly tariff charts and the high and low tariff schedule for the household's electricity contract.       |


# Dashboard

The **Dashboard** tab is the default view when opening a Household Detail page. It presents a grid of KPI cards that summarise the household's energy consumption, grid usage, and generation data for the selected date range.

<figure><img src="/files/tzGlRDNXUWiM1LOZvKSG" alt="Household Dashboard"><figcaption></figcaption></figure>

## Controlling the Date Range

Use the **date range picker** and **Delivery Status** filter in the page header to control which data is displayed on the Dashboard. All KPI cards update automatically when the date range or delivery status changes.

## Data Point Summary Cards

The top row of cards shows the total number of data points available for the selected date range. These counters give a quick indication of data completeness and coverage.

| Card                        | Description                                                                                                              |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| **Grid energy consumption** | Number of data points for energy drawn from the grid.                                                                    |
| **Energy consumption**      | Number of data points for total energy consumed by the household.                                                        |
| **Energy generation**       | Number of data points for energy generated (e.g., solar). Displayed in green to distinguish generation from consumption. |

## Yearly Overview Cards

The second row provides a 12-month rolling view of the household's energy profile. Each card includes a headline value and a trend chart.

| Card                                    | Description                                                                                               |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| **Grid energy consumption over a year** | Total grid energy consumed within the last 12 months (in kWh), with a monthly trend chart.                |
| **Energy consumption over a year**      | Total energy consumed within the last 12 months (in kWh), with a monthly trend chart.                     |
| **Energy generation over a year**       | Total energy generated within the last 12 months (in kWh), with a monthly trend chart displayed in green. |

## Seasonal and Peak Cards

The remaining rows surface patterns and extremes that can drive personalised energy recommendations.

#### Seasonal Patterns

| Card                             | Description                                                                                                                                                                     |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Energy consumption in winter** | Percentage of the household's energy consumption that occurs during winter months, with a monthly bar chart showing the seasonal distribution.                                  |
| **Energy consumption at night**  | Proportion of total energy consumption that is accounted for by nighttime hours, with a daily bar chart. A value of 100 % indicates all recorded consumption occurred at night. |

## Daily Peaks

| Card                               | Description                                                                                                         |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Highest daily grid consumption** | The peak single-day grid energy consumption in the last month (in kWh), with a daily bar chart for the full month.  |
| **Highest daily consumption**      | The peak single-day total energy consumption in the last month (in kWh), with a daily bar chart.                    |
| **Highest daily generation**       | The peak single-day energy generation in the last month (in kWh), with a daily bar chart displayed in yellow/green. |

## Hourly Peaks

| Card                                | Description                                                                                                                                                        |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Highest hourly grid consumption** | The hour of day with the highest grid energy consumption in the last month (e.g., `23 to 24 o'clock`), with a histogram showing energy consumption by hour of day. |
| **Highest hourly consumption**      | The hour of day with the highest total energy consumption in the last month, with a histogram by hour of day.                                                      |
| **Highest hourly generation**       | The hour of day with the highest energy generation in the last month (e.g., `0 to 1 o'clock`), with a histogram by hour of day.                                    |

## Peer Comparison

| Card                              | Description                                                                                                                                                                                                             |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Energy consumption comparison** | Ranks the household's energy consumption against other households in the same consumer category. Displays a rank (e.g., `Rank 2 of 2`) and the category label (e.g., `VERY_HIGH`), along with a comparison trend chart. |

## Reading the Charts

Each KPI card contains a small inline chart. The chart type varies by card:

* **Line charts** are used for yearly trend cards, showing monthly aggregated values.
* **Bar charts** are used for daily and seasonal cards, showing per-day values.
* **Histograms** are used for hourly peak cards, showing energy distribution across hours of the day.

Values on the y-axis are in **kWh** and on the x-axis in the relevant time unit (month, day, or hour). Hover over data points in the platform to see exact values.


# Profile & Devices

The **Profile & Devices** tab displays the household's behavioural profile — derived by the platform's analytics engine — as well as the inventory of connected devices. The profile cards give energy advisors and rule authors insight into what drives a household's energy behaviour.

<figure><img src="/files/rwvgqY9ZditcInh9FJrQ" alt=""><figcaption></figcaption></figure>

## Profile

The **Profile** section contains a set of cards that represent the platform's inferred characteristics of the household. These characteristics are computed from the household's event data and are used by the recommendation engine to personalise notifications.

## Motivations

The **Motivations** card displays a radar chart (triangle) with three axes representing the household's inferred energy-related motivations:

| Axis                 | Description                                                                                                                  |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| **Ecological**       | How strongly the household appears motivated by environmental concerns.                                                      |
| **Economical**       | How strongly the household appears motivated by cost savings.                                                                |
| **Self Sufficiency** | How strongly the household appears motivated by energy independence (e.g., maximising self-consumption of solar generation). |

Each axis shows a value between 0 and 1. For example, a household with values of 0.35 (Ecological), 0.31 (Economical), and 0.24 (Self Sufficiency) leans slightly more towards ecological motivation.

Below the chart, a **Confidence Score** (e.g., 93 %) indicates how certain the platform is about the motivation profile. Higher confidence scores are achieved when more event data is available.

## Household Type

The **Household Type** card displays a gauge chart that classifies the household by occupancy:

* **Single Person** — The household's consumption pattern is consistent with a single occupant.
* **Multi Person** — The household's consumption pattern is consistent with multiple occupants.

The gauge needle indicates the platform's best estimate, and the shaded segments reflect the probability distribution across the two categories.

## Building Type

The **Building Type** card classifies the type of building (commercial vs residential). This classification is inferred from the energy consumption profile and, where available, from external data sources.

## Devices

The lower part of the tab lists all devices currently linked to the household. Each device entry typically includes:

* **Device ID** — Unique identifier of the device.
* **Device Type** — The category of the device (e.g., smart meter, inverter, temperature sensor).
* **Status** — Whether the device is currently active or inactive.
* **Last Event** — Timestamp of the most recent event received from this device.

Devices are registered automatically when the first event for a new device ID is received, or they can be pre-registered via the API.


# Notifications

The **Notifications** tab provides a comprehensive overview of all notifications generated for this household within the selected date range. It combines high-level distribution charts with a detailed daily heatmap and interaction tracking, making it easy to audit what was sent and how the end user responded.

<figure><img src="/files/iChZGrt4OLjBgvEbTFyO" alt=""><figcaption></figcaption></figure>

## Distribution Charts

The top section displays three donut charts that summarise the notification activity for the selected period.

### Notifications by Rule

Shows the percentage breakdown of notifications grouped by the rule that triggered them. Each segment represents a different rule, for example:

* Daily energy report
* High excess energy available
* Peer Comparison: Total Energy Consumption
* Typical high consumption hours
* Monthly Energy Report
* Car is charging with grid power
* New top consuming device

This chart helps identify which rules are most active for the household.

### Notifications by Delivery Status

Shows the proportion of notifications in each delivery state. Possible statuses include:

* **Delivered** — The notification was successfully sent to the end user.
* **Pending** — The notification is queued for delivery.
* **Failed** — The notification could not be delivered.

A household showing 100 % Delivered indicates that all notifications within the selected date range were successfully sent.

### Notifications by Interaction Type

Shows how the end user interacted with the delivered notifications. Interaction types include:

| Interaction      | Description                                                         |
| ---------------- | ------------------------------------------------------------------- |
| **Openapp**      | The user tapped the notification and opened the app.                |
| **Viewed**       | The notification was displayed and seen by the user.                |
| **Dismiss**      | The user actively dismissed the notification.                       |
| **Stopdelivery** | The user opted out of receiving further notifications of this type. |

This chart is useful for gauging engagement and identifying notification fatigue.

## Notification Heatmap

<figure><img src="/files/1Cgu3HScdqJxWP0mYbf4" alt=""><figcaption></figcaption></figure>

Below the donut charts, a heatmap table displays the number of notifications per rule per day. The table is filtered by the **Delivery Status** selected in the page header (e.g., `DELIVERED`).

* **Rows** represent individual rules (e.g., "Daily energy report", "High excess energy available").
* **Columns** represent individual days within the selected date range.
* **Cells** contain the count of notifications sent for that rule on that day. Cells are shaded in green, with darker shades indicating higher counts.
* The **Sum\[rules]** row at the top shows the total number of notifications across all rules for each day.
* The **Sum\[days]** column on the right shows the total number of notifications for each rule across the entire period.

This view makes it easy to spot patterns — for example, rules that fire daily versus those that trigger only on specific days.

## Interactions

<figure><img src="/files/CTaPe8psxMQZniwqWSGH" alt=""><figcaption></figcaption></figure>

The **Interactions** section displays a time-series line chart tracking how the end user responded to notifications over the selected date range. Four series are plotted:

* **OPENAPP** — Percentage of notifications where the user opened the app.
* **VIEWED** — Percentage of notifications that were viewed.
* **DISMISS** — Percentage of notifications that were dismissed.
* **STOPDELIVERY** — Percentage of notifications where the user requested to stop delivery.

The y-axis shows the interaction rate as a percentage (0–100 %) and the x-axis shows individual dates. This chart helps monitor engagement trends — for instance, a rising dismiss rate may indicate notification fatigue and signal that rule frequency or content should be adjusted.


# Events

The **Events** tab lets you visualise and explore the raw event data received from the household's connected devices. It combines an interactive time-series graph with a configurable event selector, making it useful for debugging data ingestion issues, verifying sensor readings, and understanding a household's energy profile at a glance.

<figure><img src="/files/KSO6risFFkFawjH5dqIk" alt=""><figcaption></figcaption></figure>

### Event Graph

The main area of the tab displays a **time-series line chart** that plots the selected event types over the date range configured in the page header.

* **Y-axis** — Values in Watts (W). The scale adjusts automatically to fit the data and can include negative values (e.g., when power is fed back into the grid).
* **X-axis** — Timestamps covering the selected date range.
* **Series** — Each selected event type is rendered as a separate coloured line. The legend above the chart shows the event name, source type (e.g., `GATEWAY`), and the device identifier for each series.
* **Pagination** — When more series are selected than can be displayed in the legend at once, use the arrow controls (e.g., `1/2`) in the top-right corner to page through the legend entries.

Common event types plotted on the graph include:

| Event Type               | Description                                                                      |
| ------------------------ | -------------------------------------------------------------------------------- |
| **PowerConsumption**     | Total power consumed by the household at each measurement interval.              |
| **PowerGeneration**      | Power produced by the household's energy generation system (e.g., solar panels). |
| **PowerExcess**          | Surplus power available after self-consumption — typically fed into the grid.    |
| **GridPowerConsumption** | Power drawn from the grid (i.e., consumption minus self-consumed generation).    |

### Event Selection

Below the graph, the **Event Selection** panel lets you choose which data points are displayed on the chart. The panel heading reads *"Select the data points to be displayed"*.

#### Active Event Types

A table lists the event types currently plotted on the graph:

| Column     | Description                                                                                                          |
| ---------- | -------------------------------------------------------------------------------------------------------------------- |
| **Type**   | The event type identifier (e.g., `GRID_POWER_CONSUMPTION`, `POWER_CONSUMPTION`, `POWER_EXCESS`, `POWER_GENERATION`). |
| **Source** | The source system that produced the events (e.g., `GATEWAY`).                                                        |
| **Action** | A remove button (✕) to remove the event type from the graph.                                                         |

#### Adding Event Types

At the bottom of the panel, two dropdown menus allow you to add additional event types to the graph:

* **Event Type** — Select from the available event types for this household.
* **Source Type** — Select the source system (e.g., `GATEWAY`).

Click the **+** button to add the selected combination to the graph. The chart updates immediately to include the new data series.


# Settings

The **Settings** tab allows you to configure household-level options that affect how the platform processes data and generates recommendations for this specific household. Changes made here apply only to the selected household and override any organisation-wide defaults where applicable.

<figure><img src="/files/aeFfGhiV4BliIN1tbPPg" alt=""><figcaption></figcaption></figure>

## Users

The **Users** section manages the end-user accounts linked to this household. A household can have zero or more users associated with it. When no users have been assigned, the message *"No users defined"* is displayed.

| Column      | Description                               |
| ----------- | ----------------------------------------- |
| **User ID** | The unique identifier of the linked user. |
| **Action**  | Remove the user from this household.      |

To add a user, enter the **User ID** in the input field and click the **+** button. The user will then receive notifications generated for this household.

## Disabled Rules for all Users

This section lets you disable specific rules for every user of this household. Disabled rules will not generate notifications for this household, regardless of whether the rule's conditions are met.

* **Disable rules for all users by default** — A toggle that, when enabled, disables all rules by default. Individual rules can then be re-enabled selectively.

Below the toggle, a table lists the rules that are currently disabled:

| Column     | Description                                                                                                                                          |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Rule**   | The name of the disabled rule (displayed as a link, e.g., `New top consuming device`). Clicking the rule name navigates to the rule's configuration. |
| **Action** | A remove button (✕) to re-enable the rule for this household.                                                                                        |

To disable an additional rule, select it from the **Rule** dropdown and click the **+** button.

## Early Adopter

The **Early Adopter** toggle controls whether this household is included in the early adopter programme. When enabled (shown with a green checkmark), the household may receive newly created or experimental rules before they are rolled out to all households. This is useful for testing new recommendation logic on a controlled subset of households.

## Properties

The **Properties** section provides a flexible key-value store for attaching custom metadata to the household. Properties can be used by rules, integrations, or reporting workflows.

| Column     | Description                                               |
| ---------- | --------------------------------------------------------- |
| **Key**    | The property name (e.g., `contractType`, `installerRef`). |
| **Value**  | The property value.                                       |
| **Action** | A remove button (✕) to delete the property.               |

To add a new property, enter a **Key** and **Value** in the input fields at the bottom of the table and click the **+** button.


# Tariffs

The **Tariffs** tab shows the electricity tariff details assigned to this household. Tariff information is used by the platform's recommendation engine to calculate cost-based insights and personalise energy-saving recommendations — for example, suggesting that a user shift consumption to off-peak hours.

<figure><img src="/files/1Mav2T0JxjJ6RN8UeWp9" alt=""><figcaption></figcaption></figure>

## Tariff Chart

The main area displays a **bar chart** that visualises the applicable electricity rate for every hour of the week.

* **Y-axis** — Price in EUR / kWh.
* **X-axis** — Hourly time slots from Monday 00:00 through Sunday 23:00.
* **Bar colour and height** — Taller, darker bars represent the **High Tariff** rate; shorter, lighter bars represent the **Low Tariff** rate.

A legend in the top-right corner of the chart shows the exact rates, for example:

| Band            | Rate            |
| --------------- | --------------- |
| **Low Tariff**  | 0.296 EUR / kWh |
| **High Tariff** | 0.385 EUR / kWh |

The chart makes it easy to see at a glance when expensive and cheap periods occur throughout the week. For a household on a dual tariff, the high-tariff periods typically correspond to daytime hours on weekdays, while nights and weekends fall under the low tariff.

## High Tariff Schedule

Below the chart, a table lists the exact **high-tariff windows** for each day of the week:

| Weekday   | High Tariff   |
| --------- | ------------- |
| Monday    | 07:00 – 19:00 |
| Tuesday   | 07:00 – 19:00 |
| Wednesday | 07:00 – 19:00 |
| Thursday  | 07:00 – 19:00 |
| Friday    | 07:00 – 19:00 |
| Saturday  | —             |
| Sunday    | —             |

All hours outside the listed windows are charged at the low-tariff rate. The schedule varies by tariff provider, so the exact windows may differ between households.

### Tariff Types

The platform supports different tariff structures depending on the household's electricity contract:

* **Single Tariff** — A flat rate applies at all times. The chart shows bars of uniform height.
* **Dual Tariff** — Two rate bands (high / low) apply depending on the time of day and day of the week, as shown in the example above. Households on a dual tariff are tagged with the `Dual Tariff` label in the sidebar.
* **Dynamic Tariff -** Any rate that can be applied outside of a single and dual tariff like 15min changed tariffs, tripple tariffs and so on.


# Settings

This page displays settings related to your organization.

You can access it with a click on your company name in the top right corner and then clicking on "Settings".

The page is seprated into three Tabs:

* Data
* Fulfillment
* Event Categories

## Data

<figure><img src="/files/PJZBp6JyGhoGvq7MqJh6" alt=""><figcaption></figcaption></figure>

Under the Data Tab you find information for the currently logged in tenant.

### Organization

This section displays the primary identifiers and settings for your entire organization.

* ID: A unique system-generated identifier for your organization (e.g., `6d219fa7f940792b17f82bae`).
* Name: The display name of your company (e.g., "Demo Meter Company").
* Subscription: Your current subscription plan (e.g., "BASIC").

### Notification Languages

A grid of all available languages for system notifications. Languages that are highlighted (e.g., EN, DE) are currently enabled for your users.

* Client Id & Client Secret: These information are only shown to Users with the Admin:Users Permission. These credentials are needed to integrate against the MOOST API, see [Cloud-to-Cloud Integration](/technical-integration/cloud-to-cloud-integration)

### Billing

Billing account, payment method and a navigation button to the [Billing Portal](/platform-manual/billing-portal).

## Fulfillment

This section display the notification delivery endpoint. Each generated push notification is delivered to this configured endpoint.

<figure><img src="/files/wifjqOyeJDd3oPvByV2U" alt=""><figcaption></figcaption></figure>

## Event Categories

The table under Event Categories defines every type of data event that your organization collects, processes, or generates.

<figure><img src="/files/rbLN19Bzd0bNmHigo7A3" alt=""><figcaption></figcaption></figure>

* Measure Current Interval: This button, located at the top right of the table, triggers a manual data reading or computation for the currently defined intervals.

**Table Columns**

* Type: The internal name for the data event, which describes what is being measured (e.g., `ENERGY_CONSUMPTION`, `ENERGY_CONSUMPTION_FORECAST_1H`).
* Source: The origin of the data. This could be a specific device type (e.g., `GATEWAY`, `CAR_CHARGER`, `HEAT_PUMP`) or the system itself (e.g., `MOOST`, `APPLIANCE`).
* Interval: The frequency at which this data is collected or expected (e.g., `15min`, `1d` for one day).
* Computed: Indicates whether the data is a raw value from the source (`-`) or a value calculated by the platform (`yes`).
* Description: A plain-language description of the event (if one has been added).


# Billing Portal

{% hint style="info" %}
To access billing information you need the *BILLING\_ADMIN* role granted to your user.&#x20;

To add this role to an existing user please open a request in the MOOST Support Center.
{% endhint %}

The Billing Portal is a self-service portal where you can manage your subscription billing, update payment information, view past invoices, and perform other billing-related tasks.

To access the Billing portal first select the navigation point "Settings" in the User Navigation (top right corner). Afterwards click on the Button "Show Billing Subscription" in the Billing Card under the Data Tab.

<figure><img src="/files/ryAOTEyg5QBbS8bRsFc7" alt=""><figcaption></figcaption></figure>


# Payment Method

## Add Payment Method

By Clicking on the "Add Payment Method" or "Change Payment Method" link you can add a new payment method to your account. Currently supported payment methods are:

* VISA
* Mastercard
* AMEX
* UnionPay

<figure><img src="/files/jUfmz81PycwaUSmVEaxK" alt="" width="563"><figcaption><p>Add Payment Method</p></figcaption></figure>


# Update Payment Address

By clicking on the link "Edit information" you can update the billing address for your company.

<figure><img src="/files/3cXEcPw6cdZM5j67sijU" alt="" width="563"><figcaption><p>Change Payment Address</p></figcaption></figure>


# Invoices

At the bottom of the billing portal you see the last invoices which have been processed. After clicking on the link icon next to an invoice, you will be redirected to the invoice screen on which you can download a copy of the invoice or a receipt for the charge.

<figure><img src="/files/GGvjgq3GwidiUToU11Wv" alt="" width="563"><figcaption><p>Invoice Detail Screen</p></figcaption></figure>


# Terminate Subscription

If you want to terminate your subscription you can do this be clicking on the "terminate" button in the MOOST Billing Platform.

<figure><img src="/files/FGDOQy0JbcU2pbiubs51" alt="" width="563"><figcaption><p>Billing Portal</p></figcaption></figure>


# Context Services

The Recommender Platform offers multiple context services that provide valuable information for making recommendations. The available context services include Weather Forecast, Solar Production Forecast and Power Consumption Forecast.&#x20;

You find information about each context service and how to utilize them within the recommendation platform on the next pages.

{% content-ref url="/pages/BPcnm0jdgVfQtAg2dUkF" %}
[Weather Forecast](/platform-manual/context-services/weather-forecast)
{% endcontent-ref %}

{% content-ref url="/pages/TSwJJRf5KbiMp53xCXnb" %}
[Solar Production Forecast](/platform-manual/context-services/solar-production-forecast)
{% endcontent-ref %}

{% content-ref url="/pages/mk5NmniANepCpu8wmbCc" %}
[Power Consumption Forecast](/platform-manual/context-services/power-consumption-forecast)
{% endcontent-ref %}


# Weather Forecast

The Weather Forecast service provides accurate and up-to-date weather information for a given location. It offers various meteorological data points, such as temperature, humidity, wind speed, precipitation, and more.&#x20;

With the Weather Forecast service we can leverage weather conditions to enhance the quality of recommendations.

## Types

The Weather Forecast generates events with the following  types. This information is available for all customers of the recommender platform which provide a ZIP for their buildings.

<table><thead><tr><th width="387">Type</th><th>Description</th></tr></thead><tbody><tr><td>EXPECTED_OUTSIDE_TEMPERATURE</td><td>The expected outside temperature on the location of a given building for the next calendar day in degrees celsius.</td></tr><tr><td>EXPECTED_OUTSIDE_TEMPERATURE_4DAYS</td><td>The expected outside temperature on the location of a given building in 4 days in degrees celsius.</td></tr></tbody></table>


# Solar Production Forecast

The Solar Production Forecast service predicts the amount of solar energy that can be generated from a solar power system at a specific location. It takes into account factors like solar radiation, cloud cover, and other environmental conditions to provide accurate solar production forecasts.&#x20;

By incorporating the Solar Production Forecast service into the recommendation platform, we can optimize recommendations based on expected solar energy availability.

## Types

The Solar Production Forecast generates events with the following types. This information is available for all customers of the recommender platform which provide a ZIP for their buildings.

<table><thead><tr><th width="359">Type</th><th>Description</th></tr></thead><tbody><tr><td>POWER_GENERATION_FORECAST_1H</td><td>The expected power generated in the next hour in Watt.</td></tr><tr><td>POWER_GENERATION_FORECAST_24H</td><td>The expected power generated in the next 24 hours in Watt.</td></tr><tr><td>POWER_GENERATION_FORECAST_48H</td><td>The expected power generated in the next 48 hours in Watt.</td></tr><tr><td>POWER_GENERATION_FORECAST_TOMORROW</td><td>The expected power generated on the next calendar day.</td></tr><tr><td>POWER_GENERATION_FORECAST_DAY_AFTER_TOMORROW</td><td>The expected power generated on the day after tomorrow in Watt.</td></tr></tbody></table>


# Power Consumption Forecast

The Power Consumption Forecast is a context service that applies machine learning algorithms to historical power consumption data to forecast the power consumption for a building. The Power Consumption Forecast can be used within rules as a normal event type.

## Types

The Energy Consumption Forecast generates events with the following types.

<table><thead><tr><th width="359">Type</th><th>Description</th></tr></thead><tbody><tr><td>POWER_CONSUMPTION_FORECAST_1H</td><td>The expected power consumption in exacly one hour in Watt.</td></tr><tr><td>POWER_CONSUMPTION_FORECAST_24H</td><td>The expected power consumption in exactly 24hours from now in Watt.</td></tr></tbody></table>

> NOTE: The Power Consumption Forecast is only available in the Premium Subscription


# Power Generation Forecast

The Power Generation Forecast is a context service that applies machine learning algorithms to historical power generation data to forecast the power generation for a building. The Power Generation Forecast can be used within rules as a normal event type.

## Types

The Power Generation Forecast generates events with the following types.

<table><thead><tr><th width="359">Type</th><th>Description</th></tr></thead><tbody><tr><td>POWER_GENERATION_FORECAST_1H</td><td>The expected power generation in exacly one hour in Watt.</td></tr><tr><td>POWER_GENERATION_FORECAST_24H</td><td>The expected power generation in exactly 24hours from now in Watt.</td></tr></tbody></table>

> NOTE: The Power Generation Forecast is only available in the Premium Subscription


# Support Center

At the bottom of the navigation pane on the left you find a link called "[MOOST Help Center](https://moost.atlassian.net/servicedesk/customer/portals)" which will bring you to our support center after clicking it.

<figure><img src="/files/CisQ7cb74wWT5UWej3y5" alt="" width="241"><figcaption><p>Navigation Panel</p></figcaption></figure>

On the MOOST Help Center you can choose the type of issue you are facing and create a ticket which will be handled by the MOOST Support regarding the SLA's for the Recommender Platform.

<figure><img src="/files/j8Fzs6SjSkI3EnMfQKv3" alt="" width="563"><figcaption><p>MOOST Help Center</p></figcaption></figure>


# Cloud-to-Cloud Integration

This guide helps you as a customer with the necessary steps on how to integrate your cloud offering with the MOOST Platform. The integration consists of a common data-input layer shared by both MOOST services, and service-specific output interfaces.

## Services Overview

MOOST offers two services that share the same data integration but differ in their outputs:

| Aspect               | Engagement Service                                                      | Intelligence Service                                                                                 |
| -------------------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| **Purpose**          | Personalized notifications and recommendations delivered to households  | Energy statistics, forecasts, and peer comparisons for household dashboards                          |
| **Data Input**       | Buildings + Events (common)                                             | Buildings + Events (common)                                                                          |
| **Data Output**      | Push notifications via REST webhook, Email, or Firebase Cloud Messaging | Energy statistics REST API (hourly, daily, monthly, comparison, forecast) polled by your application |
| **Typical Use Case** | *"Your always-on consumption is 43% higher than usual."*                | Consumption charts, generation history, peer comparison widgets in your app                          |

## Integration Architecture

<figure><img src="/files/KjZt7yhA5bgoMr9ynxYq" alt=""><figcaption></figcaption></figure>

## Integration Steps

Integration follows three phases:

### 1. Platform On-boarding

As a first step ask MOOST to on-board you onto the MOOST Platform.

**What we need from you:**

* **Organisation name** — optionally you may provide an organisation logo URL.
* **Email addresses** of members of your organisation who need access to the admin frontend. We can add more members at a later stage.
* **Email address of billing administrator.**
* **Set of desired notification languages.** By default we activate English, German and French.

**What we do for you:**

* We create a customer on the MOOST Platform. This key element is needed for many processes, such as registering buildings and feeding events.
* We set up logins to the MOOST admin frontend ([admin.moost.io](https://admin.moost.io/)) for members of your organisation. The frontend enables you to see the available rules and buildings, processed events, or delivered notifications.
* We set up the rules by selecting the relevant ones from our use-case library.
* We set up the pricing for the monthly billing.
* We set up the training jobs that are needed to periodically train our Machine Learning models with your events to get more precise results over time.

### 2. Configure Notification Settings (Engagement Service only)

If you use the Engagement Service, configure how MOOST shall deliver notifications to you via the MOOST admin frontend: [admin.moost.io/settings](https://admin.moost.io/settings).

See Engagement Service Integration for details.

### 3. MOOST API Integration

Implement the API integration to synchronize buildings, send events, and — depending on your service — consume the output:

* **Common**: Authenticate, synchronize buildings and devices, send events. See Common Integration.
* **Engagement Service**: Receive notifications, return user interactions. See Engagement Service Integration.
* **Intelligence Service**: Poll energy-statistics and forecast endpoints for your household dashboards. See Intelligence Service Integration.

## API Documentation

All API endpoints are served from `https://api.moost.io`. The interactive Swagger API documentation is available at [doc.api.moost.io](https://doc.api.moost.io/).

## API Usage

The MOOST API is OAuth2-based. You first obtain an access token with your client ID and client secret, then use that token for all subsequent API calls. The access token can be re-used for multiple calls but expires after **86,400 seconds (24 hours)**.

{% content-ref url="/pages/Zrabb6oLYGOQLhNC1Eye" %}
[Common Integration](/technical-integration/cloud-to-cloud-integration/common-integration)
{% endcontent-ref %}

{% content-ref url="/pages/8i138KTqs9irqYTyboPd" %}
[Engagement Service Integration](/technical-integration/cloud-to-cloud-integration/engagement-service-integration)
{% endcontent-ref %}

{% content-ref url="/pages/tAOqxIjWdRvKc3W36bbL" %}
[Intelligence Service Integration](/technical-integration/cloud-to-cloud-integration/intelligence-service-integration)
{% endcontent-ref %}


# Common Integration

This section covers the data input layer shared by both the **Engagement Service** and the **Intelligence Service**. Regardless of which service you use, you need to authenticate, synchronize buildings, and send events.

<figure><img src="/files/wm99SkQ75Xu2Vqzjya3c" alt=""><figcaption></figcaption></figure>

## Request Access Token

To perform operations with the MOOST API, it is essential to obtain a new access token. The steps to obtain and manage access tokens are as follows:

1. **Request a New Token**: Send a request to `https://api.moost.io/auth/token/v1` with your client ID and client secret. The endpoint will issue a token that is valid for **86,400 seconds (24 hours)**.
2. **Use the Token**: Include the access token in the `Authorization` header of your API requests.
3. **Token Expiration**: Monitor the token's validity and request a new one before it expires to maintain uninterrupted access.
4. **Refresh the Token**: Once the token expires, repeat the process to lease a new access token.

### Auth Token API

## GET /auth/token/v1

> Receive a new access token

```json
{"openapi":"3.0.3","info":{"title":"MOOST Public API","version":"latest"},"servers":[{"url":"https://api.moost.io"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","name":"bearerAuth","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"AccessTokenResponse":{"type":"object","properties":{"access_token":{"type":"string","pattern":"^[a-zA-Z0-9_]{1,100}$"},"expires_in":{"type":"integer","format":"int32"},"scope":{"type":"string"},"token_type":{"type":"string","enum":["Bearer","mac"]}},"required":["access_token","token_type"]}}},"paths":{"/auth/token/v1":{"get":{"tags":["PublicAPI"],"summary":"Receive a new access token","operationId":"tokenV1","parameters":[{"name":"clientId","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-zA-Z0-9]{1,100}$"}},{"name":"clientSecret","in":"query","required":true,"schema":{"type":"string","pattern":"^[a-zA-Z0-9_-]{1,100}$"}}],"responses":{"200":{"description":"Success. Valid access token obtained.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccessTokenResponse"}}}},"401":{"description":"Unauthorized. Client id or secret is invalid."},"500":{"description":"Server error. Failed to obtain a valid access token."}}}}}}
```

### **Example with CURL**

```bash
# use your own id and secret:
CLIENT_ID="6c............................Uo"
CLIENT_SECRET="Df.............................Ye"

curl -X GET "https://api.moost.io/auth/token/v1?clientId=$CLIENT_ID&clientSecret=$CLIENT_SECRET"
```

**Sample response**

```json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "expires_in": 86400,
  "scope": "read:buildings write:buildings read:events write:events read:pushnotifications write:pushnotifications",
  "token_type": "Bearer"
}
```

You may store the access token in a variable for subsequent API calls:

```bash
ACCESS_TOKEN="eyJhbGciOiJSUzI1NiIs..."
```

Then include it in all requests:

```
Authorization: Bearer $ACCESS_TOKEN
```

{% hint style="info" %}
You find your `clientId` and `clientSecret` in the MOOST Admin Portal under Settings.
{% endhint %}

## Synchronize Buildings and Devices

Make sure all buildings which shall be taken into consideration by the MOOST Platform are synchronised to MOOST. New buildings shall be created, and changed building states shall be updated (including deactivation and reactivation) via MOOST API.

### Building Data Structure

Following example depicts a building located in Dresden, with connected devices and dual tariff:

```json
{
  "id": "66d07a7fd51e3528aa5673ba",
  "customerId": "656730be2bc9719e6e5ed51d",
  "customerBuildingId": "99900000C048ADA6",
  "zip": "01067",
  "city": "Dresden",
  "countryCode": "DE",
  "timeZoneId": "Europe/Berlin",
  "geolocation": {
    "lat": 47.2383918762207,
    "lon": 8.284913063049316
  },
  "registrationTimestamp": 1645630735,
  "activationTimestamp": 1645640000,
  "isEarlyAdopter": false,
  "userIds": ["user2547865"],
  "disabledNotifications": [
    {
      "ruleId": "63fb7ac54978916150dc5572",
      "userId": "user2547865"
    }
  ],
  "inactiveRules": [],
  "devices": [
    {
      "id": "656830efc4515778b1ce4f2f",
      "type": "CAR",
      "name": "John's Car",
      "product_name": "Device Link Car",
      "createdAt": 1701327087,
      "updatedAt": 1724896211
    },
    {
      "id": "621660e410c1012648d7bed2",
      "type": "CAR_CHARGER",
      "name": "easee Home",
      "product_name": "easee Home",
      "createdAt": 1645633761,
      "updatedAt": 1724936865
    }
  ],
  "settings": {
    "tariff": {
      "type": "DUAL",
      "minPrice": "0.296",
      "maxPrice": "0.385",
      "currency": "EUR",
      "dual": {
        "lowTariff": {
          "mondayStartTime": "19:00",
          "mondayEndTime": "07:00",
          "tuesdayStartTime": "19:00",
          "tuesdayEndTime": "07:00",
          "wednesdayStartTime": "19:00",
          "wednesdayEndTime": "07:00",
          "thursdayStartTime": "19:00",
          "thursdayEndTime": "07:00",
          "fridayStartTime": "19:00",
          "fridayEndTime": "07:00",
          "saturdayStartTime": "00:00",
          "saturdayEndTime": "07:00",
          "sundayStartTime": "00:00",
          "sundayEndTime": "07:00"
        }
      }
    }
  },
  "properties": {}
}
```

### Data Structure Attributes

| Attribute               | Description                                                                                                                                                                  |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                    | The building ID on MOOST side (generated when inserting a new building).                                                                                                     |
| `customerId`            | The ID of the customer (cannot be set or changed).                                                                                                                           |
| `customerBuildingId`    | The building ID on customer side.                                                                                                                                            |
| `zip`                   | Postal code.                                                                                                                                                                 |
| `city`                  | Name of the city.                                                                                                                                                            |
| `countryCode`           | Country code ([ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)).                                                                                       |
| `timeZoneId`            | The [IANA time zone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) of the building. Automatically added by MOOST if not provided (and verified if provided). |
| `geolocation`           | The geo location of the building ([ISO 6709](https://en.wikipedia.org/wiki/ISO_6709)). Automatically added by MOOST if not provided.                                         |
| `registrationTimestamp` | Epoch time (seconds, UTC) when the building was registered on customer side.                                                                                                 |
| `activationTimestamp`   | Epoch time (seconds, UTC) when the building was added to MOOST. Automatically set by MOOST when inserting a new building.                                                    |
| `deactivatedTimestamp`  | If building has been deregistered and shall no longer receive notifications, this attribute is set (Epoch time in seconds, UTC).                                             |
| `isEarlyAdopter`        | If `true`, the building also receives notifications from rules marked for "Early Adopter" (typically new rules tested on a small user base).                                 |
| `devices`               | List of devices. Each device contains `id`, `type` (see Device Types), and `product_name`.                                                                                   |
| `userIds`               | A list of users attached to the building. If present, push notifications can be disabled for individual users instead of for the entire building.                            |
| `disabledNotifications` | If `userIds` are attached, this may contain a list of disabled rules per user.                                                                                               |
| `inactiveRules`         | A list of disabled rules for this building.                                                                                                                                  |
| `settings.tariff`       | Tariff data for the building. See Tariff Configuration below.                                                                                                                |

{% hint style="info" %}
When reading building data, there might be additional attributes such as `profile` or `geolocation`. These fields are calculated by MOOST and should not be modified by the customer.
{% endhint %}

### Tariff Configuration

Buildings support three tariff models, configured in `settings.tariff`:

**Single tariff** — flat rate, no additional configuration needed:

```json
{
  "settings": {
    "tariff": {
      "type": "SINGLE",
      "minPrice": "34.95",
      "maxPrice": "34.95",
      "currency": "CHF"
    }
  }
}
```

**Dual tariff** — defines the low-tariff time window for each day of the week:

```json
{
  "settings": {
    "tariff": {
      "type": "DUAL",
      "minPrice": "0.296",
      "maxPrice": "0.385",
      "currency": "EUR",
      "dual": {
        "lowTariff": {
          "mondayStartTime": "19:00",
          "mondayEndTime": "07:00"
        }
      }
    }
  }
}
```

**Dynamic tariff** — defines time slices with individual prices (typically for 1–2 days ahead):

```json
{
  "settings": {
    "tariff": {
      "type": "DYNAMIC",
      "minPrice": "0.1231",
      "maxPrice": "0.3334",
      "currency": "EUR",
      "dynamic": {
        "tariffs": [
          { "price": "0.1546", "from": 1732212000, "to": 1732212900 },
          { "price": "0.1546", "from": 1732212900, "to": 1732213800 }
        ]
      }
    }
  }
}
```

{% hint style="info" %} The `currency`, `minPrice`, and `maxPrice` attributes are optional and are per-kWh based. If dual or dynamic tariff settings are defined, MOOST is able to generate additional tariff-related events. {% endhint %}

## Building API Endpoints

| Method   | Endpoint                                     | Description                                                                        |
| -------- | -------------------------------------------- | ---------------------------------------------------------------------------------- |
| `POST`   | `/buildings/v1`                              | Create a building. Returns `201` on success, `409` if building already exists.     |
| `PUT`    | `/buildings/v1`                              | Update a building. Returns `200` on success, `404` if not found.                   |
| `GET`    | `/buildings/v1?activeOnly=false`             | Get all buildings.                                                                 |
| `GET`    | `/buildings/core-data/v1?activeOnly=false`   | Get core data of all buildings (lightweight — without devices, settings, profile). |
| `GET`    | `/buildings/{customerBuildingId}/v1`         | Get a single building.                                                             |
| `DELETE` | `/buildings/{customerBuildingId}/v1`         | Delete a building.                                                                 |
| `PUT`    | `/buildings/{customerBuildingId}/devices/v1` | Attach a set of devices to a building.                                             |

{% hint style="info" %}
When a building is created in `EXCLUDE` mode and no `activeRules` are provided, all existing rules of the customer are automatically added to the building.
{% endhint %}

### Device Types

The household itself is referred to as `GATEWAY`. Every event received with device type `GATEWAY` represents a value for the total of the household. The following device types are supported:

| Device Type                                                                                          | Description                                                 |
| ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
| `GATEWAY`                                                                                            | The household itself. Used for whole-building measurements. |
| `SMART_METER`                                                                                        | A smart meter used to monitor energy passing through.       |
| `INVERTER`                                                                                           | A power inverter (DC to AC).                                |
| `BATTERY`                                                                                            | A battery used to store energy.                             |
| `CAR`                                                                                                | A car connected to the HEMS.                                |
| `CAR_CHARGER`                                                                                        | A car charger in the household.                             |
| `HEAT_PUMP`                                                                                          | A heat pump connected to the system.                        |
| `SMART_PLUG`                                                                                         | A smart plug to control a connected device.                 |
| `SOLAR_PANEL`                                                                                        | A solar panel connected to the system.                      |
| `WATER_HEATER`                                                                                       | A water heater connected to the system.                     |
| `THERMOSTAT`                                                                                         | A thermostat to measure temperature.                        |
| `SWITCH`                                                                                             | A smart switch (e.g. for lights).                           |
| `ENERGY_MEASUREMENT`                                                                                 | A device solely used to measure energy.                     |
| `APPLIANCE`                                                                                          | Any appliance that does not fit into another category.      |
| `WALL_TABLET`                                                                                        | A wall tablet used to control the HEMS.                     |
| `THERMAL_ZONE` / `THERMAL_STORAGE`                                                                   | Thermal zone or storage components.                         |
| `DISHWASHER` / `DRYER` / `WASHING_MACHINE` / `OVEN` / `REFRIGERATION` / `LIGHTING` / `ENTERTAINMENT` | Specific household appliances.                              |

## Add Events

Make sure that event data which shall be taken into consideration by the MOOST Platform are forwarded to MOOST via MOOST API. See Event Types below for an overview of which events should be forwarded for which devices and at which interval.

### Event Data Structure

Following example depicts an energy consumption event:

```json
{
  "customerId": "656730be2bc9719e6e5ed51d",
  "customerBuildingId": "99900000C048ADA6",
  "deviceId": "c8f09e83046c",
  "deviceName": "Sinemaa main meter",
  "source": "GATEWAY",
  "type": "ENERGY_CONSUMPTION",
  "value": 858.71,
  "timestamp": 1727992800
}
```

### Data Structure Attributes

| Attribute            | Description                                                                                                                         |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `id`                 | The event ID on MOOST side (generated when inserting a new event).                                                                  |
| `customerId`         | The ID of the customer (cannot be set or changed).                                                                                  |
| `customerBuildingId` | The building ID on customer side.                                                                                                   |
| `deviceId`           | The ID of the device producing this event (should refer to the device within the building).                                         |
| `deviceName`         | The name of the device. If the device does not have its own name, the product name can be used.                                     |
| `source`             | The device type related to this event (see Device Types). Use `GATEWAY` when this is a data point for the whole household/building. |
| `type`               | The event type. This value also implies the data unit. See Event Types.                                                             |
| `value`              | The value of the event data point. Must be a number.                                                                                |
| `timestamp`          | Time of the event (Epoch time in seconds, UTC).                                                                                     |

### Add an Event

## POST /events/v1

> Add an event to a building

```json
{"openapi":"3.0.3","info":{"title":"MOOST Public API","version":"latest"},"servers":[{"url":"https://api.moost.io"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","name":"bearerAuth","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"Event":{"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"customerId":{"type":"string","pattern":"^[a-zA-Z0-9]{24}$"},"customerBuildingId":{"type":"string","pattern":"^[a-zA-Z0-9:._-]{1,100}$"},"deviceId":{"type":"string","pattern":"^[a-zA-Z0-9:._-]{3,64}$"},"deviceName":{"type":"string","pattern":"^.{1,100}$"},"value":{"type":"number","format":"float"},"type":{"type":"string","enum":["CHARGING_MODE","DEVICE_STATUS","ENERGY_CONSUMPTION","ENERGY_CONSUMPTION_LAST_24H","ENERGY_CONSUMPTION_YESTERDAY","ENERGY_EXCESS_LAST_24H","ENERGY_EXCESS_YESTERDAY","ENERGY_GENERATION_LAST_24H","ENERGY_GENERATION_YESTERDAY","ENERGY_IMPORT","ENERGY_IMPORT_YESTERDAY","ENERGY_EXPORT","ENERGY_EXPORT_YESTERDAY","EXPECTED_OUTSIDE_TEMPERATURE","EXPECTED_OUTSIDE_TEMPERATURE_4DAYS","GRID_POWER_CONSUMPTION","GRID_POWER_CONSUMPTION_ANOMALY_SCORE","IS_LOW_TARIFF_HOURS","POWER_CONSUMPTION","POWER_CONSUMPTION_FORECAST_1H","POWER_CONSUMPTION_FORECAST_24H","POWER_EXCESS","POWER_GENERATION","POWER_GENERATION_FORECAST_1H","POWER_GENERATION_FORECAST_1H_MIN","POWER_GENERATION_FORECAST_24H","POWER_GENERATION_FORECAST_48H","POWER_GENERATION_FORECAST_DAY_AFTER_TOMORROW","POWER_GENERATION_FORECAST_TOMORROW","SELF_CONSUMPTION_RATE","SELF_CONSUMPTION_RATE_YESTERDAY","SELF_SUFFICIENCY_RATE","SELF_SUFFICIENCY_RATE_YESTERDAY","STATE_OF_CHARGE_FORECAST_RATE","STATE_OF_CHARGE_RATE","SWITCH_STATE","TEMPERATURE","WATER_TEMPERATURE","POWER_CONSUMPTION_BASE_LOAD","DYNAMIC_TARIFF_PRICE","DYNAMIC_TARIFF_PRICE_FORECAST_1H","DYNAMIC_TARIFF_LOWEST_PRICE_FORECAST_TOMORROW","IS_HIGH_TARIFF_HOURS","ENERGY_EXCESS","ENERGY_BASE_CONSUMPTION","ENERGY_CONSUMPTION_FORECAST_1H","ENERGY_CONSUMPTION_FORECAST_24H","ENERGY_GENERATION","ENERGY_GENERATION_FORECAST_1H","ENERGY_GENERATION_FORECAST_24H","GRID_ENERGY_CONSUMPTION","GRID_ENERGY_CONSUMPTION_YESTERDAY","GRID_ENERGY_CONSUMPTION_ANOMALY_SCORE","GRID_ENERGY_BASE_CONSUMPTION","ENERGY_GENERATION_FORECAST_DAY_AFTER_TOMORROW","ENERGY_GENERATION_FORECAST_TOMORROW","GLOBAL_HORIZONTAL_IRRADIATION_FORECAST_TOMORROW_HOURLY","GLOBAL_HORIZONTAL_IRRADIATION_FORECAST_TOMORROW","ENERGY_CONSUMPTION_FORECAST_TOMORROW","ENERGY_CONSUMPTION_FORECAST_DAY_AFTER_TOMORROW","GRID_BASE_LOAD_CONSUMPTION","DYNAMIC_TARIFF_PRICE_FORECAST_24H","GRID_POWER_CONSUMPTION_YESTERDAY"]},"source":{"type":"string","enum":["APPLIANCE","BATTERY","CAR","CAR_CHARGER","ENERGY_MEASUREMENT","GATEWAY","HEAT_PUMP","INPUT_DEVICE","INVERTER","MOOST","SMART_METER","SMART_PLUG","SWITCH","THERMOSTAT","THERMAL_ZONE","THERMAL_STORAGE","WATER_HEATER","WALL_TABLET","SOLAR_PANEL","DISHWASHER","DRYER","ENTERTAINMENT","LIGHTING","OVEN","REFRIGERATION","WASHING_MACHINE"]},"forecastTimestamp":{"type":"integer","format":"int64"},"ingestionTimestamp":{"type":"integer","format":"int64"}},"required":["customerBuildingId","customerId","deviceId","deviceName","type","value"]}}},"paths":{"/events/v1":{"post":{"tags":["PublicAPI"],"summary":"Add an event to a building","operationId":"addEventV1","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}},"required":true},"responses":{"201":{"description":"Success. Event accepted."},"400":{"description":"Invalid event data, e.g. because specified customerBuildingId is unknown."}}}}}}
```

**Example with CURL:**

```bash
curl -X POST "https://api.moost.io/events/v1" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "656730be2bc9719e6e5ed51d",
    "customerBuildingId": "99900000C048ADA6",
    "deviceId": "c8f09e83046c",
    "deviceName": "Sinemaa main meter",
    "source": "GATEWAY",
    "type": "ENERGY_CONSUMPTION",
    "value": 858.71,
    "timestamp": 1727992800
  }'
```

### Insert Historical Events

## POST /events/historical/v1

> Inserts historical events to the database. I.e. these events are not processed by the rule engine when receiving these events.Event customerId has to be in-line with customerId of the JWT).

```json
{"openapi":"3.0.3","info":{"title":"MOOST Public API","version":"latest"},"servers":[{"url":"https://api.moost.io"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","name":"bearerAuth","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"Event":{"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"customerId":{"type":"string","pattern":"^[a-zA-Z0-9]{24}$"},"customerBuildingId":{"type":"string","pattern":"^[a-zA-Z0-9:._-]{1,100}$"},"deviceId":{"type":"string","pattern":"^[a-zA-Z0-9:._-]{3,64}$"},"deviceName":{"type":"string","pattern":"^.{1,100}$"},"value":{"type":"number","format":"float"},"type":{"type":"string","enum":["CHARGING_MODE","DEVICE_STATUS","ENERGY_CONSUMPTION","ENERGY_CONSUMPTION_LAST_24H","ENERGY_CONSUMPTION_YESTERDAY","ENERGY_EXCESS_LAST_24H","ENERGY_EXCESS_YESTERDAY","ENERGY_GENERATION_LAST_24H","ENERGY_GENERATION_YESTERDAY","ENERGY_IMPORT","ENERGY_IMPORT_YESTERDAY","ENERGY_EXPORT","ENERGY_EXPORT_YESTERDAY","EXPECTED_OUTSIDE_TEMPERATURE","EXPECTED_OUTSIDE_TEMPERATURE_4DAYS","GRID_POWER_CONSUMPTION","GRID_POWER_CONSUMPTION_ANOMALY_SCORE","IS_LOW_TARIFF_HOURS","POWER_CONSUMPTION","POWER_CONSUMPTION_FORECAST_1H","POWER_CONSUMPTION_FORECAST_24H","POWER_EXCESS","POWER_GENERATION","POWER_GENERATION_FORECAST_1H","POWER_GENERATION_FORECAST_1H_MIN","POWER_GENERATION_FORECAST_24H","POWER_GENERATION_FORECAST_48H","POWER_GENERATION_FORECAST_DAY_AFTER_TOMORROW","POWER_GENERATION_FORECAST_TOMORROW","SELF_CONSUMPTION_RATE","SELF_CONSUMPTION_RATE_YESTERDAY","SELF_SUFFICIENCY_RATE","SELF_SUFFICIENCY_RATE_YESTERDAY","STATE_OF_CHARGE_FORECAST_RATE","STATE_OF_CHARGE_RATE","SWITCH_STATE","TEMPERATURE","WATER_TEMPERATURE","POWER_CONSUMPTION_BASE_LOAD","DYNAMIC_TARIFF_PRICE","DYNAMIC_TARIFF_PRICE_FORECAST_1H","DYNAMIC_TARIFF_LOWEST_PRICE_FORECAST_TOMORROW","IS_HIGH_TARIFF_HOURS","ENERGY_EXCESS","ENERGY_BASE_CONSUMPTION","ENERGY_CONSUMPTION_FORECAST_1H","ENERGY_CONSUMPTION_FORECAST_24H","ENERGY_GENERATION","ENERGY_GENERATION_FORECAST_1H","ENERGY_GENERATION_FORECAST_24H","GRID_ENERGY_CONSUMPTION","GRID_ENERGY_CONSUMPTION_YESTERDAY","GRID_ENERGY_CONSUMPTION_ANOMALY_SCORE","GRID_ENERGY_BASE_CONSUMPTION","ENERGY_GENERATION_FORECAST_DAY_AFTER_TOMORROW","ENERGY_GENERATION_FORECAST_TOMORROW","GLOBAL_HORIZONTAL_IRRADIATION_FORECAST_TOMORROW_HOURLY","GLOBAL_HORIZONTAL_IRRADIATION_FORECAST_TOMORROW","ENERGY_CONSUMPTION_FORECAST_TOMORROW","ENERGY_CONSUMPTION_FORECAST_DAY_AFTER_TOMORROW","GRID_BASE_LOAD_CONSUMPTION","DYNAMIC_TARIFF_PRICE_FORECAST_24H","GRID_POWER_CONSUMPTION_YESTERDAY"]},"source":{"type":"string","enum":["APPLIANCE","BATTERY","CAR","CAR_CHARGER","ENERGY_MEASUREMENT","GATEWAY","HEAT_PUMP","INPUT_DEVICE","INVERTER","MOOST","SMART_METER","SMART_PLUG","SWITCH","THERMOSTAT","THERMAL_ZONE","THERMAL_STORAGE","WATER_HEATER","WALL_TABLET","SOLAR_PANEL","DISHWASHER","DRYER","ENTERTAINMENT","LIGHTING","OVEN","REFRIGERATION","WASHING_MACHINE"]},"forecastTimestamp":{"type":"integer","format":"int64"},"ingestionTimestamp":{"type":"integer","format":"int64"}},"required":["customerBuildingId","customerId","deviceId","deviceName","type","value"]}}},"paths":{"/events/historical/v1":{"post":{"tags":["PublicAPI"],"summary":"Inserts historical events to the database. I.e. these events are not processed by the rule engine when receiving these events.Event customerId has to be in-line with customerId of the JWT).","operationId":"insertHistoricalEventsV1","requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Event"}}}},"required":true},"responses":{"200":{"description":"Success. All events have been inserted."},"400":{"description":"Invalid event data.","content":{"*/*":{"schema":{"type":"object","additionalProperties":{"type":"string"}}}}},"500":{"description":"Internal error. None or only part of the data could be written."}}}}}}
```

{% hint style="info" %}
Accepts an array of event objects. Historical events are stored in the database but **not** processed by the rule engine. Use this endpoint for backfilling data.
{% endhint %}

#### Event Types

Every event that occurs on a sensor or actor within a building can be seen as a change of a state. Below you find the event types the platform can receive from your environment.

{% hint style="warning" %}
MOOST recommends sending an update for each sensor **every 15 minutes**. Avoid excessive event generation to prevent unnecessary processing and network overhead.
{% endhint %}

**Customer Provided Types**

| Event Type                          | Unit        | Interval     | Device Type                                    | Description                                                 |
| ----------------------------------- | ----------- | ------------ | ---------------------------------------------- | ----------------------------------------------------------- |
| `ENERGY_CONSUMPTION`                | Wh          | Every 15 min | ALL                                            | Energy consumption of the last time interval.               |
| `ENERGY_CONSUMPTION_YESTERDAY`      | Wh          | Once a day   | ALL                                            | Energy consumed on the previous calendar day.               |
| `ENERGY_GENERATION`                 | Wh          | Every 15 min | GATEWAY                                        | Energy generation of the last time interval.                |
| `ENERGY_GENERATION_YESTERDAY`       | Wh          | Once a day   | GATEWAY                                        | Energy generated on the previous calendar day.              |
| `GRID_ENERGY_CONSUMPTION`           | Wh          | Every 15 min | GATEWAY                                        | Energy consumption from the grid of the last time interval. |
| `GRID_ENERGY_CONSUMPTION_YESTERDAY` | Wh          | Once a day   | GATEWAY                                        | Energy consumption from the grid on the previous day.       |
| `POWER_CONSUMPTION`                 | W           | Every 15 min | ALL                                            | Current power consumed.                                     |
| `POWER_GENERATION`                  | W           | Every 15 min | GATEWAY                                        | Current power generated.                                    |
| `POWER_EXCESS`                      | W           | Every 15 min | GATEWAY                                        | Current power excess.                                       |
| `GRID_POWER_CONSUMPTION`            | W           | Every 15 min | GATEWAY                                        | Current power consumed from the grid.                       |
| `ENERGY_IMPORT`                     | Wh          | Every 15 min | BATTERY                                        | Energy used to charge a battery.                            |
| `ENERGY_IMPORT_YESTERDAY`           | Wh          | Once a day   | BATTERY                                        | Energy used to charge a battery on the previous day.        |
| `ENERGY_EXPORT`                     | Wh          | Every 15 min | BATTERY                                        | Energy drawn from a battery.                                |
| `ENERGY_EXPORT_YESTERDAY`           | Wh          | Once a day   | BATTERY                                        | Energy drawn from a battery on the previous day.            |
| `ENERGY_EXCESS`                     | Wh          | Every 30 min | GATEWAY                                        | Energy excess of the last time interval.                    |
| `ENERGY_EXCESS_YESTERDAY`           | Wh          | Once a day   | GATEWAY                                        | Energy excess on the previous calendar day.                 |
| `SELF_CONSUMPTION_RATE`             | %           | Every 15 min | GATEWAY                                        | Current self-consumption rate.                              |
| `SELF_CONSUMPTION_RATE_YESTERDAY`   | %           | Once a day   | GATEWAY                                        | Self-consumption rate on the previous day.                  |
| `SELF_SUFFICIENCY_RATE`             | %           | Every 15 min | GATEWAY                                        | Current self-sufficiency rate.                              |
| `SELF_SUFFICIENCY_RATE_YESTERDAY`   | %           | Once a day   | GATEWAY                                        | Self-sufficiency rate on the previous day.                  |
| `STATE_OF_CHARGE_RATE`              | %           | On change    | CAR                                            | Current state of charge as percentage.                      |
| `CHARGING_MODE`                     | ID \[0..7]  | On change    | CAR\_CHARGER, HEAT\_PUMP, BATTERY, SMART\_PLUG | Current charging mode of a device.                          |
| `DEVICE_STATUS`                     | ID \[-1..4] | On change    | ALL                                            | State of a device.                                          |
| `SWITCH_STATE`                      | ID          | On change    | SMART\_PLUG, CAR\_CHARGER, SWITCH              | Connection state of a switch.                               |
| `TEMPERATURE`                       | °C          | Every 15 min | THERMOSTAT                                     | Current temperature.                                        |
| `WATER_TEMPERATURE`                 | °C          | Every 15 min | HEAT\_PUMP, WATER\_HEATER                      | Current water temperature.                                  |

**MOOST Provided Types (Context Services)**

These event types are generated by MOOST context services and do not need to be provided by you.

| Event Type                           | Unit     | Interval     | Description                                             |
| ------------------------------------ | -------- | ------------ | ------------------------------------------------------- |
| `EXPECTED_OUTSIDE_TEMPERATURE`       | °C       | Once a day   | Expected outside temperature for the next calendar day. |
| `EXPECTED_OUTSIDE_TEMPERATURE_4DAYS` | °C       | Once a day   | Expected outside temperature in 4 days.                 |
| `POWER_GENERATION_FORECAST_1H`       | W        | Every hour   | Expected power generated in the next hour.              |
| `POWER_GENERATION_FORECAST_24H`      | W        | Every hour   | Expected power generated in the next 24 hours.          |
| `POWER_GENERATION_FORECAST_TOMORROW` | W        | Once a day   | Expected power generated on the next calendar day.      |
| `POWER_CONSUMPTION_FORECAST_1H`      | W        | Every hour   | Expected power consumption in one hour.                 |
| `POWER_CONSUMPTION_FORECAST_24H`     | W        | Every hour   | Expected power consumption in 24 hours.                 |
| `ENERGY_BASE_CONSUMPTION`            | Wh       | Once a day   | Calculated base consumption from recent data.           |
| `GRID_ENERGY_BASE_CONSUMPTION`       | Wh       | Once a day   | Calculated grid base consumption.                       |
| `DYNAMIC_TARIFF_PRICE`               | currency | Every 30 min | Current tariff price for dynamic tariff buildings.      |
| `DYNAMIC_TARIFF_PRICE_FORECAST_1H`   | currency | Every 30 min | Tariff price of next hour.                              |
| `ENERGY_CONSUMPTION_FORECAST_1H`     | Wh       | Every hour   | Expected energy consumption in the next hour.           |
| `ENERGY_GENERATION_FORECAST_1H`      | Wh       | Every hour   | Expected energy generation in the next hour.            |

{% hint style="info" %}
Context services such as Weather Forecast, Solar Production Forecast, and Power Consumption Forecast are automatically enabled for customers who provide a ZIP code and geolocation for their buildings. The Power Consumption Forecast and Power Generation Forecast are available in the Premium subscription.
{% endhint %}


# Engagement Service Integration

The Engagement Service turns building and event data into personalized, contextual notifications delivered to your end users. The MOOST rule engine evaluates incoming events against your configured rules and generates recommendations that are pushed to your system via the fulfillment channel of your choice.

## How It Works

1. You send events to the MOOST platform (see Common Integration).
2. The rule engine evaluates each event against the active rules for the building.
3. When a rule matches, a notification is generated with localized text.
4. The notification is delivered through the configured fulfillment channel.

## Receive Notifications

MOOST delivers the generated notifications to a REST endpoint which can be specified by you. You can then deliver these notifications to your households (end-users) e.g. via Firebase Cloud Messaging.

<figure><img src="/files/X5aYbC1Vt8MaQau48rAD" alt=""><figcaption></figcaption></figure>

The endpoint to be used can be configured per customer, per end-user, or a combination of both via the MOOST admin frontend: [admin.moost.io/settings](https://admin.moost.io/settings).

#### REST Endpoint

To receive notifications via REST, the endpoint has to be exposed on a public domain accessible via the Internet. Any REST endpoint accessible via Internet should be protected via authentication.

#### Authentication

The MOOST Platform can authenticate against your REST endpoint via the following methods:

| Method                  | Description                                      |
| ----------------------- | ------------------------------------------------ |
| **Username / Password** | Basic authentication with username and password. |
| **API-Token**           | Authentication using an API token.               |
| **Bearer-Token**        | Authentication using a bearer token.             |
| **OAuth**               | OAuth client credentials authentication.         |

#### API Throttling

If your REST endpoint is protected against DDOS attacks with request throttling, please inform MOOST during the integration phase about the limitations in place. MOOST will configure the platform accordingly.

If the limits are exceeded, the MOOST Platform will not be able to send all notifications back to your REST endpoint. Ensure that the chosen limitation is in line with the expected notifications per time unit.

### Notification Schema

The notification object is posted to your REST endpoint as a JSON message in the body.

#### Format

```json
{
  "id": "631b3a453d6ddc7effeebb17",
  "notification": {
    "actionQualifier": {
      "primary": "OPENAPP",
      "secondary": "STOPDELIVERY"
    },
    "texts": {
      "de": {
        "title": "Ihr täglicher Energiebericht",
        "message": "Ihre Solaranlage hat gestern 1000 kWh produziert. Eigenverbrauchsrate: 70%. Autarkiegrad: 40%.",
        "actions": {
          "primary": {
            "text": "DETAILS"
          },
          "secondary": {
            "text": "NICHT ERNEUT ANZEIGEN"
          }
        }
      },
      "en": {
        "title": "Your daily energy report",
        "message": "Your PV system generated 1000 kWh yesterday. Self-consumption rate: 70%. Self-sufficiency rate: 40%.",
        "actions": {
          "primary": {
            "text": "DETAILS"
          },
          "secondary": {
            "text": "DON'T NOTIFY AGAIN"
          }
        }
      },
      "fr": {
        "title": "Votre rapport énergétique journalier",
        "message": "Votre système a produit hier 1000 kWh. Taux d'auto-consommation: 70%. Degré d'autonomie: 40%.",
        "actions": {
          "primary": {
            "text": "DÉTAILS"
          },
          "secondary": {
            "text": "NE PLUS NOTIFIER"
          }
        }
      }
    },
    "command": null
  },
  "createdAtTimeMillis": 1662728773479,
  "customerId": "6149884d0aafc84cb196d4c8",
  "customerBuildingId": "6149884d0aafc84cb196d4c8",
  "ruleId": "61e19fc6c7e5a80a4c764213",
  "priority": "high"
}
```

#### Notification Fields

| Field                          | Description                                                                                                                                    |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                           | Unique notification ID. Used to return interactions.                                                                                           |
| `notification.actionQualifier` | Maps action names (e.g. `primary`, `secondary`) to action qualifier types.                                                                     |
| `notification.texts`           | Localized notification content keyed by language code (e.g. `en`, `de`, `fr`). Each entry contains `title`, `message`, and optional `actions`. |
| `notification.command`         | Optional command field for machine-to-machine instructions. Customer-specific; initially empty but can be defined by the customer.             |
| `createdAtTimeMillis`          | Timestamp when the notification was created (milliseconds).                                                                                    |
| `customerId`                   | The customer ID.                                                                                                                               |
| `customerBuildingId`           | The building that this notification is for.                                                                                                    |
| `ruleId`                       | The rule that triggered this notification.                                                                                                     |
| `priority`                     | Notification priority (`high` or `normal`).                                                                                                    |

#### Actions and Action Qualifiers

A notification includes two actions that should be presented to the end-user. Each action consists of a text field (presented to the user in different languages) and an action qualifier that describes the type of action.

| Action Qualifier | Description                                                                                                                                                                 |
| ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DISMISS`        | The push notification should be dismissed.                                                                                                                                  |
| `OPENAPP`        | The app should be opened. The optional `parameter` attribute may contain a language-specific value, such as a URL or use-case name.                                         |
| `STOPDELIVERY`   | The notification should be dismissed and the action qualifier returned to the MOOST API, indicating that no more notifications of this type should be sent to the end-user. |
| `OPENWEB`        | A browser should be opened. The optional `parameter` attribute typically contains a language-specific URL.                                                                  |

## Firebase Cloud Messaging

To integrate MOOST notifications into your mobile application, you can use Firebase Cloud Messaging (FCM). With FCM you have a centralized tool to send notifications to your end-users on both Android and iOS apps.

#### iOS Notification Handling

iOS differentiates between the action that happens when pressing the notification and the quick actions presented when long-pressing a notification.

**Example (Swift):**

```swift
func userNotificationCenter(_ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void) {
    
    let userInfo = response.notification.request.content.actions
    let url = userInfo["parameter"]["en"] as! String
    
    switch response.actionIdentifier {
    case "OPENAPP":
        sharedAppManager.openApp()
    case "STOPDELIVERY":
        sharedAppManager.sendStopDelivery()
    case "OPENWEB":
        sharedAppManager.openWeb(url)
    default:
        sharedAppManager.openApp(url)
    }
    
    completionHandler()
}
```

## Notifications and Interactions

#### Load Notifications of a Building

Your app may need to display the history of generated notifications (e.g. in a Notification Center). Use the following API:

```
GET https://api.moost.io/pushnotifications/buildings/{customerBuildingId}/v1?deliveryStatus=DELIVERED
```

| Parameter            | Required | Description                         |
| -------------------- | -------- | ----------------------------------- |
| `customerBuildingId` | yes      | The building identifier.            |
| `deliveryStatus`     | no       | Filter by `DELIVERED` or `DROPPED`. |

**Example with CURL:**

```bash
curl -X GET \
  "https://api.moost.io/pushnotifications/buildings/99900000ECA31C6E/v1?deliveryStatus=DELIVERED" \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

#### Return Notification Interaction

To train our algorithms based on the actions taken by end-users, it is important that the action qualifier is returned to the MOOST Platform.

```
POST https://api.moost.io/pushnotifications/{pushNotificationId}/interactions/v1
```

**Request body:**

```json
{
  "actionQualifier": "OPENAPP",
  "userId": "user@example.com"
}
```

**Example with CURL:**

```bash
curl -X POST \
  "https://api.moost.io/pushnotifications/668b8e708f308b0123074cb7/interactions/v1" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"actionQualifier":"OPENWEB"}'
```

{% hint style="warning" %}
Notification interactions **must** be returned to MOOST on **every** interaction the end-user has with a notification. This includes: pressing the primary button, pressing the secondary button, pressing on the notification itself, and dismissing the notification (swipe left).&#x20;
{% endhint %}

## Rules API

Rules define the conditions under which notifications are triggered. They are typically created and managed through the MOOST admin frontend, but can be read and managed via the API.

| Method | Endpoint                                                          | Description                                                                          |
| ------ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| `GET`  | `/rules/v1?tags=solar`                                            | Get all rules. Optionally filter by tags.                                            |
| `GET`  | `/rules/tags/v1`                                                  | Get all distinct tags used across rules.                                             |
| `GET`  | `/buildings/{customerBuildingId}/rules/v2`                        | Get active rules of a specific building.                                             |
| `PUT`  | `/buildings/{customerBuildingId}/rules/{ruleId}/v1?status=active` | Activate or deactivate a rule for a building. Set `status` to `active`or `inactive`. |

This allows you to enable or disable specific rules per building — for example, to let end users opt out of certain notification types.

## Integration Checklist

* [ ] &#x20;Obtain API credentials (`clientId`, `clientSecret`) from MOOST.
* [ ] &#x20;Implement authentication (see Common Integration).
* [ ] Register your buildings with devices and tariff settings.
* [ ] Configure a fulfillment channel in [admin.moost.io/settings](https://admin.moost.io/settings) (REST webhook, email, or FCM).
* [ ] If using REST: expose an authenticated endpoint and inform MOOST of any throttling limits.
* [ ] Begin sending events at the recommended interval (\~15 min per sensor).
* [ ] Verify notifications are being delivered via `GET /pushnotifications/buildings/{id}/v1`.
* [ ] Implement interaction tracking — return the `actionQualifier` for **every** user interaction.
* [ ] Build a Notification Center in your app using the push notifications API.


# Intelligence Service Integration

The Intelligence Service processes building and event data into aggregated energy statistics, forecasts, and peer comparisons. These endpoints power the **Households Dashboard** — giving end users insight into their energy consumption, generation, and how they compare to similar households.

### How It Works

<figure><img src="/files/QrLXuVGRl2We21TfnISb" alt=""><figcaption></figcaption></figure>

1. You send events to the MOOST platform (see [Common Integration](/technical-integration/cloud-to-cloud-integration/common-integration#add-events)).
2. MOOST aggregates and processes the raw event data.
3. Your application polls the energy-statistics and energy-forecast endpoints.
4. You render the data in your Households Dashboard — charts, comparisons, and forecasts.

Unlike the Engagement Service, the Intelligence Service does **not** push data to your system. Instead, your application pulls processed data on demand through the API.

### Sections

{% content-ref url="/pages/KXyUIjmg1dsIYTL3vp32" %}
[Energy Statistics](/technical-integration/cloud-to-cloud-integration/intelligence-service-integration/energy-statistics)
{% endcontent-ref %}

{% content-ref url="/pages/KRu3pQ8mFyE9YDqn3TVo" %}
[Peer Comparison](/technical-integration/cloud-to-cloud-integration/intelligence-service-integration/peer-comparison)
{% endcontent-ref %}

{% content-ref url="/pages/fnI8ci3F28zBtpLzP9fO" %}
[Energy Forecast](/technical-integration/cloud-to-cloud-integration/intelligence-service-integration/energy-forecast)
{% endcontent-ref %}

{% content-ref url="/pages/6koxTdUHOnsalrJWc1ib" %}
[Households Dashboard](/technical-integration/cloud-to-cloud-integration/intelligence-service-integration/households-dashboard)
{% endcontent-ref %}

### Integration Checklist

* [ ] Obtain API credentials (`clientId`, `clientSecret`) from MOOST.
* [ ] Implement authentication (see Common Integration).
* [ ] Register your buildings with devices, location (`zip`, `geolocation`), and tariff settings.
* [ ] Begin sending events — at minimum `ENERGY_CONSUMPTION` and `ENERGY_GENERATION` (if applicable). For faster energy-statistics queries, also send daily aggregate `*_YESTERDAY` event types.
* [ ] Implement polling for the energy-statistics endpoints to populate your dashboard.
* [ ] Add peer comparison using the comparison endpoint with an appropriate category (`CONSUMPTION_CATEGORY`, `ELCOM`, or `GRID_POWER_CONSUMPTION_CLUSTER`).
* [ ] Integrate forecasts for forward-looking dashboard features.
* [ ] Use the MOOST admin frontend Household Detail → Dashboard tab as a reference implementation.


# Energy Statistics

The energy-statistics endpoints aggregate raw event data into time-bucketed readings for a specific building. Three temporal resolutions are available: hourly, daily, and monthly.

{% hint style="info" %}
The building's local time zone is taken into account for all energy-statistics endpoints. The time window can be up to 1 year.
{% endhint %}

## Hourly Energy Statistics

Returns energy data aggregated on an hourly basis. Use this for intraday charts (e.g. "Today's energy").

## A building’s energy data with hourly readings

> Get energy related data of a building, aggregated on a hourly basis.

```json
{"openapi":"3.0.3","info":{"title":"MOOST Public API","version":"latest"},"servers":[{"url":"https://api.moost.io"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","name":"bearerAuth","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"HourlyEnergyStatistics":{"type":"object","properties":{"startTimestamp":{"type":"integer","format":"int64"},"energyGeneration":{"type":"number","format":"float"},"energyConsumption":{"type":"number","format":"float"},"gridEnergyConsumption":{"type":"number","format":"float"}}}}},"paths":{"/buildings/{customerBuildingId}/energy-statistics/hourly/v1":{"get":{"tags":["PublicAPI"],"summary":"A building’s energy data with hourly readings","description":"Get energy related data of a building, aggregated on a hourly basis.","operationId":"getHourlyEnergyStatistics","parameters":[{"name":"customerBuildingId","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-zA-Z0-9:._-]{1,100}$"}},{"name":"eventTypes","in":"query","description":"Comma-separated set of event types, which defines what data is to be aggregated.\n","required":true,"schema":{"type":"array","items":{"type":"string","enum":["ENERGY_GENERATION","ENERGY_CONSUMPTION","GRID_ENERGY_CONSUMPTION"]}}},{"name":"from","in":"query","description":"The starting date of the time window for selecting event data.\n<br/>\nRemark: The building's local time zone is taken into account.\n","required":true,"schema":{"type":"string","pattern":"[0-9]{4}-[0-9]{2}-[0-9]{2}"}},{"name":"to","in":"query","description":"The final date of the time window for selecting event data.\n<br/>\nRemark: the time window can be up to 1 year.\n","required":true,"schema":{"type":"string","pattern":"[0-9]{4}-[0-9]{2}-[0-9]{2}"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HourlyEnergyStatistics"}}}}}}}}}}
```

## Daily Energy Statistics

Returns energy data aggregated on a daily basis. Use this for "this month" charts and seasonal analysis.

## A building’s energy data with daily readings

> Get energy related data of a building, aggregated on a daily basis.

```json
{"openapi":"3.0.3","info":{"title":"MOOST Public API","version":"latest"},"servers":[{"url":"https://api.moost.io"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","name":"bearerAuth","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"DailyEnergyStatistics":{"type":"object","properties":{"day":{"type":"string"},"energyGeneration":{"type":"number","format":"float"},"energyConsumption":{"type":"number","format":"float"},"gridEnergyConsumption":{"type":"number","format":"float"},"energyBaseConsumption":{"type":"number","format":"float"},"gridEnergyBaseConsumption":{"type":"number","format":"float"}}}}},"paths":{"/buildings/{customerBuildingId}/energy-statistics/daily/v1":{"get":{"tags":["PublicAPI"],"summary":"A building’s energy data with daily readings","description":"Get energy related data of a building, aggregated on a daily basis.","operationId":"getDailyEnergyStatistics","parameters":[{"name":"customerBuildingId","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-zA-Z0-9:._-]{1,100}$"}},{"name":"eventTypes","in":"query","description":"Comma-separated set of event types, which defines what data is to be aggregated.\n<ul>\n<li>For energy generation use <code>ENERGY_GENERATION_YESTERDAY</code> or <code>ENERGY_GENERATION</code>.</li>\n<li>For energy consumption use <code>ENERGY_CONSUMPTION_YESTERDAY</code> or <code>ENERGY_CONSUMPTION</code>.</li>\n<li>For grid energy consumption use <code>GRID_ENERGY_CONSUMPTION_YESTERDAY</code> or <code>GRID_ENERGY_CONSUMPTION</code>.</li>\n</ul>\n<br/>\nRemark: prefer the <code>*_YESTERDAY</code> event types, i.e. daily resolution data over high resolution data, as it is much faster.\nIf both low and high resolution types have been declared, then this API tries with low resolution, and only if nothing found\ntries with high resolution type.","required":true,"schema":{"type":"array","items":{"type":"string","enum":["ENERGY_GENERATION_YESTERDAY","ENERGY_GENERATION","ENERGY_CONSUMPTION_YESTERDAY","ENERGY_CONSUMPTION","GRID_ENERGY_CONSUMPTION_YESTERDAY","GRID_ENERGY_CONSUMPTION","ENERGY_BASE_CONSUMPTION","GRID_ENERGY_BASE_CONSUMPTION"]}}},{"name":"from","in":"query","description":"The starting date of the time window for selecting event data.\n<br/>\nRemark: The building's local time zone is taken into account.\n","required":true,"schema":{"type":"string","pattern":"[0-9]{4}-[0-9]{2}-[0-9]{2}"}},{"name":"to","in":"query","description":"The final date of the time window for selecting event data.\n<br/>\nRemark: the time window can be up to 1 year.\n","required":true,"schema":{"type":"string","pattern":"[0-9]{4}-[0-9]{2}-[0-9]{2}"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DailyEnergyStatistics"}}}}}}}}}}
```

{% hint style="warning" %}
Prefer the `*_YESTERDAY` event types (daily resolution data) over high-resolution types — they are significantly faster to query. If both low and high resolution types are declared, the API tries daily resolution first and falls back to high resolution only if no data is found.
{% endhint %}

## Monthly Energy Statistics

Returns energy data aggregated on a monthly basis. Use this for yearly overview charts and 12-month trend cards.

## A building’s energy data with monthly readings

> Get energy related data of a building, aggregated on a monthly basis.

```json
{"openapi":"3.0.3","info":{"title":"MOOST Public API","version":"latest"},"servers":[{"url":"https://api.moost.io"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","name":"bearerAuth","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"MonthlyEnergyStatistics":{"type":"object","properties":{"month":{"type":"string"},"energyGeneration":{"type":"number","format":"float"},"energyConsumption":{"type":"number","format":"float"},"gridEnergyConsumption":{"type":"number","format":"float"}}}}},"paths":{"/buildings/{customerBuildingId}/energy-statistics/monthly/v1":{"get":{"tags":["PublicAPI"],"summary":"A building’s energy data with monthly readings","description":"Get energy related data of a building, aggregated on a monthly basis.","operationId":"getMonthlyEnergyStatistics","parameters":[{"name":"customerBuildingId","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-zA-Z0-9:._-]{1,100}$"}},{"name":"eventTypes","in":"query","description":"Comma-separated set of event types, which defines what data is to be aggregated.\n<ul>\n<li>For energy generation use <code>ENERGY_GENERATION_YESTERDAY</code> or <code>ENERGY_GENERATION</code>.</li>\n<li>For energy consumption use <code>ENERGY_CONSUMPTION_YESTERDAY</code> or <code>ENERGY_CONSUMPTION</code>.</li>\n<li>For grid energy consumption use <code>GRID_ENERGY_CONSUMPTION_YESTERDAY</code> or <code>GRID_ENERGY_CONSUMPTION</code>.</li>\n</ul>\nRemark: prefer the <code>*_YESTERDAY</code> event types, i.e. daily resolution data over high resolution data, as it is much faster.\nIf both low and high resolution types have been declared, then this API tries with low resolution, and only if nothing found\ntries with high resolution type.","required":true,"schema":{"type":"array","items":{"type":"string","enum":["ENERGY_GENERATION_YESTERDAY","ENERGY_GENERATION","ENERGY_CONSUMPTION_YESTERDAY","ENERGY_CONSUMPTION","GRID_ENERGY_CONSUMPTION_YESTERDAY","GRID_ENERGY_CONSUMPTION"]}}},{"name":"from","in":"query","description":"The starting month of the time window for selecting event data.\n<br/>\nRemark: The building's local time zone is taken into account.\n","required":true,"schema":{"type":"string","pattern":"[0-9]{4}-[0-9]{2}"}},{"name":"to","in":"query","description":"The final month of the time window for selecting event data.\n<br/>\nRemark: the time window can be up to 1 year.\n","required":true,"schema":{"type":"string","pattern":"[0-9]{4}-[0-9]{2}"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MonthlyEnergyStatistics"}}}}}}}}}}
```


# Peer Comparison

The comparison endpoint enriches daily energy statistics with percentile rankings against a group of comparable buildings. This powers "How do I compare?" features in your Households Dashboard.

## A building’s energy data with daily readings, and comparison with comparable buildings

> Get energy related data of a building, aggregated on a daily basis and compare it with comparable buildings.\
> \<br/>\
> The comparison group has the following values:\
> \<ul>\
> \<li>Rank in comparison group. Rank 1 is the one with highest value.\</li>\
> \<li>Size of the comparison group.\</li>\
> \<li>Minimum value in the comparison group.\</li>\
> \<li>Maximum value in the comparison group.\</li>\
> \<li>25th percentile in the comparison group.\</li>\
> \<li>Median (50th percentile) in the comparison group.\</li>\
> \<li>75th percentile in the comparison group.\</li>\
> \</ul><br>

```json
{"openapi":"3.0.3","info":{"title":"MOOST Public API","version":"latest"},"servers":[{"url":"https://api.moost.io"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","name":"bearerAuth","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"DailyComparisonEnergyStatistics":{"type":"object","properties":{"day":{"type":"string"},"energyGeneration":{"$ref":"#/components/schemas/ComparisonGroup"},"energyConsumption":{"$ref":"#/components/schemas/ComparisonGroup"},"gridEnergyConsumption":{"$ref":"#/components/schemas/ComparisonGroup"}}},"ComparisonGroup":{"type":"object","properties":{"value":{"type":"number","format":"float"},"rank":{"type":"integer","format":"int32"},"size":{"type":"integer","format":"int32"},"min":{"type":"number","format":"float"},"max":{"type":"number","format":"float"},"p25":{"type":"number","format":"float"},"median":{"type":"number","format":"float"},"p75":{"type":"number","format":"float"}}}}},"paths":{"/buildings/{customerBuildingId}/energy-statistics/comparison/daily/v1":{"get":{"tags":["PublicAPI"],"summary":"A building’s energy data with daily readings, and comparison with comparable buildings","description":"Get energy related data of a building, aggregated on a daily basis and compare it with comparable buildings.\n<br/>\nThe comparison group has the following values:\n<ul>\n<li>Rank in comparison group. Rank 1 is the one with highest value.</li>\n<li>Size of the comparison group.</li>\n<li>Minimum value in the comparison group.</li>\n<li>Maximum value in the comparison group.</li>\n<li>25th percentile in the comparison group.</li>\n<li>Median (50th percentile) in the comparison group.</li>\n<li>75th percentile in the comparison group.</li>\n</ul>\n","operationId":"getDailyComparisonEnergyStatistics","parameters":[{"name":"customerBuildingId","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-zA-Z0-9:._-]{1,100}$"}},{"name":"eventTypes","in":"query","description":"Comma-separated set of event types, which defines what data is to be aggregated.\n","required":true,"schema":{"type":"array","items":{"type":"string","enum":["ENERGY_GENERATION_YESTERDAY","ENERGY_CONSUMPTION_YESTERDAY","GRID_ENERGY_CONSUMPTION_YESTERDAY"]}}},{"name":"from","in":"query","description":"The starting date of the time window for selecting event data.\n<br/>\nRemark: The building's local time zone is taken into account.\n","required":true,"schema":{"type":"string","pattern":"[0-9]{4}-[0-9]{2}-[0-9]{2}"}},{"name":"to","in":"query","description":"The final date of the time window for selecting event data.\n<br/>\nRemark: the time window can be up to 1 year.\n","required":true,"schema":{"type":"string","pattern":"[0-9]{4}-[0-9]{2}-[0-9]{2}"}},{"name":"category","in":"query","description":"The profile comparison category that is to be used for the comparison.\n","required":true,"schema":{"type":"string","enum":["GRID_POWER_CONSUMPTION_CLUSTER","CONSUMPTION_CATEGORY","ELCOM","BEP"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DailyComparisonEnergyStatistics"}}}}}}}}}}
```

## Dashboard Usage

The peer comparison data is typically rendered in the Households Dashboard as a ranking indicator with a comparison trend chart. The MOOST admin frontend shows this in the Household Detail → Dashboard tab as a card displaying, for example, "Rank 2 of 2" with the category label (e.g. `VERY_HIGH`).

Percentile bars or box-plot-style visualisations work well for showing the building's position relative to `p25`, `median`, and `p75`.


# Energy Forecast

The forecast endpoint returns predicted energy values generated by MOOST's Machine Learning models. These forecasts enable forward-looking features in your Households Dashboard, such as "Low solar generation expected tomorrow" or consumption planning widgets.

## A building’s energy forecast data

> Get energy forecast data of a building.

```json
{"openapi":"3.0.3","info":{"title":"MOOST Public API","version":"latest"},"servers":[{"url":"https://api.moost.io"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","name":"bearerAuth","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"EnergyForecast":{"type":"object","properties":{"timestamp":{"type":"integer","format":"int64"},"forecastTimestamp":{"type":"integer","format":"int64"},"value":{"type":"number","format":"float"}}}}},"paths":{"/buildings/{customerBuildingId}/energy-forecast/v1":{"get":{"tags":["PublicAPI"],"summary":"A building’s energy forecast data","description":"Get energy forecast data of a building.","operationId":"getEnergyForecast","parameters":[{"name":"customerBuildingId","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-zA-Z0-9:._-]{1,100}$"}},{"name":"eventType","in":"query","description":"Target energy forecast event type, which defines what data to select.\n","required":true,"schema":{"type":"string","enum":["ENERGY_CONSUMPTION_FORECAST_1H","ENERGY_CONSUMPTION_FORECAST_24H","ENERGY_CONSUMPTION_FORECAST_TOMORROW","ENERGY_CONSUMPTION_FORECAST_DAY_AFTER_TOMORROW","ENERGY_GENERATION_FORECAST_1H","ENERGY_GENERATION_FORECAST_24H","ENERGY_GENERATION_FORECAST_TOMORROW","ENERGY_GENERATION_FORECAST_DAY_AFTER_TOMORROW"]}},{"name":"from","in":"query","description":"The starting time (inclusive) in epoch seconds of the forecast time window.\n<br/>\nRemark: The time window between `from` and `to` must not exceed one month.\n","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"to","in":"query","description":"The final time (exclusive) in epoch seconds of the forecast time window.\n","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/EnergyForecast"}}}}}}}}}}
```

## Relationship to Context Services

The energy forecasts exposed by this API are generated by MOOST's Context Services — specifically the **Power Consumption Forecast** and the **Solar Production Forecast** (also known as Power Generation Forecast). These services apply machine learning algorithms to historical data to produce their predictions.

The Context Services also generate power-level forecast events (e.g. `POWER_GENERATION_FORECAST_1H`, `POWER_CONSUMPTION_FORECAST_24H`) that are used internally by the rule engine for the Engagement Service. The energy-forecast API provides the energy-level equivalents in a format optimised for dashboard rendering.

{% hint style="info" %}
The Power Consumption Forecast and Power Generation Forecast are available in the **Premium** subscription. The Solar Production Forecast (weather-based) is available for all customers who provide a ZIP code and geolocation for their buildings.
{% endhint %}

## Dashboard Usage

Forecasts are typically rendered in the Households Dashboard as:

* **Generation forecast chart** — showing expected solar production for the next 1–2 days, helping users plan energy-intensive activities.
* **Consumption forecast chart** — showing expected household consumption, useful for alerting users to unusually high predicted consumption.
* **Combined outlook** — overlaying generation and consumption forecasts to visualise upcoming self-sufficiency or grid dependency periods.


# Households Dashboard

The energy-statistics and forecast endpoints are designed to power a **Households Dashboard** in your end-user application. The MOOST admin frontend provides a reference implementation of such a dashboard (visible in the Household Detail → Dashboard tab), which you can use as inspiration for your own.

<figure><img src="/files/GPyx6Ehpke9lOoCrvmQH" alt=""><figcaption></figcaption></figure>

## Dashboard Components

### Data Point Summary Cards

The top row of cards shows the total number of data points available for the selected date range. These counters give a quick indication of data completeness and coverage.

| Card                        | Description                                                                                                             |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| **Grid energy consumption** | Number of data points for energy drawn from the grid.                                                                   |
| **Energy consumption**      | Number of data points for total energy consumed by the household.                                                       |
| **Energy generation**       | Number of data points for energy generated (e.g. solar). Displayed in green to distinguish generation from consumption. |

### Yearly Overview Cards

The second row provides a 12-month rolling view of the household's energy profile. Each card includes a headline value and a trend chart.

| Card                                    | Description                                                                                               |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| **Grid energy consumption over a year** | Total grid energy consumed within the last 12 months (in kWh), with a monthly trend chart.                |
| **Energy consumption over a year**      | Total energy consumed within the last 12 months (in kWh), with a monthly trend chart.                     |
| **Energy generation over a year**       | Total energy generated within the last 12 months (in kWh), with a monthly trend chart displayed in green. |

### Seasonal and Peak Cards

The remaining rows surface patterns and extremes that can drive personalised energy recommendations.

#### **Seasonal Patterns**

| Card                             | Description                                                                                                                                                            |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Energy consumption in winter** | Percentage of the household's energy consumption that occurs during winter months, with a monthly bar chart showing the seasonal distribution.                         |
| **Energy consumption at night**  | Proportion of total energy consumption accounted for by nighttime hours, with a daily bar chart. A value of 100% indicates all recorded consumption occurred at night. |

#### **Daily Peaks**

| Card                               | Description                                                                                                         |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| **Highest daily grid consumption** | The peak single-day grid energy consumption in the last month (in kWh), with a daily bar chart for the full month.  |
| **Highest daily consumption**      | The peak single-day total energy consumption in the last month (in kWh), with a daily bar chart.                    |
| **Highest daily generation**       | The peak single-day energy generation in the last month (in kWh), with a daily bar chart displayed in yellow/green. |

#### **Hourly Peaks**

| Card                                | Description                                                                                                                                                       |
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Highest hourly grid consumption** | The hour of day with the highest grid energy consumption in the last month (e.g. "23 to 24 o'clock"), with a histogram showing energy consumption by hour of day. |
| **Highest hourly consumption**      | The hour of day with the highest total energy consumption in the last month, with a histogram by hour of day.                                                     |
| **Highest hourly generation**       | The hour of day with the highest energy generation in the last month (e.g. "0 to 1 o'clock"), with a histogram by hour of day.                                    |

### Peer Comparison

| Card                              | Description                                                                                                                                                                                                           |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Energy consumption comparison** | Ranks the household's energy consumption against other households in the same consumer category. Displays a rank (e.g. "Rank 2 of 2") and the category label (e.g. `VERY_HIGH`), along with a comparison trend chart. |

## Mapping Dashboard Widgets to API Endpoints

| Dashboard Widget         | API Endpoint                                                   | Event Types                                                          | Resolution     |
| ------------------------ | -------------------------------------------------------------- | -------------------------------------------------------------------- | -------------- |
| Today's energy chart     | `/energy-statistics/hourly/v1`                                 | `ENERGY_CONSUMPTION`, `ENERGY_GENERATION`, `GRID_ENERGY_CONSUMPTION` | Hourly         |
| This month's daily chart | `/energy-statistics/daily/v1`                                  | `*_YESTERDAY` variants                                               | Daily          |
| Yearly overview cards    | `/energy-statistics/monthly/v1`                                | `*_YESTERDAY` variants                                               | Monthly        |
| Seasonal / peak cards    | `/energy-statistics/daily/v1` + `/energy-statistics/hourly/v1` | Various                                                              | Daily + Hourly |
| Peer comparison          | `/energy-statistics/comparison/daily/v1`                       | `*_YESTERDAY` variants + `category`                                  | Daily          |
| Generation forecast      | `/energy-forecast/v1`                                          | `ENERGY_GENERATION_FORECAST_*`                                       | Per forecast   |
| Consumption forecast     | `/energy-forecast/v1`                                          | `ENERGY_CONSUMPTION_FORECAST_*`                                      | Per forecast   |
| Base load monitoring     | `/energy-statistics/daily/v1`                                  | `ENERGY_BASE_CONSUMPTION`, `GRID_ENERGY_BASE_CONSUMPTION`            | Daily          |

## Chart Guidelines

Values on the y-axis are in kWh and on the x-axis in the relevant time unit (month, day, or hour):

* **Line charts** are used for yearly trend cards, showing monthly aggregated values.
* **Bar charts** are used for daily and seasonal cards, showing per-day values.
* **Histograms** are used for hourly peak cards, showing energy distribution across hours of the day.

Hover over data points in the platform to see exact values.

## Building Your Own Dashboard

When implementing your own Households Dashboard, we recommend starting with the following minimum set of widgets:

1. **Yearly overview** (monthly endpoint) — gives the user a high-level sense of their energy profile.
2. **Daily detail** (daily endpoint) — lets the user drill into a specific month.
3. **Intraday chart** (hourly endpoint) — shows today's energy flow in real time.
4. **Peer comparison** (comparison endpoint) — provides social context and motivation.
5. **Forecast** (forecast endpoint) — enables forward-looking planning.

Use the MOOST admin frontend (Household Detail → Dashboard tab) as a reference for layout, colour coding (green for generation, default for consumption), and card design.


# User Acceptance Testing

A User Acceptance Test (UAT) is an instrument in Software Development which helps to ensure that a system or application meets the needs and expectations of its intended users.

In this section you can find a blueprint on how you can run a UAT with the MOOST Recommender Platform to test if the engagement of your users can be increased (and the churn rate reduced).

## Timeline

To observe an impact of a new system on your users behavior it will take some time. MOOST proposes to plan for a 8 week long UAT with 4 checkpoints every two weeks to check on how your KPI's have evolved.

## Testing Strategy

### Quantitative

By comparing the engagement and churn rate of the both groups an impact can be observed while seasonalities can be subtracted from the influence onto the users.

To determinate if the MOOST Recommender Platform has an impact on the behavior of your end users the best way is to compare a test user group to a control user group.&#x20;

The test user group will receive recommendations from the MOOST Recommender Platform while the Control user group won't.

To be of statistical significance MOOST recommends to have a test and a control user group of at least each 600 users.

### Qualitative

In contrast to the quantative approach also a qualitative approach can be selected. In this approach a test user group would be selected which receives the notifications from the MOOST Recommender Platform.

After each partial time frame a survey can be send to the test users to gather feedback of the impact and acceptance of the notifications sent by the MOOST Recommender Platform.

#### Survey Template

A survey for a qualitative test could have the following content.

## Communication

If you want to test the notifications from the MOOST Recommender Platform with your users MOOST recommends that you send upfront a message e.g. via email that informs the users about the test and gives them the option to opt-in to the test user group.

## Preparation Checklist <a href="#useracceptancetesting-preparationchecklist" id="useracceptancetesting-preparationchecklist"></a>

* [ ] Integration Testing successful
* [ ] Decide Testing Strategy
* [ ] Test user group defined
* [ ] *Control user group defined (Optional)*
* [ ] Setup communication for users in UAT group


# User Experience

Guidance on delivering recommendations clearly and effectively for maximum user engagement.

<figure><img src="/files/GLcMH91KJeFqXwJgYSky" alt=""><figcaption></figcaption></figure>

A powerful recommendation engine is most effective when its output is presented to the user in a clear, timely, and intuitive manner. The User Experience (UX) surrounding your notifications and recommendations is critical for user adoption, engagement, and satisfaction.

This section provides best practices for designing and implementing various user-facing components that deliver recommendations from the MOOST platform. A well-thought-out UX strategy ensures that users see value in the recommendations and can act on them with minimal friction.

This guide is divided into the following sub-sections:

[Onboarding](/best-practices/user-experience/onboarding)

How to build user trust from the start and maximize your notification opt-in rates.

[Designing Rules](/best-practices/user-experience/designing-rules)

Best practices for defining effective rule logic in the Rule Configurator.

[Writing Notifications](/best-practices/user-experience/writing-notifications)

How to write clear, actionable content for your recommendations.

[Notification Center](/best-practices/user-experience/notification-center)

Best practices for creating a persistent, in-app space for users to review all past recommendations.

[In-App Notifications](/best-practices/user-experience/in-app-notifications)

Guidance on using in-app modals, banners, or pop-ups for less urgent recommendations.

[Push Notifications](/best-practices/user-experience/push-notifications)

How to leverage immediate, actionable push notifications for time-sensitive advice.


# Onboarding

How to build user trust from the start and maximize your notification opt-in rates.

A successful recommendation strategy begins the moment a user first opens your app. Your best rules and clearest notifications are all useless if the user denies your app permission to send them.

The onboarding and notification permission flow is not just a technical step; it is your first and most critical opportunity to build trust. A poor strategy can lead to users permanently disabling notifications before they ever receive a single valuable recommendation.

This guide provides best practices for explaining the value of your recommendations, respecting user privacy, and asking for permission in a way that maximizes opt-in rates.

***

## **The Foundation: Privacy, Security, and Opt-In**

Before you ask for any permissions, you must be transparent about data. Your users (households) are trusting you with their personal data (e.g., energy consumption, device status).

**Data Privacy and Transparency**

Your app's Privacy Policy must clearly state *what* data is being used to generate recommendations (e.g., "energy consumption data," "profile settings"). Users must be informed that their data is being processed to provide them with proactive tips.

**Data Security**

While the MOOST platform is built with robust security, you (as the app provider) are responsible for ensuring your app's integration and handling of user data are secure. This transparency is fundamental to building the trust required for a user to opt-in.

**Opt-In is Mandatory**

Sending push notifications is an "opt-in" service, not "opt-out." By law (like GDPR) and by platform rules (Apple and Android), you must receive a user's explicit consent *before* sending them any push notifications.

***

## The Four-Step Permission Strategy

Never ask for push notification permission on the very first app launch. The user has no context, no trust, and no understanding of the value you offer. They will likely deny the request out of reflex.

Instead, follow this proven four-step strategy:

### **Step 1: Don't Ask Immediately**

Let the user explore the app first. Allow them to see their energy dashboard, set up their profile, or add a device. A user who has already seen some value in your app is far more likely to trust you with notifications.

### **Step 2: Find a Contextual Moment**

Ask for permission at a time when the user is most likely to understand the benefit.

{% hint style="success" %}
Good Contextual Moment

After the user completes their profile setup, you can ask if they want alerts tailored to their new settings.
{% endhint %}

{% hint style="success" %}
Good Contextual Moment

When they first look at their solar production dashboard, you can ask if they'd like to be notified when they have surplus power.
{% endhint %}

### **Step 3: Use a "Pre-Permission" Prompt**

This is the most critical step. Before triggering the official, system-level (iOS/Android) permission dialog, first show an in-app screen that *you* design. This "priming" screen should:

1. Have a clear headline: "Get Smarter Energy Alerts?"
2. Explain the value: "We'll send you timely recommendations to help you save money, use your own solar power, and avoid high-cost energy peaks."
3. Provide clear choices:
   * Primary Action: "Yes, Notify Me"
   * Secondary Action: "Maybe Later"

### **Step 4: Trigger the System Dialog (Only on "Yes")**

* If the user taps "Yes, Notify Me": Immediately trigger the official OS permission dialog. Because they have just agreed, they are primed to tap "Allow."
* If the user taps "Maybe Later": Do not show the system dialog. Simply dismiss your prompt. You can gently ask again in the future (e.g., in a week, or from the settings screen).

***

## **Long-Term Management and Recovery**

### **Provide an In-App Settings Screen**

If a user taps "Don't Allow" on the system dialog, you *cannot* ask them again. Your only path to recovery is an in-app settings screen.

This screen should:

1. Clearly list the *types* of notifications you offer (e.g., "Cost-Saving Alerts," "Safety Warnings").
2. Show the current permission status (e.g., "Push Notifications: Disabled").
3. Provide a single button like "Manage Notification Settings" that deep-links the user *directly* to your app's notification page in their phone's OS Settings. This makes it easy for them to re-enable permissions.

### **Use Notification Channels (Android)**

For Android, we strongly recommend using the Notification Channels feature. This allows you to categorize your rules (e.g., using `Tags` in the Rule Configurator) and map them to different channels.

This gives the user granular control. Instead of turning *all* notifications off, they can simply disable "Efficiency Tips" while keeping "Critical Alerts" active, which is a much better outcome for both you and the user.


# Designing Rules

Best practices for defining effective and relevant rule logic in the Rule Configurator.

A well-designed rule is the foundation of a good user experience. The core of the MOOST platform is the rule engine, which allows you to define the precise conditions that trigger a recommendation. If this underlying logic is irrelevant, poorly timed, or triggers too often, the notification will be perceived as spam, no matter how well-designed the app is.

This guide covers best practices for designing the logical conditions for your rules in the Rule Configurator.

***

## Best Practices for Rule Design

### Be Proactive, Not Just Reactive

Your most valuable rules will anticipate user needs. Leverage the Context Services(like `$WeatherForecast` or `$SolarProductionForecast`) to send recommendations *before* an event happens.

{% hint style="danger" %}
Bad Rule - Only act on current status without considering future changes.

`IF $GridPowerConsumption > 5000 THEN send "High grid usage"`
{% endhint %}

{% hint style="success" %}
Good Rule - Taking forecast information into consideration before sending a recommendation.

`IF $SolarProductionForecast_Next3Hours < 500 AND $CarChargingMode = "ON" THEN send "Cloudy weather is expected. We recommend pausing car charging to save costs."`
{% endhint %}

### Prevent "Notification Flapping"&#x20;

Avoid rules that trigger on and off rapidly from fluctuating data. Use **aggregation functions** ([AVG](/platform-manual/rules/rule-language/functions/avg), [MIN](/platform-manual/rules/rule-language/functions/min), [MAX](/platform-manual/rules/rule-language/functions/max), [SUM](/platform-manual/rules/rule-language/functions/sum)) to check for conditions over a stable time window or use properties [Match Threshold](/platform-manual/rules/rule-configurator/settings#match-threshold) or [Time Between Notifications](/platform-manual/rules/rule-configurator/settings#time-between-notifications) to prevent over notification.

{% hint style="danger" %}
Bad Rule - Always act on a single data point that can change heavily

`IF $GridPowerConsumption > 1000 THEN send "High Usage"`
{% endhint %}

{% hint style="success" %}
Good Rule - Acting on an average of timeseries dataset over a period of time

`IF AVG($GridPowerConsumption) > 1000 THEN send "Your average usage has been high for the last 15 minutes."`
{% endhint %}

### Create Highly Specific, Contextual Triggers

The best rules combine multiple data points using `AND` / `OR` logic to find a truly specific, actionable moment. A simple threshold is mostly not enough to send hyper personalised recommendations.

{% hint style="danger" %}
Bad Rule - Reyling on a single dataset to find a specific, actionable moment.

`IF $BatteryLevel < 20 THEN send "Battery is low"`
{% endhint %}

{% hint style="success" %}
Good Rule - Comining multiple datasets to find a specific, actinable moment.

`IF $BatteryLevel < 20 AND $IsLowTariffHours = 1 AND $SolarProductionForecast_NextHour = 0 THEN send "Your battery is low. We recommend charging from the grid now while tariffs are cheap."`
{% endhint %}

### Personalize with Profile Data

Use information from the Household Profile to make rules relevant to the specific user. A rule for a "cost-saving-motivated" user should be different from one for an "eco-motivated" user.

### Always Use the Rule Simulator&#x20;

This is the most critical step. Before activating any rule, use the Rule Simulator to test it against historical data from several different buildings and time ranges. The simulator allows you to:

* Verify the rule triggers only when you expect it to.
* See the exact notification text with dynamic data filled in.
* Ensure the rule does *not* trigger when it shouldn't.


# Writing Notifications

How to write clear, actionable, and user-friendly content for your recommendations.

After designing your rule's logic, the final step is writing the notification content that the user will see. The text in your Notification Templates is what translates complex data into a simple, helpful, and actionable message.

Clear and empathetic writing is essential for building user trust and ensuring your recommendations are well-received, whether they are delivered via push notification or in-app.

***

## Best Practices

### Write a Clear, Actionable Title

The title is the first thing the user sees. It should summarize the situation and the recommended action.

{% hint style="danger" %}
Bad Title - Not specific to the content of the Recommendation

`New Alert` or `MOOST Notification`
{% endhint %}

{% hint style="success" %}
Good Title - Summarizing the situation and the recommended action.

`High Grid Usage: Pause Charging?` or `Solar Surplus: Start Appliances`
{% endhint %}

### Be Clear and Direct, Not Vague

The message body must be easy to understand in 3 seconds. Avoid technical jargon.

{% hint style="danger" %}
Bad Message - Explaining the situation instead of showing the impact of given situation.

`Your PV system is currently experiencing a high delta between production and consumption.`
{% endhint %}

{% hint style="success" %}
Good Message - Presenting the result of the triggering situation with an actionable recommendation

`You're producing 2.1 kW more solar power than you're using. This is a good time to run your dishwasher.`
{% endhint %}

### Front-Load Key Info and Respect Character Limits

Users scan notifications, so put the most critical information first. Aim for your core message to be visible without requiring the user to expand the notification.

**Safe Zone**

Character limits vary by OS (iOS, Android), device, and font size. As a general best practice, aim to keep your title under 40 characters and the most important part of your message body under 50 characters.

**Visibility**

* Android: Typically shows a title (approx. 30-65 chars) and a single line of body text (approx. 40-50 chars) when collapsed.
* iOS: Shows several lines of text (approx. 150-170 chars total) but can be truncated by banner previews.

**The Rule**

Always assume the user will only see the first few words of your title and the first line of your message. Ensure this preview is compelling enough to make them want to read more.

### Use the Notification Preview Feature

Text can look very different once it's inside a notification bubble. Always use the built-in preview feature within the Notification Templates editor. This will show you an image of how your title and message will appear on a standard Example device.

This step is essential to confirm that your message is clear and effective *before* you test it with the Rule Simulator.

### Use Dynamic Data (Variables) in the Message

Your rule logic uses specific data points; put them in the notification. This builds trust and provides concrete context. The Rule Language allows you to format data directly in the message.

{% hint style="danger" %}
Bad Message - Generic message without any personlized insights.

`You are currently drawing power from the grid.`
{% endhint %}

{% hint style="success" %}
Good Message - Using personlized data to show the exact impact for the user.

`"You are currently drawing " + ($GridPowerConsumption / 1000) + " kW from the grid.`
{% endhint %}

### Frame it Positively

Frame recommendations as helpful advice or opportunities, not as warnings or criticisms.

{% hint style="danger" %}
Bad Message - Negative critic against the users.

`You failed to use your solar power. You are wasting 1.5 kW.`
{% endhint %}

{% hint style="success" %}
Good Message - Positive, opportunity focused message

`You have 1.5 kW of extra solar power! We recommend turning on your water heater to use it.`
{% endhint %}

### Match the Message to the Delivery Method

**For Push Notifications**

Keep the message short and ensure the primary action is clear, as the user is likely on their lock screen.&#x20;

**For In-App Recommendations**

You can be slightly more detailed, as the user is already engaged with your application.


# Notification Center

Best practices for creating a persistent, in-app log of all user recommendations.

An in-app notification center provides a central, persistent location for users to review all recommendations sent by the MOOST platform. It serves as a complete historical log, ensuring that users can find and review notifications they may have missed or dismissed.

<figure><img src="/files/v3nsfI4JxfjFxzEs8s26" alt="Example of a notification center" width="188"><figcaption><p>Example notification center</p></figcaption></figure>

## **Core Functionality**

We recommend a clean, chronological-order feed for your notification center. For simplicity and ease of use, the center should focus on presenting information clearly rather than tracking interaction status.

Each entry in the notification center should display:

* **Title**: The title of the recommendation.
* **Text**: The full text of the recommendation.
* **Date and Time**: The timestamp of when the recommendation was sent, localized to the user's timezone.

***

## **Best Practices**

### Include All Notifications

The notification center should be the single source of truth for all recommendations, regardless of whether they were originally delivered as an in-app message or a push notification.

### Use Unread Indicators

Clearly differentiate new (unread) notifications from those the user has already seen. This is most commonly achieved by:

* Displaying a "bell" icon with a badge count of unread items.
* Placing a small dot next to unread entries in the list.
* Bolding the title or text of unread entries.

### Provide a "Mark All as Read" Action

To complement unread indicators, include a single button or link (e.g., "Mark all as read") that allows users to clear all new notification states at once. This prevents users from having to click every single entry.

### Provide a Path to Notification Preferences

Users must have a clear way to manage their notification preferences and re-enable messages they have previously disabled. We strongly recommend placing a "Settings" icon (e.g., a cogwheel) on your Notification Center screen. This icon should link to a dedicated "Notification Preferences" page within your app.

For more information see [Notification Preferences](/best-practices/user-experience/notification-preferences)

### Allow Dismissal

Users should be able to clear individual notifications from the feed. This is typically handled via a "swipe to dismiss" gesture or a small 'X' icon on the notification entry.

### Design a Clear Empty State

When a user opens the notification center for the first time or after clearing all messages, do not show a blank screen. A well-designed "empty state" improves UX by:

* Confirming that the feature is working.
* Reassuring the user (e.g., "You're all caught up!").
* Briefly explaining what kind of information will appear there (e.g., "Your recommendations will appear here.").

### Maintain Simplicity

To reduce implementation complexity, we advise against showing which action (if any) was taken on a notification. The center's primary purpose is to serve as a simple, easy-to-read log.

### Advanced Grouping

For more advanced implementations, consider grouping notifications by date (e.g., "Today," "Yesterday," "Last 7 Days") to make the chronological feed easier to scan.


# Notification Preferences

Best practices for creating a clear and user-friendly screen to manage notification settings.

A dedicated "Notification Preferences" screen is a critical component of a trustworthy application. It provides users with a central place to control how and when they receive communications from you.

Giving users granular control is the best way to prevent them from disabling *all* notifications at the OS level—an action that is often difficult to reverse. This screen is where you empower your users, respect their choices, and give them a clear path to re-engage with recommendations they may have previously disabled.

***

## **Key Components of a Notification Preferences Screen**

Your preferences screen should be simple, clear, and organized into logical sections. We recommend including the following components.

### **Master Push Notification Control**

This section should transparently reflect the app's overall permission status at the operating system level.

**If Permissions are Enabled**

* Display a clear status indicator: Push Notifications: Enabled
* Provide a button or link that deep-links the user directly to your app's notification settings within their phone's OS. This gives them a quick path to the system-level controls if they need them.

**If Permissions are Disabled**:

* Display a clear status indicator: Push Notifications: Disabled
* Provide a brief, helpful message explaining the value of notifications (e.g., "You are missing out on important alerts about your energy usage and potential savings.").
* Provide a single, prominent button like "Enable in Settings" that deep-links the user directly to your app's notification settings in the phone's OS. This is the only way to allow a user to re-opt-in after denying system-level permission.

### Category-Level Toggles

This is the most important part of the screen for long-term engagement. It allows users to opt-out of *some* recommendations instead of *all* of them. These categories should directly correspond to the types of rules you have configured (e.g., using [Tags](/platform-manual/rules/rule-configurator/tags)).

* Functionality: Each category should have a simple on/off toggle.

***

## **Best Practices for Design and UX**

**Use Plain Language**

Avoid technical jargon. "Cost-Saving Tips" is much clearer to a user than "Rule Category 7B."

**Make it Easy to Find**

This screen should be accessible from your app's main "Settings" or "Profile" menu. As recommended, you should also place a direct link (e.g., a settings icon) to this page from the Notification Center.

**Provide Immediate Feedback**

When a user flips a toggle, the setting should be saved immediately. There is no need for a "Save" button on this type of screen.

**Respect User Choices**

This is paramount. If a user turns a category off, your system must honor that choice without exception.


# In-App Notifications

Guidance on using in-app modals and banners for non-time-sensitive recommendations.

In-app recommendations (such as modals, banners, or pop-ups) are ideal for recommendations that add value to the user's current session but do not require their immediate, time-sensitive action. They appear within the app's interface, allowing for richer content and more complex interactions than push notifications.

<figure><img src="/files/ox6t09TP7hY4Kbsl6mEu" alt="Showing a smart phone that has an app opened in which an  in-app notification is visible." width="188"><figcaption><p>In-app notification within  example app</p></figcaption></figure>

## Design and Layout

### Display Full Text

Unlike push notifications, in-app messages have more screen real estate. The recommendation text should always be presented in full without requiring the user to expand it.

### Clear Call-to-Action (CTA)

Action buttons for the recommendation should be clearly displayed and visually distinct. We suggest a consistent layout:

* Primary Action: Positioned prominently (e.g., on the left or as a primary button). This action should be the most desired outcome for the recommendation.
* Secondary Action(s): Positioned less prominently (e.g., on the right, as a text link, or an outline button). These offer alternatives, such as "Later" or "Learn More."

### Contextual Relevance

Ensure the in-app recommendation appears at a logical moment in the user's journey, relevant to their current activity or recent behavior. Avoid interrupting critical tasks.

### Visual Consistency

Maintain a consistent visual style that aligns with your app's overall design language. This helps the recommendation feel like an integrated part of the experience, not an intrusive ad.

### Concise Messaging

While you have more space than a push notification, keep the message as brief and to-the-point as possible. Users scan, they don't read every word.

### Accessibility

Design with accessibility in mind:

* Ensure sufficient contrast between text and background.
* Use appropriately sized fonts for readability.
* Support screen readers by providing proper labels for interactive elements.

***

## Interaction and Dismissal

### Dismissal Mechanism

Users must have a clear and easy way to dismiss the in-app notification. This prevents frustration and allows users to return to their task. Common implementations include:

* Close Button: A standard 'X' icon, typically in the top-right corner.
* Swipe to Dismiss: Mirroring the behavior of an OS-level push notification by allowing the user to swipe it away (if applicable to the UI element type, e.g., a banner).
* Tap Outside (for Modals): For modal pop-ups, consider allowing a tap outside the notification area to dismiss it, provided it's not a critical, blocking message.

### Non-Interruptive Placement

For less critical recommendations, consider using less intrusive placements like banners at the top/bottom of the screen or subtle in-feed cards rather than full-screen modals.

### Testing and Iteration

Continuously test different placements, timings, and messaging for your in-app recommendations to understand what resonates best with your users and drives the desired actions. A/B testing can be invaluable here.


# Push Notifications

Using immediate, actionable push notifications for time-sensitive recommendations.

We strongly recommend using push notifications for recommendations that advise an "immediate" or time-sensitive action. This delivery method allows users to interact with the advice directly from their device's lock screen or notification shade, making them highly effective for driving timely engagement.

<figure><img src="/files/EuvPTQMm35oPOJwEYLl3" alt="" width="188"><figcaption><p>Example Push Notificaiton</p></figcaption></figure>

## **Style and Implementation**

Your push notifications' overall design should adhere to the style guides of the respective operating systems:

* **Android**: [Notifications developer guide](https://developer.android.com/develop/ui/views/notifications)
* **iOS**: [Human Interface Guidelines for Notifications](https://developer.apple.com/design/human-interface-guidelines/notifications)

***

## **Best Practices**

### **Use Expandable Options**

Always utilize the OS's built-in expandable notification feature. This ensures that longer recommendation messages can be viewed in their entirety directly from the OS notification center.

### **Provide Actionable Buttons**

Define notification actions that allow the user to interact immediately.

### **Include a "Stop Notification" Action**

We strongly advise that at least one defined action be of the "STOP NOTIFICATION" type. This empowers the user to immediately opt out of receiving similar recommendations in the future, which is crucial for a positive user experience.

### **Define the Primary Action (Tap)**

The primary tap action (tapping the notification body itself) should be carefully considered. You have two main implementation approaches:

1. Simple Implementation: The tap action guides the user to your app's main notification center or the main overview view.
2. Advanced (Recommended) Implementation: The tap action deep-links the user directly to the relevant screen configured for that specific recommendation. While this requires more implementation effort, it provides a superior and more seamless user experience.

### Deliver Timely and Contextual Notifications

Send notifications when they are most relevant to the user's current situation or immediate needs. Sending a recommendation about a local store closing *after* it's already closed will lead to frustration.


# Measuring Impact

How to track user interactions and use data to prove ROI and optimize your rules.

Sending a recommendation is only the first step. To build a successful, long-term strategy, you must measure how your users respond to it. Measuring effectiveness is the only way to know if your rules are providing real value or just creating noise.

A rule that is ignored by 99% of users should be improved or retired. A rule that drives a positive behavior change should be expanded. This guide provides best practices for "closing the loop"—collecting user feedback to optimize your rules and demonstrate the ROI of your recommendations.

***

## **Define "Success" for Each Rule**

Before you can measure, you must define what "success" looks like. Not all notifications have the same goal. Your rules should be tagged (e.g., using the `Tags` feature) by their primary goal:

**Goal 1 - Direct Action (e.g., "Pause Charging")**

* Success is: The user taps the primary action button on the notification.
* How to measure: Tracking which action button was pressed.

**Goal 2: Behavior Change (e..g., "You have surplus solar power")**

* Success is: The user *performs the recommended action* inside the app, even if they don't tap the notification (e.g., they see the notification, dismiss it, open the app, and *then* turn on their water heater).
* How to measure: This is more advanced. You must correlate the notification send time with a subsequent change in device state (e.g., `Device Status` changed to `ON` within 10 minutes of the notification).

**Goal 3: Awareness (e.g., "Your weekly summary is ready")**

* Success is: The user taps the notification to view the content. A dismissal is not a failure; the user may have just read the text and felt informed.
* How to measure: Tracking the `OPENED` (tap) rate.

***

## **Implement the Feedback Loop**

The most important tool for measuring effectiveness is the Notification Interaction API. Your application *must* report back to the MOOST platform when a user interacts with a notification.

This data is critical for building reports and for future platform features like AI-powered optimization. We recommend reporting all of the following interaction types:

* `OPENED`: The user tapped on the main body of the notification. This is your primary measure of *engagement*.
* `ACTION_TAKEN`: The user tapped on a specific action button (e.g., "Pause Charging" or "Remind Me Later"). You should include which specific action was taken.
* `DISMISSED`: The user swiped the notification away. This is a crucial metric. A rule with a very high dismissal rate is a signal of a poorly-tuned or low-value recommendation.
* `STOP_NOTIFICATION`: The user tapped the "Stop Notification" action you provided (as recommended in our push notification best practices). This is your strongest negative signal. It indicates a user was frustrated enough to opt-out of that rule *specifically*.

***

## Use Data to Optimize: A/B Testing

You should never "set and forget" a rule. Use the data you've collected to continuously improve. The best method for this is A/B testing.

### **Copy Testing (Testing your Writing):**

1. Create two separate Notification Templates for the *same rule* (e.g., one focused on cost, one on eco-friendliness).
2. Use `Properties` to assign one template to 50% of your users and the second template to the other 50%.
3. After one week, compare the `OPENED` and `ACTION_TAKEN` rates. Keep the winning message.

* Example A: "High grid prices! We recommend pausing your car charger."
* Example B: "Help the grid! We recommend pausing your car charger during this peak time."

### **Rule Testing (Testing your Logic):**

1. Duplicate a rule and slightly change the logic.
2. Assign the "Control" rule (e.g., triggers at `AVG($GridPower, 15min) > 3000`) to one group of users.
3. Assign the "Challenger" rule (e.g., triggers at `AVG($GridPower, 10min) > 3500`) to another group.
4. Compare the results. Does the Challenger rule lead to fewer `DISMISSED` interactions? Does it lead to more `ACTION_TAKEN` events?

***

## **Prove Your ROI: The Control Group Method (Advanced)**

This is the gold standard for proving the business value of your recommendations.

1. Identify a Key Goal: e.g., "Reduce average household energy costs."
2. Create a Control Group: When you activate a set of cost-saving rules, apply them to 90% of your user base (the "Test Group").
3. Isolate the Control: Do not send these rules to the remaining 10% (the "Control Group").
4. Measure and Compare: After 30 or 60 days, compare the average energy cost between the two groups.

The difference in cost (e.g., "The Test Group saved an average of $5.30 more than the Control Group") is the provable, monetary ROI of your recommendation strategy. This data is invaluable for demonstrating the power of your app and the MOOST platform to your stakeholders.


# FAQ

**How often should I send events to the platform server?**\
Typically, you should send events whenever a significant state change occurs in the sensors. Avoid excessive event generation to prevent unnecessary processing and network overhead.

MOOST recommends an update for each sensor every 30 minutes.

**Can I customize the recommendation algorithms used by the platform?**\
Currently, the MOOST Recommender Platform provides a set of predefined algorithms. However, we are working on introducing customization options in future updates to allow Platform users to tailor the recommendation algorithms based on their specific needs.

**How can I integrate the platform's recommendations into my application?**\
The platform provides configuration options to receive recommendations via API or SDK.


