some updates

This commit is contained in:
pablorevilla-meshtastic committed 2026-10-15 22:49:55 -07:00
1 parent 012150cba9
commit fb3a0154dd
7 files changed
+68 -22

No files matched your search

+3
View File
@@ -23,6 +23,9 @@ The project serves as a real-time monitoring and diagnostic tool for the Meshtas
### Version 3.0.6 — March 2026
- Daily snapshots: added the `daily_snapshot` table, scheduled snapshot capture, and `/api/snapshots/daily` for historical node, packet, and gateway counts.
Each new snapshot stores packets imported during the previous complete UTC day,
labeled with that day's date. Node and gateway counts are database totals at capture
time. Existing historical snapshots retain their previously stored packet totals.
- Stats/UI: expanded stats views with snapshot data and added channel filters to Chat and Firehose.
- Map/node fixes: corrected node location handling, map popup behavior, packet page display, and node list sorting.
- Ingestion/API reliability: improved duplicate `PacketSeen` handling, guarded invalid node info packets, and made traceroute/edge APIs more tolerant of decode failures.
+23
View File
@@ -227,9 +227,32 @@ Response Example
## 6. Config API
### GET `/api/snapshots/daily`
Returns stored daily snapshots. For new snapshots, `packet_count` is the number of
packet records imported during `date` (00:00 UTC inclusive to the next midnight
exclusive). The scheduled capture records the previous complete UTC day. Repeated
observations of the same packet are not counted separately. Node and gateway counts
remain database totals at capture time. Older snapshots retain the cumulative packet
counts originally stored; they are not automatically converted.
### GET `/api/config`
Returns a safe subset of server configuration.
`site.map_channel_colors` contains channel color overrides from the optional
`[map_channel_colors]` INI section. Each entry is `channel = #RRGGBB`; invalid colors
are omitted. Channel matching on the map ignores case, spaces, hyphens, and underscores.
Unlisted channels use generated colors. For example:
```ini
[map_channel_colors]
LongFast = #4363d8
LocalChat = #18aa18
```
Restart the web server and reload the map after changing these settings. Omitting
the section uses generated colors for every channel.
Response Example
```json
{
+14 -5
View File
@@ -1,7 +1,7 @@
import logging
import re
import time
from datetime import UTC, datetime
from datetime import UTC, datetime, timedelta
from sqlalchemy import func, select, update
from sqlalchemy.dialects.postgresql import insert as pg_insert
@@ -110,12 +110,21 @@ async def ensure_node_exists(session, node_id: int | None, channel: str | None)
async def capture_daily_snapshot() -> None:
today = datetime.now(UTC).date()
"""Record packet traffic for the previous complete UTC day."""
day_end = datetime.now(UTC).replace(hour=0, minute=0, second=0, microsecond=0)
day_start = day_end - timedelta(days=1)
start_us = int(day_start.timestamp() * 1_000_000)
end_us = int(day_end.timestamp() * 1_000_000)
snapshot_date = day_start.date()
async with mqtt_database.async_session() as session:
node_count = (await session.execute(select(func.count()).select_from(Node))).scalar_one()
packet_count = (
await session.execute(select(func.count()).select_from(Packet))
await session.execute(
select(func.count())
.select_from(Packet)
.where(Packet.import_time_us >= start_us, Packet.import_time_us < end_us)
)
).scalar_one()
gateway_count = (
await session.execute(
@@ -124,7 +133,7 @@ async def capture_daily_snapshot() -> None:
).scalar_one()
captured_at_us = int(time.time() * 1_000_000)
values = {
"snapshot_date": today,
"snapshot_date": snapshot_date,
"node_count": node_count,
"packet_count": packet_count,
"gateway_count": gateway_count,
@@ -151,7 +160,7 @@ async def capture_daily_snapshot() -> None:
else:
snapshot = (
await session.execute(
select(DailySnapshot).where(DailySnapshot.snapshot_date == today)
select(DailySnapshot).where(DailySnapshot.snapshot_date == snapshot_date)
)
).scalar_one_or_none()
if snapshot is None:
+8 -16
View File
@@ -275,23 +275,11 @@ const PACKET_LANDING_DURATION_MS = 1000;
const portMap = window.PORT_LABEL_MAP;
// Fallback colors for channels that are not Meshtastic presets.
// Every entry clears 3:1 contrast against both the light map tiles and the dark
// filter panel, and stays perceptually far from every channelColorOverrides value.
// Generated colors for channels without a configured override.
const palette = ["#18aa18","#976049","#a5508c","#1898dc","#3a8846","#dc1884",
"#b8813d","#b64f35","#408b18","#4c719e","#be379c","#18aa52"];
const channelColorOverrides = {
longfast: "#4363d8",
longslow: "#000075",
verylongslow: "#808000",
mediumslow: "#008080",
mediumfast: "#e6194b",
shortslow: "#911eb4",
shortfast: "#f58231",
longmoderate: "#46f0f0",
shortturbo: "#f032e6"
};
const channelColorOverrides = new Map();
const colorMap = new Map();
const channelSet = new Set();
@@ -316,7 +304,7 @@ function hashToColor(str){
const channel = String(str || "Unknown");
if(colorMap.has(channel)) return colorMap.get(channel);
const override = channelColorOverrides[normalizeChannelName(channel)];
const override = channelColorOverrides.get(normalizeChannelName(channel));
const c = override || palette[Math.floor(hashToUnit(channel) * palette.length) % palette.length];
colorMap.set(channel,c);
return c;
@@ -477,6 +465,9 @@ async function waitForConfig() {
}
try {
const cfg = await window._siteConfigPromise;
for(const [channel, color] of Object.entries(cfg.site?.map_channel_colors || {})){
channelColorOverrides.set(normalizeChannelName(channel), color);
}
return cfg.site || {};
} catch (err) {
console.error("Error loading site config:", err);
@@ -525,7 +516,8 @@ initMapPolling();
fetch('/api/nodes?days_active=3')
.then(r=>r.json())
.then(data=>{
.then(async data=>{
await waitForConfig();
if(!data.nodes) return;
nodes = data.nodes.map(n=>({
+1 -1
View File
@@ -535,7 +535,7 @@ seenTableBody.innerHTML = Object.keys(hopGroups)
Escape HTML helper
----------------------------------------------*/
function escapeHtml(unsafe) {
return (unsafe ?? "").replace(/[&<"'>]/g, m => ({
return String(unsafe ?? "").replace(/[&<"'>]/g, m => ({
"&":"&amp;",
"<":"&lt;",
">":"&gt;",
+6
View File
@@ -744,6 +744,12 @@ async def api_config(request):
"map_bottom_right_lat": get_float(site, "map_bottom_right_lat", 36.0),
"map_bottom_right_lon": get_float(site, "map_bottom_right_lon", -121.0),
"map_interval": get_int(site, "map_interval", 3),
"map_channel_colors": {
channel: color.strip()
for channel, color in CONFIG.get("map_channel_colors", {}).items()
if re.fullmatch(r"#[0-9a-fA-F]{6}", color.strip())
},
"firehose_interval": get_int(site, "firehose_interval", 3),
"weekly_net_message": get_str(
site, "weekly_net_message", "Weekly Mesh check-in message."
+13
View File
@@ -59,6 +59,19 @@ firehose_interal=3
weekly_net_message = Weekly Mesh check-in. We will keep it open on every Wednesday from 5:00pm for checkins. The message format should be (LONG NAME) - (CITY YOU ARE IN) #BayMeshNet.
net_tag = #BayMeshNet
# Map channel color overrides. Use six-digit hex colors; other channels get generated colors.
# Names ignore case, spaces, hyphens, and underscores. Restart the web server after editing.
[map_channel_colors]
LongFast = #4363d8
LongSlow = #000075
VeryLongSlow = #808000
MediumSlow = #008080
MediumFast = #e6194b
ShortSlow = #911eb4
ShortFast = #f58231
LongModerate = #46f0f0
ShortTurbo = #f032e6
# -------------------------
# MQTT Broker Configuration
# -------------------------