* Rename Connect vocabulary across the integrations framework Three coordinated rename groups (no behavior change): - test_connection -> validate_access on IntegrationGateway and all service overrides + call sites. The generalized verb covers both network probes (today) and access checks for future Import-capability integrations. - is_initial_import -> is_initial_connect across the synchronizer protocol, view layer, templates, and tests. Internal sync / synchronize vocabulary is preserved. - UI labels updated: IMPORT -> CONNECT (in live-discovery contexts), REFRESH -> UPDATE (button) / Update (inline link), Initial Import -> Initial Connect, Refresh Result -> Update Check Result. The no-items hero copy reads 'No items found.' with non-redundant sub-text. Prerequisite for the broader integration-capabilities work in #355. * Restructure HomeBox importer/ — Connect code to connector/, dormant preserved The reachable Connect-mode synchronizer code moves from services/homebox/importer/ to connector/: - HomeBoxSynchronizer relocates to connector/hb_sync.py. - New HbEntityFactory in connector/hb_entity_factory.py carries the two entity-create/update classmethods previously on HbImporter. The dormant attribute-population code stays in importer/ for #358: - HbImporter retains only its four attribute-side classmethods with a narrowed docstring noting their dormant status. - The synchronizer's removed attribute-orchestration methods extract as HbAttributeSync in importer/hb_attribute_sync.py with a docstring marking them preserved for the HomeBox Importer in #358. No behavior change. (#355) * Phase 3 (#355): split integrations app into umbrella + connect sub-package Relocate the Connect-capability machinery (gateways, synchronizers, sync-check, views, view mixins, entity operations, attribute edit context, sync-result, and related transient models) from hi/integrations/ into hi/integrations/connect/. Umbrella-level modules (models, forms, enums, manager, urls, exceptions, etc.) stay at hi/integrations/ as the framework root. This re-shapes the package along capability lines ahead of the Import capability being added by #358: the umbrella holds capability-agnostic framework code; each capability sub-package holds the per-capability machinery. No behavior changes. All call sites updated to the new module paths; all 3141 tests pass and lint is clean. * Tolerate unreadable sync-check cache entries The sync-check probe state is stored in Redis as a pickled SyncCheckResult dataclass. Any class rename, module move, or shape change can leave stale entries that fail to deserialize on read, which previously propagated ModuleNotFoundError / AttributeError / UnpicklingError straight out through the manage page. Wrap the cache.get in get_state with a broad except: log the failure, evict the bad key so the next request runs clean, and degrade to a cache miss. The next probe cycle (or a Refresh) writes fresh state. The cache is informational drift state, not load-bearing — a soft miss is the right failure mode. * Phase 4 (#355): add EntityDataSource enum + Entity.data_source field EntityDataSource is the entity-wide provenance signal (INTERNAL when HI owns the representation; EXTERNAL when an upstream system does). Surfaces via Entity.data_source — a typed accessor over the new data_source_str CharField, following the existing entity_type pattern. Migration 0021 backfills integration-attached entities that disallow internal attributes to EXTERNAL. Today this picks out only HomeBox- Connect entities; HA / ZM / Frigate / native entities stay INTERNAL. No UI impact yet. Phase 6 will consume data_source for cross-capability transition prompts and Import-discard scoping. * Phase 5 (#355): add IntegrationCapability enum + metadata declaration IntegrationCapability declares what an integration brings to HI: CONNECT (live mirror) or IMPORT (one-shot pull). IntegrationMetaData now carries a `capabilities` frozenset declaring which capability set the integration participates in; defaults to {CONNECT} since that is the existing behavior for all four current integrations. An empty set raises at construction time so a future integration cannot silently register with no capability. ALL_CAPABILITIES is provided as a frozenset convenience over all enum members for callers that want "available everywhere" semantics. No UI impact yet. Per-attribute capability filtering deferred until Phase 6 introduces the consumer. * Phase 6 (#355): make capability filtering explicit at integration list and attribute formset sites Extend IntegrationAttributeType to accept an optional 8th tuple element declaring which IntegrationCapabilities the attribute applies to (defaults to ALL_CAPABILITIES, so existing declarations are unchanged). Convert the previously-implicit "everything is Connect" assumption into explicit capability filters at four sites that all run in Connect-mode context today: * IntegrationSelectView (Enable Integrations picker modal) * IntegrationManageView (Config tab — enabled-integrations list and default-integration selector) * IntegrationAttributeItemEditContext (formset construction, used by both the Configure modal and the Config tab attribute pane) IntegrationManager.get_integration_data_list() and get_default_integration_data() gain an optional `capabilities` frozenset kwarg. IntegrationAttributeItemEditContext gains a required `capability` __init__ arg; its formset queryset is now filtered to attribute rows whose AttributeType includes that capability. The same class can be reused for the Import edit context in #358 by passing IntegrationCapability.IMPORT. Behavior unchanged today (all integrations are Connect, all attributes default to ALL_CAPABILITIES) but the filter rules are now testable with mixed-capability fixtures. The Importer protocol, workflow/views/templates, and cross-capability transition prompts originally listed in Phase 6 absorb into #358 (the HomeBox Importer) — they are easier to design with a concrete consumer driving them. * Phase 7 (#355): collapse Configure + Pre-Sync + Sync into one CONNECT click The initial-connect flow becomes a single workflow under IntegrationEnableView: the user fills the configure form once and clicks CONNECT, which validates access, saves attributes, enables the integration, and runs the synchronizer in one pass — landing directly on the sync-result modal. The previous Configure → Pre-Sync confirm → Sync handshake is gone from this path, along with the REVIEW CONFIG round-trip affordance. Extracted IntegrationViewMixin.render_sync_result so the synchronizer-invocation + cache-invalidation + sync-result rendering is shared between IntegrationEnableView (initial-connect) and IntegrationSyncView (update-check). Action button label flips from the conditional CONFIGURE/UPDATE to a fixed CONNECT. IntegrationPreSyncView and its template still serve the manage-page UPDATE / CONNECT-when-no-entities paths unchanged; the removal_summary policy choice (RETAIN MISSING / REMOVE MISSING) for user-data-bearing integrations is preserved end-to-end. Net behavior change: one fewer modal click on first-time setup. * Phase 8 (#355): align documentation with the new Connect/Update vocabulary Update the user-facing button-label references in the per-integration docs (Refresh / REFRESH → Update / UPDATE; Import action → Connect) and the developer-facing references where they were tied to user surfaces. Internal developer vocabulary stays as "sync" — that is the implementation-side term. Add a Capability Model subsection to integration-guidelines.md covering IntegrationCapability, the optional per-attribute capability restriction, and EntityDataSource. Points at the enum modules; keeps the explanation brief since the code is the authoritative source. Update the per-integration doc template's vocabulary instruction so new integrations start with the right terms. * Address review feedback: sharpen vocabulary and add IntegrationEnableView POST tests Four fixes from the pre-PR review pass: - Sharpen EntityDataSource enum descriptions to disambiguate from raw value provenance: "HI owns the editable representation" / "Upstream constrains HI-side edits" (the actual semantics codified by migration 0021's allow_internal_attributes-based backfill). - Replace the self-contradicting "first **Import**" / "post-import state" in docs/integrations/_template.md with the **Connect** vocabulary the same template instructs authors to use. - Reword frigate.md troubleshooting heading "Update imports zero cameras" → "Update finds zero cameras" to stop mixing the UPDATE button label with the "imports" verb. - Add two IntegrationEnableView.post() tests covering the Phase 7 collapse: the happy path verifies enable + synchronizer.sync() fire and the sync-result modal renders; the synchronizer-less path verifies enable still flips but no sync-result is returned.
6.1 KiB
Simulator Testing
There is a simulator app that lives inside the same Django code structure, but represents a completely different running application, albeit, sharing some code with the main app. The simulator is used to simulate integrations for testing the Home Informaton app. It's a separate Django application with its own database, located in the hi/simulator directory. There's a special simulator.py command alongside the main manage.py script.
Simulator Setup (Testing Integration)
Initialize Simulator Database
cd $PROJ_DIR/src
# Simulator uses same commands as main manage.py
./simulator.py migrate
./simulator.py hi_createsuperuser
./simulator.py hi_creategroups
# Run simulator server
./simulator.py runserver
Access simulator: Visit http://127.0.0.1:7411
The simulator.py script acts just like the main manage.py script with all the same commands (runserver, migrate, etc.), but manages the simulator application instead.
Purpose and architecture
The simulator stands in for the upstream services that real
integrations talk to. It mounts service-shaped HTTP endpoints under
http://127.0.0.1:7411/services/<service>/... that respond with the
same shapes the real services would, sourced from a curated
SimProfile. Switching profiles changes what the integrations see
on their next sync, which is the lever for exercising
sync behavior end-to-end.
The simulator is a separate Django app with its own database (see the top of this file). It does not talk to HI directly — HI's integration configurations point at the simulator's URLs and treat it as the upstream service.
Per-integration simulator code lives at
src/hi/simulator/services/<service>/:
src/hi/simulator/services/hass/— HA/api/statesand related endpoints.src/hi/simulator/services/zoneminder/— ZM monitor and event endpoints, plus an MJPEG-shaped stream endpoint.src/hi/simulator/services/homebox/— HomeBox/api/v1/...inventory endpoints.
Each service folder typically contains simulator.py (the
service-specific subclass of the Simulator base), sim_models.py
(the field shapes the seed command writes), and api/ (the HTTP
views that produce the upstream-shaped responses).
Configuring HI integrations to point at the simulator
Run the simulator on 127.0.0.1:7411 (the default) and configure HI
on its own port (typically 8411). In HI's integration Configure
forms, paste the corresponding URL:
| Integration | HI Configure field | URL to enter |
|---|---|---|
| Home Assistant | Server URL | http://127.0.0.1:7411/services/hass |
| HomeBox | API URL | http://127.0.0.1:7411/services/homebox/api |
| ZoneMinder | API URL | http://127.0.0.1:7411/services/zoneminder/api |
| ZoneMinder | Portal URL | http://127.0.0.1:7411/services/zoneminder/ |
For the credentials fields, any non-empty values work — the
simulator does not validate passwords or tokens. Anything sensible
(e.g., simuser / simpass) is fine.
For the Home Assistant integration, set the Allowed Item Types to a narrow list that matches what your active SimProfile produces; otherwise the import will pull only the items your profile actually has.
Profile seeding (seed_sim_profiles)
The seed_sim_profiles management command populates the simulator
database with a curated suite of SimProfiles for manual testing. It
is the source of every realistic upstream payload the simulator
serves; without it the simulator is empty.
cd $PROJ_DIR/src
./simulator.py seed_sim_profiles
Re-running the command is a no-op when a profile already exists.
Pass --reset to delete and recreate the matching profile (and
its entities) before recreating.
After seeding, switch the simulator's active profile from the web UI at http://127.0.0.1:7411.
Profiles
Five profiles are seeded, each designed to exercise a specific
scenario. The authoritative list and per-profile contents live in
the command's own docstring at
src/hi/simulator/management/commands/seed_sim_profiles.py; the
table below is a short orientation.
| Profile | What it exercises |
|---|---|
empty |
Zero items in every integration. Tests the initial-import-with-nothing path and the refresh-against-emptied-upstream path. |
baseline |
Realistic small-install set: mixed HA device types, a handful of HomeBox items, a few ZM monitors. The "before" state for delta tests. |
baseline-changed |
Same shape as baseline with deltas in every integration. Pairing it with baseline exercises the five sync-result categories — created, updated, reconnected, detached, removed — in a single flip. |
hass-zoo |
One HA entity of every supported type. Visual / grouping coverage for the HASS converter; HomeBox and ZM stay empty. |
volume |
Large counts (30 HA, 25 HomeBox, 10 ZM monitors). Stresses modal list overflow scrolling and dispatcher group sizing. |
Operator workflow for full-category sync-result coverage
The baseline ↔ baseline-changed pair is the canonical workflow for
exercising every sync-result category. The command's docstring
spells out the exact step-by-step (which entities to add custom
attributes to, what each sync shows in the result modal); read
it directly when running the workflow rather than trying to keep a
copy of the steps here in sync.
Dependencies
Python 3.11
- macOS: Download from python.org
- Ubuntu: Use deadsnakes PPA
Redis
- macOS:
brew install redis - Ubuntu: Manual installation from source
Docker (Optional)
- macOS: Docker Desktop
- Ubuntu: docker.io package
Related Documentation
- Workflow guidelines: Workflow Guidelines
- Release process: Release Process
- Dependencies: Dependencies