Add environmental telemetry collection (temperature, humidity, barometric
pressure, voltage) from both the repeater node (over LoRa) and companion
node (local serial). Telemetry is stored in the same EAV metrics table
with `telemetry.` prefix.
Key changes:
- Add TELEMETRY_ENABLED feature flag (defaults to OFF)
- Add telemetry-specific timeout/retry settings
- Create shared telemetry.py module with extract_lpp_from_payload()
and extract_telemetry_metrics() helpers
- Handle MeshCore API dict payload format: {'pubkey_pre': '...', 'lpp': [...]}
- Repeater: store status metrics BEFORE attempting telemetry (LoRa reliability)
- Companion: merge telemetry into single DB write (serial is reliable)
- Telemetry failures do NOT affect circuit breaker state
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The container runs with read_only: true for security hardening, but
fontconfig needs a writable cache directory. Added tmpfs mount at
/var/cache/fontconfig to allow fontconfig to write its cache without
compromising the read-only filesystem security.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Implements fcntl.flock() based locking for serial transport to prevent
USB serial conflicts when collect_companion and collect_repeater run
simultaneously. This addresses Ofelia's limitation where no-overlap
only prevents a job from overlapping with itself, not other jobs.
Key changes:
- Add connect_with_lock() async context manager to meshcore_client.py
- Use non-blocking LOCK_NB with async polling to avoid freezing event loop
- Only lock for serial transport (TCP/BLE don't need it)
- 60s timeout with clear error message if lock cannot be acquired
- Update collector scripts to use new context manager
- Remove external flock from cron examples (now handled in Python)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Completely restructured README.md to prioritize Docker installation:
- Added Quick Start section with copy-pasteable commands
- Reorganized with Docker as recommended, manual as alternative
- Added Platform Notes (Linux/macOS/Windows) with collapsible sections
- Streamlined configuration reference table
- Added troubleshooting table with common issues
- Included resource requirements and backup instructions
- Moved metrics reference to CLAUDE.md (linked from docs section)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
GITHUB_TOKEN cannot trigger other workflows by design. Using a
fine-grained PAT (RELEASE_PLEASE_TOKEN) scoped to this repo only
allows releases to properly trigger the docker-publish workflow.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Docker Compose merges arrays instead of replacing them, so having
a default device in docker-compose.yml caused conflicts with override
files. Serial device configuration now requires docker-compose.override.yml,
which is cleaner since:
- Device paths vary per system
- TCP transport users don't need devices at all
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rename to shorter docker-compose.dev.yml
- Add docker-compose.override.yml to .gitignore for local customizations
- Document override file pattern for device path customization
- Update README and CLAUDE.md with new naming convention
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change Python path defaults to Docker paths (/data/state, /out)
- Remove STATE_DIR/OUT_DIR from Dockerfile ENV (Python defaults now correct)
- Remove REPEATER_FETCH_ACL feature (unsupported)
- Fix nginx tmpfs permissions with uid=101,gid=101
- Remove Ofelia [global] save=true (caused config parse error)
- Switch to bind mounts for ./out instead of named volume
- Comment out devices section (not available on macOS Docker)
- Add TCP and BLE transport options to meshcore.conf.example
- Document correct macOS socat command for serial-over-TCP
- Update README with macOS Docker workaround instructions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Multi-stage Dockerfile with Python 3.12 + Ofelia scheduler
- docker-compose.yml for production (ghcr.io image)
- docker-compose.development.yml for local builds
- GitHub Actions workflow for multi-arch builds (amd64/arm64)
- Security hardening: non-root user, cap_drop, read_only filesystem
- Trivy vulnerability scanning and SBOM generation
- Nightly rebuilds for OS security patches
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Simplify setup by having Python automatically load configuration from
meshcore.conf at module import time. This eliminates the need to source
config files in cron jobs or use direnv.
- Add _load_config_file() to env.py that parses shell-style config
- Environment variables always take precedence (Docker-friendly)
- Rename .envrc.example to meshcore.conf.example (no direnv dependency)
- Update cron examples to use flock for USB serial locking
- Simplify documentation to use traditional .venv/ virtualenv
BREAKING CHANGE: Configuration file renamed from .envrc to meshcore.conf.
Users must copy meshcore.conf.example to meshcore.conf and migrate their
settings. The new file format is the same (shell-style exports) but
without the direnv-specific "layout python3" command.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
A Python-based monitoring system for MeshCore LoRa mesh networks.
Collects metrics from companion and repeater nodes, stores them in
a SQLite database, and generates a static website with interactive
SVG charts and statistics.
Features:
- Data collection from local companion and remote repeater nodes
- SQLite database with EAV schema for flexible metric storage
- Interactive SVG chart generation with matplotlib
- Static HTML site with day/week/month/year views
- Monthly and yearly statistics reports (HTML, TXT, JSON)
- Light and dark theme support
- Circuit breaker for unreliable LoRa connections
- Battery percentage calculation from 18650 discharge curves
- Automated releases via release-please
Live demo: https://meshcore.jorijn.com