Single source of truth for all AI agents working on this codebase. Agent-specific wrappers (
CLAUDE.md,GEMINI.md,.github/copilot-instructions.md) reference this file.
| Field | Value |
|---|---|
| Domain | unraid |
| Integration name | Unraid |
| Class prefix | Unraid |
| Code path | custom_components/unraid/ |
| Test path | tests/ |
| Python | 3.13+ |
| HA minimum | 2026.5.0 |
| Key dependency | unraid-api>=1.10.0 |
| iot_class | local_polling |
| Config flow | Yes (UI only, no YAML) |
| Platforms | sensor, binary_sensor, switch, button |
# Lint
./script/lint # or: ruff check . --fix && ruff format .
# Test
./script/test # or: pytest
# Full validation
./script/check
# Setup dev environment
./script/setup
# Start dev Home Assistant
./script/develop
# Type checking
mypy custom_components/unraidEntities ──▶ Coordinator ──▶ UnraidClient (unraid-api) ──▶ Unraid Server (GraphQL)
▲ │
└──────────────┘
(coordinator.data)
Three DataUpdateCoordinator subclasses poll at different intervals:
| Coordinator | Interval | Data |
|---|---|---|
UnraidSystemCoordinator |
30s | Server info, CPU/RAM metrics, Docker containers, VMs, UPS, notifications |
UnraidStorageCoordinator |
5min | Array state, disks, parity, shares, capacity |
UnraidInfraCoordinator |
15min | Services, registration, cloud, remote access, plugins |
Intervals are fixed per HA Core guidelines (not user-configurable). Users can call homeassistant.update_entity for on-demand refresh.
@dataclass
class UnraidRuntimeData:
api_client: UnraidClient
system_coordinator: UnraidSystemCoordinator
storage_coordinator: UnraidStorageCoordinator
infra_coordinator: UnraidInfraCoordinator
server_info: dict
type UnraidConfigEntry = ConfigEntry[UnraidRuntimeData]Access in platform setup: entry.runtime_data.system_coordinator, etc.
Each coordinator returns a typed dataclass:
UnraidSystemData—info,metrics,containers,vms,ups_devices,notification_overviewUnraidStorageData—array,shares,parity_history(+ convenience properties:array_state,capacity,parity_status,boot,disks,parities,caches)UnraidInfraData—services,registration,cloud,remote_access,plugins,vars
custom_components/unraid/
├── __init__.py # Setup/teardown, UnraidRuntimeData, platform forwarding
├── config_flow.py # Config flow (user, reauth, reconfigure), options flow (UPS)
├── const.py # All constants: domain, intervals, keys, icons, states
├── coordinator.py # Triple coordinator pattern + data classes
├── entity.py # UnraidBaseEntity, UnraidEntity, UnraidEntityDescription
├── sensor.py # All sensor entities
├── binary_sensor.py # All binary sensor entities
├── switch.py # Container, VM, array, parity switches
├── button.py # Array start/stop, parity, disk spin buttons
├── diagnostics.py # Diagnostic data for troubleshooting
├── icons.json # Icon definitions per translation key
├── strings.json # English translations (source of truth)
├── translations/en.json # Generated from strings.json
├── manifest.json # Integration metadata
└── quality_scale.yaml # HA quality scale self-assessment
- Line length: 88 characters (ruff formatter)
- Linter: ruff (
E,W,F,I,C,B,UPrules) - Target: Python 3.13 (
target-version = "py313") - Imports: Use
from __future__ import annotationsin every file - Type hints: Required on all public functions; use
TYPE_CHECKINGguard for import-only types - Logging:
_LOGGER = logging.getLogger(__name__)— use_LOGGER.debug()for data updates,.info()for lifecycle,.warning()for user-visible issues,.error()for failures - Constants: Define in
const.pywithFinaltype annotation - Strings: f-strings preferred
Entity class hierarchy:
CoordinatorEntity
└── UnraidBaseEntity # Base: device_info, unique_id, availability
└── UnraidEntity # + EntityDescription support
Entity MRO for platform entities: PlatformEntity, UnraidBaseEntity (e.g., SensorEntity, UnraidBaseEntity)
Key patterns:
_attr_has_entity_name = True(always, set onUnraidBaseEntity)_attr_translation_key = "..."for translated names viastrings.json- Unique ID format:
{server_uuid}_{resource_id} - Access coordinator data:
data: UnraidSystemData | None = self.coordinator.data - Use
@propertyfornative_value,is_on,extra_state_attributes - Per-resource entities (disks, containers, VMs) store
_disk_id/_container_idand implement_get_disk()/_get_container()helper
- Extend
DataUpdateCoordinator[UnraidXxxData] - Authentication errors →
raise ConfigEntryAuthFailed - Connection/timeout errors →
raise UpdateFailed - Optional services (Docker, VMs, UPS) → fail gracefully with
_LOGGER.debug(), return empty list - Log recovery when connection restored after previous failure
- Pass
config_entry=tosuper().__init__()
- Steps:
user→reauth_confirm→reconfigure - SSL auto-detection: Try HTTPS first, fall back to HTTP on
UnraidSSLError - Version checking: API version from
get_server_info()compared againstMIN_API_VERSION(requires GraphQL APIv4.31.1+; no Unraid OS version requirement — API upgradeable via Connect plugin) - Options flow:
OptionsFlowWithReloadfor UPS capacity/power settings - Unique ID: server UUID from
api_client.get_server_info()
- Use HA translation system for user-facing errors:
translation_domain=DOMAIN,translation_key="...",translation_placeholders={...} - Wrap API errors in
HomeAssistantErrorfor entity actions (switch on/off, button press) - Create repair issues (
ir.async_create_issue) for persistent auth failures - Clear repair issues on successful reconnection
- All API data comes as Pydantic v2 models from
unraid-apilibrary - Use
extra="ignore"on Pydantic models to handle API evolution - Enum values from API: always compare with
.upper()for case-insensitive matching
pip install -e ".[dev]"
# or
./script/setuptests/
├── conftest.py # Shared fixtures: mock API client, coordinators, config entries
├── fixtures/ # JSON fixtures for API responses
├── test_init.py # Setup/teardown tests
├── test_config_flow.py # Config flow tests
├── test_coordinator.py # Coordinator tests
├── test_sensor.py # Sensor entity tests
├── test_binary_sensor.py
├── test_switch.py
└── test_button.py
- Use
pytest-homeassistant-custom-componentfor HA test infrastructure - Mock
UnraidClientmethods, not HTTP calls - Use
pytest-asynciowithasyncio_mode = "auto" - Fixtures provide pre-built coordinator data classes
- Test both happy path and error scenarios (auth failure, connection loss, missing optional data)
pytest # All tests with coverage
pytest tests/test_sensor.py # Single module
pytest -k "test_cpu" # Pattern match
pytest --no-cov # Skip coverage for speed- Read existing code before modifying — understand patterns in place
- Follow the entity class hierarchy (
UnraidBaseEntityorUnraidEntity) - Use
_attr_translation_keyfor entity names (not hardcoded strings) - Add translation keys to
strings.jsonwhen creating new entities - Match existing icon patterns in
icons.json - Use typed coordinator data access (e.g.,
data: UnraidSystemData | None = self.coordinator.data) - Handle
Nonecoordinator data gracefully - Use
Finaltype annotations for constants inconst.py - Run
./script/lintbefore committing - Pass
config_entry=to coordinatorsuper().__init__() - Use
entry.runtime_datato access runtime data (nothass.data[DOMAIN])
- Adding new platforms (beyond sensor, binary_sensor, switch, button)
- Changing coordinator polling intervals
- Modifying the config flow steps
- Adding new dependencies to
manifest.jsonorpyproject.toml - Changing entity unique ID format (breaks existing installations)
- Modifying
UnraidRuntimeDatastructure - Updating the
unraid-apilibrary version
- Make polling intervals user-configurable (HA Core policy)
- Skip
from __future__ import annotations - Use bare
except:— always catch specific exceptions - Hardcode entity names (use translation keys)
- Auto-update
translations/files other thanen.json - Import from
homeassistant.coreat module level in entity files (useTYPE_CHECKING) - Use
entity_idfor identification (useunique_id) - Store state in the entity that should be in the coordinator
- Commit secrets, API keys, or credentials
- Use
hass.data[DOMAIN]directly (useconfig_entry.runtime_data) - Use blocking I/O in async functions
- Remove existing tests without replacement
type: short description
Longer explanation if needed.
Types: feat, fix, refactor, test, docs, chore, build
# Enable debug logging in configuration.yaml
logger:
default: info
logs:
custom_components.unraid: debug
unraid_api: debugCheck diagnostics via: Settings > Devices & Services > Unraid > (...) > Download diagnostics
The integration targets Platinum level on the HA Integration Quality Scale. Current status:
- Bronze: All rules done/exempt
- Silver: All rules done except
test-coverage(must reach >=95%) - Gold: All done/exempt
- Platinum: All done (async dependency, inject websession, strict typing)