Skip to content

Services

The typed service layer is the supported write path. Services own business logic, enforce object scope, and accept Pydantic command objects.

micboard.services.core.hardware

Source

Manufacturer-neutral normalization for hardware payloads.

NormalizedHardware

Bases: PydanticBaseDTO

Source

Normalized hardware payload independent of manufacturer key names.

canonicalize_mac(value: Any) -> str

Source

Canonicalize hardware identity on creation and assignment.

from_api(data: dict[str, Any]) -> NormalizedHardware | None

Source

Best-effort normalization for heterogeneous vendor payloads.

micboard.services.core.hardware_lifecycle

Source

Device lifecycle management service.

Handles all device state transitions, validation, and bi-directional sync with manufacturer APIs. Replaces signal-based state management with direct, testable method calls.

State Transitions: DISCOVERED → PROVISIONING → ONLINE ↓ DEGRADED → MAINTENANCE ↓ OFFLINE → RETIRED

HardwareStatus

Bases: StrEnum

Source

Device lifecycle states.

HardwareLifecycleManager

Source

Centralized manager for device lifecycle operations.

Handles:

  • State transitions with validation
  • Bi-directional sync with manufacturer APIs
  • Health monitoring

Does NOT use signals for state management (signals only for broadcasts).

transition_device(device: WirelessChassis | WirelessUnit, to_status: str, reason: str = '', metadata: dict[str, Any] | None = None) -> bool

Source

Transition a device on the database that supplied the instance.

mark_online(device: WirelessChassis | WirelessUnit, health_data: dict[str, Any] | None = None) -> bool

Source

Mark device as online/operational.

mark_offline(device: WirelessChassis | WirelessUnit, reason: str = 'Not responding') -> bool

Source

Mark device as offline.

map_api_state_to_status(api_state: str, current_status: str) -> str

Source

Map manufacturer API state to HardwareStatus.

micboard.services.core.hardware_sync

Source

Hardware sync operations for status, battery, and channel management.

Provides write operations that synchronize hardware state from external sources.

HardwareSyncService

Source

sync_hardware_status(obj: WirelessChassis | WirelessUnit, online: bool) -> None

Source

Update hardware online status.

Uses direct status update - lifecycle hooks handle timestamps, audit, broadcast.

ensure_channel_count(chassis: WirelessChassis, using: str = DEFAULT_DB_ALIAS) -> tuple[int, int]

Source

Ensure RFChannel rows for a chassis match its model capacity.

Returns (created_count, deleted_count).

micboard.services.core.performer_assignment

Source

Performer assignment service layer for binding devices to performers.

Handles assignment lifecycle, state transitions, and lookup helpers.

PerformerAssignmentService

Source

Business logic for performer-to-device assignments.

get_visible_assignments(user: Any) -> QuerySet[PerformerAssignment]

Source

Return user-scoped assignments with all row relations eager loaded.

get_visible_assignment_rows(user: Any, page: int | str | None = 1) -> QuerySet[PerformerAssignment]

Source

Return one bounded live-refresh slice without a count query.

get_preferred_active_assignments_for_units(user: Any, unit_ids: Collection[int]) -> QuerySet[PerformerAssignment]

Source

Return at most one deterministic active assignment for each requested unit.

get_preferred_active_assignments_for_serials(user: Any, serial_numbers: Collection[str]) -> QuerySet[PerformerAssignment]

Source

Return at most one deterministic active assignment for each requested serial.

ensure_group_can_manage_unit(group: MonitoringGroup, unit: WirelessUnit) -> None

Source

Require the selected group to cover the unit in tenant-aware deployments.

ensure_can_modify_unit(user: Any, unit: WirelessUnit) -> None

Source

Require an MSP role that permits assignment changes for the unit.

create_assignment(command: CreatePerformerAssignment, user: Any) -> PerformerAssignment

Source

Create an assignment after validating every object against user scope.

update_assignment(command: UpdatePerformerAssignment, user: Any) -> PerformerAssignment

Source

Update fields on an existing assignment and return the instance.

Raises PerformerAssignment.DoesNotExist if the assignment is missing.

delete_assignment(assignment_id: int, user: Any) -> bool

Source

Permanently delete an assignment. Returns True if deleted, False if not found.

deactivate_assignment(assignment_id: int, user: Any) -> bool

Source

Deactivate an existing assignment.

micboard.services.core.performer_assignment_dtos

Source

Validated commands for performer assignment writes.

CreatePerformerAssignment

Bases: PydanticBaseDTO

Source

Data required to bind a performer, wireless unit, and monitoring group.

UpdatePerformerAssignment

Bases: PydanticBaseDTO

Source

Partial assignment update with omission distinct from false or blank.

micboard.services.core.device_metadata

Source

Device metadata accessor pattern for manufacturer-agnostic metadata handling.

Provides strategy pattern for accessing manufacturer-specific metadata from DiscoveredDevice models without hardcoding assumptions about metadata structure.

DeviceMetadataAccessor

Bases: ABC

Source

Abstract base for accessing manufacturer-specific metadata.

get_compatibility_status() -> str | None

Source

Get device compatibility status (e.g., COMPATIBLE, INCOMPATIBLE_TOO_OLD).

get_device_state() -> str | None

Source

Get device state (e.g., DISCOVERED, ONLINE, OFFLINE).

get_incompatibility_reason() -> str | None

Source

Get human-readable reason if device is incompatible.

get_for(manufacturer: Manufacturer | None, device_data: dict[str, Any] | None = None) -> DeviceMetadataAccessor

Source

Factory method to get appropriate accessor for manufacturer.

Parameters:

  • manufacturer (Manufacturer | None) — Manufacturer instance (can be None).
  • device_data (dict[str, Any] | None) — Metadata dict from DiscoveredDevice.

Returns:

  • DeviceMetadataAccessor — Appropriate DeviceMetadataAccessor subclass instance.

GenericMetadataAccessor

Bases: DeviceMetadataAccessor

Source

Generic accessor for unknown manufacturers.

get_compatibility_status() -> str | None

Source

get_device_state() -> str | None

Source

get_incompatibility_reason() -> str | None

Source

ShureMetadataAccessor

Bases: DeviceMetadataAccessor

Source

Accessor for Shure-specific metadata structure.

get_compatibility_status() -> str | None

Source

Get Shure compatibility status.

get_device_state() -> str | None

Source

Get Shure device state.

get_incompatibility_reason() -> str | None

Source

Get human-readable Shure incompatibility reason.

get_communication_protocol() -> str | None

Source

Get Shure communication protocol name.

SennheiserMetadataAccessor

Bases: DeviceMetadataAccessor

Source

Accessor for Sennheiser-specific metadata structure.

get_compatibility_status() -> str | None

Source

Get Sennheiser compatibility status.

get_device_state() -> str | None

Source

Get Sennheiser device state.

get_incompatibility_reason() -> str | None

Source

Get human-readable Sennheiser incompatibility reason.

micboard.services.core.device_specs

Source

Device specifications service for centralized spec lookups.

Provides a clean interface for accessing device specifications from the registry without embedding lookup logic in models.

DeviceSpec

Source

Device specifications from registry.

DeviceSpecService

Source

Service for looking up standardized device specifications.

get_specs(manufacturer: Manufacturer | None, model: str) -> DeviceSpec | None

Source

Get device specifications for a manufacturer/model combination.

Parameters:

  • manufacturer (Manufacturer | None) — Manufacturer instance (can be None).
  • model (str) — Device model name/code.

Returns:

  • DeviceSpec | None — DeviceSpec instance with specs, or None if not found.

apply_specs_to_chassis(chassis: object) -> None

Source

Apply specifications to a WirelessChassis instance (for use in save()).

Parameters:

  • chassis (object) — WirelessChassis instance with manufacturer and model set.

micboard.services.core.user_profile

Source

User profile preference operations.

UserProfileService

Source

Persist validated user profile preferences.

set_display_width(user: Any, width_px: int) -> UserProfile

Source

Set a user’s charger-dashboard width within supported browser limits.

micboard.services.settings.settings_service

Source

Unified settings resolution service.

Composes Django settings, feature flags, package defaults, and the DB-backed SettingsRegistry into a single resolution chain.

SettingsService

Source

Unified settings resolution with multi-source fallback.

Resolution order for get():

  1. Deployment controls mapped to immutable Django MICBOARD_* settings
  2. DB Setting with scope (org/site/manufacturer) via SettingsRegistry
  3. settings.MICBOARD_CONFIG dict key
  4. Package defaults (POLL_INTERVAL, etc.)
  5. Registered SettingDefinition default
  6. Provided default

get(key: str, default: Any = None, organization: Any = None, site: Any = None, manufacturer: Any = None) -> Any

Source

Resolve a setting value through the multi-source fallback chain.

Parameters:

  • key (str) — Canonical setting key.
  • default (Any) — Fallback if not found in any source.
  • organization (Any) — Scope hint for DB-backed setting.
  • site (Any) — Scope hint for DB-backed setting.
  • manufacturer (Any) — Scope hint for DB-backed setting.

Returns:

  • Any — Resolved value or default.

get_config_dict() -> dict[str, Any]

Source

Return MICBOARD_CONFIG merged with package defaults.

invalidate_value_cache(key: str | None = None) -> None

Source

Invalidate one resolved database value or every cached value.

invalidate_definition_cache(key: str | None = None) -> None

Source

Invalidate definition metadata and every value derived from it.

micboard.services.settings.browser_refresh_service

Source

One module that decides how often each live browser surface re-polls the server.

Micboard delivers every live browser update by short-polling over ordinary HTTP, so the poll interval multiplied by the number of open tabs is the entire request volume the deployment’s reverse proxy carries. Leaving each interval as a literal in its template put that number out of a deployer’s reach: slowing a busy page down meant forking presentation markup. This module owns the decision instead, resolving each surface through the same host-configuration seam the rest of Micboard uses and clamping the result to the bounds that already govern stored kiosk refresh rates.

bounded_refresh_interval(value: Any, default: int) -> int

Source

Return one refresh interval clamped into the range a browser can be trusted with.

MICBOARD_CONFIG is host-supplied and unvalidated, so this has to survive anything a deployment puts there. An interval that cannot be read as a whole number falls back to default rather than raising, because a browser surface that will not render is worse than one refreshing at the shipped rate.

Parameters:

  • value (Any) — Candidate interval from host configuration or a stored row.
  • default (int) — Interval to use when value cannot be read as a whole number.

Returns:

  • int — A whole number of seconds within the shared refresh bounds.

BrowserRefreshCadence

Source

Resolve the bounded poll interval for one named browser surface.

seconds_for(surface: str) -> int

Source

Return how many seconds surface waits between refreshes.

Parameters:

  • surface (str) — A key of :data:BROWSER_REFRESH_SURFACES.

Returns:

  • int — The configured interval, clamped to the shared refresh bounds.

Raises:

  • ValueError — If surface is not a declared browser refresh surface.

milliseconds_for(surface: str) -> int

Source

Return the same bounded interval as a JavaScript timer duration.

Parameters:

  • surface (str) — A key of :data:BROWSER_REFRESH_SURFACES.

Returns:

  • int — The configured interval in milliseconds.

micboard.services.settings.registry

Source

Internal registry implementation for typed, explicitly scoped settings.

SettingsScopeReference

Bases: Protocol

Source

Minimal model contract required to identify one settings scope.

SettingsRegistry

Source

Centralized settings accessor honoring each definition’s declared scope.

get(key: str, default: Any = None, organization: SettingsScopeReference | None = None, site: SettingsScopeReference | None = None, manufacturer: SettingsScopeReference | None = None, required: bool = False, include_definition_default: bool = True) -> Any

Source

Get a setting value at its definition’s declared scope.

Resolution order:

  1. Stored value at the definition’s exact scope
  2. SettingDefinition default, when requested
  3. User-provided default
  4. Raise if required

Parameters:

  • key (str) — Setting key
  • default (Any) — Fallback default value
  • organization (SettingsScopeReference | None) — Organization for scope (MSP mode)
  • site (SettingsScopeReference | None) — Site for scope (multi-site mode)
  • manufacturer (SettingsScopeReference | None) — Manufacturer for scope
  • required (bool) — Raise error if not found
  • include_definition_default (bool) — Whether to use the registered definition default

Returns:

  • Any — Resolved setting value

Raises:

  • SettingNotFoundError — If required=True and not found

get_definition_default(key: str, default: Any = None) -> Any

Source

Return the typed definition default without consulting stored values.

invalidate_cache(key: str | None = None) -> None

Source

Invalidate settings cache.

Parameters:

  • key (str | None) — Specific key to invalidate, or None for all

invalidate_definition(key: str | None = None) -> None

Source

Invalidate cached definition metadata and all values derived from it.

micboard.services.settings.dtos

Source

Data-transfer objects for tenant-aware settings presentation.

SettingsVisibilityScope

Bases: PydanticBaseDTO

Source

Identifiers whose stored overrides a user may inspect.

None means unrestricted access for that dimension. An empty set means fail closed and expose no overrides for that dimension.

SettingWriteTarget

Bases: PydanticBaseDTO

Source

One exact global, organization, site, or manufacturer scope.

validate_exact_scope() -> SettingWriteTarget

Source

Reject identifiers that do not match the declared scope.

SettingWriteItem

Bases: PydanticBaseDTO

Source

One validated form value awaiting definition serialization.

validate_identifier() -> SettingWriteItem

Source

Require exactly one definition identifier.

SettingsWriteRequest

Bases: PydanticBaseDTO

Source

Authorized batch of setting override writes.

SettingsWriteResult

Bases: PydanticBaseDTO

Source

Best-effort persistence result safe to render to an operator.

micboard.services.shared.access_policy

Source

Shared policy for tenant-wide read and mutation access decisions.

has_unrestricted_tenant_access(user: Any) -> bool

Source

Return whether user may bypass organization membership boundaries.

visible_to(model: type[models.Model], user: Any, using: str | None = None) -> models.QuerySet[Any]

Source

Return the rows of model that user may see.

Models with a tenant-aware manager narrow visibility themselves, sometimes with a model-specific rule on top of the shared cascade; models on Django’s default manager get the shared cascade directly. Callers ask the same question either way.

The database is bound before the tenant boundary is applied, not after. Answering this in MSP mode takes two reads: for_user materialises the caller’s active memberships as it builds the queryset, so retargeting only the finished queryset would leave that boundary read on whichever database the manager defaulted to.

TenantRoleAccessService

Source

Apply MSP membership roles without narrowing read-only visibility.

management_memberships(user: Any, using: str | None = None) -> list[tuple[int, int | None]]

Source

Return active organization/campus scopes where user may administer.

is_platform_global_model(model: type[models.Model]) -> bool

Source

Return whether model is a reviewed host-wide admin surface.

scope_manageable_queryset(queryset: models.QuerySet[Any], user: Any) -> models.QuerySet[Any]

Source

Intersect queryset with scopes where user has an admin role.

can_add_model(user: Any, model: type[models.Model]) -> bool

Source

Authorize adds only where a new row can carry exclusive tenant ownership.

can_manage_model(user: Any, model: type[models.Model]) -> bool

Source

Authorize adding or bulk-mutating rows of one tenant-owned model.

can_manage_object(user: Any, obj: models.Model) -> bool

Source

Authorize mutation only when the object’s exact tenant role permits it.

micboard.services.shared.base_dto

Source

Base DTO class for all data transfer objects in the service layer.

All DTOs should inherit from this class to ensure consistent configuration.

PydanticBaseDTO

Bases: BaseModel

Source

Base DTO with standard configuration for all service layer DTOs.

micboard.services.common.base.plugin

Source

clear_plugin_cache() -> None

Source

Forget resolved plugin classes, so a test starts from a cold cache.

build_manufacturer_plugin(manufacturer: Manufacturer) -> ManufacturerPlugin

Source

Return a plugin bound to manufacturer.

This is the one way to obtain a plugin. It raises when a manufacturer has no shipped integration, so every caller sees the same failure rather than a None some branch on and others do not.

get_manufacturer_plugin(code: str) -> type[ManufacturerPlugin]

Source

Return the plugin class for a manufacturer code, resolving it at most once.

Attempts to import micboard.integrations.<code>.plugin and locate a concrete ManufacturerPlugin subclass. Prefers <CodeTitle>Plugin, then falls back to another plugin subclass.

BasePlugin

Bases: ABC

Source

Base interface for all manufacturer plugins.

get_devices() -> list[dict[str, Any]]

Source

Retrieve a list of all devices associated with or discovered by this plugin.

ManufacturerPlugin

Bases: BasePlugin

Source

Extended plugin interface specifically for manufacturer hardware integrations.

async subscribe_to_chassis(chassis: WirelessChassis, callback: Callable[[dict[str, Any]], Awaitable[None]]) -> None

Source

Open this integration’s stream for one chassis and await its updates.

The integration owns connection setup, authentication, framing, and cleanup; the runner owns leasing, inventory selection, connection tracking, and persistence.

get_device_channels(device_id: str) -> list[dict[str, Any]]

Source

Retrieve all channels associated with a specific device identifier.

get_client() -> BaseAPIClient

Source

Get an instance of the configured API client for this manufacturer.

transform_device_data(api_data: dict[str, Any]) -> dict[str, Any] | None

Source

Transform raw API device data into the standardized application format.

transform_transmitter_data(api_data: dict[str, Any], channel_number: int) -> dict[str, Any] | None

Source

Normalize one raw wireless-unit payload for a channel.

DeviceUpdateService requires this of every plugin it persists through, so it is part of the contract rather than an optional addition.

get_device(device_id: str) -> dict[str, Any] | None

Source

Fetch details for a single device by its identifier.

is_healthy() -> bool

Source

Check if the plugin and its underlying integrations are currently healthy.

check_health() -> dict[str, Any]

Source

Perform a detailed health check and return the results as a dictionary.

add_discovery_ips(ips: list[str]) -> bool

Source

Add a list of IP addresses to the plugin’s discovery targets.

get_discovery_ips() -> list[str]

Source

Retrieve the list of currently configured discovery IP addresses.

remove_discovery_ips(ips: list[str]) -> bool

Source

Remove a list of IP addresses from the plugin’s discovery targets.

micboard.services.common.base.client

Source

standardize_health_response(status: str, details: dict[str, Any] | None = None, error: str | None = None) -> dict[str, Any]

Source

Return one health result in the shape every consumer reads.

Consumers — the admin, the API-health context processor, and the manufacturer health task — read status from a closed vocabulary plus a timestamp, so an unrecognized status becomes unknown rather than propagating a vendor’s own word for it.

BaseAPIClient

Bases: ABC

Source

Base API client interface.

is_healthy() -> bool

Source

Check if the client is healthy.

check_health() -> dict[str, Any]

Source

Perform a health check and return details.

BaseHTTPClient

Bases: BaseAPIClient

Source

Base HTTP client with circuit breaker and retries.

get_exception_class() -> type[APIError]

Source

Get the exception class for API errors.

get_rate_limit_exception_class() -> type[APIRateLimitError]

Source

Get the exception class for rate limit errors.

is_healthy() -> bool

Source

check_health() -> dict[str, Any]

Source

close() -> None

Source

Close the underlying HTTP connection pool.

micboard.services.common.base.resilience

Source

create_resilient_session(max_retries: int = 3, pool_connections: int = 10, pool_maxsize: int = 20, follow_redirects: bool = True) -> httpx.Client

Source

Create an HTTPX client with bounded connection retries and pooling.

HTTP status retries belong in the calling service because only that layer knows whether a request is safe to replay and how to interpret Retry-After.

micboard.services.common.base.rate_limiter

Source

rate_limit(calls_per_second: float = 10.0) -> Callable[[_CallableT], _CallableT]

Source

Rate-limit calls to a decorated client method through the shared cache.

micboard.services.common.base.circuit_breaker

Source

CircuitBreaker

Source

allow_request() -> bool

Source

record_success() -> None

Source

record_failure() -> None

Source