uMINK System Agent Configuration (JSON)

This page documents the application configuration file used by uMINK. The config is a single JSON document controlling:

  • Plugin discovery and loading
  • The embedded Lua runtime and environments
  • MQTT broker connections (subscriptions, dispatch)
  • CoAP servers and clients (resources, dispatch)
  • System bus interface (dbus and OpenWRT ubus)

Tip: JSON does not support comments. If you need comments, keep them in a separate README or use a // schema helper during development.


Quick Start

Create a file (e.g., config.json) and pass its path to the application at startup (per your run script/tooling). A minimal, end‑to‑end example:

{
  "plugins_dir": "/tmp/plugins",
  "umlua": {
    "db": "test/test.db",
    "aggressive_gc": true,
    "conserve_memory": true,
    "envs": [
      {
        "name": "ENV_COAP_RX",
        "auto_start": false,
        "interval": 0,
        "path": "/tmp/test_event_02.lua",
        "events": ["COAP_RX"]
      },
      {
        "name": "ENV_MQTT_RX",
        "auto_start": false,
        "interval": 0,
        "path": "/tmp/test_event_03.lua",
        "events": ["MQTT_RX"]
      },
      {
        "name": "ENV_TEST_EVENT_01",
        "auto_start": false,
        "interval": 0,
        "path": "/tmp/test_event_01.lua",
        "events": ["TEST_EVENT_01", "TEST_EVENT_01_02"]
      }
    ]
  },
  "mqtt": {
    "connections": [
      {
        "name": "local",
        "address": "127.0.0.1",
        "client_id": "umink_mqtt_client",
        "username": "",
        "password": "",
        "rx_handler": "MQTT_RX",
        "subscriptions": ["umink/test"]
      }
    ]
  },
  "coap": {
    "servers": [
      {
        "name": "TEST_SERVER",
        "ipv4": "192.168.0.13",
        "port": 5683,
        "resources": [
          { "name": "test", "rx_handler": "COAP_RX" }
        ]
      }
    ],
    "clients": [
      { "name": "COAP_TEST", "uri": "coap://192.168.0.13/test" }
    ]
  }
}

How Lua Signal Handling Works

  1. Transports emit events:
    • MQTT: when a message is received on a subscribed topic, the connection invokes the signal handler named in its rx_handler.
    • CoAP: when a request is received for a server resource, the resource invokes the signal handler named in its rx_handler.
  2. Lua environments attach a signal handler to a list of event names in umlua.envs[*].events.
  3. Signal invocation: when an event occurs, a Lua signal handler is invoked. For automatic events, like MQTT/COAP packet reception, the rx_handler signal handler is invoked.
  4. Periodic execution: if an environment sets auto_start: true and a non‑zero interval, it can run periodically in addition to event‑driven execution.

The exact shape of the event payload available inside Lua is defined by the transport/adapter; use consistent event names to decouple transports from scripts.


Field Reference

All fields are case‑sensitive. Unless explicitly stated, fields are mandatory and have no implicit default—set values explicitly for predictable behavior.

Top‑Level

plugins_dir

  • Type: string (filesystem path)
  • Purpose: Directory where dynamically loadable plugins are discovered/loaded.
  • Example: /opt/micro-mink/plugins

umlua

  • Type: object
  • Purpose: Settings for the embedded Lua runtime and its environments.
  • Required: No
umlua.db
  • Type: string (path to file)
  • Purpose: Path to a SQLite database file used by Lua code (for data sharing between signal handlers).
umlua.aggressive_gc
  • Type: boolean
  • Purpose: Enable more frequent garbage collection in Lua to reduce memory spikes.
  • Trade‑off: May slightly reduce throughput in exchange for lower memory.
umlua.conserve_memory
  • Type: boolean
  • Purpose: Tunings for lower memory usage
umlua.envs
  • Type: array<object>
  • Purpose: Declares Lua execution environments and their signals.

Environment object fields:

  • namestring; unique identifier for the environment.
  • auto_startboolean; if true, the environment is started on boot.
  • intervalinteger; execution interval for periodic runs when auto_start is true. 0 disables periodic scheduling.
  • pathstring; absolute or relative path to the Lua script file.
  • eventsarray<string>; list of signal names handled by this environment.

MQTT Protocol Plugin

mqtt

  • Type: object
  • Purpose: Defines one or more MQTT broker sessions.
mqtt.connections
  • Type: array<object>
  • Purpose: Each object configures a separate MQTT connection.

Connection object fields:

  • namestring; connection label.
  • addressstring; broker address (IPv4/IPv6 or hostname). Include port if your runtime requires (e.g., broker.local:1883).
  • client_idstring; MQTT client identifier.
  • username / passwordstring; credentials if the broker requires authentication.
  • rx_handlerstring; signal handler to invoke when a subscribed message is received.
  • subscriptionsarray<string>; topics to subscribe to (wildcards follow broker/runtime support).

Example connection:

{
  "name": "local",
  "address": "127.0.0.1",
  "client_id": "umink_mqtt_client",
  "username": "",
  "password": "",
  "rx_handler": "MQTT_RX",
  "subscriptions": ["umink/test"]
}

CoAP Protocol Plugin

coap

  • Type: object
  • Purpose: Configures CoAP servers (resources) and clients (URIs).
coap.servers
  • Type: array<object>
  • Purpose: Each object declares a CoAP server instance.

Server object fields:

  • namestring; server label.
  • ipv4string; IP address to bind.
  • portinteger; UDP port. The CoAP default is 5683 (set explicitly for clarity).
  • resourcesarray<object>; resources exposed by this server.

Resource object fields:

  • namestring; resource identifier (e.g., test becomes /test).
  • rx_handlerstring; signal handler to invoke on inbound CoAP traffic for this resource.
coap.clients
  • Type: array<object>
  • Purpose: Declares remote CoAP endpoints for client operations.

Client object fields:

  • namestring; client label.
  • uristring; full CoAP URI (e.g., coap://127.0.0.1/test).

Design and Conventions

  • Signal handlers are free‑form strings; reuse the same name across transports to route to the same Lua environment(s).
  • One‑to‑many dispatch: a single event can trigger multiple environments; order is not guaranteed.
  • Isolation: use separate environments for unrelated concerns (e.g., ENV_MQTT_RX vs. ENV_COAP_RX).
  • Paths: prefer absolute paths for plugins and scripts in production.

Validation Schema (Suggested)

You can validate your configuration using the following JSON Schema (2020‑12). Treat this as a strict schema for CI checks; relax it as needed if the runtime accepts additional fields.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/micro-mink-config.schema.json",
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "plugins_dir": { "type": "string", "minLength": 1 },
    "umlua": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "db": { "type": "string", "minLength": 1 },
        "aggressive_gc": { "type": "boolean" },
        "conserve_memory": { "type": "boolean" },
        "envs": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "name": { "type": "string", "minLength": 1 },
              "auto_start": { "type": "boolean" },
              "interval": { "type": "integer", "minimum": 0 },
              "path": { "type": "string", "minLength": 1 },
              "events": {
                "type": "array",
                "minItems": 1,
                "items": { "type": "string", "minLength": 1 },
                "uniqueItems": true
              }
            },
            "required": ["name", "auto_start", "interval", "path", "events"]
          }
        }
      },
      "required": ["db", "aggressive_gc", "conserve_memory", "envs"]
    },
    "mqtt": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "connections": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "name": { "type": "string", "minLength": 1 },
              "address": { "type": "string", "minLength": 1 },
              "client_id": { "type": "string", "minLength": 1 },
              "username": { "type": "string" },
              "password": { "type": "string" },
              "rx_handler": { "type": "string", "minLength": 1 },
              "subscriptions": {
                "type": "array",
                "minItems": 1,
                "items": { "type": "string", "minLength": 1 }
              }
            },
            "required": ["name", "address", "client_id", "rx_handler", "subscriptions"]
          }
        }
      },
      "required": ["connections"]
    },
    "coap": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "servers": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "name": { "type": "string", "minLength": 1 },
              "ipv4": { "type": "string", "format": "ipv4" },
              "port": { "type": "integer", "minimum": 1, "maximum": 65535 },
              "resources": {
                "type": "array",
                "minItems": 1,
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "properties": {
                    "name": { "type": "string", "minLength": 1 },
                    "rx_handler": { "type": "string", "minLength": 1 }
                  },
                  "required": ["name", "rx_handler"]
                }
              }
            },
            "required": ["name", "ipv4", "port", "resources"]
          }
        },
        "clients": {
          "type": "array",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "name": { "type": "string", "minLength": 1 },
              "uri": { "type": "string", "minLength": 1 }
            },
            "required": ["name", "uri"]
          }
        }
      }
    }
  },
  "required": ["umlua"]
}

Examples

1) MQTT‑only runtime

{
  "umlua": {
    "db": "/var/lib/micro-mink/state.db",
    "aggressive_gc": true,
    "conserve_memory": false,
    "envs": [
      {
        "name": "ENV_MQTT_RX",
        "auto_start": true,
        "interval": 0,
        "path": "/opt/micro-mink/lua/on_mqtt.lua",
        "events": ["MQTT_RX"]
      }
    ]
  },
  "mqtt": {
    "connections": [
      {
        "name": "prod",
        "address": "broker.prod.local",
        "client_id": "mmink-node-01",
        "username": "sensor",
        "password": "${MQTT_PASSWORD}",
        "rx_handler": "MQTT_RX",
        "subscriptions": ["sensors/#"]
      }
    ]
  }
}

2) CoAP server with one resource

{
  "umlua": {
    "db": "./state.db",
    "aggressive_gc": true,
    "conserve_memory": true,
    "envs": [
      {
        "name": "ENV_COAP_RX",
        "auto_start": false,
        "interval": 0,
        "path": "./lua/on_coap.lua",
        "events": ["COAP_RX"]
      }
    ]
  },
  "coap": {
    "servers": [
      {
        "name": "edge01",
        "ipv4": "0.0.0.0",
        "port": 5683,
        "resources": [
          { "name": "telemetry", "rx_handler": "COAP_RX" }
        ]
      }
    ]
  }
}

Security and Operations

  • Secrets: Prefer environment injection or external secret stores over hard‑coding credentials. If you must place secrets in JSON, lock down file permissions.
  • Network exposure: Bind CoAP servers to trusted interfaces; restrict firewall rules to known peers.
  • Observability: Route transport events to distinct rx_handler names so you can measure handler‑level latency and error rates.
  • Immutability: Keep production configs under version control; treat them as artifacts.

Troubleshooting Lua signal handlers

  • If environments do not fire, confirm the signal name in the transport (rx_handler) exactly matches the name in umlua.envs[*].events.
  • If scripts appear not to run, verify paths and process permissions to read the Lua files and DB.
  • For CoAP, ensure the port is open and not already in use.
  • For MQTT, confirm the broker address, client ID uniqueness, and topic spelling (wildcards included).

D-Bus Interface: com.umink.Signals

This page documents the D-Bus interface com.umink.Signals exported by uMINK on the object path /com/umink/Signals.


Overview

  • Service name: com.umink.Signals
  • Object path: /com/umink/Signals
  • Primary interface: com.umink.Signals
  • Additional interface: org.freedesktop.DBus.Introspectable (standard)

This interface allows clients to:

  • Enumerate available Lua signal handlers (list method)
  • Invoke a Lua signal handler (run method)

Methods

list

method list()
  → response: a(sstb)
  • Description: Returns a list of available Lua signal handlers.
  • Return type: a(sstb) (array of structs)
    • Each tuple contains:
      1. s — signal identifier (string)
      2. s — handler path (string, Lua script path)
      3. t — signal execution interval (unsigned 64-bit integer)
      4. b — auto start (boolean)

Example call:

gdbus call --session \
  --dest com.umink.Signals \
  --object-path /com/umink/Signals \
  --method com.umink.Signals.list

Sample output:

([('COAP_RX', '/tmp/test_event_02.lua', uint64 0, false),
  ('MQTT_RX', '/tmp/test_event_03.lua', 0, false),
  ('TEST_EVENT_01', '/tmp/test_event_01.lua', 0, false),
  ('TEST_EVENT_01_02', '/tmp/test_event_01.lua', 0, false)],)

Explanation of fields:

  • COAP_RX — Identifier of the Lua signal handler.
  • /tmp/test_event_02.lua — Lua script path executed for this handler.
  • uint64 0 — Lua signal execution interval.
  • false — Whether the handler is currently auto-started.

run

method run(
    in  s id,
    in  s args,
    in  s auth,
    out s response
)
  • Description: Executes a Lua signal handler identified by id.
  • Parameters:
    • id (s) — Identifier of the Lua signal handler to run.
    • args (s) — Arguments as a serialized string (format defined by user context).
    • auth (s) — Authentication string or session token.
  • Return:
    • response (s) — Response message (format defined by handler).

Example call:

gdbus call --session \
  --dest com.umink.Signals \
  --object-path /com/umink/Signals \
  --method com.umink.Signals.run "TEST_EVENT_01" "{\"value\":42}" "token123"

Signals

The interface com.umink.Signals does not define any outbound D-Bus signals in the current version.


Properties

The interface com.umink.Signals does not expose any properties.


Standard Interfaces

The object also implements org.freedesktop.DBus.Introspectable, which provides the standard Introspect() method to query the XML introspection data.

Example:

gdbus introspect --session \
  --dest com.umink.Signals \
  --object-path /com/umink/Signals

Example Usage in Python

import dbus

bus = dbus.SessionBus()
proxy = bus.get_object('com.umink.Signals', '/com/umink/Signals')
iface = dbus.Interface(proxy, 'com.umink.Signals')

# List available signals
signals = iface.list()
print(signals)

# Run a handler
resp = iface.run('TEST_EVENT_01', '{"value":42}', 'token123')
print(resp)

Troubleshooting D-bus

  • Ensure the uMINK instance is properly loading the plg_sysagent_dbus.so plugin
  • Ensure the service com.umink.Signals is running and exported on the session bus.
  • If calls fail with UnknownMethod, confirm you are using the correct interface name (com.umink.Signals).

Lua API

This page documents the Lua API for writing uMINK signal handlers.
All handlers run in an isolated Lua environment but have access to a global table/module M which exposes helper functions and plugin-specific APIs.


Overview

  • Global table: M
  • Purpose: Provides core uMINK helpers and plugin APIs.
  • Structure:
    • Core functions: M.*
    • Plugin-specific tables: M.plugin_name.*
      • Example: M.mqtt for MQTT helper functions
      • Example: M.coap for CoAP helper functions

Handlers are invoked by events or periodic timers defined in the configuration. M is always available within the Lua script scope.


Core M Functions

Function Parameters Return Description
M.log(level, msg) level (string, e.g., "info", "warn", "error"), msg (string) nil Logs a message to the uMINK runtime log.
M.get_args() None table Returns the input arguments passed to the signal handler. The format depends on the transport/plugin that triggered the event.
M.signal(signal_name, input_data) signal_name (string), input_data (string) s_out (string), s_res (integer) Invokes another signal handler by name. Returns the string output of the invoked signal and a result code (0 for success, non-zero for errors such as unknown signal). If a signal handler attempts to call itself recursively, the call is prevented and the returned string indicates the recursion error.
M.db_set(db_name, key, value, persistent) db_name (string), key (string), value (string), persistent (integer, optional) nil Sets a value in the uMINK in-memory database. By default, the data is volatile and lost when the system agent terminates. Pass 1 as persistent to save the value persistently in the SQLite database configured in umlua.db.
M.db_get(db_name, key) db_name (string), key (string) string or nil Retrieves a value previously set via M.db_set. Returns nil if the key does not exist.
M.perf_inc(name, value) name (string), value (number, optional, default 1) nil Increments a performance counter by value. Useful for tracking event counts or custom metrics.
M.perf_set(name, value) name (string), value (number) nil Sets a performance metric to a specific value.
M.perf_match(pattern) pattern (string) table Returns performance metrics that match the given pattern. Useful for querying signal execution statistics, counters, and error rates.

Example Usage: Signal Handler Arguments (M.get_args())

The function M.get_args() returns the input arguments passed to the signal handler. The structure depends on the transport/plugin.

MQTT Event Example

local args = M.get_args()
-- Example output for MQTT:
-- {
--   {
--     mqtt_connection = "local",
--     mqtt_payload = "AAAb",
--     mqtt_topic = "umink/test"
--   }
-- }

for _, arg in ipairs(args) do
    M.log("info", "Received MQTT payload: " .. arg.mqtt_payload)
end

CoAP Event Example

local args = M.get_args()
-- Example output for CoAP:
-- {
--   {
--     data = "aa",
--     request_type = "GET"
--   }
-- }

for _, arg in ipairs(args) do
    M.log("info", "CoAP request type: " .. arg.request_type .. ", data: " .. arg.data)
end

Tip: Always iterate over the returned table from M.get_args() because some transports may pass multiple argument sets if multiple events are triggered simultaneously.


Plugin Modules

M.mqtt

Provides helpers for publishing and subscribing to MQTT topics.

Function Parameters Return Description
M.mqtt.publish(connection, topic, payload, retain) connection (string) — MQTT connection name, topic (string), payload (string), retain (boolean) boolean Publishes a message to the broker on the given connection. Returns true if successful.

M.coap

Provides helpers for CoAP client/server interactions.

Function Parameters Return Description
M.coap.send(connection, payload) connection (string) — CoAP connection name, payload (string) string Sends a CoAP message to the server using the specified connection. Returns the response payload.

Tip: Plugin sub-tables are only available if the corresponding plugin is loaded in the configuration (plugins_dir).


Signal Handler Example

Example Lua handler for a TEST_EVENT_01 event:

-- /tmp/test_event_01.lua
local payload = "dummy_payload"

-- Log start
M.log("info", "Handler invoked for TEST_EVENT_01")

-- Perform MQTT publish
local ok = M.mqtt.publish("local", "umink/test", payload, 0)
if ok then
    M.log("info", "MQTT message sent")
else
    M.log("error", "Failed to send MQTT message")
end

-- send to CoAP connection also
M.coap.send("COAP_TEST". payload)

-- Invoke another signal directly
local s_out, s_res = M.signal("TEST_SIGNAL", payload)
if s_res == 0 then
    M.log("info", "Signal TEST_SIGNAL executed successfully: " .. s_out)
elseif s_out:find("signal recursion prevented") then
    M.log("warn", s_out)
else
    M.log("error", "Failed to invoke TEST_SIGNAL signal, code: " .. s_res)
end

-- Store temporary data
M.db_set("test_db", "test_key", "test_value")

-- Retrieve temporary data
local val = M.db_get("test_db", "test_key")
M.log("info", "Retrieved value: " .. tostring(val))

-- Store persistent data
M.db_set("test_db", "persist_key", "persistent_value", 1)

Multi-Node Example Diagram

   +------------------+                  
   |   um1 (node)     |                  
   |------------------|                  
   | ENV_SENSOR (Lua) |  (periodic 5000ms)
   +--------+---------+                  
            | CoAP (sensor data)         
            v                            
   +------------------+                  
   |   um2 (node)     |                  
   |------------------|                  
   | ENV_COAP_RX (Lua)| <-- receives CoAP
   | ENV_MQTT_TX (Lua)| --> sends MQTT   
   +--------+---------+                  
            | MQTT (topic: umink/sensor) 
            v                            
   +---------------------+               
   |  Mosquitto Broker   |               
   +---------+-----------+               
             | MQTT (umink/sensor)       
             v                           
   +------------------+                  
   |   um3 (node)     |                  
   |------------------|                  
   | ENV_MQTT_RX (Lua)| <-- receives MQTT
   +------------------+                  

Sensor Node (um1)

Config (um1.json)

{
  "plugins_dir": "/tmp/plugins",
  "umlua": {
    "db": "test/test.db",
    "aggressive_gc": true,
    "conserve_memory": true,
    "envs": [
      {
        "name": "ENV_SENSOR",
        "auto_start": true,
        "interval": 5000,
        "path": "/tmp/ENV_SENSOR.lua",
        "events": [
          "SENSOR_EVENT"
        ]
      }
    ]
  },
  "coap": {
    "clients": [
      {
        "name": "COAP_TEST",
        "uri": "coap://192.168.0.22/test"
      }
    ]
  }
}

Script (ENV_SENSOR.lua)

-- ENV_SENSOR.lua
-- Periodically fetch CPU temperature using lm_sensors
-- and send via CoAP to um2.

local function get_cpu_temp()
    -- Try reading via sensors command. Adjust grep pattern for your hardware.
    local handle = io.popen("sensors | grep -m1 -E 'Core|temp1|Package id' | awk '{print $3}'")
    if not handle then return nil end
    local result = handle:read("*l")
    handle:close()
    if not result then return nil end
    -- strip + and °C
    result = result:gsub("[+°C]","")
    return result
end

local function main()
    local temp = get_cpu_temp() or "N/A"
    M.log("info", "ENV_SENSOR: CPU Temp=" .. tostring(temp))

    -- Send via CoAP client "COAP_TEST"
    local res = M.coap.send("COAP_TEST", tostring(temp))
    if res ~= 0 then
        M.log("error", "ENV_SENSOR: CoAP send failed, code=" .. tostring(res))
    end
end

main()

Bridge Node CoAP → MQTT (um2)

Config (um2.json)

{
  "plugins_dir": "/tmp/plugins",
  "umlua": {
    "db": "test/test.db",
    "aggressive_gc": true,
    "conserve_memory": true,
    "envs": [
      {
        "name": "ENV_COAP_RX",
        "auto_start": true,
        "interval": 0,
        "path": "/tmp/ENV_COAP_RX.lua",
        "events": [
          "COAP_RX"
        ]
      },
      {
        "name": "ENV_MQTT_TX",
        "auto_start": false,
        "interval": 0,
        "path": "/tmp/ENV_MQTT_TX.lua",
        "events": [
          "MQTT_TX"
        ]
      }
    ]
  },
  "coap": {
    "servers": [
      {
        "name": "TEST_SERVER",
        "ipv4": "192.168.0.22",
        "port": 5683,
        "resources": [
          {
            "name": "test",
            "rx_handler": "COAP_RX"
          }
        ]
      }
    ]
  },
  "mqtt": {
    "connections": [
      {
        "name": "mosq",
        "address": "127.0.0.1",
        "client_id": "um2_mqtt_client",
        "username": "",
        "password": "",
        "rx_handler": "",
        "subscriptions": []
      }
    ]
  }
}

Script (ENV_COAP_RX.lua)

-- ENV_COAP_RX.lua
-- Receives CoAP messages from um1 and forwards to MQTT_TX.

local function main()
    local args = M.get_args()
    if not args or not args[1] then
        M.log("warn", "ENV_COAP_RX: No arguments received")
        return
    end

    local coap_data = args[1].data or args[1] or "<nil>"
    M.log("info", "ENV_COAP_RX: Received CoAP data: " .. tostring(coap_data))

    -- Forward via MQTT_TX (invoke ENV_MQTT_TX)
    local s_out, s_res = M.signal("MQTT_TX", tostring(coap_data))
    if s_res ~= 0 then
        M.log("error", "ENV_COAP_RX: Failed to forward, reason=" .. tostring(s_out))
    end
end

main()

Script (ENV_MQTT_TX.lua)

-- ENV_MQTT_TX.lua
-- Publishes payload to MQTT broker.

local function main()
    local args = M.get_args()
    if not args or not args[1] then
        M.log("warn", "ENV_MQTT_TX: No payload received")
        return
    end

    local payload = args[1].data or args[1][1] or "<nil>"

    local ok = M.mqtt.publish("mosq", "umink/sensor", tostring(payload), false)
    if not ok then
        M.log("error", "ENV_MQTT_TX: MQTT publish failed")
    else
        M.log("info", "ENV_MQTT_TX: Published to topic umink/sensor -> " .. tostring(payload))
    end
end

main()

Consumer Node (um3)

Config (um3.json)

{
  "plugins_dir": "/tmp/plugins",
  "umlua": {
    "db": "test/test.db",
    "aggressive_gc": true,
    "conserve_memory": true,
    "envs": [
      {
        "name": "ENV_MQTT_RX",
        "auto_start": true,
        "interval": 0,
        "path": "/tmp/ENV_MQTT_RX.lua",
        "events": [
          "MQTT_RX"
        ]
      }
    ]
  },
  "mqtt": {
    "connections": [
      {
        "name": "mosq",
        "address": "127.0.0.1",
        "client_id": "um3_mqtt_client",
        "username": "",
        "password": "",
        "rx_handler": "MQTT_RX",
        "subscriptions": [
          "umink/sensor"
        ]
      }
    ]
  }
}

Script (ENV_MQTT_RX.lua)

-- ENV_MQTT_RX.lua
-- Handles incoming MQTT messages from broker.

local function main()
    local args = M.get_args()
    if not args or not args[1] then
        M.log("warn", "ENV_MQTT_RX: No arguments received")
        return
    end

    local mqtt_data = args[1]
    local topic   = mqtt_data.mqtt_topic or "<unknown>"
    local payload = mqtt_data.mqtt_payload or tostring(mqtt_data)
    local conn    = mqtt_data.mqtt_connection or "<no-conn>"

    M.log("info", string.format(
        "ENV_MQTT_RX: Received MQTT (conn=%s, topic=%s) -> %s",
        tostring(conn), tostring(topic), tostring(payload)
    ))

    -- Store last payload in memory
    M.db_set("sensor_cache", "last_payload", tostring(payload))

    -- Increment perf counter
    M.perf_inc("lua.signal.ENV_MQTT_RX.count")
end

main()

Performance Monitoring

uMINK tracks signal handler execution performance. Lua handlers can access these stats via:

local stats = M.perf_match('lua*TEST_EVENT_01*')

Example returned data:

{
  "lua.signal.ENV_TEST_EVENT_01.count": {
    "last": 12,
    "rate": 0.094676926215134002
  },
  "lua.signal.ENV_TEST_EVENT_01.lag": {
    "last": 184640,
    "max": 259362
  },
  "lua.signal.ENV_TEST_EVENT_01.error": {
    "last": 0,
    "rate": 0
  }
}
  • count.last — Number of times handler was invoked in the last measurement period.
  • count.rate — Execution rate (per second) over the measurement period.
  • lag.last — Last execution duration in microseconds.
  • lag.max — Maximum execution duration observed.
  • error.last — Number of errors during last measurement period.
  • error.rate — Error rate.

Tip: Use wildcard patterns to match multiple Lua environments.


Best Practices

  1. Use M.log for debugging instead of print(); runtime collects structured logs.
  2. Keep handlers short and non-blocking; avoid long loops or heavy computation.
  3. Check plugin availability before calling sub-table functions:
    if M.mqtt then
        M.mqtt.publish("topic", "payload")
    end
    
  4. Invoke other signals carefully; check the result code and handle recursion errors returned by M.signal.
  5. Use M.db_set and M.db_get to share data between signal handlers. Remember that in-memory data is lost when the system agent terminates; pass 1 as the fourth argument to M.db_set for persistent storage.
  6. Lua memory management is handled automatically; you can enable aggressive_gc in config if needed for constrained devices.

Changelog

  • v1.0 — Initial public documentation of uMINK configuration format.