Mqtt#135
Conversation
Wrap the ESP-IDF esp-mqtt client in a new lua_module_mqtt component, add the APP_CLAW_LUA_MODULE_MQTT Kconfig option and app_claw registration, enable it in the edge_agent defaults, and document the module in the EN/ZH lua-modules reference.
Store broker URL, username, password, and client id in settings_store (NVS) and expose them through a dedicated MqttPage in the web config UI, following the per-subsystem page pattern. The values flow app_config -> app_claw_config -> the Lua mqtt module via lua_module_mqtt_set_defaults(), so scripts can call mqtt.new() with no arguments to reuse the configured broker, or override per call.
Add MQTT JWT authentication (for brokers like EMQX) and protect the MQTT settings behind a developer-mode switch, in the edge_agent app only (mcp_server_point has no MQTT config). JWT auth: - New persisted field mqtt_jwt across the config chain: app_config struct/defaults/NVS field, app_claw config struct, config API field, and the frontend AppConfig + mqtt group. The lua_module_mqtt module is not touched. - JWT gets its own 1024-byte buffer (APP_CONFIG_MQTT_JWT_LEN / APP_CLAW_MQTT_JWT_LEN) instead of reusing the 320-byte string length — EMQX JWTs with claims easily exceed 320 and would otherwise truncate. - Auth selection lives where the broker defaults are seeded (app_lua_register_mqtt): when mqtt_jwt is set it is passed in the password slot and takes priority; otherwise the plain password is used, so username/password auth is unchanged when no JWT is configured. - Frontend: a separate "JWT Token" password field on the MQTT page. Developer mode (mirrors FilesPage): - A dev-mode toggle that starts OFF and asks for confirmation when enabled. While OFF the MQTT fields are read-only and Save is disabled; enabling it unlocks editing and the destructive action. - A "Clear credentials" button, enabled only in dev mode (with a confirm and a guard), wipes all MQTT credentials (uri, username, password, jwt) to empty in NVS. - New i18n keys (en + zh-cn) for the JWT field, dev mode, and clearing. Verified on hardware: a 441-char JWT persists intact (would truncate at 320), and clearing empties all four MQTT fields.
Add a "Test connection" button that probes the stored MQTT broker
credentials on demand. There is no persistent app-level MQTT connection
to read, so this is a one-shot probe, not a live monitor.
- New endpoint POST /api/mqtt/probe (in http_server_config_api.c): loads
the saved config, spins up a throwaway esp-mqtt client using the same
JWT-or-password auth logic as the runtime, waits up to 4s for it to
connect or error, and reports {connected: bool}. An empty broker URL
returns {connected:false, reason:"not configured"}. The probe client is
always stopped and destroyed (success, failure or timeout) — no leaked
connections. network.timeout_ms is bounded so an unreachable broker
fails fast instead of blocking teardown. Adds the mqtt component to the
http_server REQUIRES. lua_module_mqtt is not touched.
- Frontend: a read-only "Test connection" button (works without dev mode)
and a badge showing only the LAST test result, not live status — grey
"Not tested", green "Last test: OK", red "Last test: failed". New i18n
keys in en + zh-cn.
Verified on hardware: valid creds connect, a wrong password and an
unreachable broker both report failed (the latter without hanging), and
an empty URL reports not configured.
There was a problem hiding this comment.
Pull request overview
Adds end-to-end MQTT support to ESP-Claw: a new Lua mqtt module backed by ESP-IDF’s esp-mqtt, plus device web configuration (including a one-shot connection probe) and docs updates so users can configure a default broker and use it from Lua scripts.
Changes:
- Introduces
lua_module_mqttwith publish/subscribe, poll-based receive, and callback/dispatch receive modes. - Extends app config + web UI with MQTT broker settings (URI/username/password/JWT/client ID) and adds
/api/mqtt/probe. - Updates EN/ZH docs and Lua module reference list; enables the MQTT Lua module in
sdkconfig.defaults.
Reviewed changes
Copilot reviewed 27 out of 29 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/src/content/docs/zh-cn/tutorial/web-config.mdx | Documents MQTT settings in the web config tutorial (ZH). |
| docs/src/content/docs/zh-cn/reference-cap/lua-modules.mdx | Adds mqtt to Lua modules list (ZH). |
| docs/src/content/docs/en/tutorial/web-config.mdx | Documents MQTT settings in the web config tutorial (EN). |
| docs/src/content/docs/en/reference-cap/lua-modules.mdx | Adds mqtt to Lua modules list (EN). |
| components/lua_modules/lua_module_mqtt/test/mqtt_pubsub.lua | Adds a pub/sub polling example script for the MQTT Lua module. |
| components/lua_modules/lua_module_mqtt/test/mqtt_callbacks.lua | Adds a callback/dispatch example script for the MQTT Lua module. |
| components/lua_modules/lua_module_mqtt/src/lua_module_mqtt.h | Declares the Lua module entrypoints and defaults injection API. |
| components/lua_modules/lua_module_mqtt/src/lua_module_mqtt.c | Implements the MQTT Lua binding, rx queue, and callback registry. |
| components/lua_modules/lua_module_mqtt/README.md | Adds usage documentation for the Lua MQTT module. |
| components/lua_modules/lua_module_mqtt/idf_component.yml | Declares the component manager manifest (no external deps). |
| components/lua_modules/lua_module_mqtt/CMakeLists.txt | Registers the new component and its dependencies. |
| components/common/app_claw/Kconfig | Adds a Kconfig option to enable the MQTT Lua module. |
| components/common/app_claw/include/app_claw.h | Extends app_claw_config_t with MQTT fields (incl. JWT buffer). |
| components/common/app_claw/idf_component.yml | Wires lua_module_mqtt into app_claw dependency rules. |
| components/common/app_claw/app_lua_modules.c | Registers the MQTT Lua module and seeds broker defaults from config. |
| application/edge_agent/sdkconfig.defaults | Enables CONFIG_APP_CLAW_LUA_MODULE_MQTT by default. |
| application/edge_agent/components/http_server/http_server_config_api.c | Adds MQTT config fields and /api/mqtt/probe route/handler. |
| application/edge_agent/components/http_server/frontend_source/src/state/dirty.ts | Adds mqtt as a tracked dirty tab. |
| application/edge_agent/components/http_server/frontend_source/src/pages/MqttPage.tsx | Adds the MQTT settings page UI (edit, probe, clear). |
| application/edge_agent/components/http_server/frontend_source/src/i18n/zh-cn.ts | Adds MQTT UI strings (ZH). |
| application/edge_agent/components/http_server/frontend_source/src/i18n/en.ts | Adds MQTT UI strings (EN). |
| application/edge_agent/components/http_server/frontend_source/src/components/layout/Sidebar.tsx | Adds MQTT navigation entry/icon. |
| application/edge_agent/components/http_server/frontend_source/src/App.tsx | Adds the MQTT page route/tab rendering. |
| application/edge_agent/components/http_server/frontend_source/src/api/client.ts | Adds MQTT config fields/groups and the probeMqtt() API client. |
| application/edge_agent/components/http_server/CMakeLists.txt | Adds mqtt dependency for the server (probe implementation). |
| application/edge_agent/components/app_config/include/app_config.h | Extends persisted config with MQTT fields and JWT buffer size. |
| application/edge_agent/components/app_config/app_config.c | Adds MQTT fields to config storage + claw config projection. |
| .gitignore | Ignores a local flash helper script. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| esp_err_t lua_module_mqtt_set_defaults(const char *uri, | ||
| const char *username, | ||
| const char *password, | ||
| const char *client_id) | ||
| { | ||
| free(s_default_uri); | ||
| free(s_default_username); | ||
| free(s_default_password); | ||
| free(s_default_client_id); | ||
| s_default_uri = lua_module_mqtt_dup_or_null(uri); | ||
| s_default_username = lua_module_mqtt_dup_or_null(username); | ||
| s_default_password = lua_module_mqtt_dup_or_null(password); | ||
| s_default_client_id = lua_module_mqtt_dup_or_null(client_id); | ||
| return ESP_OK; | ||
| } |
| case MQTT_EVENT_DATA: { | ||
| /* Only the first fragment carries the topic. Larger-than-buffer | ||
| * payloads arrive fragmented; this binding keeps single-fragment | ||
| * messages and drops continuation fragments. */ | ||
| if (event->topic_len <= 0 || event->current_data_offset != 0) { | ||
| break; | ||
| } |
| const clearCredentials = async () => { | ||
| if (!devMode()) { | ||
| pushToast(t('mqttDevModeRequired') as string, 'error'); | ||
| return; | ||
| } | ||
| if (clearing()) return; | ||
| if (!window.confirm(t('mqttClearConfirm') as string)) return; | ||
| setClearing(true); | ||
| try { | ||
| await saveConfigPatch({ mqtt_uri: '', mqtt_username: '', mqtt_password: '', mqtt_jwt: '' }); | ||
| await tab.reload(); | ||
| pushToast(t('mqttClearDone') as string, 'success'); | ||
| } catch (err) { | ||
| pushToast((err as Error).message, 'error'); | ||
| } finally { | ||
| setClearing(false); | ||
| } | ||
| }; |
|
Thanks for the review! I've addressed all three comments:
|
|
Thanks for the PR! Would you mind updating the PR description to include the use case for this feature, as well as a brief explanation of how it is intended to be used? That would make it much easier for reviewers and future contributors to understand the motivation and usage of this feature. |
Description
Related
Testing
Checklist
Before submitting a Pull Request, please ensure the following: