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.
Building a Library of Reusable Logic
Any Action without a trigger clause is a callable — a reusable function with typed inputs and outputs. Build a small library of utility callables for the calculations and transformations your project does repeatedly, then call them from your trigger-based Actions.When to Reach for This
The moment you write the same expression a second time, or any time a logic block deserves its own name to make the parent Action readable. Also useful when several Actions need to share a calculation that might evolve over time — fix it in the callable, and every caller is updated.The Pattern
A callable has three parts: declared inputs, the logic that runs, and declared outputs to return:INPUT types are STRING, INT, DOUBLE, BOOL, and JSON. A callable can return one or many OUTPUT values — the caller names the return variables in the order they’re declared.
Here’s a callable that returns three values from one calculation:
One Action, One Job
Each Action should do one thing well. Break complex logic into callable Actions withINPUT/OUTPUT:
- Good: Modular
- Bad: Monolithic
Separate reusable logic into callable Actions, then compose them in a main Action:
Internal State: KEEP vs PUBLISH
UseKEEP TOPIC for internal persistent state and PUBLISH TOPIC for external broadcast. Never use PUBLISH for values only your system reads:
| Operation | Use When |
|---|---|
PUBLISH TOPIC | Other systems or subscribers need to see the value |
KEEP TOPIC | Only your own Actions read the value (internal state, caches, counters) |
One Action for Many Devices
The moment you find yourself about to copy-paste an Action and change one device ID, stop. Use a wildcard — the MQTT character+ matches exactly one topic level, so sensors/+/power matches sensors/inv-01/power, sensors/inv-47/power, and any other topic with that shape. The broker fires your Action once per matching message, and TOPIC POSITION lets you read which device the message came from.
Wildcard guidelines
| Wildcard | Meaning | Where to Use |
|---|---|---|
+ | Matches exactly one level | Action triggers, Model topics, Route mappings |
# | Matches one or more levels | Route mappings, Rule topic patterns, broad subscriptions |
+ in Actions and Models for precise instance matching. Use # in Routes and Rules for broad coverage.
When to Reach for This
The moment you have two or more devices that should be processed the same way. Wildcards are also the foundation that makes Models that publish for every device work — and they let new devices join your system without any code changes.The Pattern
Imagine a solar park with a hundred inverters, each publishing power readings. One Action handles all of them:- The trigger
inverters/+/powermatchesinverters/inv-01/power,inverters/inv-47/power, and any other topic that fits. TOPIC POSITION 2reads whatever the+matched — so on one trigger it’sinv-01, on the next it’sinv-47. Position counting is 1-based and starts at the topic root.- The published outputs use that value to build per-inverter topics, so each inverter gets its own processed reading and last-seen cache.
+ value flows through every GET TOPIC and PUBLISH TOPIC inside the Action, so you don’t have to parse the topic string yourself.
sensors/temp001/temperature, the GET TOPIC reads from sensors/temp001/humidity — the wildcard resolves to the same instance.
A single Action like this can replace fifty hand-written copies — one for every device. When a new inverter comes online, you don’t deploy new code; the existing Action just starts handling it.
The same approach works equally well for hotel rooms (rooms/+/temperature), parking sensors (parking/+/occupied), or delivery vans (vans/+/fuel_level). Anywhere you have many devices doing the same thing, one wildcard Action covers them all.
Counting Things Over Time
Plenty of real-world questions are counting questions. How many cycles has this machine completed today? How many vehicles entered the garage this hour? How many error events has this device emitted this week? LoT has no built-in counter type, but a counter is just a number you keep on a topic and increment whenever something happens.When to Reach for This
Whenever you need a value that persists across messages — not just within a single Action run. Production cycles, error totals, visitor counts, button presses, alarms-this-shift, anything that should keep counting whether you reset the system or not.The Pattern
The trick is two-part: on the very first run there’s no value yet, so you initialize to zero. On every run after that, you read the current value, add one, and write it back.- Every time a
cycle_completemessage arrives, the Action runs. - The first time ever,
counters/cyclesdoesn’t exist yet, soGET TOPICreturnsEMPTY. The Action creates the counter at zero. - Every subsequent run reads the current value, adds one, and publishes the new total.
counters/cycles to see the live count.
Counters Per Device
Combine with a wildcard to keep one counter per device:counters/machine-01/cycles, counters/machine-02/cycles, and so on each track their own machine independently — and one Action handles all of them.
Resetting on a Schedule
For shift counters or daily totals, use a scheduled Action to reset:Running Python from a LoT Action
LoT is great for the operations that 90% of IoT logic actually needs — math, conditionals, JSON extraction, MQTT publishes. But sometimes you need real programming: regex parsing, statistical analysis, calling a Python library, complex validation rules. For those cases,CALL PYTHON lets a LoT Action invoke a Python function and use the result inline.
When to Stay in LoT vs. Go to Python
Stay in LoT for simple math, string concatenation, basic conditionals, MQTT operations, JSON field extraction, route triggering, scheduled or topic-triggered logic. Anything LoT does natively, do natively — Python adds overhead and complexity. Reach for Python when you need:- Regex matching or advanced string parsing
- Statistical or scientific calculations (averages over windows, anomaly detection, ML inference)
- Library functions:
numpy,pandas,scipy,scikit-learn, custom company libraries - Complex validation rules with many conditional branches
- JSON restructuring beyond simple field extraction
When to Reach for This
When your Action has hit the limits of LoT’s expressiveness — typically around regex, statistics, ML, or library calls — and the work isn’t worth wrapping in an external service.The Pattern
A Python script that validates a complex sensor payload using regex and a custom rule:- The Action receives a sensor registration with a serial number.
- It hands the serial to the Python function, which uses regex (something LoT can’t do natively) to validate the format.
- The function returns a dictionary, which LoT receives as a JSON object.
- The Action extracts the
validanderrorfields withGET JSONand routes the message accordingly.
Practical Tips
- Keep Python functions fast (under ~100ms). They run inline in your Action and block message processing while they execute. For heavy computation, consider an external service.
- Always handle errors. Wrap the Python in
try/exceptand return structured results —{"success": false, "error": "..."}— so the LoT side can branch on it cleanly. - Return JSON-friendly values. Dictionaries, lists, strings, numbers, booleans. Custom objects won’t serialize.
- Convert types explicitly. Python receives parameters as strings —
float(value),int(count)at the top of your function.
Next Steps
Callable Actions
Full reference for
INPUT, OUTPUT, and CALL ACTION.Python Integration
CALL PYTHON syntax, deployment, and best practices.
