Use configured local timezone in web UI and drop legacy history CSV parsing

This commit is contained in:
2026-02-16 11:14:30 +01:00
parent 4de1dda82b
commit 6ea8d9d5fc

View File

@@ -57,26 +57,23 @@ static HistoryJob g_history = {};
static constexpr size_t SD_LIST_MAX_FILES = 200;
static constexpr size_t SD_DOWNLOAD_MAX_PATH = 160;
static String format_utc_hms(uint32_t ts_utc) {
static String format_local_hms(uint32_t ts_utc) {
if (ts_utc == 0) {
return "n/a";
}
time_t t = static_cast<time_t>(ts_utc);
struct tm tm_utc;
gmtime_r(&t, &tm_utc);
char buf[16];
snprintf(buf, sizeof(buf), "%02d:%02d:%02d UTC",
tm_utc.tm_hour,
tm_utc.tm_min,
tm_utc.tm_sec);
struct tm tm_local;
localtime_r(&t, &tm_local);
char buf[24];
strftime(buf, sizeof(buf), "%H:%M:%S %Z", &tm_local);
return String(buf);
}
static String format_epoch_hms(uint32_t ts_utc) {
static String format_epoch_local_hms(uint32_t ts_utc) {
if (ts_utc == 0) {
return "n/a";
}
return String(ts_utc) + " (" + format_utc_hms(ts_utc) + ")";
return String(ts_utc) + " (" + format_local_hms(ts_utc) + ")";
}
static uint32_t timestamp_age_seconds(uint32_t ts_utc) {
@@ -277,12 +274,12 @@ static bool history_parse_line(const char *line, uint32_t &ts_out, float &p_out)
if (!line || line[0] < '0' || line[0] > '9') {
return false;
}
const char *comma = strchr(line, ',');
if (!comma) {
const char *comma1 = strchr(line, ',');
if (!comma1) {
return false;
}
char ts_buf[16];
size_t ts_len = static_cast<size_t>(comma - line);
size_t ts_len = static_cast<size_t>(comma1 - line);
if (ts_len >= sizeof(ts_buf)) {
return false;
}
@@ -293,32 +290,22 @@ static bool history_parse_line(const char *line, uint32_t &ts_out, float &p_out)
if (end == ts_buf) {
return false;
}
auto parse_float_field = [](const char *start, const char *end, float &out) -> bool {
if (!start) {
return false;
}
char p_buf[16];
size_t p_len = end ? static_cast<size_t>(end - start) : strlen(start);
if (p_len == 0 || p_len >= sizeof(p_buf)) {
return false;
}
memcpy(p_buf, start, p_len);
p_buf[p_len] = '\0';
char *endp = nullptr;
out = strtof(p_buf, &endp);
return endp != p_buf;
};
const char *field2_start = comma + 1;
const char *field2_end = strchr(field2_start, ',');
float p = 0.0f;
bool parsed_power = parse_float_field(field2_start, field2_end, p);
if (!parsed_power && field2_end) {
const char *field3_start = field2_end + 1;
const char *field3_end = strchr(field3_start, ',');
parsed_power = parse_float_field(field3_start, field3_end, p);
const char *comma2 = strchr(comma1 + 1, ',');
if (!comma2) {
return false;
}
if (!parsed_power) {
const char *p_start = comma2 + 1;
const char *p_end = strchr(p_start, ',');
char p_buf[16];
size_t p_len = p_end ? static_cast<size_t>(p_end - p_start) : strlen(p_start);
if (p_len == 0 || p_len >= sizeof(p_buf)) {
return false;
}
memcpy(p_buf, p_start, p_len);
p_buf[p_len] = '\0';
char *endp = nullptr;
float p = strtof(p_buf, &endp);
if (endp == p_buf) {
return false;
}
ts_out = ts;
@@ -418,7 +405,7 @@ static String render_sender_block(const SenderStatus &status) {
if (!status.has_data) {
s += "No data";
} else {
s += "Last update: " + format_epoch_hms(status.last_update_ts_utc);
s += "Last update: " + format_epoch_local_hms(status.last_update_ts_utc);
if (time_is_synced()) {
s += " (" + String(timestamp_age_seconds(status.last_update_ts_utc)) + "s ago)";
}
@@ -437,7 +424,7 @@ static String render_sender_block(const SenderStatus &status) {
duplicate_pct = (static_cast<float>(duplicate_batches) * 100.0f) / static_cast<float>(total_batches);
}
s += "<br>Dup batches: " + String(duplicate_batches) + "/" + String(total_batches) + " (" + String(duplicate_pct, 1) + "%)";
s += " last: " + format_epoch_hms(status.rx_last_duplicate_ts_utc);
s += " last: " + format_epoch_local_hms(status.rx_last_duplicate_ts_utc);
if (time_is_synced() && status.rx_last_duplicate_ts_utc > 0) {
s += " (" + String(timestamp_age_seconds(status.rx_last_duplicate_ts_utc)) + "s ago)";
}
@@ -665,14 +652,14 @@ static void handle_sender() {
if (g_last_batch_count[i] > 0) {
html += "<h3>Last batch (" + String(g_last_batch_count[i]) + " samples)</h3>";
html += "<table border='1' cellspacing='0' cellpadding='3'>";
html += "<tr><th>#</th><th>ts_utc</th><th>ts_hms_utc</th><th>e_kwh</th><th>p_w</th><th>p1_w</th><th>p2_w</th><th>p3_w</th>";
html += "<tr><th>#</th><th>ts_utc</th><th>ts_hms_local</th><th>e_kwh</th><th>p_w</th><th>p1_w</th><th>p2_w</th><th>p3_w</th>";
html += "<th>bat_v</th><th>bat_pct</th><th>rssi</th><th>snr</th><th>err_tx</th><th>err_last</th><th>rx_reject</th></tr>";
for (uint8_t r = 0; r < g_last_batch_count[i]; ++r) {
const MeterData &d = g_last_batch[i][r];
html += "<tr>";
html += "<td>" + String(r) + "</td>";
html += "<td>" + String(d.ts_utc) + "</td>";
html += "<td>" + format_utc_hms(d.ts_utc) + "</td>";
html += "<td>" + format_local_hms(d.ts_utc) + "</td>";
html += "<td>" + String(d.energy_total_kwh, 2) + "</td>";
html += "<td>" + String(round_power_w(d.total_power_w)) + "</td>";
html += "<td>" + String(round_power_w(d.phase_power_w[0])) + "</td>";