Release fw 0.8.4: doc sync, System UI, BT/FM polish.

Align README, manual, and backlog to 0.8.4; add Web UI System tab for OTA/DSP uploads, serial in health header, FM seek down, and BT1035 paired list plus auto-reconnect API.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-07 09:56:44 +02:00
co-authored by Cursor
parent b9ed8a2dcd
commit 176cdd9319
28 changed files with 988 additions and 66 deletions
@@ -12,6 +12,8 @@
*/
#include "core/Bt1035At.hpp"
#include "core/Bt1035PairedDevice.hpp"
#include "core/BluetoothJson.hpp"
#include <cstdlib>
#include <iostream>
@@ -74,6 +76,50 @@ namespace {
return EXIT_SUCCESS;
}
[[nodiscard]] int runNameAutoConnPairedParseTest()
{
const auto name =
core::parseBt1035NameResponse("+NAME=DigiRadio-A1B2\r\nOK\r\n");
if (!name || *name != "DigiRadio-A1B2") {
std::cerr << "NAME parse failed\n";
return EXIT_FAILURE;
}
const auto autoconn =
core::parseBt1035AutoConnResponse("+AUTOCONN=3\r\nOK\r\n");
if (!autoconn || *autoconn != 3U) {
std::cerr << "AUTOCONN parse failed\n";
return EXIT_FAILURE;
}
const auto plist = core::parseBt1035PairedListResponse(
"+PLIST=1,001122334455,Phone\r\n"
"+PLIST=2,FFEEDDCCBBAA,\r\n"
"OK\r\n");
if (!plist || plist->size() != 2U || (*plist)[0U].index != 1U
|| (*plist)[0U].mac != "001122334455"
|| (*plist)[0U].name != "Phone") {
std::cerr << "PLIST parse failed\n";
return EXIT_FAILURE;
}
const std::string pairedJson =
core::serializeBluetoothPairedJson(*plist);
if (pairedJson.find("\"index\":1") == std::string::npos
|| pairedJson.find("\"mac\":\"001122334455\"") == std::string::npos) {
std::cerr << "paired JSON serialise failed: " << pairedJson << '\n';
return EXIT_FAILURE;
}
const auto times =
core::parseBluetoothAutoReconnectJson(R"({"times":5})");
if (!times || *times != 5U) {
std::cerr << "auto-reconnect JSON parse failed\n";
return EXIT_FAILURE;
}
if (core::buildBt1035SetAutoConnLine(5) != "AT+AUTOCONN=5\r\n") {
std::cerr << "AUTOCONN command line mismatch\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
} // namespace
int main()
@@ -84,5 +130,8 @@ int main()
if (runParseTest() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
if (runNameAutoConnPairedParseTest() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
@@ -178,6 +178,34 @@ namespace {
return EXIT_SUCCESS;
}
[[nodiscard]] int runTunerSeekParseTest()
{
const auto empty = core::parseTunerSeekJson("");
if (!empty || *empty != core::SeekDirection::Up) {
std::cerr << "empty seek body should default to up\n";
return EXIT_FAILURE;
}
const auto up =
core::parseTunerSeekJson(R"({"direction":"up"})");
if (!up || *up != core::SeekDirection::Up) {
std::cerr << "seek up parse failed\n";
return EXIT_FAILURE;
}
const auto down =
core::parseTunerSeekJson(R"({"direction":"down"})");
if (!down || *down != core::SeekDirection::Down) {
std::cerr << "seek down parse failed\n";
return EXIT_FAILURE;
}
const auto bad =
core::parseTunerSeekJson(R"({"direction":"sideways"})");
if (bad) {
std::cerr << "expected invalid seek direction rejection\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
} // namespace
int main()
@@ -209,5 +237,8 @@ int main()
if (runTunerErrorSerialiseTest() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
if (runTunerSeekParseTest() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}