Skip to content

feat(telemetry): wire up newer AirQualityMetrics fields (#3507)#3517

Merged
Yeraze merged 2 commits into
mainfrom
fix/3507-airquality-newer-fields
Jun 17, 2026
Merged

feat(telemetry): wire up newer AirQualityMetrics fields (#3507)#3517
Yeraze merged 2 commits into
mainfrom
fix/3507-airquality-newer-fields

Conversation

@Yeraze

@Yeraze Yeraze commented Jun 17, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #3507. Final issue from the protobuf.js _<digit> field-drop audit (coverage gap, not a regression).

Several newer AirQualityMetrics fields are decoded by firmware but were unwired in MeshMonitor — never graphed or labelled:

  • particles_40um (4.0µm bin — the field that prompted this) and pm40_standard (PM4.0)
  • particles_tps (typical particle size)
  • Formaldehyde sensor: form_formaldehyde / form_humidity / form_temperature
  • PM-sensor extras: pm_temperature / pm_humidity / pm_voc_idx / pm_nox_idx

What changed

  • telemetryKeys.ts — added canonical units to AIR_QUALITY_UNITS (units sourced from the proto field comments: formaldehyde in ppb, particle bins in #/0.1L, PM in µg/m³, VOC/NOx as dimensionless indices labelled like iaq, tps in µm).
  • Frontend mapsTelemetryChart.tsx (full labels), TelemetryGraphs.tsx (colors + pm40Standard added to INTEGER_TELEMETRY_TYPES), UnifiedTelemetryPage.tsx (short labels + units).

Why this is small

#3506 unified serial ingest onto the canonical normalizer and MQTT_KEY_MIGRATIONS derives from AIR_QUALITY_UNITS — so adding a unit is all it takes for both serial and MQTT to ingest these, including the underscore-before-digit particles_40um. No serial-list edit, no DB migration (these were never stored before).

Tests

  • telemetryKeys.test.ts: asserts particles_40umparticles40um and all new units.
  • meshtasticManager.airQualityParticles.test.ts: end-to-end — particles_40um / pm40_standard / pm_voc_idx now flow through processTelemetryMessageProtobuf and get stored.

Full suite: 6570 passed, 0 failed.

🤖 Generated with Claude Code

Several newer AirQualityMetrics fields were decoded by firmware but unwired in
MeshMonitor — never graphed or labelled: particles_40um (the field that
prompted this), pm40_standard, particles_tps, the formaldehyde sensor trio
(form_formaldehyde/humidity/temperature), and the PM-sensor extras
(pm_temperature/humidity/voc_idx/nox_idx).

Add canonical units in telemetryKeys.ts (CANONICAL_TELEMETRY_UNITS) and the
frontend label/color/short-label/unit maps (TelemetryChart, TelemetryGraphs,
UnifiedTelemetryPage). Because #3506 unified serial ingest onto the canonical
normalizer and MQTT_KEY_MIGRATIONS derives from AIR_QUALITY_UNITS, adding the
unit is all it takes for both transports to ingest these — including the
underscore-before-digit particles_40um.

pm40_standard / particles_40um are uint32 → added to INTEGER_TELEMETRY_TYPES;
the rest are floats. No DB migration needed (these were never stored before).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


PR Review: feat(telemetry): wire up newer AirQualityMetrics fields

Tasks:

  • Gather context
  • Read changed files
  • Analyze code quality, bugs, performance, security, test coverage
  • Post review feedback

Overall Assessment

This is a clean, well-scoped PR that properly extends AirQualityMetrics support. The architecture is sound — adding entries to AIR_QUALITY_UNITS automatically wires up both serial and MQTT ingestion paths, which is elegant. Below are some observations.


Minor Issues

UnifiedTelemetryPage.tsx — Missing particles40um and pmVocIdx/pmNoxIdx units in TYPE_UNITS

src/pages/UnifiedTelemetryPage.tsx:186-193 adds labels for pm40Standard, particlesTps, formFormaldehyde, etc. in TYPE_LABELS, and units in TYPE_UNITS — but particles40um, pmVocIdx, and pmNoxIdx are missing from TYPE_UNITS:

// TYPE_UNITS in UnifiedTelemetryPage.tsx — these entries appear missing:
particles40um: '#/0.1L',  // present in AIR_QUALITY_UNITS but missing here
pmVocIdx: 'VOC',          // present in AIR_QUALITY_UNITS but missing here
pmNoxIdx: 'NOx',          // present in AIR_QUALITY_UNITS but missing here

The inferUnit() fallback at line 206–213 won't catch these (no matching pattern), so the fleet-overview card will show no unit for those three types. The telemetryKeys.ts CANONICAL_TELEMETRY_UNITS is the correct source of truth, but UnifiedTelemetryPage maintains its own parallel TYPE_UNITS map rather than importing the canonical one.

TelemetryChart.tsx — New labels not added to TELEMETRY_LABEL_KEYS

The new fields are added to TELEMETRY_LABELS (the fallback English map, line 160–169) but not to TELEMETRY_LABEL_KEYS (the i18n translation key map, line 58–79). This is consistent with how other air-quality fields are handled in this file (none of them have i18n keys either), so this is acceptable as-is — just worth noting for a future i18n pass.


Observations

INTEGER_TELEMETRY_TYPES comment is slightly misleading (TelemetryGraphs.tsx:38-40)

The comment says // particlesTps fields are floats and are intentionally NOT listed here but then only adds pm40Standard to the set. pm40Standard is a uint32 per the protobuf, so this is correct. However particles40um is already listed at line 36 (added alongside the existing particles25um, particles50um, etc.), so the comment focusing only on pm40Standard is slightly incomplete — it doesn't mention that particles40um was also added. Minor readability nit.

Test coverage is solid. telemetryKeys.test.ts correctly asserts the normalization path for particles_40um → particles40um (the key protobuf.js snake_case edge case). meshtasticManager.airQualityParticles.test.ts provides end-to-end coverage including zero-value storage (important for particle counters). No gaps found.

No security concerns. The changes are entirely UI label/unit mapping and test code, with no external input handling, auth changes, or raw SQL.

No DB migration needed — confirmed by the PR description. These fields were never stored before, so there's nothing to backfill.


Summary

One real gap to address: particles40um, pmVocIdx, and pmNoxIdx are missing from UnifiedTelemetryPage.tsx's TYPE_UNITS map, which means those three will display without units in the fleet telemetry overview. Everything else looks correct and well-tested.

…verview (review #3517)

UnifiedTelemetryPage's TYPE_UNITS was missing particles40um / pmVocIdx / pmNoxIdx
(they'd render with no unit). Mirror AIR_QUALITY_UNITS for the whole particle
family (03–100µm) and the two PM-sensor indices so every AQ field shows a unit.
Also clarify the INTEGER_TELEMETRY_TYPES comment re: particles40um.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown

Claude finished @Yeraze's task —— View job


PR Review: feat(telemetry): wire up newer AirQualityMetrics fields

Tasks:

  • Gather context
  • Read changed files
  • Analyze code quality, bugs, performance, security, test coverage
  • Post review feedback

Overall Assessment

This PR is clean and correct. The previous review's gap (missing particles40um, pmVocIdx, pmNoxIdx from UnifiedTelemetryPage.tsx's TYPE_UNITS) has been addressed in the latest push — all three are now present at lines 189, 202, and 203 with the correct units (#/0.1L, VOC, NOx).


What was verified

UnifiedTelemetryPage.tsx — gap is closed. TYPE_UNITS now has entries for all 11 new fields: pm40Standard, particles40um, particlesTps, formFormaldehyde, formHumidity, formTemperature, pmTemperature, pmHumidity, pmVocIdx, pmNoxIdx. Short labels in TYPE_LABELS are also correct (e.g. 'CH₂O' for formaldehyde, 'Part 4.0µm' for particles40um).

TelemetryGraphs.tsx — all 10 new fields have color assignments (lines 818–827). The INTEGER_TELEMETRY_TYPES correctly includes pm40Standard and particles40um (uint32 per proto) but excludes the float fields (particlesTps, formFormaldehyde, pmVocIdx, pmNoxIdx, etc.).

TelemetryChart.tsx — fallback English labels correct. The new fields are added to TELEMETRY_LABELS consistently with adjacent entries. The intentional omission from TELEMETRY_LABEL_KEYS (i18n map) is consistent with every other air-quality field — acceptable as-is.

telemetryKeys.ts — architecture is sound. Adding to AIR_QUALITY_UNITS automatically propagates to CANONICAL_TELEMETRY_UNITS (serial + MQTT unit lookup) and MQTT_KEY_MIGRATIONS (historical row rewrite). The snakeToCamel regex handles the particles_40um underscore-before-digit case correctly.

Tests — comprehensive. telemetryKeys.test.ts line 65 asserts the normalization path for all 11 new canonical units. meshtasticManager.airQualityParticles.test.ts line 92 provides end-to-end coverage of the three fields most at risk from the protobuf.js quirk.


Pre-existing minor inconsistencies (not introduced by this PR)

These were present before and are out of scope for this PR, but flagged for awareness:

  • envCurrent unit mismatchtelemetryKeys.ts:48 stores 'A', UnifiedTelemetryPage.tsx:176 shows 'mA'. Confirmed pre-existing by mqttIngestion.test.ts:723 which asserts 'A'.
  • lux unit mismatchtelemetryKeys.ts uses 'lux', UnifiedTelemetryPage.tsx uses 'lx'. Both are valid symbols; just inconsistent.

Security / Performance

No concerns. All changes are UI label/unit mapping and tests. No external input handling, auth, raw SQL, or performance-sensitive paths are touched.

The PR is ready to merge. The gap flagged in the prior review is resolved.

View job

@Yeraze Yeraze merged commit e75298f into main Jun 17, 2026
20 checks passed
@Yeraze Yeraze deleted the fix/3507-airquality-newer-fields branch June 17, 2026 04:39
@Yeraze Yeraze mentioned this pull request Jun 19, 2026
Yeraze added a commit that referenced this pull request Jun 19, 2026
Finalize the 4.11.0 release (from 4.11.0-rc2) and bring documentation up to
date with everything new since 4.10.

Version bump across all five files: package.json, package-lock.json,
helm/meshmonitor/Chart.yaml, desktop/src-tauri/tauri.conf.json,
desktop/package.json.

Documentation:
- CHANGELOG: finalize the Unreleased section as [4.11.0], adding the
  previously-missing entries — headline MeshCore virtual node (#3540),
  MeshCore path-by-repeater-name (#3550), MeshCore map icons + filtering
  (#3563), per-node Hide from Map (#3565), telemetry time-range selector
  (#3530), newer AirQualityMetrics fields (#3517), UI/map unification
  (#3561), plus the bug-fix and documentation changes since 4.10.4.
- virtual-node.md: document the new MeshCore Virtual Node alongside the
  Meshtastic one (default port 5000, per-source enablement, admin-command
  safety, MeshCore app setup, troubleshooting) + a two-variants intro note.
- configuration/index.md, docs/index.md, README.md: mention the MeshCore
  Virtual Node in the Virtual Node feature blurbs.
- CLAUDE.md: refresh stale version header (4.10.0 -> 4.11.0) and migration
  count (84 -> 92, latest 092_add_hide_from_map_to_nodes).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Coverage: wire up newer AirQualityMetrics fields (particles_40um and adjacent)

1 participant