Django Micboard - Architecture & Developer Guide
Core Architecture
Django Micboard is a reusable Django app for monitoring multi-manufacturer wireless audio hardware. It emphasizes:
- DRY Code: Reduced duplication through registries and base classes
- Manufacturer-Agnostic Core: Plugin architecture for manufacturer-specific logic
- Multi-Tenant Safe: Site/Organization/Campus scoping with settings inheritance
- Settings Registry: Centralized typed config at each definition’s exact declared scope
Key Components
1. Settings & Configuration
The settings service provides the single access point for all Micboard settings:
from micboard.services.settings.settings_service import settings as micboard_settings
# Feature flagsif micboard_settings.msp_enabled: print("MSP mode is enabled")
# Settings from MICBOARD_CONFIG dicttimeout = micboard_settings.get("SHURE_API_TIMEOUT", default=10)
# Direct property accessallowed = micboard_settings.allow_cross_org_viewResolution Order (services/settings/settings_service.py):
- Immutable Django host setting for
MICBOARD_*deployment controls - Scoped database setting (organization, site, or manufacturer)
- Host
MICBOARD_CONFIGdictionary - App default
- Registered definition default
- Caller-provided default
2. Plugin Architecture (Manufacturer-Agnostic)
Manufacturer-specific protocol logic lives in micboard/integrations/<manufacturer>/. Shared transport, response bounds, retries, rate limiting, health behavior, and plugin contracts live in micboard/services/common/base/; the common exception hierarchy lives in micboard/exceptions.py.
micboard/ exceptions.py services/ common/base/ plugin.py # ManufacturerPlugin, cached class discovery, bound construction client.py # Verified HTTP transport bounded_transport.py rate_limiter.py integrations/ shure/ # REST, discovery, transforms, WebSocket sennheiser/ # REST, discovery, transforms, SSEObtaining a plugin:
from micboard.services.common.base.plugin import ( build_manufacturer_plugin, get_manufacturer_plugin,)
# Resolve the plugin class for a manufacturer code (cached per process)plugin_class = get_manufacturer_plugin("shure")
# Build a plugin bound to a persisted manufacturer — the one way callers get an instanceplugin = build_manufacturer_plugin(shure_obj)build_manufacturer_plugin raises ModuleNotFoundError or ImportError when a manufacturer
has no shipped integration, so every outbound path sees the same failure instead of a None
some callers branch on and others do not.
Implementing a New Plugin: Create micboard/integrations/<code>/plugin.py with a concrete, conventionally named ManufacturerPlugin subclass. For code my_manufacturer, the loader prefers MyManufacturerPlugin. Create a matching active Manufacturer row, then verify discovery with get_manufacturer_plugin("my_manufacturer"). There is no central registration map or package re-export to edit. See Manufacturer plugin development for the complete contract.
3. Multi-Tenancy
Configure multi-tenancy in your Django settings:
# Minimal (single-site)MICBOARD_MULTI_SITE_MODE = FalseMICBOARD_MSP_ENABLED = False
# Multi-site enterpriseMICBOARD_MULTI_SITE_MODE = TrueMICBOARD_SITE_ISOLATION = 'site'
# Full MSPMICBOARD_MULTI_SITE_MODE = TrueMICBOARD_MSP_ENABLED = TrueMICBOARD_SITE_ISOLATION = 'organization'Scoping Queries:
# Scope querysets explicitly to the authenticated user.devices = WirelessUnit.objects.for_user(user=request.user)4. Scoped Settings
Add custom app settings with scope-aware resolution:
from micboard.services.settings.settings_service import settings as micboard_settings
# Resolve at the setting definition's declared scopevalue = micboard_settings.get( 'CUSTOM_KEY', organization=org, site=site, manufacturer=manufacturer, default='fallback',)
# Deployment controls are host-owned and cannot be overridden by database rows.limit = micboard_settings.get('MICBOARD_REALTIME_MAX_DEVICES', 128)Models & Domains
Models are organized by business domain in micboard/models/:
micboard/models/ __init__.py # All exports audit/ # Activity logs, audit trails discovery/ # Device discovery, manufacturers hardware/ # Wireless units, chassis, chargers integrations/ # Third-party integrations locations/ # Buildings, rooms, zones monitoring/ # Alerts, performers, assignments realtime/ # WebSocket connections rf_coordination/ # Frequency bands, channels telemetry/ # Samples, sessions, health users/ # User profiles, permissionsServices & Business Logic
Core services in micboard/services/:
common/base/plugin.py: Manufacturer plugin resolution and constructionsettings/settings_service.py: Unified host and scoped settings resolutionsettings/registry.py: Internal database-backed scope resolutionsettings/persistence_service.py: Authorized scoped setting writeshardware/wireless_chassis_persistence_service.py: Typed chassis create/update/upsert boundaryhardware/chassis_lifecycle_service.py: Chassis save transitions and committed side effectshardware/chassis_regulatory_service.py: Band-plan detection, enrichment, and coveragecore/hardware.py: Hardware query and synchronization facadecore/hardware_sync.py: Hardware status and channel synchronizationsync/polling_api.py: Direct API pollingsync/discovery_service.py: Device discoverymonitoring/alerts.py: Alert managementcore/performer_assignment.py: Performer assignment
Testing
Run the full test suite:
uv run --no-sync pytest # All testsuv run --no-sync pytest tests/test_chargers.py # Specific test fileuv run --no-sync pytest -m unit # Unit tests onlyuv run --no-sync pytest -m integration # Integration testsuv run --no-sync pytest --cov=micboard # With coverageTest markers (see pyproject.toml):
unit: Fast, isolated testsintegration: Slower, external dependenciese2e: Full workflow testsslow: Long-running testsplugin: Plugin-specific testsdjango_db: Requires database
Best Practices for Contributors
- Always use
SettingsServicefor settings reads and the persistence service for writes - Extend base classes for models, views, services
- Add type hints for all public functions
- Document scope requirements (tenant, site, org)
- Test multi-tenant behavior in integration tests
- Avoid hard-coded manufacturer names – use plugins
- Don’t modify migrations – create new ones only if schema changes
Release Checklist
- All tests pass:
uv run --no-sync pytest --cov=micboard --cov-branch --cov-fail-under=95 - Ruff checks:
uv run --no-sync ruff check . - Prek hooks:
uv run --no-sync prek run --all-files - No tracked dev artifacts (db.sqlite3, .env, egg-info)
- CHANGELOG.md updated
- Version number updated in
pyproject.toml(micboard.__version__reads package metadata)
