Removed EastMesh MQTT broker support.
It's intended for Eastern Australia repeaters only. Using original EastMesh firmware is prefered in this case.
Tento commit je obsažen v:
+9
-81
@@ -1,64 +1,25 @@
|
||||
# MeshCore-EastMesh-MeshCoreTel
|
||||
# MeshCoreTel Firmware
|
||||
|
||||
This is a fork of MeshCore-EastMesh firmware with MeshCoreTel broker support.
|
||||
|
||||
Original firmware (credits to Jared Dohrman, Scott Powell / Ripple Radios and the MeshCore contributors):
|
||||
|
||||
- <https://github.com/xJARiD/MeshCore-EastMesh>
|
||||
This is a fork of MeshCore-EastMesh firmware with MeshCoreTel broker support instead of EastMesh.
|
||||
|
||||
MeshCoreTel:
|
||||
|
||||
- <https://meshcoretel.ru/>
|
||||
|
||||
EastMesh firmware builds for MeshCore, with MQTT repeaters, WiFi companions, and simple release downloads for supported boards.
|
||||
Original firmware (credits to Jared Dohrman, Scott Powell / Ripple Radios and the MeshCore contributors):
|
||||
|
||||
This repository keeps the upstream MeshCore firmware intact and adds an EastMesh layer on top for packaging, release automation, WiFi-enabled companion builds, and MQTT-enabled repeater builds.
|
||||
- <https://github.com/xJARiD/MeshCore-EastMesh>
|
||||
|
||||
Additinally, the MeshCoreTel broker added in this fork.
|
||||
MeshCoreTel firmware builds for MeshCore, with MQTT repeaters, WiFi companions, and simple release downloads for supported boards.
|
||||
|
||||
## What This Repo Adds
|
||||
This repository keeps the upstream MeshCore firmware intact and adds an MeshCoreTel layer on top for packaging, release automation, WiFi-enabled companion builds, and MQTT-enabled repeater builds.
|
||||
|
||||
- `*_repeater_mqtt` firmware targets with:
|
||||
- native WiFi
|
||||
- MQTT over WSS with JWT auth or over plain TCP (for MeshCoreTel support)
|
||||
- optional local HTTPS config panel on supported ESP32 targets
|
||||
- `*_companion_radio_wifi` firmware targets for WiFi-connected companion devices
|
||||
- EastMesh-specific release workflows and versioning on top of upstream MeshCore releases
|
||||
- docs and release guidance for EastMesh users instead of the full upstream MeshCore docs set
|
||||
|
||||
## Status
|
||||
|
||||
I've tested it only with my Heltec V4.
|
||||
|
||||
## Key Files
|
||||
|
||||
- [`build.sh`](./build.sh)
|
||||
- main local build wrapper
|
||||
- injects `FIRMWARE_VERSION`, `CLIENT_VERSION`, and EastMesh release metadata
|
||||
- [`pyproject.toml`](./pyproject.toml)
|
||||
- Python tooling and docs dependencies
|
||||
- [`platformio.ini`](./platformio.ini)
|
||||
- root PlatformIO config and ESP32 helper scripts
|
||||
- [`variants/eastmesh_mqtt/platformio.ini`](./variants/eastmesh_mqtt/platformio.ini)
|
||||
- shared EastMesh MQTT repeater env definitions
|
||||
- [`examples/simple_repeater/MyMesh.cpp`](./examples/simple_repeater/MyMesh.cpp)
|
||||
- repeater CLI wiring, MQTT command surface, and web allowlist integration
|
||||
- [`src/helpers/mqtt/MQTTUplink.cpp`](./src/helpers/mqtt/MQTTUplink.cpp)
|
||||
- MQTT uplink implementation, HTTPS web panel, WSS/JWT handling, and repeater WiFi control
|
||||
- [`examples/companion_radio`](./examples/companion_radio)
|
||||
- companion firmware implementation
|
||||
- [`RELEASE.md`](./RELEASE.md)
|
||||
- tag formats and release workflow behavior
|
||||
- [`docs/`](./docs)
|
||||
- EastMesh-focused docs published to GitHub Pages
|
||||
|
||||
## Key EastMesh Features
|
||||
## Key MeshCoreTel Features
|
||||
|
||||
### MQTT Repeater Additions
|
||||
|
||||
- hardcoded broker support for:
|
||||
- `meshcoretel`
|
||||
- `eastmesh-au`
|
||||
- `letsmesh-eu`
|
||||
- `letsmesh-us`
|
||||
- WSS transport at `/mqtt`
|
||||
@@ -105,46 +66,13 @@ These rescue commands are only available after entering `CLI Rescue`:
|
||||
- `set wifi.pwd <password>`
|
||||
- `set wifi.powersaving none|min|max`
|
||||
|
||||
## Active GitHub Workflows
|
||||
## Status
|
||||
|
||||
- `.github/workflows/build-companion-wifi-firmwares.yml`
|
||||
- `.github/workflows/build-repeater-mqtt-firmwares.yml`
|
||||
- `.github/workflows/pr-build-check.yml`
|
||||
- `.github/workflows/push-build-check.yml`
|
||||
- `.github/workflows/github-pages.yml`
|
||||
|
||||
The current release workflows intentionally focus only on:
|
||||
|
||||
- `companion-wifi`
|
||||
- `repeater-mqtt`
|
||||
|
||||
## Release Tags
|
||||
|
||||
Current release tags are:
|
||||
|
||||
```bash
|
||||
git tag companion-wifi-v1.14.1
|
||||
git tag repeater-mqtt-eastmesh-v1.0.1
|
||||
```
|
||||
|
||||
Companion WiFi uses the upstream MeshCore version in the tag.
|
||||
|
||||
Repeater MQTT uses:
|
||||
|
||||
- `OFFICIAL_MESHCORE_VERSION` from GitHub Actions variables as `FIRMWARE_VERSION`
|
||||
- the EastMesh tag version as `EASTMESH_VERSION`
|
||||
|
||||
See [RELEASE.md](./RELEASE.md) for the full release flow.
|
||||
Tested with my Heltec V4 OLED.
|
||||
|
||||
## Documentation
|
||||
|
||||
Published docs site:
|
||||
|
||||
- <https://xjarid.github.io/MeshCore-EastMesh/>
|
||||
|
||||
Current docs pages:
|
||||
|
||||
- [Home](./docs/index.md)
|
||||
- [Download and Flash Releases](./docs/releases.md)
|
||||
- [Build Locally With uv](./docs/local-builds.md)
|
||||
- [Custom CLI Commands](./docs/custom-cli.md)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Custom CLI Commands
|
||||
|
||||
This page covers the EastMesh-specific CLI commands added in this repository.
|
||||
This page covers the MeshCoreTel-specific CLI commands added in this repository.
|
||||
|
||||
It does not try to repeat the full upstream MeshCore CLI surface.
|
||||
|
||||
@@ -38,11 +38,8 @@ 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`
|
||||
- `set mqtt.meshcoretel on|off`
|
||||
- `get mqtt.letsmesh-eu`
|
||||
- `set mqtt.letsmesh-eu on|off`
|
||||
- `get mqtt.letsmesh-us`
|
||||
@@ -52,11 +49,10 @@ 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`, `meshcoretel`, `eastmesh-au`, `letsmesh-eu`, and `letsmesh-us` will not connect even if they are toggled on
|
||||
- if `mqtt.iata` is `UNSET`, `meshcoretel`, `letsmesh-eu`, and `letsmesh-us` will not connect even if they are toggled on
|
||||
|
||||
Legacy dotted aliases are also accepted:
|
||||
|
||||
- `mqtt.eastmesh.au`
|
||||
- `mqtt.letsmesh.eu`
|
||||
- `mqtt.letsmesh.us`
|
||||
|
||||
|
||||
@@ -2302,14 +2302,12 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
|
||||
sprintf(reply, "> %s", mqtt.isRawEnabled() ? "on" : "off");
|
||||
} else if (memcmp(command, "get mqtt.tx", 11) == 0) {
|
||||
sprintf(reply, "> %s", mqtt.isTxEnabled() ? "on" : "off");
|
||||
} else if (memcmp(command, "get mqtt.eastmesh-au", 20) == 0 || memcmp(command, "get mqtt.eastmesh.au", 20) == 0) {
|
||||
} else if (memcmp(command, "get mqtt.meshcoretel", 21) == 0) {
|
||||
sprintf(reply, "> %s", mqtt.isEndpointEnabled(0x01) ? "on" : "off");
|
||||
} else if (memcmp(command, "get mqtt.letsmesh-eu", 21) == 0 || memcmp(command, "get mqtt.letsmesh.eu", 21) == 0) {
|
||||
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");
|
||||
@@ -2340,7 +2338,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, char *command, char *reply
|
||||
} else if (memcmp(command, "set mqtt.status ", 16) == 0) {
|
||||
mqtt.setStatusEnabled(memcmp(&command[16], "on", 2) == 0);
|
||||
strcpy(reply, "OK");
|
||||
} else if (memcmp(command, "set mqtt.eastmesh-au ", 21) == 0 || memcmp(command, "set mqtt.eastmesh.au ", 21) == 0) {
|
||||
} else if (memcmp(command, "set mqtt.meshcoretel ", 21) == 0) {
|
||||
if (mqtt.setEndpointEnabled(0x01, memcmp(&command[21], "on", 2) == 0)) {
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
@@ -2358,12 +2356,6 @@ 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
|
||||
|
||||
@@ -2,41 +2,6 @@
|
||||
|
||||
namespace mqtt_ca_certs {
|
||||
|
||||
// EastMesh currently presents a Let's Encrypt E7-issued certificate.
|
||||
static const char kEastmeshIsrgRootX1Pem[] =
|
||||
R"PEM(-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
)PEM";
|
||||
|
||||
// LetsMesh currently presents a certificate issued by Google Trust Services WE1.
|
||||
static const char kLetsmeshWe1Pem[] =
|
||||
R"PEM(-----BEGIN CERTIFICATE-----
|
||||
@@ -58,55 +23,4 @@ sQIwJonMaAFi54mrfhfoFNZEfuNMSQ6/bIBiNLiyoX46FohQvKeIoJ99cx7sUkFN
|
||||
-----END CERTIFICATE-----
|
||||
)PEM";
|
||||
|
||||
static const char kCombinedPem[] =
|
||||
R"PEM(-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICnzCCAiWgAwIBAgIQf/MZd5csIkp2FV0TttaF4zAKBggqhkjOPQQDAzBHMQsw
|
||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
||||
MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMjMxMjEzMDkwMDAwWhcNMjkwMjIwMTQw
|
||||
MDAwWjA7MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVR29vZ2xlIFRydXN0IFNlcnZp
|
||||
Y2VzMQwwCgYDVQQDEwNXRTEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARvzTr+
|
||||
Z1dHTCEDhUDCR127WEcPQMFcF4XGGTfn1XzthkubgdnXGhOlCgP4mMTG6J7/EFmP
|
||||
LCaY9eYmJbsPAvpWo4H+MIH7MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggr
|
||||
BgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU
|
||||
kHeSNWfE/6jMqeZ72YB5e8yT+TgwHwYDVR0jBBgwFoAUgEzW63T/STaj1dj8tT7F
|
||||
avCUHYwwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzAChhhodHRwOi8vaS5wa2ku
|
||||
Z29vZy9yNC5jcnQwKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL2MucGtpLmdvb2cv
|
||||
ci9yNC5jcmwwEwYDVR0gBAwwCjAIBgZngQwBAgEwCgYIKoZIzj0EAwMDaAAwZQIx
|
||||
AOcCq1HW90OVznX+0RGU1cxAQXomvtgM8zItPZCuFQ8jSBJSjz5keROv9aYsAm5V
|
||||
sQIwJonMaAFi54mrfhfoFNZEfuNMSQ6/bIBiNLiyoX46FohQvKeIoJ99cx7sUkFN
|
||||
7uJW
|
||||
-----END CERTIFICATE-----
|
||||
)PEM";
|
||||
|
||||
} // namespace mqtt_ca_certs
|
||||
|
||||
@@ -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 &= 0x0F;
|
||||
prefs.enabled_mask &= 0x07;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,15 +17,15 @@
|
||||
#include <time.h>
|
||||
|
||||
#ifndef FIRMWARE_VERSION
|
||||
#define FIRMWARE_VERSION "v1.15.0 (VBart EastMesh)"
|
||||
#define FIRMWARE_VERSION "v1.15.0 (VBart MeshCoreTel)"
|
||||
#endif
|
||||
|
||||
#ifndef FIRMWARE_BUILD_DATE
|
||||
#define FIRMWARE_BUILD_DATE "20 Mar 2026"
|
||||
#define FIRMWARE_BUILD_DATE "27 Apr 2026"
|
||||
#endif
|
||||
|
||||
#ifndef CLIENT_VERSION
|
||||
#define CLIENT_VERSION "eastmesh-repeater-mqtt"
|
||||
#define CLIENT_VERSION "meshcoretel-repeater-mqtt"
|
||||
#endif
|
||||
|
||||
#ifndef MQTT_DEBUG
|
||||
@@ -95,15 +95,13 @@ void logMqttMemorySnapshot(const char*, const char* = nullptr) {
|
||||
|
||||
}
|
||||
|
||||
const MQTTUplink::BrokerSpec MQTTUplink::kBrokerSpecs[4] = {
|
||||
{"eastmesh-au", "eastmesh-au", "mqtt2.eastmesh.au", "wss://mqtt2.eastmesh.au:443/mqtt",
|
||||
kEastmeshBit, false, nullptr, nullptr},
|
||||
const MQTTUplink::BrokerSpec MQTTUplink::kBrokerSpecs[3] = {
|
||||
{"meshcoretel", "meshcoretel", "meshcoretel.ru", "mqtt://meshcoretel.ru:1883",
|
||||
kMeshcoretelBit, true, "meshcore", "meshcore"},
|
||||
{"letsmesh-eu", "letsmesh-eu", "mqtt-eu-v1.letsmesh.net", "wss://mqtt-eu-v1.letsmesh.net:443/mqtt",
|
||||
kLetsmeshEuBit, false, nullptr, nullptr},
|
||||
{"letsmesh-us", "letsmesh-us", "mqtt-us-v1.letsmesh.net", "wss://mqtt-us-v1.letsmesh.net:443/mqtt",
|
||||
kLetsmeshUsBit, false, nullptr, nullptr},
|
||||
{"meshcoretel", "meshcoretel", "meshcoretel.ru", "mqtt://meshcoretel.ru:1883",
|
||||
kMeshcoretelBit, true, "meshcore", "meshcore"},
|
||||
};
|
||||
|
||||
MQTTUplink::MQTTUplink(mesh::RTCClock& rtc, mesh::LocalIdentity& identity)
|
||||
@@ -112,7 +110,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 < 4; ++i) {
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
memset(&_brokers[i], 0, sizeof(_brokers[i]));
|
||||
_brokers[i].spec = &kBrokerSpecs[i];
|
||||
}
|
||||
@@ -128,7 +126,7 @@ bool MQTTUplink::savePrefs() {
|
||||
}
|
||||
|
||||
bool MQTTUplink::hasEnabledBroker() const {
|
||||
return (_prefs.enabled_mask & 0x0F) != 0;
|
||||
return (_prefs.enabled_mask & 0x07) != 0;
|
||||
}
|
||||
|
||||
bool MQTTUplink::isUnsetIataValue(const char* iata) {
|
||||
@@ -676,7 +674,7 @@ void MQTTUplink::ensureBroker(BrokerState& broker, bool allow_new_connect) {
|
||||
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.broker.verification.certificate = mqtt_ca_certs::kLetsmeshWe1Pem;
|
||||
cfg.credentials.authentication.password = broker.token;
|
||||
}
|
||||
cfg.credentials.username = broker.username;
|
||||
@@ -700,7 +698,7 @@ void MQTTUplink::ensureBroker(BrokerState& broker, bool allow_new_connect) {
|
||||
cfg.port = 443;
|
||||
cfg.transport = MQTT_TRANSPORT_OVER_WSS;
|
||||
cfg.path = "/mqtt";
|
||||
cfg.cert_pem = mqtt_ca_certs::kCombinedPem;
|
||||
cfg.cert_pem = mqtt_ca_certs::kLetsmeshWe1Pem;
|
||||
cfg.password = broker.token;
|
||||
}
|
||||
cfg.username = broker.username;
|
||||
@@ -741,7 +739,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 & 0x0F);
|
||||
uint8_t normalized_mask = normalizeEnabledMask(_prefs.enabled_mask & 0x07);
|
||||
if (normalized_mask != _prefs.enabled_mask) {
|
||||
_prefs.enabled_mask = normalized_mask;
|
||||
savePrefs();
|
||||
@@ -885,16 +883,16 @@ void MQTTUplink::formatStatusReply(char* reply, size_t reply_size) const {
|
||||
return "retry";
|
||||
};
|
||||
|
||||
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",
|
||||
snprintf(reply, reply_size, "> wifi:%s ntp:%s iata:%s meshcoretel:%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(kMeshcoretelBit), broker_state(kEastmeshBit), broker_state(kLetsmeshEuBit), broker_state(kLetsmeshUsBit),
|
||||
broker_state(kMeshcoretelBit), 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 & 0x0F;
|
||||
uint8_t next_mask = _prefs.enabled_mask & 0x07;
|
||||
if (enabled) {
|
||||
next_mask = normalizeEnabledMask(next_mask | bit);
|
||||
if ((next_mask & bit) == 0) {
|
||||
|
||||
@@ -110,15 +110,14 @@ private:
|
||||
NetworkStateProvider* _network;
|
||||
|
||||
#if defined(ESP_PLATFORM)
|
||||
static constexpr uint8_t kEastmeshBit = 0x01;
|
||||
static constexpr uint8_t kMeshcoretelBit = 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[4];
|
||||
static const BrokerSpec kBrokerSpecs[3];
|
||||
static bool isUnsetIataValue(const char* iata);
|
||||
|
||||
BrokerState _brokers[4];
|
||||
BrokerState _brokers[3];
|
||||
|
||||
static void handleMqttEvent(void* handler_args, esp_event_base_t base, int32_t event_id, void* event_data);
|
||||
bool hasEnabledBroker() const;
|
||||
|
||||
@@ -854,71 +854,6 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
<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>
|
||||
<optgroup label="New South Wales">
|
||||
<option value="ABX">ABX - Albury</option>
|
||||
<option value="ARM">ARM - Armidale</option>
|
||||
<option value="BHQ">BHQ - Broken Hill</option>
|
||||
<option value="BNK">BNK - Ballina</option>
|
||||
<option value="CFS">CFS - Coffs Harbour</option>
|
||||
<option value="DBO">DBO - Dubbo</option>
|
||||
<option value="GFF">GFF - Griffith</option>
|
||||
<option value="GFN">GFN - Grafton</option>
|
||||
<option value="LDH">LDH - Lord Howe Island</option>
|
||||
<option value="LSY">LSY - Lismore</option>
|
||||
<option value="MIM">MIM - Merimbula</option>
|
||||
<option value="MRZ">MRZ - Moree</option>
|
||||
<option value="MYA">MYA - Moruya</option>
|
||||
<option value="NTL">NTL - Newcastle</option>
|
||||
<option value="OAG">OAG - Orange</option>
|
||||
<option value="PQQ">PQQ - Port Macquarie</option>
|
||||
<option value="SYD">SYD - Sydney</option>
|
||||
<option value="WGA">WGA - Wagga Wagga</option>
|
||||
</optgroup>
|
||||
<optgroup label="Queensland">
|
||||
<option value="ABM">ABM - Bamaga</option>
|
||||
<option value="BNE">BNE - Brisbane</option>
|
||||
<option value="CNS">CNS - Cairns</option>
|
||||
<option value="HTI">HTI - Hamilton Island</option>
|
||||
<option value="HVB">HVB - Hervey Bay</option>
|
||||
<option value="ISA">ISA - Mount Isa</option>
|
||||
<option value="LRE">LRE - Longreach</option>
|
||||
<option value="MCY">MCY - Sunshine Coast</option>
|
||||
<option value="MKY">MKY - Mackay</option>
|
||||
<option value="OOL">OOL - Gold Coast</option>
|
||||
<option value="PPP">PPP - Proserpine</option>
|
||||
<option value="ROK">ROK - Rockhampton</option>
|
||||
<option value="TSV">TSV - Townsville</option>
|
||||
<option value="WEI">WEI - Weipa</option>
|
||||
<option value="WTB">WTB - Toowoomba Wellcamp</option>
|
||||
</optgroup>
|
||||
<optgroup label="South Australia">
|
||||
<option value="ADL">ADL - Adelaide</option>
|
||||
<option value="KGC">KGC - Kingscote</option>
|
||||
<option value="MGB">MGB - Mount Gambier</option>
|
||||
<option value="PLO">PLO - Port Lincoln</option>
|
||||
<option value="WYA">WYA - Whyalla</option>
|
||||
</optgroup>
|
||||
<optgroup label="Tasmania">
|
||||
<option value="BWT">BWT - Burnie</option>
|
||||
<option value="DPO">DPO - Devonport</option>
|
||||
<option value="FLS">FLS - Flinders Island</option>
|
||||
<option value="HBA">HBA - Hobart</option>
|
||||
<option value="KNS">KNS - King Island</option>
|
||||
<option value="LST">LST - Launceston</option>
|
||||
</optgroup>
|
||||
<optgroup label="Victoria">
|
||||
<option value="AVV">AVV - Avalon</option>
|
||||
<option value="GEX">GEX - Geelong West</option>
|
||||
<option value="MEB">MEB - Essendon Fields</option>
|
||||
<option value="MEL">MEL - Melbourne</option>
|
||||
<option value="MQL">MQL - Mildura</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<button class="iconbtn" data-load-cmd="get mqtt.iata" data-load-input="mqttIata" title="Refresh MQTT IATA">↻</button>
|
||||
<button class="savebtn" data-prefix="set mqtt.iata " data-input="mqttIata">Save</button>
|
||||
@@ -942,15 +877,15 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
</div>
|
||||
<div class="field-card">
|
||||
<label class="label">MQTT Servers</label>
|
||||
<div class="broker-stack" style="grid-template-columns:minmax(0,1fr) minmax(0,1fr) minmax(0,1fr)">
|
||||
<div class="broker-stack">
|
||||
<div class="broker-group">
|
||||
<div class="broker-group-title">Meshcoretel</div>
|
||||
<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-title">MeshCoreTel RU</div>
|
||||
<div class="broker-state" id="mqttMeshcoretelState">Off</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -966,29 +901,6 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="broker-group">
|
||||
<div class="broker-group-title">EastMesh</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">EastMesh AU</div>
|
||||
<div class="broker-state" id="mqttEastmeshAuState">Off</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mode-slider">
|
||||
<input id="mqttEastmeshMode" type="range" min="0" max="1" step="1" value="0" aria-label="EastMesh mode">
|
||||
<div class="mode-labels two" aria-hidden="true">
|
||||
<div class="mode-label" data-eastmesh-label="off">Off</div>
|
||||
<div class="mode-label" data-eastmesh-label="on">On</div>
|
||||
</div>
|
||||
</div>
|
||||
<input id="mqttEastmeshAu" class="visually-hidden" type="checkbox" tabindex="-1" aria-hidden="true">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="broker-group">
|
||||
<div class="broker-group-title">LetsMesh</div>
|
||||
<div class="broker-grid single">
|
||||
@@ -1731,12 +1643,7 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
}
|
||||
const renderNeighbourId = (neighbour) => {
|
||||
const shortId = escapeHtml(neighbour.id || "--");
|
||||
const fullId = typeof neighbour.full_id === "string" ? neighbour.full_id.trim() : "";
|
||||
if (!/^[0-9A-Fa-f]{64}$/.test(fullId)) {
|
||||
return shortId;
|
||||
}
|
||||
const href = "https://core.eastmesh.au/#/nodes/" + encodeURIComponent(fullId.toLowerCase());
|
||||
return `<a href="${href}" target="_blank" rel="noopener noreferrer">${shortId}</a>`;
|
||||
return shortId;
|
||||
};
|
||||
const rows = neighbours.map((neighbour) => `<tr>
|
||||
<td>${renderNeighbourId(neighbour)}</td>
|
||||
@@ -2126,7 +2033,7 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
}
|
||||
if (inlineWarning) {
|
||||
inlineWarning.textContent = showWarning
|
||||
? "MQTT IATA is unset. Set it before enabling any MQTT brokers."
|
||||
? "MQTT IATA is unset. Set it before enabling MeshCoreTel or LetsMesh brokers."
|
||||
: "";
|
||||
}
|
||||
}
|
||||
@@ -2257,15 +2164,15 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
if (usOn) return "us";
|
||||
return "off";
|
||||
}
|
||||
function refreshEastmeshModeUi() {
|
||||
const input = document.getElementById("mqttEastmeshAu");
|
||||
function refreshMeshcoretelModeUi() {
|
||||
const input = document.getElementById("mqttMeshcoretel");
|
||||
const enabled = !!(input && input.checked);
|
||||
const slider = document.getElementById("mqttEastmeshMode");
|
||||
const slider = document.getElementById("mqttMeshcoretelMode");
|
||||
if (slider) {
|
||||
slider.value = enabled ? "1" : "0";
|
||||
}
|
||||
document.querySelectorAll("[data-eastmesh-label]").forEach((label) => {
|
||||
label.classList.toggle("active", label.dataset.eastmeshLabel === (enabled ? "on" : "off"));
|
||||
document.querySelectorAll("[data-meshcoretel-label]").forEach((label) => {
|
||||
label.classList.toggle("active", label.dataset.meshcoretelLabel === (enabled ? "on" : "off"));
|
||||
});
|
||||
}
|
||||
function getLetsmeshModeIndex(mode) {
|
||||
@@ -2273,27 +2180,15 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
return Object.prototype.hasOwnProperty.call(order, mode) ? order[mode] : 0;
|
||||
}
|
||||
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));
|
||||
if ((eastmeshEnabled || meshcoretelEnabled) && bounded === 3) {
|
||||
return 1;
|
||||
}
|
||||
if (eastmeshEnabled && meshcoretelEnabled && bounded > 0) {
|
||||
return 0;
|
||||
}
|
||||
return bounded;
|
||||
return meshcoretelEnabled && bounded === 3 ? 1 : 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) {
|
||||
@@ -2304,15 +2199,11 @@ 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",
|
||||
(bothBlocked && labelMode === "both") ||
|
||||
(allBlocked && labelMode !== "off")
|
||||
);
|
||||
label.classList.toggle("disabled", meshcoretelEnabled && labelMode === "both");
|
||||
});
|
||||
}
|
||||
function setBrokerToggle(inputId, state) {
|
||||
@@ -2327,11 +2218,9 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
}
|
||||
if (inputId === "mqttLetsmeshEu" || inputId === "mqttLetsmeshUs") {
|
||||
refreshLetsmeshModeUi();
|
||||
} else if (inputId === "mqttEastmeshAu") {
|
||||
refreshEastmeshModeUi();
|
||||
refreshLetsmeshModeUi();
|
||||
} else if (inputId === "mqttMeshcoretel") {
|
||||
refreshMeshcoretelModeUi();
|
||||
refreshLetsmeshModeUi();
|
||||
}
|
||||
}
|
||||
async function loadBrokerState(cmd, inputId, options = {}) {
|
||||
@@ -2449,54 +2338,34 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
if (mqttIataSelect) {
|
||||
mqttIataSelect.addEventListener("change", refreshMqttIataWarning);
|
||||
}
|
||||
async function setEastmeshMode(enabled) {
|
||||
if (enabled) {
|
||||
if (getLetsmeshMode() === "both") {
|
||||
await setLetsmeshMode("eu");
|
||||
}
|
||||
const meshcoretel = document.getElementById("mqttMeshcoretel");
|
||||
if (meshcoretel && meshcoretel.checked && getLetsmeshMode() !== "off") {
|
||||
await setLetsmeshMode("off");
|
||||
}
|
||||
async function setMeshcoretelMode(enabled) {
|
||||
if (enabled && getLetsmeshMode() === "both") {
|
||||
await setLetsmeshMode("eu");
|
||||
}
|
||||
const result = await runCommand(enabled ? "set mqtt.eastmesh-au on" : "set mqtt.eastmesh-au off");
|
||||
const result = await runCommand(enabled ? "set mqtt.meshcoretel on" : "set mqtt.meshcoretel off");
|
||||
if (!result.ok) {
|
||||
refreshEastmeshModeUi();
|
||||
refreshMeshcoretelModeUi();
|
||||
refreshLetsmeshModeUi();
|
||||
return;
|
||||
}
|
||||
setBrokerToggle("mqttEastmeshAu", enabled ? "on" : "off");
|
||||
refreshEastmeshModeUi();
|
||||
refreshLetsmeshModeUi();
|
||||
setBrokerToggle("mqttMeshcoretel", enabled ? "on" : "off");
|
||||
refreshMeshcoretelModeUi();
|
||||
refreshLetsmeshModeUi();
|
||||
}
|
||||
const eastmeshModeSlider = document.getElementById("mqttEastmeshMode");
|
||||
if (eastmeshModeSlider) {
|
||||
eastmeshModeSlider.addEventListener("input", () => {
|
||||
eastmeshModeSlider.value = (Number.parseInt(eastmeshModeSlider.value, 10) || 0) >= 1 ? "1" : "0";
|
||||
const meshcoretelModeSlider = document.getElementById("mqttMeshcoretelMode");
|
||||
if (meshcoretelModeSlider) {
|
||||
meshcoretelModeSlider.addEventListener("input", () => {
|
||||
meshcoretelModeSlider.value = (Number.parseInt(meshcoretelModeSlider.value, 10) || 0) >= 1 ? "1" : "0";
|
||||
});
|
||||
eastmeshModeSlider.addEventListener("change", () => {
|
||||
setEastmeshMode((Number.parseInt(eastmeshModeSlider.value, 10) || 0) >= 1);
|
||||
meshcoretelModeSlider.addEventListener("change", () => {
|
||||
setMeshcoretelMode((Number.parseInt(meshcoretelModeSlider.value, 10) || 0) >= 1);
|
||||
});
|
||||
}
|
||||
async function setLetsmeshMode(mode) {
|
||||
const eastmesh = document.getElementById("mqttEastmeshAu");
|
||||
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;
|
||||
if (mode === "both" && meshcoretel && meshcoretel.checked) {
|
||||
refreshLetsmeshModeUi();
|
||||
return;
|
||||
}
|
||||
const desired = {
|
||||
eu: mode === "eu" || mode === "both",
|
||||
@@ -2530,49 +2399,6 @@ 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);
|
||||
@@ -2699,10 +2525,9 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
() => loadField("get mqtt.iata", "mqttIata", null, quiet),
|
||||
() => loadField("get mqtt.owner", "mqttOwner", null, quiet),
|
||||
() => loadField("get mqtt.email", "mqttEmail", null, quiet),
|
||||
() => loadBrokerState("get mqtt.eastmesh-au", "mqttEastmeshAu", quiet),
|
||||
() => loadBrokerState("get mqtt.meshcoretel", "mqttMeshcoretel", quiet),
|
||||
() => loadBrokerState("get mqtt.letsmesh-eu", "mqttLetsmeshEu", quiet),
|
||||
() => loadBrokerState("get mqtt.letsmesh-us", "mqttLetsmeshUs", quiet),
|
||||
() => loadBrokerState("get mqtt.meshcoretel", "mqttMeshcoretel", quiet)
|
||||
() => loadBrokerState("get mqtt.letsmesh-us", "mqttLetsmeshUs", quiet)
|
||||
]);
|
||||
statusEl.textContent = "Ready";
|
||||
} catch (error) {
|
||||
@@ -2713,9 +2538,8 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
|
||||
}
|
||||
loadRadioPresets();
|
||||
}
|
||||
refreshEastmeshModeUi();
|
||||
refreshLetsmeshModeUi();
|
||||
refreshMeshcoretelModeUi();
|
||||
refreshLetsmeshModeUi();
|
||||
initApp();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Odkázat v novém úkolu
Zablokovat Uživatele