A straightforward model that formats raw sensor data with static metadata.This model adds context (sensor ID, unit, status) to a raw temperature value:
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" ADD STRING "status" WITH "ACTIVE"
When a value arrives at sensors/raw/temperature, the model publishes:
Combine data from multiple topics into a unified equipment status report.Each field pulls from a different source topic. The status topic acts as the trigger:
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" ADD STRING "location" WITH "Factory Floor A"
When the status topic updates, the model publishes a combined report to equipment/status/formatted:
Automatically calculate derived values like efficiency percentages.The efficiency_percent field is calculated inline from other topic values:
DEFINE MODEL ProductionRecord WITH TOPIC "production/records/completed" ADD STRING "batch_id" WITH TOPIC "production/current/batch_id" ADD STRING "product_code" WITH TOPIC "production/current/product_code" ADD INT "quantity_produced" WITH TOPIC "production/current/quantity" AS TRIGGER ADD INT "quantity_target" WITH TOPIC "production/current/target" ADD DOUBLE "efficiency_percent" WITH (GET TOPIC "production/current/quantity" AS DOUBLE / GET TOPIC "production/current/target" AS DOUBLE * 100) ADD STRING "operator" WITH TOPIC "production/current/operator" ADD STRING "completion_time" WITH TIMESTAMP "UTC"
Handle multiple sensors with a single model definition using the + wildcard.The + wildcard matches any single level in the topic hierarchy:
DEFINE MODEL GenericSensorData 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 TOPIC "sensors/+/unit" ADD STRING "location" WITH TOPIC "sensors/+/location" ADD STRING "timestamp" WITH TIMESTAMP "UTC"
When sensors/temp001/value receives data, the model publishes to sensors/temp001/formatted:
Handle multi-level topic hierarchies with multiple wildcards.Multiple + wildcards capture different levels of the hierarchy:
DEFINE MODEL MachineData WITH TOPIC "factory/+/+/data/formatted" ADD STRING "line_id" WITH TOPIC "factory/+/id" ADD STRING "machine_id" WITH TOPIC "factory/+/+/id" ADD STRING "status" WITH TOPIC "factory/+/+/status" AS TRIGGER ADD DOUBLE "output_rate" WITH TOPIC "factory/+/+/output_rate" ADD STRING "timestamp" WITH TIMESTAMP "UTC"
When factory/line1/machine1/status updates, the model publishes to factory/line1/machine1/data/formatted:
Use this pattern when you need full control over publishing logic—conditional decisions, complex calculations, JSON extraction, or custom routing. The action determines when and where to publish.
Then create an action that extracts JSON fields and publishes the model:
DEFINE ACTION ProcessAlarmJSONON TOPIC "alarms/+/json_input" DO SET "equipment_id" WITH TOPIC POSITION 2 PUBLISH MODEL AlarmRecord TO "alarms/structured/" + {equipment_id} WITH alarm_id = (RANDOM UUID) equipment_id = {equipment_id} alarm_type = (GET JSON "type" IN PAYLOAD AS STRING) trigger_value = (GET JSON "value" IN PAYLOAD AS DOUBLE) threshold = (GET JSON "threshold" IN PAYLOAD AS DOUBLE) severity = (GET JSON "severity" IN PAYLOAD AS STRING) message = (GET JSON "message" IN PAYLOAD AS STRING) timestamp = TIMESTAMP "UTC" auto_generated = TRUE
When a JSON payload arrives at alarms/pump003/json_input, the action publishes to alarms/structured/pump003:
The action performs calculations before publishing:
DEFINE ACTION ProcessProductionEventON TOPIC "production/+/events" DO SET "line_id" WITH TOPIC POSITION 2 SET "parts" WITH (GET JSON "parts_produced" IN PAYLOAD AS INT) SET "cycle" WITH (GET JSON "cycle_time" IN PAYLOAD AS DOUBLE) SET "target" WITH (GET JSON "target_cycle_time" IN PAYLOAD AS DOUBLE) SET "eff" WITH ({target} / {cycle} * 100) PUBLISH MODEL ProductionEvent TO "production/events/structured/" + {line_id} WITH event_id = (RANDOM UUID) line_id = {line_id} event_type = (GET JSON "event_type" IN PAYLOAD AS STRING) parts_count = {parts} cycle_time = {cycle} efficiency_percent = {eff} operator = (GET JSON "operator" IN PAYLOAD AS STRING) timestamp = TIMESTAMP "UTC"
When a production event arrives at production/line2/events with a target cycle time of 10s and an actual cycle time of 8.5s, the action publishes to production/events/structured/line2:
Determine pass/fail status based on measurement tolerances.
Complex conditional logic (like tolerance checking) is cleaner in an action than inline in a model. The action can also route to different topics based on the result.
The action checks if the measurement is within tolerance and routes accordingly:
DEFINE ACTION ValidateQualityON TOPIC "quality/+/measurement" DO SET "part_id" WITH TOPIC POSITION 2 SET "value" WITH (GET JSON "value" IN PAYLOAD AS DOUBLE) SET "target" WITH (GET JSON "target" IN PAYLOAD AS DOUBLE) SET "tolerance" WITH (GET JSON "tolerance" IN PAYLOAD AS DOUBLE) SET "min_allowed" WITH ({target} - {tolerance}) SET "max_allowed" WITH ({target} + {tolerance}) IF {value} >= {min_allowed} AND {value} <= {max_allowed} THEN PUBLISH MODEL QualityReading TO "quality/passed/" + {part_id} WITH inspection_id = (RANDOM UUID) part_id = {part_id} measurement = {value} target = {target} tolerance = {tolerance} result = "PASS" timestamp = TIMESTAMP "UTC" ELSE PUBLISH MODEL QualityReading TO "quality/failed/" + {part_id} WITH inspection_id = (RANDOM UUID) part_id = {part_id} measurement = {value} target = {target} tolerance = {tolerance} result = "FAIL" timestamp = TIMESTAMP "UTC"
When a measurement of 10.02 mm arrives for a part with a target of 10.00 mm and tolerance of 0.05 mm, it passes and publishes to quality/passed/PART_A1:
Then create specialized models that inherit and extend it:
DEFINE MODEL TemperatureAlert FROM BaseAlert ADD DOUBLE "temperature_value" ADD DOUBLE "threshold_exceeded" ADD STRING "sensor_location" ADD STRING "unit"
DEFINE MODEL PressureAlert FROM BaseAlert ADD DOUBLE "pressure_value" ADD DOUBLE "max_safe_pressure" ADD STRING "pressure_unit" ADD STRING "system_affected"
DEFINE MODEL MaintenanceAlert FROM BaseAlert ADD STRING "equipment_id" ADD STRING "maintenance_type" ADD INT "hours_since_service" ADD INT "recommended_interval"
Use the specialized model in an action:
DEFINE ACTION GenerateTemperatureAlertON TOPIC "sensors/temperature/+/alert" DO SET "sensor_id" WITH TOPIC POSITION 3 PUBLISH MODEL TemperatureAlert TO "alerts/temperature/" + {sensor_id} WITH alert_id = (RANDOM UUID) source_system = "TemperatureMonitor" timestamp = TIMESTAMP "UTC" severity = "HIGH" message = "Temperature threshold exceeded" acknowledged = FALSE temperature_value = (GET JSON "value" IN PAYLOAD AS DOUBLE) threshold_exceeded = (GET JSON "threshold" IN PAYLOAD AS DOUBLE) sensor_location = (GET JSON "location" IN PAYLOAD AS STRING) unit = "celsius"
When an alert triggers for sensor TH_042, the action publishes to alerts/temperature/TH_042 with both inherited and specialized fields:
Group related configuration and context fields into nested sub-objects using the OBJECT type and indentation.The metadata field uses OBJECT to group location details into a nested object. Fields indented under ADD OBJECT become its properties. When indentation returns to the parent level, fields belong to the top-level model again:
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 OBJECT "metadata" ADD STRING "unit" WITH TOPIC "sensors/+/unit" 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"
When sensors/temp001/value receives data, the model publishes to sensors/temp001/formatted:
Use when: Data flows continuously and formatting is straightforward.Best for: Sensor readings, status updates, simple aggregation.Trigger: Automatic when source topic updates.
Calculated Fields
Use when: You need derived metrics computed from source values.Best for: Percentages, totals, averages, unit conversions.Note: Keep calculations simple; use actions for complex logic.
Wildcard Models (+)
Use when: You have multiple instances of the same data structure.Best for: Fleets of sensors, multi-line factories, device groups.Benefit: One model definition handles all instances.
Action-Published Models (COLLAPSED)
Use when: You need control over timing, routing, or complex logic.Best for: JSON extraction, conditional publishing, pass/fail routing, complex calculations.Benefit: Full action power with structured output.
Inherited Models (FROM)
Use when: You have related data types sharing common fields.Best for: Alert families, equipment types, event categories.Benefit: DRY principle—define common fields once.
Nested Objects (OBJECT)
Use when: Your JSON output needs grouped or hierarchical data.Best for: Metadata grouping, configuration objects, multi-level status reports.Benefit: Clean nested JSON without external formatting. Indentation controls structure.