Skip to content

Add MQTT device commands (reboot, factory reset, version-pinned fwupgrade)#1212

Open
gskjold wants to merge 5 commits into
feat/config_via_mqttfrom
feat/mqtt-commands
Open

Add MQTT device commands (reboot, factory reset, version-pinned fwupgrade)#1212
gskjold wants to merge 5 commits into
feat/config_via_mqttfrom
feat/mqtt-commands

Conversation

@gskjold

@gskjold gskjold commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

Adds remote device commands over MQTT — reboot, factory reset, version-pinned firmware upgrade, and day/month plot requests — shared across the JSON, Raw and Home Assistant payload handlers, plus a per-device config flag that gates the destructive ones. Requested by a client.

Stacked on top of #1169 (feat/config_via_mqtt); base branch is feat/config_via_mqtt, so this should merge after it. The diff here is only the command work.

Commands

Sent to the device's command topic (the configured subscribeTopic, or <publishTopic>/command when blank). Accepts a JSON object or a plain payload:

Command Example Gating
Firmware upgrade {"action":"fwupgrade"} or {"action":"fwupgrade","version":"v2.5.6"} always allowed
Day plot {"action":"dayplot"} → published to <publishTopic>/dayplot always allowed
Month plot {"action":"monthplot"} → published to <publishTopic>/monthplot always allowed
Reboot {"action":"reboot"} requires allowDestructiveCommands
Factory reset {"action":"factoryreset","confirm":true} requires the flag and "confirm":true
  • fwupgrade now accepts an optional version (falls back to the latest detected version) — so a specific release can be pinned.
  • These commands previously lived only in the JSON handler; they now work on JSON, Raw and Home Assistant.
  • Domoticz and Passthrough intentionally keep their empty message handlers (no command surface).

Destructive-actions flag

New MqttConfig.allowDestructiveCommands (default off). Reboot and factory reset are no-ops unless it's enabled; factory reset additionally requires an explicit confirm. The only authorization boundary for MQTT is the broker's auth/ACLs, so destructive actions are opt-in per device.

Configurable from the web UI (Configuration → MQTT checkbox), the web /save form (qdc), and MQTT setconfig (q.dc); exposed in configuration.json (q.dc).

Stored via the MqttConfig.magic migration (new magic 0xB6) rather than a global EEPROM_EXPECTED_VERSION bump — existing configs default the field to false on read.

Implementation notes

  • Generic commands live in AmsMqttHandler::handleCommand() (base class); JsonMqttHandler, RawMqttHandler and HomeAssistantMqttHandler call it from onMessage, replacing their duplicated fwupgrade handling. JSON keeps its extra getconfig/setconfig (ESP32-only).
  • AmsDataStorage moved to the base handler (set via setDataStorage in the bridge) so the shared plot commands can generate their JSON.
  • Works on both ESP8266 and ESP32 (unlike getconfig/setconfig, which remain ESP32-only).
  • MQTT-initiated reboots are tagged with new causes REBOOT_CAUSE_MQTT_REBOOT / _FACTORY_RESET / _FIRMWARE_UPGRADE (via a ResetDataContainer wired to the active handler), so the reboot reason shows up in sysinfo.

Testing

Builds on all targets (esp8266, esp32, esp32s2, esp32solo, esp32c3, esp32s3); native decoder tests unaffected. ESP8266 flash ~97.4%.

Verified on a live ESP32-S2 (broker = RabbitMQ), in both JSON and Home Assistant payload formats:

  • reboot denied with flag off; allowed with flag on (reboot cause 11); flag persists across reboot.
  • factory-reset gating (no confirm → no-op, config intact).
  • version-pinned fwupgrade to v2.5.6 — real download+flash from the cloud, device booted into v2.5.6.
  • dayplot request → valid plot published to <publishTopic>/dayplot.
  • Raw and HA handlers both execute commands (confirms the shared path on non-JSON handlers; HA still also handles its discovery status topic).
  • Web UI serves the new checkbox; the qdc save path toggles the flag.

Not exercised on hardware (covered by code + gating tests): a real factory reset (would wipe to AP mode).

Follow-ups

  • Document the destructive commands (the UI label is intentionally generic — "Allow destructive commands").

gskjold and others added 5 commits June 18, 2026 13:30
New per-handler config flag (default off) that will gate destructive MQTT
commands (reboot / factory reset). Uses the MqttConfig.magic migration (new
magic 0xB6) instead of bumping the global EEPROM version, defaulting the field
to false for existing configs. Exposed in the configuration JSON ("q":{"dc"}),
the web save form (qdc), and the MQTT setconfig parser.

Also adds REBOOT_CAUSE_MQTT_REBOOT / _FACTORY_RESET / _FIRMWARE_UPGRADE for
tagging MQTT-initiated reboots.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…upgrade

Move the generic device commands into AmsMqttHandler::handleCommand so all
payload handlers share them, and wire JSON, Raw and Home Assistant onMessage to
call it (replacing their duplicated fwupgrade handling):

- fwupgrade: now accepts an optional "version" ({"action":"fwupgrade","version":
  "vX.Y.Z"}); falls back to the latest detected version. Always allowed.
- reboot: gated by MqttConfig.allowDestructiveCommands.
- factoryreset: gated by the flag AND requires {"confirm":true}.

Accepts plain payloads ("reboot") or JSON ({"action":"reboot"}). MQTT-initiated
reboots are tagged with the new REBOOT_CAUSE_MQTT_* causes when a
ResetDataContainer is wired in (set on the main handler in the bridge).

Works on both ESP8266 and ESP32 (unlike getconfig/setconfig).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Render a checkbox in the MQTT configuration section bound to q.dc, posting qdc
to /save, so the destructive-commands flag is configurable from the web UI (not
only via MQTT setconfig). Adds the conf.mqtt.allowdestructive source string and
rebuilds ui/dist.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the "(reboot, factory reset)" parenthetical for a cleaner label; the
specific commands will be covered in documentation instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Relocate the day/month plot requests from JsonMqttHandler into the shared
AmsMqttHandler::handleCommand, so Raw and Home Assistant can request plots too
(Domoticz and Passthrough keep their empty handlers by design). Moves the
AmsDataStorage pointer to the base handler (set via setDataStorage in the
bridge) since the plot generators need it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🔧 PR Build Artifacts

Version: 29aee83

All environments built successfully. Download the zip files:

Artifacts expire after 7 days. View workflow run

@gskjold gskjold marked this pull request as ready for review June 18, 2026 12:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant