feat: add mqtt client version to repeater app and cli
This commit is contained in:
+3
-2
@@ -12,6 +12,7 @@ These commands are available on `*_repeater_mqtt` firmware targets.
|
|||||||
|
|
||||||
- `get mqtt.status`: shows WiFi, NTP, IATA, endpoint status, status publishing state, and TX state.
|
- `get mqtt.status`: shows WiFi, NTP, IATA, endpoint status, status publishing state, and TX state.
|
||||||
- `get mqtt.statuscfg`: shows whether periodic status messages are enabled as a simple `on` or `off` value. Most users can just use `get mqtt.status`.
|
- `get mqtt.statuscfg`: shows whether periodic status messages are enabled as a simple `on` or `off` value. Most users can just use `get mqtt.status`.
|
||||||
|
- `get mqtt.client_version`: shows the MQTT `client_version` string published by the repeater.
|
||||||
- `get mqtt.iata`: shows the IATA/location code used in MQTT topics.
|
- `get mqtt.iata`: shows the IATA/location code used in MQTT topics.
|
||||||
- `set mqtt.iata <code>`: sets the IATA/location code, for example `MEL`.
|
- `set mqtt.iata <code>`: sets the IATA/location code, for example `MEL`.
|
||||||
- `set mqtt.iata UNSET`: marks MQTT IATA as not configured yet. While it is `UNSET`, enabled MQTT brokers stay disconnected until a real code is saved.
|
- `set mqtt.iata UNSET`: marks MQTT IATA as not configured yet. While it is `UNSET`, enabled MQTT brokers stay disconnected until a real code is saved.
|
||||||
@@ -68,7 +69,7 @@ Legacy dotted aliases are also accepted:
|
|||||||
### Web Panel Controls
|
### Web Panel Controls
|
||||||
|
|
||||||
- `get web`
|
- `get web`
|
||||||
- `get web.status`: shows whether the local HTTPS panel is available. After `start ota`, this reports `web:suspended ota` until the repeater reboots.
|
- `get web.status`: shows whether the local HTTPS panel is available.
|
||||||
- `get web.stats.status`: shows whether the dedicated stats page and history subsystem are enabled, whether recent history is active, whether PSRAM-backed history is available, and whether the SD-backed archive is mounted. When enabled, the history capture now covers supported environment telemetry too, not just the original battery/radio series. GPS-enabled boards also record per-minute satellites samples for the `/stats` history view.
|
- `get web.stats.status`: shows whether the dedicated stats page and history subsystem are enabled, whether recent history is active, whether PSRAM-backed history is available, and whether the SD-backed archive is mounted. When enabled, the history capture now covers supported environment telemetry too, not just the original battery/radio series. GPS-enabled boards also record per-minute satellites samples for the `/stats` history view.
|
||||||
- `set web on|off`
|
- `set web on|off`
|
||||||
- `set.web on|off`: enables or disables the local HTTPS panel.
|
- `set.web on|off`: enables or disables the local HTTPS panel.
|
||||||
@@ -122,7 +123,7 @@ Notes:
|
|||||||
- the panel still uses the repeater admin password for access
|
- the panel still uses the repeater admin password for access
|
||||||
- commands run with the same care as if you typed them into the repeater CLI directly
|
- commands run with the same care as if you typed them into the repeater CLI directly
|
||||||
- this is intended for local admin use on a trusted network
|
- this is intended for local admin use on a trusted network
|
||||||
- `start ota` suspends the local repeater web panel until reboot so the OTA HTTP listener can take over port `80`
|
- `start ota` releases the local HTTP redirect listener on port `80` so the OTA HTTP listener can take over without stopping the rest of the repeater services
|
||||||
|
|
||||||
## Companion WiFi Rescue Commands
|
## Companion WiFi Rescue Commands
|
||||||
|
|
||||||
|
|||||||
+11
-2
@@ -130,7 +130,7 @@ This section runs common read-only commands for:
|
|||||||
- Wi-Fi
|
- Wi-Fi
|
||||||
- MQTT
|
- MQTT
|
||||||
|
|
||||||
These are useful for quick checks without typing into the CLI field.
|
These are useful for quick checks without typing into the CLI field. The MQTT quick actions include `mqtt.status`, `mqtt.client_version`, `mqtt.iata`, `mqtt.owner`, and `mqtt.email`.
|
||||||
|
|
||||||
## Run CLI Command
|
## Run CLI Command
|
||||||
|
|
||||||
@@ -149,6 +149,7 @@ This makes it easy to see exactly what the panel sent to the repeater.
|
|||||||
This section includes:
|
This section includes:
|
||||||
|
|
||||||
- Device Name
|
- Device Name
|
||||||
|
- Clock UTC
|
||||||
- Latitude
|
- Latitude
|
||||||
- Longitude
|
- Longitude
|
||||||
- Guest Password
|
- Guest Password
|
||||||
@@ -165,6 +166,14 @@ Notes:
|
|||||||
- the refresh buttons load the current value from the repeater
|
- the refresh buttons load the current value from the repeater
|
||||||
- the save buttons send the matching CLI command immediately
|
- the save buttons send the matching CLI command immediately
|
||||||
|
|
||||||
|
## Info
|
||||||
|
|
||||||
|
This section shows:
|
||||||
|
|
||||||
|
- `Version`: firmware version with build date
|
||||||
|
- `Client Version`: MQTT client version string
|
||||||
|
- `Public Key`
|
||||||
|
|
||||||
## Ghost Node Mode
|
## Ghost Node Mode
|
||||||
|
|
||||||
Ghost Node Mode is a convenience control on `/app` for a repeater that should stay on Wi-Fi and MQTT, but should not actively behave like another nearby repeater.
|
Ghost Node Mode is a convenience control on `/app` for a repeater that should stay on Wi-Fi and MQTT, but should not actively behave like another nearby repeater.
|
||||||
@@ -303,7 +312,7 @@ On mobile:
|
|||||||
|
|
||||||
1. Press `Start OTA`.
|
1. Press `Start OTA`.
|
||||||
2. Confirm the action.
|
2. Confirm the action.
|
||||||
3. The local repeater web panel is suspended until reboot so OTA can take over HTTP on port `80`.
|
3. The local HTTP redirect listener on port `80` is released so OTA can take over that port.
|
||||||
4. Continue with your normal OTA workflow.
|
4. Continue with your normal OTA workflow.
|
||||||
|
|
||||||
### Use Historical Stats
|
### Use Historical Stats
|
||||||
|
|||||||
@@ -2056,9 +2056,6 @@ void MyMesh::clearStats() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MyMesh::prepareForOTAStart() {
|
void MyMesh::prepareForOTAStart() {
|
||||||
#if defined(ESP_PLATFORM) && WITH_WEB_PANEL
|
|
||||||
web.suspendForOTA();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) {
|
void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply) {
|
||||||
@@ -2288,6 +2285,8 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
|
|||||||
sprintf(reply, "> %s", mqtt.isStatusEnabled() ? "on" : "off");
|
sprintf(reply, "> %s", mqtt.isStatusEnabled() ? "on" : "off");
|
||||||
} else if (strcmp(command, "get mqtt.status") == 0) {
|
} else if (strcmp(command, "get mqtt.status") == 0) {
|
||||||
mqtt.formatStatusReply(reply, 160);
|
mqtt.formatStatusReply(reply, 160);
|
||||||
|
} else if (strcmp(command, "get mqtt.client_version") == 0) {
|
||||||
|
sprintf(reply, "> %s", mqtt.getClientVersion());
|
||||||
} else if (memcmp(command, "get mqtt.iata", 13) == 0) {
|
} else if (memcmp(command, "get mqtt.iata", 13) == 0) {
|
||||||
sprintf(reply, "> %s", mqtt.getIata());
|
sprintf(reply, "> %s", mqtt.getIata());
|
||||||
} else if (memcmp(command, "get mqtt.owner", 14) == 0) {
|
} else if (memcmp(command, "get mqtt.owner", 14) == 0) {
|
||||||
|
|||||||
+10
-1
@@ -442,7 +442,16 @@ releases:
|
|||||||
- type: fixed
|
- type: fixed
|
||||||
area: web-panel
|
area: web-panel
|
||||||
text: "Corrected `/stats` packet activity and GPS satellites bar charts so restored zero values no longer draw phantom bars and the final bar uses the same spacing as the rest of the series."
|
text: "Corrected `/stats` packet activity and GPS satellites bar charts so restored zero values no longer draw phantom bars and the final bar uses the same spacing as the rest of the series."
|
||||||
|
- type: added
|
||||||
|
area: mqtt
|
||||||
|
text: "Added a `get mqtt.client_version` repeater CLI command and exposed the same value in the `/app` quick MQTT command buttons."
|
||||||
|
- type: changed
|
||||||
|
area: web-panel
|
||||||
|
text: "Reworked the `/app` Info panel to show `Version` and `Client Version`, and moved `Clock UTC` into Repeater Settings beside Device Name."
|
||||||
|
- type: fixed
|
||||||
|
area: web-panel
|
||||||
|
text: "Narrowed `start ota` handling so the repeater only releases the local HTTP redirect listener on port `80`, allowing ElegantOTA to start without unnecessarily stopping the rest of the repeater services."
|
||||||
- type: docs
|
- type: docs
|
||||||
area: docs
|
area: docs
|
||||||
text: "Updated the web panel and API docs to describe the board-aware battery percentage display fields used by the repeater stats UI."
|
text: "Updated the web panel, API, and custom CLI docs to describe the board-aware battery display, the new MQTT client-version command, and the revised `/app` layout."
|
||||||
breaking_changes: []
|
breaking_changes: []
|
||||||
|
|||||||
@@ -12,7 +12,11 @@
|
|||||||
|
|
||||||
bool ESP32Board::startOTAUpdate(const char* id, char reply[]) {
|
bool ESP32Board::startOTAUpdate(const char* id, char reply[]) {
|
||||||
inhibit_sleep = true; // prevent sleep during OTA
|
inhibit_sleep = true; // prevent sleep during OTA
|
||||||
WiFi.softAP("MeshCore-OTA", NULL);
|
WiFi.mode(WIFI_AP_STA);
|
||||||
|
if (!WiFi.softAP("MeshCore-OTA", NULL)) {
|
||||||
|
strcpy(reply, "Error - OTA AP start failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
sprintf(reply, "Started: http://%s/update", WiFi.softAPIP().toString().c_str());
|
sprintf(reply, "Started: http://%s/update", WiFi.softAPIP().toString().c_str());
|
||||||
MESH_DEBUG_PRINTLN("startOTAUpdate: %s", reply);
|
MESH_DEBUG_PRINTLN("startOTAUpdate: %s", reply);
|
||||||
@@ -22,7 +26,12 @@ bool ESP32Board::startOTAUpdate(const char* id, char reply[]) {
|
|||||||
static char home_buf[90];
|
static char home_buf[90];
|
||||||
sprintf(home_buf, "<H2>Hi! I am a MeshCore Repeater. ID: %s</H2>", id);
|
sprintf(home_buf, "<H2>Hi! I am a MeshCore Repeater. ID: %s</H2>", id);
|
||||||
|
|
||||||
AsyncWebServer* server = new AsyncWebServer(80);
|
static AsyncWebServer* server = nullptr;
|
||||||
|
if (server != nullptr) {
|
||||||
|
delete server;
|
||||||
|
server = nullptr;
|
||||||
|
}
|
||||||
|
server = new AsyncWebServer(80);
|
||||||
|
|
||||||
server->on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
server->on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||||
request->send(200, "text/html", home_buf);
|
request->send(200, "text/html", home_buf);
|
||||||
|
|||||||
@@ -116,6 +116,10 @@ MQTTUplink::MQTTUplink(mesh::RTCClock& rtc, mesh::LocalIdentity& identity)
|
|||||||
MQTT_LOG("uplink init");
|
MQTT_LOG("uplink init");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* MQTTUplink::getClientVersion() const {
|
||||||
|
return CLIENT_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
bool MQTTUplink::savePrefs() {
|
bool MQTTUplink::savePrefs() {
|
||||||
return MQTTPrefsStore::save(_fs, _prefs);
|
return MQTTPrefsStore::save(_fs, _prefs);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ public:
|
|||||||
bool isTxEnabled() const { return _prefs.tx_enabled != 0; }
|
bool isTxEnabled() const { return _prefs.tx_enabled != 0; }
|
||||||
bool setIata(const char* iata);
|
bool setIata(const char* iata);
|
||||||
const char* getIata() const { return _prefs.iata; }
|
const char* getIata() const { return _prefs.iata; }
|
||||||
|
const char* getClientVersion() const;
|
||||||
void setNodeNameSource(const char* node_name) { _node_name = node_name; }
|
void setNodeNameSource(const char* node_name) { _node_name = node_name; }
|
||||||
bool setOwnerPublicKey(const char* owner_public_key);
|
bool setOwnerPublicKey(const char* owner_public_key);
|
||||||
const char* getOwnerPublicKey() const { return _prefs.owner_public_key; }
|
const char* getOwnerPublicKey() const { return _prefs.owner_public_key; }
|
||||||
|
|||||||
@@ -576,6 +576,7 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
|||||||
<span class="label">MQTT</span>
|
<span class="label">MQTT</span>
|
||||||
<div class="quick">
|
<div class="quick">
|
||||||
<button data-cmd="get mqtt.status">mqtt.status</button>
|
<button data-cmd="get mqtt.status">mqtt.status</button>
|
||||||
|
<button data-cmd="get mqtt.client_version">mqtt.client_version</button>
|
||||||
<button data-cmd="get mqtt.iata">mqtt.iata</button>
|
<button data-cmd="get mqtt.iata">mqtt.iata</button>
|
||||||
<button data-cmd="get mqtt.owner">mqtt.owner</button>
|
<button data-cmd="get mqtt.owner">mqtt.owner</button>
|
||||||
<button data-cmd="get mqtt.email">mqtt.email</button>
|
<button data-cmd="get mqtt.email">mqtt.email</button>
|
||||||
@@ -599,18 +600,15 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
|||||||
<div class="stack">
|
<div class="stack">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="field-card">
|
<div class="field-card">
|
||||||
<label class="label" for="roleValue">Role</label>
|
<label class="label" for="versionValue">Version</label>
|
||||||
<div class="fieldline">
|
<div class="fieldline">
|
||||||
<input id="roleValue" readonly disabled>
|
<input id="versionValue" readonly disabled>
|
||||||
<span class="placeholder-slot" aria-hidden="true"></span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="field-card">
|
<div class="field-card">
|
||||||
<label class="label" for="clockUtc">Clock UTC</label>
|
<label class="label" for="clientVersionValue">Client Version</label>
|
||||||
<div class="inline-actions">
|
<div class="fieldline">
|
||||||
<input id="clockUtc" readonly disabled>
|
<input id="clientVersionValue" readonly disabled>
|
||||||
<button class="iconbtn" data-load-cmd="clock" data-load-input="clockUtc" title="Refresh clock UTC">↻</button>
|
|
||||||
<button id="syncClockBtn" class="savebtn">Sync</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -629,12 +627,22 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
|||||||
<div class="stack">
|
<div class="stack">
|
||||||
<div class="section-group">
|
<div class="section-group">
|
||||||
<h3>Repeater Settings</h3>
|
<h3>Repeater Settings</h3>
|
||||||
<div class="field-card">
|
<div class="row">
|
||||||
<label class="label" for="nodeName">Device Name</label>
|
<div class="field-card">
|
||||||
<div class="inline-actions">
|
<label class="label" for="nodeName">Device Name</label>
|
||||||
<input id="nodeName" placeholder="MeshCore-HOWL">
|
<div class="inline-actions">
|
||||||
<button class="iconbtn" data-load-cmd="get name" data-load-input="nodeName" title="Refresh device name">↻</button>
|
<input id="nodeName" placeholder="MeshCore-HOWL">
|
||||||
<button class="savebtn" data-prefix="set name " data-input="nodeName">Save</button>
|
<button class="iconbtn" data-load-cmd="get name" data-load-input="nodeName" title="Refresh device name">↻</button>
|
||||||
|
<button class="savebtn" data-prefix="set name " data-input="nodeName">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field-card">
|
||||||
|
<label class="label" for="clockUtc">Clock UTC</label>
|
||||||
|
<div class="inline-actions">
|
||||||
|
<input id="clockUtc" readonly disabled>
|
||||||
|
<button class="iconbtn" data-load-cmd="clock" data-load-input="clockUtc" title="Refresh clock UTC">↻</button>
|
||||||
|
<button id="syncClockBtn" class="savebtn">Sync</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@@ -2531,7 +2539,8 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
|||||||
const quiet = { recordHistory:false, updateInput:false };
|
const quiet = { recordHistory:false, updateInput:false };
|
||||||
try {
|
try {
|
||||||
await loadSection("Loading info...", [
|
await loadSection("Loading info...", [
|
||||||
() => loadField("get role", "roleValue", null, quiet),
|
() => loadField("ver", "versionValue", null, quiet),
|
||||||
|
() => loadField("get mqtt.client_version", "clientVersionValue", null, quiet),
|
||||||
() => loadField("clock", "clockUtc", null, quiet),
|
() => loadField("clock", "clockUtc", null, quiet),
|
||||||
() => loadField("get public.key", "publicKey", "uppercase", quiet)
|
() => loadField("get public.key", "publicKey", "uppercase", quiet)
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user