Conversation
Update sensor to use UnitOfPower.WATT to match the actual firmware value. Bump version to 2.0.3 and require span-panel-api>=2.2.5.
Compare span-panel-api field metadata against sensor definitions at startup to detect unit mismatches and unmapped fields. All output is DEBUG-level log-only — no sensor behavior changes. - schema_expectations.py: SENSOR_FIELD_MAP (sensor key → field path) - schema_validation.py: unit cross-check, unmapped field reporting, collect_sensor_definitions() - coordinator.py: one-shot _run_schema_validation() after first refresh - docs/dev/schema_driven_changes.md: phased plan documentation - tests/test_schema_validation.py: 14 tests covering mapping, cross-check, unmapped detection, and no-op behavior
- Add Versioning Model section: library as semantic contract layer, schema-only vs value-change scenarios, human-gated version sequence - Add schema version vs firmware version distinction, note v2 API beta - Add new-fields-require-human-review policy - Remove auto-entity concept from Phase 2 (override table only for reviewed fields) - Update Phase 3 to clarify codegen does not bypass review gate
Replace the local YAML generation approach with a WebSocket-based flow that delegates clone work to the simulator. The integration now discovers simulators via mDNS (looking for cloneWssPort in _ebus._tcp TXT records), falls back to manual host/port entry, and sends the panel's credentials over WSS. The simulator handles eBus scraping, translation, and config writing. - Rewrite simulation_utils.py with discover_clone_simulators() and execute_clone_via_simulator() backed by aiohttp WSS - Update OptionsFlowHandler clone step: mDNS pre-fill, v2-only gate - Remove simulation_generator.py (dead code) and services.yaml (unimplemented export_synthetic_config service) - Clean up export_config references from strings.json and all translation files; add clone error strings
Documents the WebSocket-based clone architecture where the simulator scrapes a real panel's eBus, translates retained messages to YAML, and writes the simulation config. Covers the WSS contract, mDNS discovery, eBus-to-YAML translation rules, and implementation phases.
The script was hardcoding >= and ^ operators when syncing versions from manifest.json to ci.yml, ignoring the actual specifier (e.g. ==2.3.0). Now extracts and writes the full specifier as-is. Also removes unused ci-simulation-example.yml workflow.
socketio was only in manifest.json (runtime) but missing from dev dependencies, breaking test collection.
- Extract shared _sio_call helper to eliminate duplicated Socket.IO connect/call/disconnect pattern in simulation_utils.py - Move clone-then-profile orchestration from simulation_utils into config_flow.py where it belongs (OptionsFlowHandler._apply_usage_profiles) - Replace bare except Exception with narrower exception types - Use DOMAIN constant instead of raw string in simulator_profile_builder - Add Literal types for statistics_during_period parameters - Remove all lazy imports in favor of top-level imports
The coordinator is stored in config_entry.runtime_data, not hass.data[DOMAIN][entry_id]. Also guard against runtime_data being absent if the entry hasn't completed setup yet.
Instead of opening separate connections for clone and profiles, keep the connection open after clone_panel and wait for the simulator to emit clone_ready before sending apply_usage_profiles. This eliminates the race where profiles are sent before the simulator has registered the clone config. Profiles are built before connecting and passed into clone_with_profiles so the entire operation runs on one session.
HA requires database access to go through the recorder's own executor, not the general hass.async_add_executor_job. Fixes the "Detected code that accesses the database without the database executor" warning.
- statistics_during_period returns start as float (Unix timestamp), not datetime — isinstance(start, datetime) silently failed so hour_factors and monthly_factors were never computed - Convert UTC timestamps to HA's local timezone before bucketing by hour, since the simulator engine uses local time - Replace mean with median throughout to handle sensor glitch spikes (e.g. 400kW on a 15A circuit) that destroy mean-based calculations - Use IQR-based power_variation instead of coefficient of variation - Use recorder executor for database queries per HA requirements
Exposes an authoritative circuit-to-entity manifest so the simulator add-on can query HA recorder stats and build usage profiles without reverse-engineering entity IDs from the states API. The service iterates all loaded SPAN panel config entries, resolves each circuit's power sensor entity_id via the entity registry, computes clone template names from breaker tabs, and returns a flat list per panel — ready for the add-on to consume directly.
Move service registration from async_setup_entry to async_setup so the service is available even when no config entries are loaded. The handler raises ServiceValidationError with an informative message instead of silently not existing. Remove service unregistration from async_unload_entry — services stay registered for the lifetime of the domain per HA quality-scale action-setup rule. Also add host field to per-panel manifest response (entry.data[CONF_HOST]) and iterate async_loaded_entries instead of async_entries.
Panel cloning is now handled by the export_circuit_manifest service for the simulator add-on. With clone removed, general options is the only option so the menu is eliminated — the options gear goes straight to the general options form. Also adds missing v2.0.3 changelog entry.
The MQTT broker runs on the panel itself. The panel advertises its own mDNS hostname (.local) as ebusBrokerHost, but mDNS does not resolve across VLAN boundaries. Use the user-configured panel host (IP or FQDN) which is known reachable. Fixes #193
There was a problem hiding this comment.
Pull request overview
This PR updates the SPAN Panel Home Assistant integration to remove the built-in simulator, add a new export_circuit_manifest service to support an external simulator, and fix MQTT connectivity when the panel advertises an mDNS broker hostname that won’t resolve across VLANs (Issue #193).
Changes:
- Remove built-in simulation mode (config flow, options, utilities, and test scaffolding) and block migration of simulation entries.
- Fix cross-VLAN MQTT broker host resolution by using the user-configured panel host instead of the panel-advertised mDNS hostname.
- Add schema validation (Phase 1) plus new tests, and add an
export_circuit_manifestservice for external simulator tooling.
Reviewed changes
Copilot reviewed 52 out of 53 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
custom_components/span_panel/__init__.py |
Config entry migration bump to v6, remove simulation setup path, add manifest export service, and fix broker host selection across VLANs. |
custom_components/span_panel/config_flow.py |
Remove simulator flow and add HTTP port handling (including zeroconf TXT parsing). |
custom_components/span_panel/config_flow_utils/validation.py |
Thread port through host/version detection and v2 registration calls. |
custom_components/span_panel/coordinator.py |
Remove simulation polling/offline logic and add one-shot schema validation after first refresh. |
custom_components/span_panel/schema_validation.py / schema_expectations.py |
New schema validation layer and mapping between sensors and snapshot field paths. |
custom_components/span_panel/services.yaml |
Replace synthetic export service with export_circuit_manifest service definition. |
custom_components/span_panel/sensor_definitions.py |
Fix PV nameplate capacity to report in watts and use new snapshot field name. |
custom_components/span_panel/strings.json + translations/*.json |
Remove simulator UI/options strings (but port field strings need to be added). |
scripts/sync-dependencies.py |
Update dependency syncing to preserve specifiers (regex needs correction for ==). |
tests/* |
Remove simulation factory/provider/docs and add new tests for schema validation and circuit manifest service. |
custom_components/span_panel/util.py / sensor.py / binary_sensor.py / sensor_evse.py / entity.py |
Remove simulator-specific device identifier logic and simplify to serial-based identifiers. |
Guard against non-numeric httpPort values in zeroconf TXT records that would crash discovery with a ValueError. Add missing UI label and description for the http_port field in the user config step.
No integration code imports socketio — it was left over from removed functionality. Also drops 4 transitive dependencies.
There was a problem hiding this comment.
Pull request overview
Release v2.0.4 for the SPAN Panel Home Assistant custom integration, focusing on removing the built-in simulator, adding a simulator-supporting service, and addressing MQTT broker hostname resolution failures across VLANs (Issue #193).
Changes:
- Fix cross-VLAN MQTT connection failures by using the user-configured panel host instead of the panel-advertised
.localbroker hostname. - Remove built-in simulation mode/config/options and reject simulation config entries during migration (v5→v6).
- Add schema-driven validation (Phase 1) and an
export_circuit_manifestservice to support an external simulator workflow; correct PV nameplate capacity units to watts.
Reviewed changes
Copilot reviewed 51 out of 52 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_v2_config_flow.py | Updates config flow tests to remove simulator inputs and reflect config version bump. |
| tests/test_schema_validation.py | Adds test coverage for schema validation and sensor-to-field mapping. |
| tests/test_promoted_sensors.py | Updates PV nameplate capacity expectations to watts. |
| tests/test_factories/span_panel_simulation_factory.py | Removes simulation factory used for YAML-backed simulator test data. |
| tests/test_dps_and_bess.py | Renames/adjusts a test to reflect missing-client-method behavior (not simulator-specific). |
| tests/test_circuit_manifest_service.py | Adds tests for the new export_circuit_manifest service behavior. |
| tests/providers/integration_data_provider.py | Removes simulation data provider that bridged simulator data into coordinator logic. |
| tests/docs/simulation_test_documentation.md | Removes documentation specific to the removed built-in simulation testing approach. |
| tests/conftest.py | Removes simulator-related fixture wiring and references. |
| scripts/sync-dependencies.py | Improves dependency specifier parsing/preservation when syncing manifest→CI config. |
| pyproject.toml | Adds python-socketio to dev dependencies. |
| poetry.lock | Updates locked deps (notably span-panel-api) and adds socketio-related transitive packages. |
| docs/dev/schema_driven_changes.md | Adds developer documentation for schema-driven sensor validation phases. |
| docs/dev/mqtt-sensor-topic.md | Updates PV metadata naming/unit documentation to nameplate_capacity_w. |
| custom_components/span_panel/util.py | Simplifies device info creation now that simulator identifiers are removed. |
| custom_components/span_panel/translations/pt.json | Removes simulator-related strings; adjusts host-required messaging. |
| custom_components/span_panel/translations/ja.json | Removes simulator-related strings; adjusts host-required messaging. |
| custom_components/span_panel/translations/fr.json | Removes simulator-related strings; adjusts host-required messaging. |
| custom_components/span_panel/translations/es.json | Removes simulator-related strings; adjusts host-required messaging. |
| custom_components/span_panel/translations/en.json | Replaces simulator UI strings with http_port field strings; adjusts messaging. |
| custom_components/span_panel/switch.py | Updates warning text to be client-capability-based (not simulator-based). |
| custom_components/span_panel/strings.json | Replaces simulator UI strings with http_port field strings; adjusts messaging. |
| custom_components/span_panel/simulation_utils.py | Removes simulator utilities (clone-to-sim, template inference, YAML writing). |
| custom_components/span_panel/simulation_generator.py | Removes simulation YAML generator. |
| custom_components/span_panel/simulation_factory.py | Removes simulation mode factory/wrapper logic. |
| custom_components/span_panel/simulation_configs/simulation_config_8_tab_workshop.yaml | Removes bundled simulation config. |
| custom_components/span_panel/simulation_configs/simulation_config_40_circuit_with_battery.yaml | Removes bundled simulation config. |
| custom_components/span_panel/simulation_configs/simulation_config_32_circuit.yaml | Removes bundled simulation config. |
| custom_components/span_panel/simulation_configs/simple_test_config.yaml | Removes bundled simulation config. |
| custom_components/span_panel/simulation_configs/migration_test_config.yaml | Removes bundled simulation config. |
| custom_components/span_panel/services.yaml | Replaces prior synthetic export service docs with export_circuit_manifest service docs. |
| custom_components/span_panel/sensor_evse.py | Removes simulator-specific panel identifier logic for EVSE sub-devices. |
| custom_components/span_panel/sensor_definitions.py | Fixes PV nameplate capacity sensor to W and uses nameplate_capacity_w. |
| custom_components/span_panel/sensor.py | Removes simulator-specific panel identifier logic for EVSE/BESS device info. |
| custom_components/span_panel/select.py | Updates warning text to be client-capability-based (not simulator-based). |
| custom_components/span_panel/schema_validation.py | Adds schema validation logic for unit cross-checking + unmapped field reporting. |
| custom_components/span_panel/schema_expectations.py | Introduces sensor-key → snapshot-field-path mapping used by schema validation. |
| custom_components/span_panel/manifest.json | Bumps integration version to 2.0.4 and pins span-panel-api==2.3.1. |
| custom_components/span_panel/helpers.py | Removes simulator serial generator helper. |
| custom_components/span_panel/entity.py | Removes simulator-specific device identifier plumbing. |
| custom_components/span_panel/coordinator.py | Removes simulation coordinator behavior and adds one-shot schema validation on first refresh. |
| custom_components/span_panel/const.py | Removes simulation constants; adds CONF_HTTP_PORT. |
| custom_components/span_panel/config_flow_utils/validation.py | Adds port support to host/auth validation helpers; removes simulation time validation. |
| custom_components/span_panel/config_flow_utils/simulation.py | Removes simulation config discovery/validation helpers. |
| custom_components/span_panel/config_flow_utils/options.py | Removes simulation option schemas/defaults. |
| custom_components/span_panel/config_flow_utils/init.py | Removes simulation exports and validation from config flow utils exports. |
| custom_components/span_panel/config_flow.py | Removes simulator flows/options; adds http_port support and TXT-record parsing for zeroconf. |
| custom_components/span_panel/button.py | Updates warning text to be client-capability-based (not simulator-based). |
| custom_components/span_panel/binary_sensor.py | Removes simulator-specific EVSE/BESS device identifier logic. |
| custom_components/span_panel/init.py | Adds domain async_setup + circuit manifest service; rejects simulation entry migration; fixes broker host selection across VLANs. |
| CHANGELOG.md | Adds 2.0.4 release notes covering simulator removal, VLAN fix, PV unit correction, schema validation. |
| .github/workflows/ci.yml | Updates CI dependency replacement to span-panel-api==2.3.1. |
| .github/workflows/ci-simulation-example.yml | Removes the simulation CI example workflow. |
Remove simulator
Add simulator service
Fix mDNS MQQT cross VLAN issue #193