mirror of
https://github.com/pablorevilla-meshtastic/meshview.git
synced 2026-10-30 18:51:57 +01:00
some updates
This commit is contained in:
1 parent
012150cba9
commit
fb3a0154dd
7 files changed
+68
-22
No files matched your search
@@ -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.
|
||||
|
||||
@@ -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
@@ -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:
|
||||
|
||||
@@ -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=>({
|
||||
|
||||
@@ -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 => ({
|
||||
"&":"&",
|
||||
"<":"<",
|
||||
">":">",
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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
|
||||
# -------------------------
|
||||
|
||||
Reference in new issue
Block a user