151 Commits

Author SHA1 Message Date
yellowcooln af88459f29 merge: sync with openHop refactor base 2026-06-24 20:35:06 -04:00
Lloyd 2b67dea96b refactor:rename-project-to-openhop 2026-06-24 23:27:49 +01:00
yellowcooln 44a2d2f72f feat: add system info to hardware stats sensor 2026-06-24 16:23:50 -04:00
yellowcooln b4b44831b0 feat: integrate openHop Glass inform policy sync 2026-06-24 15:17:41 -04:00
Lloyd c761c425d1 Merge branch 'pr-294' into dev 2026-06-23 08:56:33 +01:00
agessaman cd763e2cac feat: implement threaded stats broadcasting in StorageCollector
- Introduced a dedicated thread for broadcasting aggregate statistics to WebSocket clients, improving performance by offloading heavy queries from the packet write path.
- Updated the _publish_packet_sync method to ensure only fast, per-packet operations are executed, while the stats are handled separately.
- Added methods for broadcasting stats at intervals and managing the lifecycle of the stats thread.
- Enhanced unit tests to validate the new stats broadcasting behavior and ensure correct functionality under various conditions.
2026-06-18 21:38:20 -07:00
agessaman 2435757197 feat: enhance SQLiteHandler and StorageCollector for improved caching and async performance
- Added caching for packet type stats and cumulative counts in SQLiteHandler to reduce database load.
- Implemented a dedicated writer thread in StorageCollector to handle blocking storage operations, preventing asyncio event loop stalls.
- Updated record_packet method to utilize the new writer thread for efficient packet processing.
2026-06-18 21:25:19 -07:00
Yellowcooln 5d3733a960 fix: satisfy pre-commit for modem HTTP telemetry 2026-06-14 20:08:29 -04:00
Yellowcooln 642c019076 fix: preserve modem GPS satellites in view 2026-06-14 19:32:41 -04:00
Yellowcooln dde487bce3 fix: add generic PyMC modem stats sensor 2026-06-14 18:00:15 -04:00
Lloyd 00682e8086 Merge pull request #282 from agessaman/companion/advanced-settings 2026-06-06 18:09:00 +01:00
agessaman dac60443f0 feat(companion): implement contact trimming and retention policies
- Introduced `enforce_companion_contact_capacity` to manage contact limits during companion loading, with an option to trim non-favourite contacts when exceeding capacity.
- Updated `SQLiteHandler` to support message retention limits, allowing for automatic trimming of older messages based on `offline_queue_size`.
- Enhanced API endpoints to handle contact trimming on overflow, providing feedback on trimmed contacts during updates.
- Added utility functions for selecting and trimming contacts while preserving favourites.
- Improved logging for contact management actions and errors related to capacity.
2026-06-05 21:39:11 -07:00
Rightup 14b4804c26 feat: Enhance logging system and introduce policy management endpoints
- Updated LogBuffer to support log entry IDs, enhanced log entry structure with additional metadata, and implemented subscriber management for real-time log streaming.
- Added OpenAPI specifications for new endpoints related to policy management, including retrieval, updating, validation, and group management for network policies.
- Implemented comprehensive tests for new policy endpoints, ensuring correct behavior for creating, updating, validating, and deleting policy groups and entries.
- Introduced policy evaluation tests to validate the functionality of the PolicyEngine, including various scenarios for action decisions based on defined rules.
- Enhanced packet routing tests to ensure proper handling of policy decisions in packet processing.
2026-06-04 15:53:17 +01:00
agessaman 499f871262 Merge upstream/dev into companion/advanced-settings
Integrate latest dev while preserving per-companion bridge settings
and contact capacity validation. Resolve import conflicts in main.py
and api_endpoints.py.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-02 15:34:52 -07:00
Adam Gessaman 7d57b34a04 feat(companion): enhance contact capacity management and bridge settings
- Introduced `CompanionContactCapacityError` to handle cases where persisted contacts exceed configured limits.
- Added utility functions for parsing companion bridge settings and validating contact capacity.
- Updated `RepeaterDaemon` to check contact capacity during companion loading and initialization.
- Enhanced API endpoints to validate companion settings and manage contact limits effectively.
- Implemented logging for bridge limits and errors related to contact capacity.
2026-06-02 07:42:40 -07:00
Rightup 60ca184dbd refactor: enhance security comments and error handling across multiple modules 2026-05-27 22:07:34 +01:00
Lloyd 45a44eb47b Refactor test cases and base code for consistency and readability
- Updated byte representations in tests to use lowercase hex format for consistency.
- Reformatted code for better readability, including line breaks and indentation adjustments.
- Consolidated multiple lines into single lines where appropriate to enhance clarity.
- Ensured that all test cases maintain consistent formatting and style across the test suite.
2026-05-27 20:15:10 +01:00
Lloyd 62f35c4b45 fix: update transport key generation to use 16-byte length and add corresponding test 2026-05-27 14:27:59 +01:00
Zindello a1c66100f5 fix: remove datetime.UTC from mqtt_handler and add 3.10 compat test
Replace the try/except UTC shim in mqtt_handler.py with timezone.utc
directly, consistent with the api_endpoints.py fix. Add a static AST
scan that fails if datetime.UTC is reintroduced anywhere in the codebase,
guarding against future regressions on Python 3.10 (LuckFox Pico Ultra).

Co-Authored-By: Zindello <josh@zindello.com.au>
2026-05-27 11:57:45 +10:00
Lloyd 31edaa9c76 fix: update installation scripts to use the correct branch name 2026-05-26 15:42:59 +01:00
Lloyd 37ee0e892a Add more unit tests for handler helpers, identity manager, CLI, key generation, and main functionality
- Introduced tests for TraceHelper and DiscoveryHelper to validate packet forwarding and discovery request handling.
- Implemented tests for LoginHelper to ensure identity registration and login packet processing.
- Added tests for IdentityManager to cover identity registration, lookup, and filtering.
- Created tests for MeshCLI to verify command handling, configuration setting, and error paths.
2026-05-26 13:01:38 +01:00
Lloyd 941c355deb feat: add pagination support and count retrieval for adverts by contact type 2026-05-11 13:54:55 +01:00
Lloyd 68656fccdd Merge pull request #231 from pyMC-dev/pr-227
Pr 227
2026-05-06 16:13:30 +01:00
Lloyd d250828197 feat: enhance MQTT connection handling with JWT refresh and error logging improvements 2026-05-06 13:53:12 +01:00
Lloyd bd7a305d7b feat: improve MQTT connection handling with enhanced reconnect logic and logging 2026-05-06 10:05:11 +01:00
Lloyd 5b20f5580a feat: enhance MQTT logging and error handling with payload summaries and improved disconnect messages 2026-05-06 09:53:44 +01:00
Lloyd e4efc8045d feat: implement resolve_storage_dir for consistent storage paths 2026-05-05 17:17:12 +01:00
Daniel Duran 0251304407 fix(mqtt): publish Semtech-derived packet duration instead of hard-coded "0"
Every MQTT-published packet has shipped with duration="0" since the
PacketRecord factory was introduced. The repeater already computes LoRa
time-on-air via AirtimeManager.calculate_airtime() (the canonical
Semtech reference formula) for duty-cycle gating and TX delay, but the
result was thrown away after each packet - never stored on the
packet_record dict that flows to MQTT/SQLite/Glass/websocket.

What changes
- engine.py: RepeaterHandler._build_packet_record() now computes
  airtime_ms once per packet (Semtech formula via AirtimeManager) and
  stores it as packet_record['airtime_ms']. Single source of truth for
  every downstream consumer.
- storage_utils.py: PacketRecord.from_packet_record() reads the new
  airtime_ms field and serializes it as a rounded integer in the
  'duration' field of the published JSON. Falls back to 0 if the field
  is missing (backward compatibility for any older code path).
- storage_collector.py: _publish_packet_to_mqtt() simplified - no
  recomputation, no helper. The publish path is now a passthrough.

Why
MQTT consumers (firmware-compatible analyzers, dashboards, the upstream
meshcoretomqtt project) expect the same time-on-air value the firmware
emits. Hard-coded "0" makes airtime/utilization charts derived from the
mqtt stream useless and silently diverges from firmware behavior.

Plumbing the value through packet_record (instead of recomputing in the
publish path) means any future consumer - SQLite schema, web UI charts,
Glass telemetry - reads the same number without separate calculations.

Tests
tests/test_packet_duration.py - 5 tests covering:
- backward compat (legacy packet_record without airtime_ms => '0')
- airtime_ms field flows through to duration as rounded integer string
- explicit zero stays '0'
- AirtimeManager output matches an independently-implemented Semtech
  reference for typical MeshCore EU settings (SF8/62.5kHz/CR4-8)
- low-data-rate optimization branch (SF12/125kHz triggers DE=1)

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-05-03 12:48:02 -07:00
Daniel Duran dfacfeade8 feat: bundled MC2MQTT broker presets (waev, letsmesh) + format family
Introduces a 'set format and forget' workflow for MQTT brokers. Users
reference a bundled preset by name inside the existing brokers: list,
and the package supplies the endpoints, audiences, and TLS settings.
Endpoint changes ship via 'pip install -U' instead of manual edits.

What changes
- New repeater/presets/ package with a tiny lazy YAML loader and two
  bundled presets: waev (mqtt-{a,b}.waev.app) and letsmesh (EU + US).
- New format-family constant MC2MQTT_FORMATS = ('meshcoretomqtt',
  'letsmesh', 'waev') replaces the inline tuple in topic resolution.
  The legacy 'mqtt' format keeps its custom-topic semantics unchanged.
- Two-pass broker assembly in mqtt_handler.py: pass 1 expands every
  {preset: <name>} entry inline; pass 2 collapses duplicates by name
  with later-wins semantics. Place override entries AFTER preset
  entries.
- Hard-coded LETSMESH_BROKERS constant deleted; its data now lives in
  repeater/presets/letsmesh.yaml.
- convert_letsmesh_to_broker_config() collapsed from ~70 to ~25 lines
  by emitting {preset: letsmesh} plus disable overrides for unwanted
  brokers. Honors broker_index in (-1, 0, 1), additional_brokers, and
  enabled flag exactly as before.
- update_mqtt_config API endpoint accepts {preset: <name>} entries and
  passes them through unchanged so the web UI can author them when the
  frontend is updated.
- config.yaml.example documents the preset entry shape, the override
  rule, and the format family hierarchy.
- pyproject.toml ships presets/*.yaml as package data.

How to use
  mqtt_brokers:
    iata_code: "LAX"
    brokers:
      - preset: waev

  # Override a single preset broker:
  brokers:
    - preset: waev
    - name: waev-b
      enabled: false

Tests
- tests/test_presets.py: 9 tests covering loader, expand/merge,
  MC2MQTT topic-family parity, and parametrized legacy migration.

Co-Authored-By: Oz <oz-agent@warp.dev>
2026-05-02 15:32:22 -07:00
Rightup 9d3d5e6ef0 refactor: update GPS configuration parameters and improve documentation 2026-04-30 20:28:50 +01:00
Mitchell Moss d83cb61fe7 Make GPS location persistence opt-in and fuzzed 2026-04-29 10:45:53 -04:00
Mitchell Moss da8f83964a Fix GPS location updates and status reporting 2026-04-29 09:58:13 -04:00
Mitchell Moss bf44efbfd9 feat: update repeater location from GPS fix 2026-04-29 09:36:05 -04:00
Rightup 76a9785218 Add GPS location configuration and diagnostics stream
- Introduced options for using GPS coordinates for repeater location fields in config.
- Implemented precision control for GPS coordinates.
- Added a new API endpoint for a Server-Sent Events stream of GPS diagnostics.
- Updated GPSService to handle new configuration options and fallback logic.
- Enhanced unit tests for GPS location handling.
2026-04-28 23:22:52 +01:00
Lloyd 42b4bbd9e9 Merge PR #199: [codex] Add GPS diagnostics API 2026-04-28 17:24:34 +01:00
Rigear d4aecf71c1 fix: Stop warning spam if brokers are disabled 2026-04-24 14:45:38 -07:00
Rigear b3fdfee474 fix: Handle TLS for all MQTT connections 2026-04-24 14:30:44 -07:00
Rigear 3a6da407be fix: Always parse additional brokers 2026-04-24 14:30:05 -07:00
Rigear 53f6e8af4f fix: Remove old letsmesh_handler.py file 2026-04-24 14:19:31 -07:00
Mitchell Moss 8ae1c0f65f feat: sync system time from GPS 2026-04-24 08:34:30 -04:00
Mitchell Moss 9cd2de94e8 chore: move GPS diagnostics UI to frontend repo 2026-04-24 07:41:13 -04:00
Mitchell Moss 18300cbf42 feat: add GPS diagnostics web UI 2026-04-23 18:54:19 -04:00
Lloyd 852939b701 fix: reorder MQTT error handling. 2026-04-22 14:02:42 +01:00
Lloyd 827b9a9f98 fix: improve logging for MQTT error decoding to reduce noise 2026-04-22 13:28:23 +01:00
Lloyd 9eae6ed872 Merge pull request #193 from tjdownes/perf/sqlite-wal-threadlocal
perf: thread-local SQLite connections, synchronous=NORMAL, dedup indexes
2026-04-22 11:00:50 +01:00
Lloyd 5c947e6c2e feat: enhance WebSocket handling and add throttling for stats broadcasting 2026-04-22 09:48:27 +01:00
Lloyd 96b3daf6e8 Merge pull request #196 from tjdownes/perf/rrdtool-batch
perf(rrdtool): cache get_data() result for 60 s to avoid repeated disk reads
2026-04-22 09:35:31 +01:00
Rigear c7b2b02316 fix: Fixed extra topic publishing to letsmesh 2026-04-21 21:21:13 -07:00
Rigear d318334288 Merge remote-tracking branch 'origin/fix-perform-speed' into feat/mqtt_merge 2026-04-21 20:59:42 -07:00
TJ Downes d592af6e19 fix(rrdtool): replace rrdtool.info() with self-tracked timestamp to eliminate allocation storm
Problem
-------
update_packet_metrics() called rrdtool.info() (cached for 5 s) to get the
RRD's last_update timestamp.  rrdtool.info() returns a massive Python dict:
17 data sources × 5 RRAs × ~8 fields each = ~700+ dict entries per call.
tracemalloc showed +10696 new allocations / +251 KB at this exact line,
flagged as "Investigate" in the memory diagnostics dashboard.

The rrdtool.info() approach was also unnecessarily complex: it required a
5-second secondary cache, a _pending_rrd_update buffer, and two extra
instance attributes — all to answer one question ("did we already write
this period?") that we can answer ourselves with a single integer.

Fix
---
Replace _last_rrd_info_cache / _last_rrd_info_time / _pending_rrd_update
with a single self._last_rrd_update: int = 0 that stores the timestamp of
the last successful rrdtool.update() call.  The throttle check becomes:

    if timestamp <= self._last_rrd_update:
        return

On success: self._last_rrd_update = timestamp

Zero dict allocations per call.  The only downside vs rrdtool.info() is
that _last_rrd_update resets to 0 on process restart, meaning the first
packet after a restart always triggers a write — correct behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-21 20:50:27 -07:00