Skip to main content
lua_device.py is a single MicroPython module that connects a microcontroller to an agent as a device over MQTT over WebSocket, runs command handlers, sends triggers, and reconnects on its own. It needs umqtt.robust or umqtt.simple, both included in the Raspberry Pi Pico W firmware, and has no CDN support. Verified against lua-device-client 1.3.0.

Getting the file

The module ships inside the lua-device-client package on PyPI as lua_device/micropython.py. Install the package on your computer, locate the file, and copy it to the board as lua_device.py.

Quick example

main.py

LuaDevice

LuaDevice(agent_id, api_key, device_name, ...) stores the configuration; nothing connects until connect() or run().
str
required
The agent the device connects to.
str
required
The credential: a device credential or a legacy API key. Sent as the MQTT password and repeated in the non-retained status message.
str
required
The device name; must match the credential’s binding.
str
Network to join in connect(). Leave unset to manage Wi-Fi yourself.
str
Password for wifi_ssid.
str
default:"mqtt.heylua.ai"
Broker host.
int
default:443
Broker port.
str
Group name reported at connect.
bool
default:true
Use TLS. Ignored in WebSocket mode, which always uses TLS.
bool
default:true
Speak MQTT over WebSocket at path /mqtt. Set False for raw MQTT over TCP on port.

Methods

command()

Decorator that registers a handler. The description defaults to the command name.

on_command()

The same registration as a plain call.
Returns — nothing. The handler’s return value is sent as data; an exception is sent as success: false with its message; an unregistered command answers Unknown command: <name>. Handlers run synchronously on the main loop, so a slow handler delays heartbeats.

connect()

Joins Wi-Fi when wifi_ssid is set, opens the MQTT connection, subscribes, and publishes the retained status and the command list. Prints [lua-device] Connected as <name>.
Errors — OSError for a failed WebSocket handshake or a refused MQTT connection (MQTT CONNACK error: rc=5 for a rejected credential). A Wi-Fi join that hasn’t succeeded after 30 seconds resets the board.

run()

Connects if needed, then loops forever: polls for commands, sends a heartbeat every 30 seconds, expires the command-ID cache, and reconnects on any exception. Prints [lua-device] Listening for commands....
int
default:100
Sleep between polls.

trigger()

Publishes a trigger and returns at once; it doesn’t wait for the acknowledgment.
Errors — RuntimeError("Not connected") before connect().

disconnect()

Publishes a retained offline status and closes the connection.

Connection behavior

  • Client ID lua-<agent_id>-<device_name>, username <agent_id>:<device_name>, keep-alive 60 seconds, retained last-will offline status.
  • In WebSocket mode the TLS connection does not verify the broker certificate; in TCP mode verification depends on the firmware’s ssl module.
  • Heartbeat every 30 seconds from run(). If you write your own loop, mirror what run() does: call check_msg() on the MQTT client, publish the heartbeat, and expire the cache.
  • Command IDs are remembered for 5 minutes; a redelivered command is ignored. The cache is pruned when it exceeds 100 entries.
  • Reconnection backs off from 2 to 30 seconds and resets the board after 10 consecutive failures.
  • The platform keeps the command list sent at connect for 24 hours and heartbeats don’t extend it, so a board connected for longer than 24 hours loses its command tools (its is_online tool stays) until it reconnects. The client has no timer for this and run() reconnects only after an error, so call disconnect() and connect() from a loop of your own, or reset the board, before 24 hours pass.
  • Messages on the connected, trigger_ack, and error topics are printed with a [lua-device] prefix; trigger_result and pong are subscribed to and ignored. trigger_error isn’t subscribed to, so a rejected trigger is silent.

Differences from the Python client

See also