> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreflux.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Models Overview

> Learn how LoT models transform, structure, and format MQTT data into consistent JSON schemas

## What are Models?

Models in LoT are like database schemas for MQTT data. They define the structure of your data, ensure consistency across your system, and provide automatic JSON formatting.

<Tip>
  Models transform raw MQTT messages into structured, typed data - eliminating the need for external data transformation layers.
</Tip>

## Model Capabilities

<AccordionGroup>
  <Accordion title="Data Structuring">
    Define fields with specific types (STRING, INT, DOUBLE, BOOL, ARRAY) to create consistent data schemas.
  </Accordion>

  <Accordion title="Auto JSON Handling">
    Automatically explode incoming JSON payloads into individual topics for easy field access.
  </Accordion>

  <Accordion title="Data Aggregation">
    Combine data from multiple MQTT topics into a single structured output.
  </Accordion>

  <Accordion title="Calculations">
    Perform inline calculations and conditional logic to derive new values.
  </Accordion>
</AccordionGroup>

***

## How Models Work

Models do more than format data. They aggregate values from multiple MQTT topics, scale across device fleets with wildcards, compute derived metrics inline, and apply conditional logic — all within the broker, with zero external code. Here's how the core flow works.

### Receiving JSON Payloads

When a device sends a full telemetry payload, you often need to access individual fields independently — for threshold alerts, dashboard widgets, or other models. All models automatically handle this by "exploding" incoming JSON payloads into individual topics:

```lot theme={null}
DEFINE MODEL SensorData WITH TOPIC "sensors/device1/data"
```

**When JSON arrives at** `sensors/device1/data`:

```json theme={null}
{
  "temperature": 23.5,
  "humidity": 65,
  "status": "active"
}
```

**LoT automatically creates:**

* `sensors/device1/data/temperature` → `23.5`
* `sensors/device1/data/humidity` → `65`
* `sensors/device1/data/status` → `"active"`

<Note>
  This "explosion" behavior allows you to subscribe to individual fields or use them in other models and actions.
</Note>

### Producing Structured Output

When downstream systems — databases, dashboards, REST APIs — expect a specific JSON schema, a model guarantees that format on every publish. When a model triggers (either automatically or via action), it publishes structured JSON combining all its fields:

```lot theme={null}
DEFINE MODEL EquipmentStatus WITH TOPIC "equipment/formatted"
    ADD STRING "equipment_id" WITH TOPIC "equipment/id"
    ADD STRING "status" WITH TOPIC "equipment/status" AS TRIGGER
    ADD INT "runtime_hours" WITH TOPIC "equipment/runtime"
    ADD STRING "last_update" WITH TIMESTAMP "UTC"
```

**Produces:**

```json theme={null}
{
  "equipment_id": "PUMP001",
  "status": "RUNNING",
  "runtime_hours": 1250,
  "last_update": "2025-10-25T14:30:15Z"
}
```

### Aggregating Multiple Sources

When you need a unified equipment dashboard from scattered PLC topics, or a combined production report from sensors across different lines, a single model pulls live values from many independent topics and merges them into one structured output — replacing external aggregation scripts or middleware entirely.

```mermaid actions={false} theme={null}
flowchart LR
    subgraph Inputs
        A[equipment/current/id]
        B[equipment/current/status]
        C[equipment/current/runtime]
        D[equipment/current/efficiency]
    end
    subgraph Model
        E[EquipmentStatus]
    end
    subgraph Output
        F[equipment/status/formatted]
    end
    A --> E
    B -->|triggers| E
    C --> E
    D --> E
    E -->|publishes| F
```

Each field draws from a different source topic, but the model waits for the trigger field (`status`) to update before publishing a combined snapshot:

```lot theme={null}
DEFINE MODEL EquipmentStatus WITH TOPIC "equipment/status/formatted"
    ADD STRING "equipment_id" WITH TOPIC "equipment/current/id"
    ADD STRING "status" WITH TOPIC "equipment/current/status" AS TRIGGER
    ADD INT "runtime_hours" WITH TOPIC "equipment/current/runtime"
    ADD DOUBLE "efficiency" WITH TOPIC "equipment/current/efficiency"
    ADD BOOL "maintenance_required" WITH TOPIC "equipment/current/maintenance_flag"
    ADD STRING "last_update" WITH TIMESTAMP "UTC"
```

### Building Nested Objects with Indentation

When your API or database expects grouped fields — such as a `metadata` object inside a sensor reading, or a `status` block with nested `maintenance` details — use the `OBJECT` type to produce nested JSON directly. Fields indented one level deeper than `ADD OBJECT` become properties of that sub-object — the same way `ADD` fields under `DEFINE MODEL` become top-level properties.

```lot theme={null}
DEFINE MODEL SensorWithMetadata WITH TOPIC "sensors/+/formatted"
    ADD STRING "sensor_id" WITH TOPIC "sensors/+/id"
    ADD DOUBLE "value" WITH TOPIC "sensors/+/value" AS TRIGGER
    ADD STRING "unit" WITH "celsius"
    ADD OBJECT "metadata"
        ADD STRING "location" WITH TOPIC "sensors/+/location"
        ADD INT "floor" WITH TOPIC "sensors/+/floor"
        ADD STRING "building" WITH TOPIC "sensors/+/building"
    ADD STRING "timestamp" WITH TIMESTAMP "UTC"
```

**Produces:**

```json theme={null}
{
  "sensor_id": "TEMP001",
  "value": 25.5,
  "unit": "celsius",
  "metadata": {
    "location": "Room 101",
    "floor": 3,
    "building": "Building A"
  },
  "timestamp": "2025-10-25T14:30:15Z"
}
```

The indentation level determines the nesting. Fields at the same depth as `ADD OBJECT` (like `"timestamp"` above) return to the parent object.

<Warning>
  Indentation must be consistent. Use either tabs or spaces throughout your model — mixing them will cause parsing errors. Each nesting level adds one indentation step.
</Warning>

### What Else Can Models Do?

The examples above cover the fundamentals, but models go much further:

<Note>
  * **Nested Objects** — Use the `OBJECT` type with indentation to group related fields into sub-objects, producing clean nested JSON hierarchies. [See examples →](./examples#sensor-with-metadata)
  * **Wildcard Models** — Define one model with `+` wildcards and it automatically handles every matching device. One definition, hundreds of instances, zero duplication. [See examples →](./examples#multi-instance-sensor-model)
  * **Inline Calculations** — Compute efficiency percentages, unit conversions, and running averages directly inside the model definition — no external processing needed. [See examples →](./examples#production-record-with-calculations)
  * **Conditional Logic** — Dynamically tag field values based on live data: label readings as `"HIGH"` or `"NORMAL"`, flag quality as `"PASS"` or `"FAIL"`, assign priority levels.
  * **Action-Published Templates** — Use `COLLAPSED` models as reusable schemas, then publish them from actions with full control over timing, routing, and conditional decisions. [Learn more →](./publish-model)
</Note>

For example, a single wildcard model automatically scales across every matching device — one definition handles your entire fleet:

```mermaid actions={false} theme={null}
flowchart LR
    subgraph singleDef [Single Definition]
        A["GenericSensor<br/>sensors/+/formatted"]
    end
    subgraph autoInst [Auto-Created Instances]
        B[sensors/temp001/formatted]
        C[sensors/pressure002/formatted]
        D[sensors/humidity003/formatted]
    end
    A --> B
    A --> C
    A --> D
```

[See the full wildcard example →](./examples#multi-instance-sensor-model)

***

## Model Types

LoT supports three model patterns, each suited for different scenarios:

| Pattern                        | Use When                                             |
| ------------------------------ | ---------------------------------------------------- |
| **Basic Model**                | You want automatic triggering on data changes        |
| **Template Model (COLLAPSED)** | You need control over when and where data is created |
| **Inheritance (FROM)**         | You have related data types sharing common fields    |

### Basic Models

Basic models automatically trigger and publish structured data when their trigger field updates:

```lot theme={null}
DEFINE MODEL SensorReading WITH TOPIC "sensors/formatted/temperature"
    ADD STRING "sensor_id" WITH "TEMP001"
    ADD DOUBLE "value" WITH TOPIC "sensors/raw/temperature" AS TRIGGER
    ADD STRING "unit" WITH "celsius"
    ADD STRING "timestamp" WITH TIMESTAMP "UTC"
```

**Key characteristics:**

* `WITH TOPIC` specifies where output is published
* `AS TRIGGER` defines which field change causes publication
* Runs automatically when trigger field updates

### Template Models (COLLAPSED)

Template models are reusable schemas for dynamic data creation from within actions. The `COLLAPSED` keyword indicates they have no automatic trigger:

```lot theme={null}
DEFINE MODEL AlarmRecord COLLAPSED
    ADD STRING "alarm_id"
    ADD STRING "equipment_id"
    ADD STRING "severity"
    ADD STRING "timestamp"

DEFINE ACTION ProcessAlarm
ON TOPIC "alarms/+/input" DO
    SET "equip_id" WITH TOPIC POSITION 2
    
    PUBLISH MODEL AlarmRecord TO "alarms/structured/" + {equip_id} WITH
        alarm_id = (RANDOM UUID)
        equipment_id = {equip_id}
        severity = (GET JSON "severity" IN PAYLOAD AS STRING)
        timestamp = TIMESTAMP "UTC"
```

**Key characteristics:**

* `COLLAPSED` means no automatic trigger
* Published explicitly with `PUBLISH MODEL`
* Full control over when and where data is created

<Note>
  Learn how to publish template models from actions in [Publishing Models](./publish-model).
</Note>

### Model Inheritance (FROM)

Create model families with shared fields using the `FROM` keyword:

```lot theme={null}
DEFINE MODEL BaseAlert COLLAPSED
    ADD STRING "alert_id"
    ADD STRING "timestamp"
    ADD STRING "severity"

DEFINE MODEL TemperatureAlert FROM BaseAlert
    ADD DOUBLE "temperature_value"
    ADD DOUBLE "threshold"
```

**Key characteristics:**

* Child models inherit all parent fields
* Add specialized fields as needed
* Promotes code reuse and consistency

<Note>
  Learn more about designing model hierarchies in [Model Inheritance](./model-inheritance).
</Note>

***

## Data Sources

Model fields can be populated from various sources:

<Tabs>
  <Tab title="Static Values">
    Use static values for constants like units, default thresholds, or fixed identifiers that never change:

    ```lot theme={null}
    DEFINE MODEL StaticExample WITH TOPIC "output/static"
        ADD STRING "unit" WITH "celsius"
        ADD INT "default_threshold" WITH 100
        ADD BOOL "enabled" WITH TRUE
    ```
  </Tab>

  <Tab title="Topic Data">
    Pull live values from MQTT topics. Use `AS TRIGGER` on the field that should cause the model to publish:

    ```lot theme={null}
    DEFINE MODEL TopicExample WITH TOPIC "output/dynamic"
        ADD DOUBLE "temperature" WITH TOPIC "sensors/raw/temp"
        ADD STRING "status" WITH TOPIC "equipment/status" AS TRIGGER
    ```
  </Tab>

  <Tab title="Timestamps">
    Add automatic timestamps in UTC format or as Unix epoch integers for time-series data:

    ```lot theme={null}
    DEFINE MODEL TimestampExample WITH TOPIC "output/timed"
        ADD STRING "created_at" WITH TIMESTAMP "UTC"
        ADD INT "unix_time" WITH TIMESTAMP "UNIX"
    ```
  </Tab>

  <Tab title="Calculations">
    Compute derived values inline using arithmetic operations on topic data:

    ```lot theme={null}
    DEFINE MODEL CalculatedExample WITH TOPIC "output/calculated"
        ADD DOUBLE "efficiency" WITH (GET TOPIC "produced" AS DOUBLE / GET TOPIC "target" AS DOUBLE * 100)
    ```
  </Tab>

  <Tab title="Conditionals">
    Apply conditional logic to set field values based on dynamic conditions:

    ```lot theme={null}
    DEFINE MODEL ConditionalExample WITH TOPIC "output/conditional"
        ADD STRING "status" WITH IF (GET TOPIC "value" > 80) THEN "HIGH" ELSE "NORMAL"
    ```
  </Tab>
</Tabs>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Model Syntax" icon="table-columns" href="./schema-definition">
    Learn the complete syntax for defining model fields and triggers.
  </Card>

  <Card title="Model Inheritance" icon="sitemap" href="./model-inheritance">
    Create model families with shared fields using FROM.
  </Card>
</CardGroup>
