Change debug logging to info level so contact processing is visible
in default log output. This helps verify that contact events are
being received and processed correctly.
Instead of sending all contacts in one MQTT message, the interface
now splits the device's contacts response into individual 'contact'
events. This is more consistent with other event patterns and makes
the collector simpler.
Interface changes:
- Add _publish_contacts() to split contacts dict into individual events
- Publish each contact as 'contact' event (not 'contacts')
Collector changes:
- Rename handle_contacts to handle_contact for single contact
- Simplify handler to process one contact per message
- Register handler for 'contact' events
The device sends contact entries with different field names than
originally expected:
- adv_name (not name) for the advertised node name
- type (numeric: 0=none, 1=chat, 2=repeater, 3=room) instead of node_type
Changes:
- Update handle_contacts to extract adv_name and convert numeric type
- Add NODE_TYPE_MAP for type conversion
- Always update node name if different (not just if empty)
- Add debug logging for node updates
- Update ContactInfo schema with actual device fields
Move _setup_event_subscriptions() from run() to connect() so that
event subscriptions are active before get_contacts() is called during
device initialization. Previously, CONTACTS events were lost because
subscriptions weren't set up until run() was called.
Trigger get_contacts() during receiver initialization to fetch the
device's contact database and broadcast CONTACTS events over MQTT.
This enables the collector to associate broadcast node names with
node records in the database.
Changes:
- Add get_contacts() abstract method to BaseMeshCoreDevice
- Implement get_contacts() in MeshCoreDevice using meshcore library
- Implement get_contacts() in MockMeshCoreDevice for testing
- Call get_contacts() in Receiver._initialize_device() after startup
- Replace JSON seed files with YAML format for better readability
- Auto-detect YAML primitive types (number, boolean, string) from values
- Add automatic seed import on collector startup
- Split lat/lon into separate tags instead of combined coordinate string
- Add PyYAML dependency and types-PyYAML for type checking
- Update example/seed and contrib/seed/ipnet with clean YAML format
- Update tests to verify YAML primitive type detection
- Add Member database model with name, callsign, role, description, contact, and public_key fields
- Add Member Pydantic schemas (MemberCreate, MemberUpdate, MemberRead, MemberList)
- Add members table to initial migration
- Add members API endpoints (GET/POST/PUT/DELETE /api/v1/members)
- Add member_import.py for importing from JSON files
- Update web layer to fetch members from API instead of file
- Add SEED_HOME setting (defaults to ./seed) for seed data files
- Add 'collector seed' command to import node_tags.json and members.json
- Rename tags.json to node_tags.json for consistency
- Move example seed data from example/data/* to example/seed/
- Update tests and configuration
Change tag import format from flat list with repeated public_keys to an
object keyed by public_key with nested tags. This makes the JSON more
intuitive and reduces redundancy.
New format supports both shorthand (string values) and full format
(with value and type):
{
"0123456789abcdef...": {
"friendly_name": "My Node",
"location": {"value": "52.0,1.0", "type": "coordinate"}
}
}
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Display friendly_name tags for nodes throughout web UI:
- nodes.html: Show friendly_name in node list table
- node_detail.html: Show in breadcrumb and page title
- network.html: Show in recent advertisements (24h stats)
- messages.html: Show sender friendly_name for direct messages
- map.py: Include friendly_name in map popup data
- API changes:
- dashboard.py: Look up friendly_name tags for recent advertisements
- messages.py: Look up sender friendly_name by pubkey_prefix
- Fix collector CLI import-tags command:
- Remove click.Path(exists=True) to allow optional file argument
- Add manual file existence check in function
- Add DATABASE_URL= to docker-compose.yml.example to prevent
host environment variable from overriding computed defaults
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add tag_import.py module with JSON file parsing and database upsert
- Convert collector CLI to group with subcommands for extensibility
- Add 'import-tags' command to import tags from JSON file
- Update docker-compose.yml.example with separated data directories:
- data/collector for tags.json
- data/web for members.json
- Add import-tags Docker service for easy containerized imports
- Add example data files in example/data/collector and example/data/web
- Add comprehensive test coverage (20 tests) for tag import
Add environment variable configuration for forwarding events to external
HTTP endpoints. Supports separate webhooks for advertisements, channel
messages, and direct messages with configurable timeouts, retries, and
authentication via X-Webhook-Secret header.
- Add webhook fields to CollectorSettings (config.py)
- Add create_webhooks_from_settings() to build webhooks from env vars
- Integrate WebhookDispatcher into Subscriber with background processing
- Update collector CLI to load and display webhook configuration
- Document webhook settings in README, AGENTS.md, .env.example
- Update docker-compose.yml.example with webhook env vars
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add --e2e flag to pytest to run e2e tests
- E2E tests skip by default with clear message
- Fix type annotations in webhook.py for mypy compliance
- Add proper type hints for comparison operations
- Add WebhookDispatcher for sending events to external services
- Webhook configuration loading from dict config
- JSONPath-like filter expression support for event filtering
- Async HTTP POST sending with httpx
- Retry logic with exponential backoff
- Comprehensive test suite
- Add health check infrastructure for Interface and Collector
- HealthReporter class for periodic status file updates
- CLI commands: meshcore-hub health interface/collector
- Updated Docker Compose to use CLI health checks
- File-based health status for non-HTTP components
- Update TASKS.md progress to 99% (218/221 tasks)
- Remaining 3 tasks are optional (docs/ directory)
Health Checks (6.3):
- Add is_healthy property and get_health_status() to Receiver/Sender
- Add is_healthy property and get_health_status() to Collector Subscriber
- Track device, MQTT, and database connection status
Documentation (6.5):
- Update README with Docker Compose profiles documentation
- Add serial device access instructions
- Update API documentation URLs and add health check info
CI/CD (6.6):
- Add .github/workflows/ci.yml for linting, testing, and building
- Add .github/workflows/docker.yml for Docker image builds
- Support Python 3.11 and 3.12 in CI matrix
- Configure Codecov for coverage reporting
End-to-End Testing (6.7):
- Add tests/e2e/ directory with Docker Compose test configuration
- Add e2e test fixtures with service health waiting
- Add comprehensive e2e tests for API, Web, and auth flows
The dashboard router was mounted with prefix /dashboard and the HTML
route was also /dashboard, making the full path /api/v1/dashboard/dashboard.
Changed the route to / so it's accessible at /api/v1/dashboard.
- Update .flake8 and pre-commit config to properly use flake8 config
- Add B008 to ignored errors (FastAPI Depends pattern)
- Add E402 to ignored errors (intentional module-level imports)
- Remove unused imports from test files and source files
- Fix f-strings without placeholders
- Add type annotations to inner async functions
- Fix SQLAlchemy execute() to use text() wrapper
- Add type: ignore comments for alembic.command imports
- Exclude alembic/ directory from mypy in pre-commit
- Update mypy overrides for test files to not require type annotations
- Fix type annotations for params dicts in web routes
- Fix generator return type in test fixtures
- Return 401 instead of 403 for invalid API keys in require_read
- Add /dashboard prefix to dashboard router so routes are at /api/v1/dashboard/*
- Rename message filter param from 'type' to 'message_type' for clarity
- Add GET /nodes/{public_key}/tags/{key} endpoint for single tag retrieval
- Fix event payload extraction: use event.payload instead of event.attributes
to get full message data (text, pubkey_prefix, etc.)
- Fix command API: use mc.commands.* pattern instead of importing functions
- Add device initialization on receiver startup:
- Set hardware clock to current Unix timestamp
- Send local (non-flood) advertisement
- Start automatic message fetching via start_auto_message_fetching()
- Add set_time() and start_message_fetching() methods to device interface
- Update AGENTS.md with meshcore library integration documentation
- Update README.md with correct meshcore library reference
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Use meshcore library's self_info property directly after connection
instead of waiting for SELF_INFO event (which was already processed)
- Add --node-address CLI option to override device public key/address
- Support NODE_ADDRESS environment variable
- Add node_address parameter to DeviceConfig and all related functions
CLI fixes:
- Add --mqtt-username and --mqtt-password options to receiver/sender
shortcut commands so they work with authenticated MQTT brokers
- These options read from MQTT_USERNAME/MQTT_PASSWORD env vars
Device integration:
- Integrate with meshcore>=2.2.0 library for actual serial device support
- Implement async-to-sync bridge for meshcore's async API
- Add proper event subscription mapping between meshcore and hub events
- Add meshcore>=2.2.0 to dependencies in pyproject.toml
- Add load_dotenv() call at module import to load .env before Click
processes envvar parameters
- Add python-dotenv>=1.0.0 as explicit dependency in pyproject.toml
Click's envvar only reads from OS environment, not .env files.
By loading dotenv early, all CLI commands now respect .env configuration.
Add web dashboard with FastAPI and Jinja2 templates for visualizing
network status, nodes, messages, and members with an interactive map.
Features:
- FastAPI app with Jinja2 templating and httpx client for API
- Responsive UI using Tailwind CSS with DaisyUI components
- Interactive map with Leaflet.js for node visualization
- Pages: home, network stats, nodes list/detail, messages, map, members
- CLI with extensive configuration (network info, API, members file)
- Development mode with uvicorn auto-reload support
- Add FastAPI application with lifespan management
- Implement bearer token authentication (read/admin levels)
- Create comprehensive REST API routes:
- Nodes: list, get by public key
- Node tags: CRUD operations
- Messages: list with filters, get by ID
- Advertisements: list with filters, get by ID
- Telemetry: list with filters, get by ID
- Trace paths: list with filters, get by ID
- Commands: send message, channel message, advertisement
- Dashboard: stats API and HTML dashboard
- Add API CLI command for running the server
- Create API test suite with 44 passing tests
Routes use proper RESTful status codes (201 Created, 204 No Content).
Authentication is optional - when keys not configured, endpoints are open.
This commit establishes the complete foundation for the MeshCore Hub project:
- Project setup with pyproject.toml (Python 3.11+, all dependencies)
- Development tools: black, flake8, mypy, pytest configuration
- Pre-commit hooks for code quality
- Package structure with all components (interface, collector, api, web)
Common package includes:
- Pydantic settings for all component configurations
- SQLAlchemy models for nodes, messages, advertisements, traces, telemetry
- Pydantic schemas for events, API requests/responses, commands
- MQTT client utilities with topic builder
- Logging configuration
Database infrastructure:
- Alembic setup with initial migration for all tables
- Database manager with session handling
CLI entry point:
- Click-based CLI with subcommands for all components
- Database migration commands (upgrade, downgrade, revision)
Tests:
- Basic test suite for config and models
- pytest fixtures for in-memory database testing