Analysis & Reports β
The Analysis & Reports workspace is a global, cross-source analytics page that runs analytical reports against the telemetry, position, and routing data MeshMonitor has collected from every source you can read. It lives at /reports and is linked from the bottom of the dashboard sidebar (right under Map Analysis).
Three reports are currently bundled with the workspace: Solar Monitoring Analysis, NodeInfo Enrichment, and ok_to_mqtt Violations β see below. The card-grid landing page is designed to host additional reports over time without changing the routing or navigation surface.
Solar Monitoring Analysis β
Identifies solar-powered nodes by analyzing battery and voltage telemetry over a configurable lookback window and looking for the characteristic morning-low β afternoon-peak charging curve, followed by overnight discharge. Ports the proven detection algorithm from MeshManager.
What gets analyzed β
For every node in your permitted sources, the report scans the following telemetry types over the lookback window:
batteryLevel(preferred when β₯ 3 readings/day are present)voltagech1Voltage,ch2Voltage,ch3Voltage(INA voltage channels)
A daily pattern is recorded when the metric shows:
- A morning low (06:00β10:00 UTC) and an afternoon peak (12:00β18:00 UTC)
- Sufficient daily variance (β₯ 10% for battery, β₯ 0.3 V for voltage) or a "high-efficiency" candidate that stays above 90% / 4.1 V with smaller swings
- A peak hour between 10:00 and 18:00 UTC and a sunrise hour β€ 12:00 UTC
A node becomes a solar candidate when at least 50% of its analyzed days show a pattern (33% for high-efficiency candidates that consistently stay above 98%).
Running the report β
- Open the dashboard, click Analysis & Reports in the sidebar.
- Click the Solar Monitoring Analysis card.
- Set the Lookback (days) between
1and90(default7) and click Run analysis. - (Optional) Click Run forecast to project battery state across the next several days using the forecast.solar production estimates.
The summary row shows the lookback window, total nodes analyzed, solar nodes detected, average charging hours per day, and average overnight discharge hours per day.
Per-node card β
Each detected node renders an expandable card with:
- Solar score (% of analyzed days that showed a clear pattern)
- Average charge / discharge rates (per hour)
- Detected metrics (
battery,voltage, INA channel names) - Insufficient solar warning when projected daily charging cannot keep up with overnight discharge
- A Recharts time-series chart showing:
- Battery / voltage line
- Solar production area overlay (Wh, right axis) when forecast.solar data is available
- Reference levels: green at 100% / 4.2 V (full), yellow at 50% / 3.7 V (nominal), red at 20% / 3.3 V (low)
- Forecast simulation as a mauve dashed extension when Run forecast has been triggered
- A Recent daily patterns table (date, sunrise / peak / sunset times and values, rise, fall, charge rate /h, discharge rate /h)
Forecast simulation β
The forecast endpoint compares projected daily Wh totals from forecast.solar to the lookback's historical average. Each day is flagged Low if it falls below 75% of the historical average. For every solar candidate, the report simulates the next ~5 days of battery state by running the node's measured charge and discharge rates against the forecast factor for each day:
- Sunrise point: battery after the overnight discharge
- Peak point: battery after the daylight charge (modulated by
forecast_factor = forecast_wh / avg_historical_wh, clamped 0β1.5) - Sunset point: battery after a small afternoon drain
Nodes whose simulated minimum drops below 50% (battery) or 3.5 V (voltage) are listed as Nodes predicted at risk so operators can intervene before they go offline.
Solar production must already be configured under Settings β Solar Monitoring for the forecast to produce useful output. See the Solar Monitoring guide for set-up details.
NodeInfo Enrichment β
Scans every node visible on more than one source for blank NodeInfo fields β Long Name, Short Name, Hardware Model, Role, MAC Address, Public Key, Firmware β that another source's record for the same physical node can fill, and lets you copy the missing data across sources. It generalizes the per-node "Copy NodeInfo" dialog (available from the node detail panel) into a batch view spanning every source you can read.
What gets analyzed β
For every node seen on two or more of your permitted sources, the report checks the fields above for blanks. Each row in the report table is one (node, target source) pair β a specific source's record for a specific node that is missing at least one field β and lists:
- The fillable fields for that row (only the ones actually blank on the target and present on the donor)
- The donor source: whichever other source holds the most complete record for that node. Completeness is ranked by number of non-blank fields, with the most recently updated record winning ties.
Running the report β
- Open the dashboard, click Analysis & Reports in the sidebar.
- Click the NodeInfo Enrichment card.
- Review the summary tiles β Nodes, Targets, Fillable fields β above the table.
- Click anywhere on a row to preview the copy in the standard Copy NodeInfo dialog: the row's donor source is preselected, its fillable fields are pre-checked, and the current vs. incoming value of every field is shown side by side. You can adjust the field selection or switch donors before confirming β note that unlike the row's Fix button, fields you check by hand in the dialog may overwrite existing values.
- Click Fix on a row to apply just that (node, target source) pair, or Fix All to apply every row currently listed.
- Click Refresh to re-run the analysis, e.g. after applying fixes or after new NodeInfo has arrived over the mesh.
Applying fixes β
Applying a row is fill-blanks-only: it writes only the fields that are currently empty on the target source's record and never overwrites a field that already has a value β even if the analysis backing the row has gone stale since the page loaded. This makes Fix and Fix All safe to click at any time.
An Also push to device NodeDB toggle (default off) sits above the table. When enabled, each node that gets enriched also receives the same NodeInfo-exchange nudge as the manual Copy NodeInfo dialog's push option (sendNodeInfoRequest on the node's channel). This generates mesh traffic, so leave it off if you only want to update MeshMonitor's own database.
Because a row reflects only its single best donor, fixing it can uncover further blanks: if that donor didn't have every missing field, the row disappears but the node may resurface with a different (partial) donor on the next analysis. Click Refresh (or re-run the report) until the table drains to fully converge a node across all sources.
On-demand only. This report has no background scheduler β it runs interactively whenever you open the card or click Refresh/Fix.
ok_to_mqtt Violations β
Reviews ok_to_mqtt violation history across every MQTT source you can read, so you can find which gateways on your mesh are relaying opted-out traffic without needing to enable the MQTT Packet Monitor on any of them. See ok_to_mqtt violation detection for what the bit means and why a violation is almost always a gateway misconfiguration, not malicious intent.
This report is cross-source: a single run scans every MQTT source you have permission to read and aggregates the results into one gateway list. Because a scan spans multiple sources, it does not run automatically β pick a window and click Run report.
What gets analyzed β
The report distinguishes two kinds of evidence, with different time horizons, and is explicit about which one you're looking at:
- Confirmed violations come from a durable record kept for up to 90 days: a gateway relayed a packet whose
ok_to_mqttbit was explicitly clear and the gateway was not the packet's originator. This is proof. - Suspected violations ("unreadable bit") are packets whose
ok_to_mqttbit could not be read at all β usually because MeshMonitor has no channel key to decrypt them. These come from the MQTT Packet Monitor's capture log, which by default only reaches back 24 hours and must be enabled to contribute anything. A suspected count is not proof of a violation β it only means the bit couldn't be evaluated either way. Suspected rows are included only when you turn on Include unproven (unreadable bit) (off by default), and the report shows a separate Suspected column and an on-screen note about the shorter horizon whenever it's on.
Running the report β
- Open the dashboard, click Analysis & Reports in the sidebar.
- Click the ok_to_mqtt Violations card.
- Set the Window β either a preset (24 h / 7 days / 30 days / 90 days) or an explicit From/To date range β and optionally check Include unproven (unreadable bit).
- Click Run report.
Gateway summary table β
One row per offending gateway, identified by its raw !hexid rather than a friendly name β resolving names would mean an extra request per source for a cosmetic label, so the report doesn't do it. Columns: Gateway, Confirmed violation count, Suspected count (only shown when the unproven toggle is on), distinct Originators affected, which Sources saw the gateway, and First seen / Last seen timestamps. The table is sortable by gateway, confirmed count, originators, and last seen, and paginated.
Drill-down β
Click a gateway row to expand it into that gateway's individual violating packets β time, source, originator, channel, port, packet ID, bitfield, and MQTT topic β sortable and paginated independently of the summary table.
Result caps β
A single scan returns at most 2,000 rows. When a scan hits that ceiling, the report shows the count as 2,000+ instead of a number it can't vouch for, and paging is limited accordingly. If you sort ascending on a capped scan, be aware the retained rows are the highest-count / most-recent ones β an ascending sort over a capped result does not show the true bottom of the full window. Narrowing the window (or the set of sources) is the reliable way to see the rest; this limitation is tracked as #4330.
CSV export β
Both the gateway summary and an individual gateway's drill-down can be exported to CSV. Exports cover the whole filtered result set, not just the page you're viewing β but they're subject to the same 2,000-row cap as the on-screen scan. If a matching set is larger than what was exported, the report tells you how many rows were exported versus how many matched, rather than silently truncating the file.
Empty is good news β
No violations in the selected window means no gateway on your mesh was caught relaying opted-out traffic β that's the expected, healthy outcome. Detection is also forward-only: it only covers traffic received after upgrading to a MeshMonitor version with this feature, so a freshly upgraded install should expect an empty report until new MQTT traffic arrives.
Permissions β
Solar Monitoring β
Both solar endpoints are scoped to the requesting user's permitted source IDs:
- Admins see all enabled sources
- Non-admin users see sources where they hold
nodes:read - An optional
?sources=id1,id2query param restricts the analysis further (intersected with permitted IDs)
NodeInfo Enrichment β
- Analysis (
GET /api/nodes/enrichment/analysis) only ever computes over sources the caller holdsnodes:readon β admins see every source, anonymous/unauthenticated callers see none (an empty, but never a403, response). - Applying (
POST /api/nodes/enrichment/apply) requiresnodes:readon the donor source andnodes:writeon every target source referenced by the request. A single missing grant rejects the whole batch with403 FORBIDDENβ partial application of an under-permissioned batch never happens.
ok_to_mqtt Violations β
Both endpoints (GET /api/analysis/mqtt-violations/gateways and /packets) scope every scan to the MQTT sources the requesting user holds packetmonitor:read on β the same permission that gates the MQTT Packet Monitor itself. Admins see every enabled source; a user with no source permissions (including signed-out/Anonymous, if that permission was revoked) gets a 200 response with an empty source list rather than an error, which the report renders as "no sources available to you" instead of "no violations found" β the two are distinguishable without an extra request.
The page itself is publicly routable for all three reports; only the underlying data is gated.
API β
Solar Monitoring β
Two endpoints under the existing /api/analysis/* namespace:
GET /api/analysis/solar-nodes?lookback_days=N&sources=β¦GET /api/analysis/solar-forecast?lookback_days=N&sources=β¦
See the REST API reference for the full request/response shapes.
NodeInfo Enrichment β
Two endpoints under /api/nodes/enrichment/* (also mirrored at /api/v1/nodes/enrichment/*), using the standard { success, data } response envelope:
GET /api/nodes/enrichment/analysisβ returns{ nodes: [...], summary: { nodeCount, targetCount, fieldCount } }.POST /api/nodes/enrichment/applyβ body{ items: [{ nodeNum, targetSourceId, donorSourceId }], pushToNodeDb? }, returns{ applied: [...], totalFieldsCopied }.
Error codes: INVALID_REQUEST / INVALID_ITEM (400, malformed request body), FORBIDDEN (403, missing nodes:read/nodes:write β includes a missing list), ENRICHMENT_ANALYSIS_FAILED / ENRICHMENT_APPLY_FAILED (500).
ok_to_mqtt Violations β
Two endpoints under /api/analysis/*, using the standard { success, data } response envelope:
GET /api/analysis/mqtt-violations/gatewaysβ the gateway summary, paginated withlimit/offset, sortable viasort/dir(violationCount,lastSeen,distinctOriginators,gatewayId).GET /api/analysis/mqtt-violations/packets?gateway=!hexidβ one gateway's drill-down, sortable viasort/dir(timestamp,fromNode,gatewayId).
Both accept lookbackDays (1β365) or an explicit since/until (epoch ms) window, an includeUnknown flag to fold in suspected rows, and an optional sources filter. Both responses cap a single scan at 2,000 rows and echo the resolved window and source list, so the client can tell "no violations in this window" apart from "no sources you can read" and can detect when the 2,000-row cap was hit.
Error codes: INVALID_RANGE (400, since after until), INVALID_SORT_FIELD / INVALID_SORT_DIRECTION (400), MQTT_VIOLATIONS_FETCH_FAILED (500).
Related β
- Solar Monitoring β configuration of the forecast.solar integration that powers the production curve and forecast factor
- Packet Monitor β how
ok_to_mqttviolation detection works, including the per-packet badge in the MQTT Packet Monitor that this report complements - Map Analysis β cross-source map / coverage workspace at
/analysis - Analytics β analytics dashboards in the per-source view