Added support for MeshCoreTel broker.
This commit is contained in:
@@ -14,7 +14,7 @@ These commands are available on `*_repeater_mqtt` firmware targets.
|
||||
- `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.
|
||||
- `set mqtt.iata <code>`: sets the IATA/location code, for example `MEL`.
|
||||
- `set mqtt.iata <code>`: sets the IATA/location code, for example `MOW`.
|
||||
- `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.
|
||||
|
||||
### MQTT Identity
|
||||
@@ -38,6 +38,9 @@ These commands are available on `*_repeater_mqtt` firmware targets.
|
||||
|
||||
### MQTT Endpoints
|
||||
|
||||
- `set mqtt.meshcoretel on|off`
|
||||
- `get mqtt.meshcoretel`
|
||||
- `set mqtt.letsmesh-us on|off`
|
||||
- `get mqtt.eastmesh-au`
|
||||
- `set mqtt.eastmesh-au on|off`
|
||||
- `get mqtt.letsmesh-eu`
|
||||
@@ -49,7 +52,7 @@ Notes:
|
||||
|
||||
- new repeater MQTT installs default `mqtt.iata` to `UNSET`
|
||||
- `letsmesh-eu` and `letsmesh-us` remain off by default unless already configured in saved prefs
|
||||
- if `mqtt.iata` is `UNSET`, `eastmesh-au`, `letsmesh-eu`, and `letsmesh-us` will not connect even if they are toggled on
|
||||
- if `mqtt.iata` is `UNSET`, `meshcoretel`, `eastmesh-au`, `letsmesh-eu`, and `letsmesh-us` will not connect even if they are toggled on
|
||||
|
||||
Legacy dotted aliases are also accepted:
|
||||
|
||||
|
||||
@@ -2308,6 +2308,8 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
|
||||
sprintf(reply, "> %s", mqtt.isEndpointEnabled(0x02) ? "on" : "off");
|
||||
} else if (memcmp(command, "get mqtt.letsmesh-us", 21) == 0 || memcmp(command, "get mqtt.letsmesh.us", 21) == 0) {
|
||||
sprintf(reply, "> %s", mqtt.isEndpointEnabled(0x04) ? "on" : "off");
|
||||
} else if (memcmp(command, "get mqtt.meshcoretel", 20) == 0) {
|
||||
sprintf(reply, "> %s", mqtt.isEndpointEnabled(0x08) ? "on" : "off");
|
||||
} else if (memcmp(command, "set mqtt.tx ", 12) == 0) {
|
||||
mqtt.setTxEnabled(memcmp(&command[12], "on", 2) == 0);
|
||||
strcpy(reply, "OK");
|
||||
@@ -2356,6 +2358,12 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
|
||||
} else {
|
||||
strcpy(reply, "Err - max 2 mqtt brokers");
|
||||
}
|
||||
} else if (memcmp(command, "set mqtt.meshcoretel ", 21) == 0) {
|
||||
if (mqtt.setEndpointEnabled(0x08, memcmp(&command[21], "on", 2) == 0)) {
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "Err - max 2 mqtt brokers");
|
||||
}
|
||||
#endif
|
||||
} else{
|
||||
_cli.handleCommand(sender_timestamp, command, reply); // common CLI commands
|
||||
|
||||
@@ -67,7 +67,7 @@ bool MQTTPrefsStore::load(FILESYSTEM* fs, MQTTPrefs& prefs) {
|
||||
StrHelper::strncpy(prefs.iata, MQTT_DEFAULT_IATA, sizeof(prefs.iata));
|
||||
}
|
||||
prefs.status_interval_ms = kFixedStatusIntervalMs;
|
||||
prefs.enabled_mask &= 0x07;
|
||||
prefs.enabled_mask &= 0x0F;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#include <time.h>
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.14.1"
|
||||
#define FIRMWARE_VERSION "v1.15.0 (VBart EastMesh)"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
@@ -95,12 +95,15 @@ void logMqttMemorySnapshot(const char*, const char* = nullptr) {
|
||||
|
||||
}
|
||||
|
||||
const MQTTUplink::BrokerSpec MQTTUplink::kBrokerSpecs[3] = {
|
||||
{"eastmesh-au", "eastmesh-au", "mqtt2.eastmesh.au", "wss://mqtt2.eastmesh.au:443/mqtt", kEastmeshBit},
|
||||
const MQTTUplink::BrokerSpec MQTTUplink::kBrokerSpecs[4] = {
|
||||
{"eastmesh-au", "eastmesh-au", "mqtt2.eastmesh.au", "wss://mqtt2.eastmesh.au:443/mqtt",
|
||||
kEastmeshBit, false, nullptr, nullptr},
|
||||
{"letsmesh-eu", "letsmesh-eu", "mqtt-eu-v1.letsmesh.net", "wss://mqtt-eu-v1.letsmesh.net:443/mqtt",
|
||||
kLetsmeshEuBit},
|
||||
kLetsmeshEuBit, false, nullptr, nullptr},
|
||||
{"letsmesh-us", "letsmesh-us", "mqtt-us-v1.letsmesh.net", "wss://mqtt-us-v1.letsmesh.net:443/mqtt",
|
||||
kLetsmeshUsBit},
|
||||
kLetsmeshUsBit, false, nullptr, nullptr},
|
||||
{"meshcoretel", "meshcoretel", "meshcoretel.ru", "mqtt://meshcoretel.ru:1883",
|
||||
kMeshcoretelBit, true, "meshcore", "meshcore"},
|
||||
};
|
||||
|
||||
MQTTUplink::MQTTUplink(mesh::RTCClock& rtc, mesh::LocalIdentity& identity)
|
||||
@@ -109,7 +112,7 @@ MQTTUplink::MQTTUplink(mesh::RTCClock& rtc, mesh::LocalIdentity& identity)
|
||||
{
|
||||
memset(_device_id, 0, sizeof(_device_id));
|
||||
MQTTPrefsStore::setDefaults(_prefs);
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
memset(&_brokers[i], 0, sizeof(_brokers[i]));
|
||||
_brokers[i].spec = &kBrokerSpecs[i];
|
||||
}
|
||||
@@ -125,7 +128,7 @@ bool MQTTUplink::savePrefs() {
|
||||
}
|
||||
|
||||
bool MQTTUplink::hasEnabledBroker() const {
|
||||
return (_prefs.enabled_mask & 0x07) != 0;
|
||||
return (_prefs.enabled_mask & 0x0F) != 0;
|
||||
}
|
||||
|
||||
bool MQTTUplink::isUnsetIataValue(const char* iata) {
|
||||
@@ -277,7 +280,11 @@ void MQTTUplink::refreshBrokerIdentity(BrokerState& broker) {
|
||||
if (broker.spec == nullptr) {
|
||||
return;
|
||||
}
|
||||
snprintf(broker.username, sizeof(broker.username), "v1_%s", _device_id);
|
||||
if (broker.spec->username != nullptr) {
|
||||
snprintf(broker.username, sizeof(broker.username), "%s", broker.spec->username);
|
||||
} else {
|
||||
snprintf(broker.username, sizeof(broker.username), "v1_%s", _device_id);
|
||||
}
|
||||
snprintf(broker.client_id, sizeof(broker.client_id), "mqtt_%s-%.6s", broker.spec->key, _device_id);
|
||||
formatTopic(broker.status_topic, sizeof(broker.status_topic), "status");
|
||||
}
|
||||
@@ -621,9 +628,11 @@ void MQTTUplink::ensureBroker(BrokerState& broker, bool allow_new_connect) {
|
||||
return;
|
||||
}
|
||||
|
||||
time_t now = time(nullptr);
|
||||
if (broker.client != nullptr && broker.token_expires_at > 0 && now + kTokenRefreshSlackSecs >= broker.token_expires_at) {
|
||||
destroyBroker(broker, false);
|
||||
if (!broker.spec->plain_tcp) {
|
||||
time_t now = time(nullptr);
|
||||
if (broker.client != nullptr && broker.token_expires_at > 0 && now + kTokenRefreshSlackSecs >= broker.token_expires_at) {
|
||||
destroyBroker(broker, false);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long now_ms = millis();
|
||||
@@ -646,26 +655,32 @@ void MQTTUplink::ensureBroker(BrokerState& broker, bool allow_new_connect) {
|
||||
broker.last_connect_attempt = now_ms;
|
||||
broker.reconnect_pending = false;
|
||||
|
||||
if (!refreshToken(broker)) {
|
||||
if (!broker.spec->plain_tcp && !refreshToken(broker)) {
|
||||
broker.reconnect_pending = true;
|
||||
broker.next_connect_attempt = now_ms + kBrokerRetryBaseMillis;
|
||||
return;
|
||||
}
|
||||
|
||||
refreshBrokerState(broker);
|
||||
MQTT_LOG("%s mqtt init host=%s port=%d path=%s client_id=%s", broker.spec->label, broker.spec->host, 443, "/mqtt",
|
||||
broker.client_id);
|
||||
MQTT_LOG("%s mqtt init host=%s uri=%s plain_tcp=%d client_id=%s",
|
||||
broker.spec->label, broker.spec->host, broker.spec->uri,
|
||||
broker.spec->plain_tcp ? 1 : 0, broker.client_id);
|
||||
logMqttMemorySnapshot("init-pre", broker.spec->label);
|
||||
esp_mqtt_client_config_t cfg = {};
|
||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||
cfg.broker.address.hostname = broker.spec->host;
|
||||
cfg.broker.address.port = 443;
|
||||
cfg.broker.address.transport = MQTT_TRANSPORT_OVER_WSS;
|
||||
cfg.broker.address.path = "/mqtt";
|
||||
cfg.broker.verification.certificate = mqtt_ca_certs::kCombinedPem;
|
||||
if (broker.spec->plain_tcp) {
|
||||
cfg.broker.address.uri = broker.spec->uri;
|
||||
cfg.credentials.authentication.password = broker.spec->password;
|
||||
} else {
|
||||
cfg.broker.address.hostname = broker.spec->host;
|
||||
cfg.broker.address.port = 443;
|
||||
cfg.broker.address.transport = MQTT_TRANSPORT_OVER_WSS;
|
||||
cfg.broker.address.path = "/mqtt";
|
||||
cfg.broker.verification.certificate = mqtt_ca_certs::kCombinedPem;
|
||||
cfg.credentials.authentication.password = broker.token;
|
||||
}
|
||||
cfg.credentials.username = broker.username;
|
||||
cfg.credentials.client_id = broker.client_id;
|
||||
cfg.credentials.authentication.password = broker.token;
|
||||
cfg.session.keepalive = 30;
|
||||
cfg.session.last_will.topic = broker.status_topic;
|
||||
cfg.session.last_will.msg = broker.offline_payload;
|
||||
@@ -677,10 +692,18 @@ void MQTTUplink::ensureBroker(BrokerState& broker, bool allow_new_connect) {
|
||||
cfg.buffer.size = 768;
|
||||
cfg.buffer.out_size = 1280;
|
||||
#else
|
||||
cfg.host = broker.spec->host;
|
||||
cfg.port = 443;
|
||||
if (broker.spec->plain_tcp) {
|
||||
cfg.uri = broker.spec->uri;
|
||||
cfg.password = broker.spec->password;
|
||||
} else {
|
||||
cfg.host = broker.spec->host;
|
||||
cfg.port = 443;
|
||||
cfg.transport = MQTT_TRANSPORT_OVER_WSS;
|
||||
cfg.path = "/mqtt";
|
||||
cfg.cert_pem = mqtt_ca_certs::kCombinedPem;
|
||||
cfg.password = broker.token;
|
||||
}
|
||||
cfg.username = broker.username;
|
||||
cfg.password = broker.token;
|
||||
cfg.client_id = broker.client_id;
|
||||
cfg.keepalive = 30;
|
||||
cfg.buffer_size = 768;
|
||||
@@ -688,13 +711,10 @@ void MQTTUplink::ensureBroker(BrokerState& broker, bool allow_new_connect) {
|
||||
cfg.reconnect_timeout_ms = 10000;
|
||||
cfg.network_timeout_ms = 10000;
|
||||
cfg.disable_auto_reconnect = true;
|
||||
cfg.transport = MQTT_TRANSPORT_OVER_WSS;
|
||||
cfg.cert_pem = mqtt_ca_certs::kCombinedPem;
|
||||
cfg.lwt_topic = broker.status_topic;
|
||||
cfg.lwt_msg = broker.offline_payload;
|
||||
cfg.lwt_qos = 1;
|
||||
cfg.lwt_retain = 1;
|
||||
cfg.path = "/mqtt";
|
||||
#endif
|
||||
|
||||
broker.client = esp_mqtt_client_init(&cfg);
|
||||
@@ -721,7 +741,7 @@ void MQTTUplink::ensureBroker(BrokerState& broker, bool allow_new_connect) {
|
||||
void MQTTUplink::begin(FILESYSTEM* fs) {
|
||||
_fs = fs;
|
||||
MQTTPrefsStore::load(_fs, _prefs);
|
||||
uint8_t normalized_mask = normalizeEnabledMask(_prefs.enabled_mask & 0x07);
|
||||
uint8_t normalized_mask = normalizeEnabledMask(_prefs.enabled_mask & 0x0F);
|
||||
if (normalized_mask != _prefs.enabled_mask) {
|
||||
_prefs.enabled_mask = normalized_mask;
|
||||
savePrefs();
|
||||
@@ -865,16 +885,16 @@ void MQTTUplink::formatStatusReply(char* reply, size_t reply_size) const {
|
||||
return "retry";
|
||||
};
|
||||
|
||||
snprintf(reply, reply_size, "> wifi:%s ntp:%s iata:%s eastmesh-au:%s letsmesh-eu:%s letsmesh-us:%s status:%s tx:%s",
|
||||
snprintf(reply, reply_size, "> wifi:%s ntp:%s iata:%s meshcoretel:%s eastmesh-au:%s letsmesh-eu:%s letsmesh-us:%s status:%s tx:%s",
|
||||
(_network != nullptr && _network->isWifiConnected()) ? "up" : "down",
|
||||
(_network != nullptr && _network->hasTimeSync()) ? "up" : "wait",
|
||||
_prefs.iata,
|
||||
broker_state(kEastmeshBit), broker_state(kLetsmeshEuBit), broker_state(kLetsmeshUsBit),
|
||||
broker_state(kMeshcoretelBit), broker_state(kEastmeshBit), broker_state(kLetsmeshEuBit), broker_state(kLetsmeshUsBit),
|
||||
_prefs.status_enabled ? "on" : "off", _prefs.tx_enabled ? "on" : "off");
|
||||
}
|
||||
|
||||
bool MQTTUplink::setEndpointEnabled(uint8_t bit, bool enabled) {
|
||||
uint8_t next_mask = _prefs.enabled_mask & 0x07;
|
||||
uint8_t next_mask = _prefs.enabled_mask & 0x0F;
|
||||
if (enabled) {
|
||||
next_mask = normalizeEnabledMask(next_mask | bit);
|
||||
if ((next_mask & bit) == 0) {
|
||||
|
||||
@@ -74,6 +74,9 @@ private:
|
||||
const char* host;
|
||||
const char* uri;
|
||||
uint8_t bit;
|
||||
bool plain_tcp;
|
||||
const char* username;
|
||||
const char* password;
|
||||
};
|
||||
|
||||
struct BrokerState {
|
||||
@@ -110,11 +113,12 @@ private:
|
||||
static constexpr uint8_t kEastmeshBit = 0x01;
|
||||
static constexpr uint8_t kLetsmeshEuBit = 0x02;
|
||||
static constexpr uint8_t kLetsmeshUsBit = 0x04;
|
||||
static constexpr uint8_t kMeshcoretelBit = 0x08;
|
||||
static constexpr uint8_t kMaxEnabledBrokers = 2;
|
||||
static const BrokerSpec kBrokerSpecs[3];
|
||||
static const BrokerSpec kBrokerSpecs[4];
|
||||
static bool isUnsetIataValue(const char* iata);
|
||||
|
||||
BrokerState _brokers[3];
|
||||
BrokerState _brokers[4];
|
||||
|
||||
static void handleMqttEvent(void* handler_args, esp_event_base_t base, int32_t event_id, void* event_data);
|
||||
bool hasEnabledBroker() const;
|
||||
|
||||
@@ -828,6 +828,35 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
<optgroup label="Configuration">
|
||||
<option value="UNSET">UNSET - To be configured</option>
|
||||
</optgroup>
|
||||
<optgroup label="Russia">
|
||||
<option value="BSK">BSK - Biysk</option>
|
||||
<option value="CEE">CEE - Cherepovets</option>
|
||||
<option value="CEK">CEK - Chelyabinsk</option>
|
||||
<option value="GOJ">GOJ - Nizhny Novgorod</option>
|
||||
<option value="IKT">IKT - Irkutsk</option>
|
||||
<option value="IWA">IWA - Ivanovo</option>
|
||||
<option value="KHV">KHV - Khabarovsk</option>
|
||||
<option value="KLD">KLD - Tver</option>
|
||||
<option value="KLF">KLF - Kaluga</option>
|
||||
<option value="KVX">KVX - Kirov</option>
|
||||
<option value="KZN">KZN - Kazan</option>
|
||||
<option value="LPK">LPK - Lipetsk</option>
|
||||
<option value="MOW">MOW - Moscow</option>
|
||||
<option value="OVB">OVB - Novosibirsk</option>
|
||||
<option value="ROV">ROV - Rostov-on-Don</option>
|
||||
<option value="RZN">RZN - Ryazan</option>
|
||||
<option value="SVX">SVX - Yekaterinburg</option>
|
||||
<option value="TBW">TBW - Tambov</option>
|
||||
<option value="TYA">TYA - Tula</option>
|
||||
<option value="UFA">UFA - Ufa</option>
|
||||
<option value="VOG">VOG - Volgograd</option>
|
||||
<option value="VOZ">VOZ - Voronezh</option>
|
||||
<option value="VSG">VSG - Lugansk</option>
|
||||
<option value="VVO">VVO - Vladivostok</option>
|
||||
</optgroup>
|
||||
<optgroup label="France">
|
||||
<option value="CHR">CHR - Chateauroux</option>
|
||||
</optgroup>
|
||||
<optgroup label="ACT">
|
||||
<option value="CBR">CBR - Canberra</option>
|
||||
</optgroup>
|
||||
@@ -913,7 +942,30 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
</div>
|
||||
<div class="field-card">
|
||||
<label class="label">MQTT Servers</label>
|
||||
<div class="broker-stack">
|
||||
<div class="broker-stack" style="grid-template-columns:minmax(0,1fr) minmax(0,1fr) minmax(0,1fr)">
|
||||
<div class="broker-group">
|
||||
<div class="broker-group-title">Meshcoretel</div>
|
||||
<div class="broker-grid single">
|
||||
<div class="broker-card">
|
||||
<div class="broker-mode">
|
||||
<div class="broker-row">
|
||||
<div class="broker-copy">
|
||||
<div class="broker-title">Meshcoretel RU</div>
|
||||
<div class="broker-state" id="mqttMeshcoretelState">Off</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mode-slider">
|
||||
<input id="mqttMeshcoretelMode" type="range" min="0" max="1" step="1" value="0" aria-label="Meshcoretel mode">
|
||||
<div class="mode-labels two" aria-hidden="true">
|
||||
<div class="mode-label" data-meshcoretel-label="off">Off</div>
|
||||
<div class="mode-label" data-meshcoretel-label="on">On</div>
|
||||
</div>
|
||||
</div>
|
||||
<input id="mqttMeshcoretel" class="visually-hidden" type="checkbox" tabindex="-1" aria-hidden="true">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="broker-group">
|
||||
<div class="broker-group-title">EastMesh</div>
|
||||
<div class="broker-grid single">
|
||||
@@ -964,7 +1016,7 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-note">If EastMesh is enabled, use only one LetsMesh broker. Enable both LetsMesh brokers only when EastMesh is off.</div>
|
||||
<div class="panel-note">Only two brokers can be enabled at the same time.</div>
|
||||
<div id="mqttBrokerWarning" class="panel-warning"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2074,7 +2126,7 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
}
|
||||
if (inlineWarning) {
|
||||
inlineWarning.textContent = showWarning
|
||||
? "MQTT IATA is unset. Set it before enabling EastMesh or LetsMesh brokers."
|
||||
? "MQTT IATA is unset. Set it before enabling any MQTT brokers."
|
||||
: "";
|
||||
}
|
||||
}
|
||||
@@ -2223,13 +2275,25 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
function clampLetsmeshModeIndex(index) {
|
||||
const eastmesh = document.getElementById("mqttEastmeshAu");
|
||||
const eastmeshEnabled = !!(eastmesh && eastmesh.checked);
|
||||
const meshcoretel = document.getElementById("mqttMeshcoretel");
|
||||
const meshcoretelEnabled = !!(meshcoretel && meshcoretel.checked);
|
||||
const bounded = Math.max(0, Math.min(3, index));
|
||||
return eastmeshEnabled && bounded === 3 ? 1 : bounded;
|
||||
if ((eastmeshEnabled || meshcoretelEnabled) && bounded === 3) {
|
||||
return 1;
|
||||
}
|
||||
if (eastmeshEnabled && meshcoretelEnabled && bounded > 0) {
|
||||
return 0;
|
||||
}
|
||||
return bounded;
|
||||
}
|
||||
function refreshLetsmeshModeUi() {
|
||||
const mode = getLetsmeshMode();
|
||||
const eastmesh = document.getElementById("mqttEastmeshAu");
|
||||
const eastmeshEnabled = !!(eastmesh && eastmesh.checked);
|
||||
const meshcoretel = document.getElementById("mqttMeshcoretel");
|
||||
const meshcoretelEnabled = !!(meshcoretel && meshcoretel.checked);
|
||||
const bothBlocked = eastmeshEnabled || meshcoretelEnabled;
|
||||
const allBlocked = eastmeshEnabled && meshcoretelEnabled;
|
||||
const state = document.getElementById("mqttLetsmeshModeState");
|
||||
const labels = { off:"Off", eu:"EU", us:"US", both:"Both" };
|
||||
if (state) {
|
||||
@@ -2240,11 +2304,15 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
if (slider) {
|
||||
slider.max = "3";
|
||||
slider.value = String(clampLetsmeshModeIndex(getLetsmeshModeIndex(mode)));
|
||||
slider.disabled = allBlocked;
|
||||
}
|
||||
document.querySelectorAll("[data-letsmesh-label]").forEach((label) => {
|
||||
const labelMode = label.dataset.letsmeshLabel;
|
||||
label.classList.toggle("active", labelMode === mode);
|
||||
label.classList.toggle("disabled", eastmeshEnabled && labelMode === "both");
|
||||
label.classList.toggle("disabled",
|
||||
(bothBlocked && labelMode === "both") ||
|
||||
(allBlocked && labelMode !== "off")
|
||||
);
|
||||
});
|
||||
}
|
||||
function setBrokerToggle(inputId, state) {
|
||||
@@ -2262,6 +2330,8 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
} else if (inputId === "mqttEastmeshAu") {
|
||||
refreshEastmeshModeUi();
|
||||
refreshLetsmeshModeUi();
|
||||
} else if (inputId === "mqttMeshcoretel") {
|
||||
refreshMeshcoretelModeUi();
|
||||
}
|
||||
}
|
||||
async function loadBrokerState(cmd, inputId, options = {}) {
|
||||
@@ -2380,8 +2450,14 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
mqttIataSelect.addEventListener("change", refreshMqttIataWarning);
|
||||
}
|
||||
async function setEastmeshMode(enabled) {
|
||||
if (enabled && getLetsmeshMode() === "both") {
|
||||
await setLetsmeshMode("eu");
|
||||
if (enabled) {
|
||||
if (getLetsmeshMode() === "both") {
|
||||
await setLetsmeshMode("eu");
|
||||
}
|
||||
const meshcoretel = document.getElementById("mqttMeshcoretel");
|
||||
if (meshcoretel && meshcoretel.checked && getLetsmeshMode() !== "off") {
|
||||
await setLetsmeshMode("off");
|
||||
}
|
||||
}
|
||||
const result = await runCommand(enabled ? "set mqtt.eastmesh-au on" : "set mqtt.eastmesh-au off");
|
||||
if (!result.ok) {
|
||||
@@ -2392,6 +2468,7 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
setBrokerToggle("mqttEastmeshAu", enabled ? "on" : "off");
|
||||
refreshEastmeshModeUi();
|
||||
refreshLetsmeshModeUi();
|
||||
refreshMeshcoretelModeUi();
|
||||
}
|
||||
const eastmeshModeSlider = document.getElementById("mqttEastmeshMode");
|
||||
if (eastmeshModeSlider) {
|
||||
@@ -2404,9 +2481,22 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
}
|
||||
async function setLetsmeshMode(mode) {
|
||||
const eastmesh = document.getElementById("mqttEastmeshAu");
|
||||
if (mode === "both" && eastmesh && eastmesh.checked) {
|
||||
refreshLetsmeshModeUi();
|
||||
return;
|
||||
const eastmeshEnabled = !!(eastmesh && eastmesh.checked);
|
||||
const meshcoretel = document.getElementById("mqttMeshcoretel");
|
||||
const meshcoretelEnabled = !!(meshcoretel && meshcoretel.checked);
|
||||
if (mode === "both") {
|
||||
if (eastmeshEnabled) {
|
||||
refreshLetsmeshModeUi();
|
||||
return;
|
||||
}
|
||||
if (meshcoretelEnabled) {
|
||||
refreshLetsmeshModeUi();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mode !== "off" && meshcoretelEnabled && eastmeshEnabled) {
|
||||
refreshLetsmeshModeUi();
|
||||
return;
|
||||
}
|
||||
const desired = {
|
||||
eu: mode === "eu" || mode === "both",
|
||||
@@ -2440,6 +2530,49 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
setLetsmeshMode(modes[index] || "off");
|
||||
});
|
||||
}
|
||||
function refreshMeshcoretelModeUi() {
|
||||
const input = document.getElementById("mqttMeshcoretel");
|
||||
const enabled = !!(input && input.checked);
|
||||
const slider = document.getElementById("mqttMeshcoretelMode");
|
||||
if (slider) slider.value = enabled ? "1" : "0";
|
||||
const state = document.getElementById("mqttMeshcoretelState");
|
||||
if (state) {
|
||||
state.textContent = enabled ? "On" : "Off";
|
||||
state.classList.toggle("on", enabled);
|
||||
}
|
||||
document.querySelectorAll("[data-meshcoretel-label]").forEach((label) => {
|
||||
label.classList.toggle("active", label.dataset.meshcoretelLabel === (enabled ? "on" : "off"));
|
||||
});
|
||||
}
|
||||
async function setMeshcoretelMode(enabled) {
|
||||
if (enabled) {
|
||||
if (getLetsmeshMode() === "both") {
|
||||
await setLetsmeshMode("eu");
|
||||
}
|
||||
const eastmesh = document.getElementById("mqttEastmeshAu");
|
||||
if (eastmesh && eastmesh.checked && getLetsmeshMode() !== "off") {
|
||||
await setLetsmeshMode("off");
|
||||
}
|
||||
}
|
||||
const result = await runCommand(enabled ? "set mqtt.meshcoretel on" : "set mqtt.meshcoretel off");
|
||||
if (!result.ok) {
|
||||
refreshMeshcoretelModeUi();
|
||||
return;
|
||||
}
|
||||
setBrokerToggle("mqttMeshcoretel", enabled ? "on" : "off");
|
||||
refreshMeshcoretelModeUi();
|
||||
refreshLetsmeshModeUi();
|
||||
refreshEastmeshModeUi();
|
||||
}
|
||||
const meshcoretelModeSlider = document.getElementById("mqttMeshcoretelMode");
|
||||
if (meshcoretelModeSlider) {
|
||||
meshcoretelModeSlider.addEventListener("input", () => {
|
||||
meshcoretelModeSlider.value = (Number.parseInt(meshcoretelModeSlider.value, 10) || 0) >= 1 ? "1" : "0";
|
||||
});
|
||||
meshcoretelModeSlider.addEventListener("change", () => {
|
||||
setMeshcoretelMode((Number.parseInt(meshcoretelModeSlider.value, 10) || 0) >= 1);
|
||||
});
|
||||
}
|
||||
document.getElementById("saveOwnerInfo").onclick = () => {
|
||||
const value = document.getElementById("ownerInfo").value.replace(/\n/g, "|");
|
||||
runCommand("set owner.info " + value);
|
||||
@@ -2568,7 +2701,8 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
() => loadField("get mqtt.email", "mqttEmail", null, quiet),
|
||||
() => loadBrokerState("get mqtt.eastmesh-au", "mqttEastmeshAu", quiet),
|
||||
() => loadBrokerState("get mqtt.letsmesh-eu", "mqttLetsmeshEu", quiet),
|
||||
() => loadBrokerState("get mqtt.letsmesh-us", "mqttLetsmeshUs", quiet)
|
||||
() => loadBrokerState("get mqtt.letsmesh-us", "mqttLetsmeshUs", quiet),
|
||||
() => loadBrokerState("get mqtt.meshcoretel", "mqttMeshcoretel", quiet)
|
||||
]);
|
||||
statusEl.textContent = "Ready";
|
||||
} catch (error) {
|
||||
@@ -2581,6 +2715,7 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
}
|
||||
refreshEastmeshModeUi();
|
||||
refreshLetsmeshModeUi();
|
||||
refreshMeshcoretelModeUi();
|
||||
initApp();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user