diff --git a/CHANGELOG.md b/CHANGELOG.md index 40d5bea..45baf38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [unreleased] — emitter live-schema alignment and abstraction (feat/emitter-integration) +## 1.0.12 — 2026-07-30 — emitter live-schema alignment and abstraction ### Changed @@ -11,4 +11,5 @@ ### Fixed +- **Dev bootstrap dependency drift**: `scripts/dev-setup.sh` installed `ebus-emitter` with a bare `uv pip install --editable`, which re-resolves the emitter's dependency constraints against PyPI and ignores its `uv.lock`. A fresh bootstrap pulled `ebus-sdk` 0.12.0 — whose `Device` constructor is incompatible with the 0.1.x API the emitter targets — and panel startup died with `AttributeError: 'NoneType' object has no attribute 'get'` in `connect_broker()`. The script now installs the emitter's locked runtime dependencies first, then the emitter itself with `--no-deps`, so the venv matches what the emitter pins. - **Type safety**: Fixed mypy error in simulator runtime (`_first_feed_for_device_type`) where template_name could be None. diff --git a/pyproject.toml b/pyproject.toml index cd1f29b..95c7d7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "span-panel-simulator" -version = "1.0.11" +version = "1.0.12" description = "Standalone eBus simulator for SPAN panels" requires-python = ">=3.14" dependencies = [ diff --git a/scripts/dev-setup.sh b/scripts/dev-setup.sh index be70b51..c6e0452 100755 --- a/scripts/dev-setup.sh +++ b/scripts/dev-setup.sh @@ -35,7 +35,18 @@ fi echo "Syncing simulator deps (excluding ebus-emitter, which is local)…" uv sync --group dev --no-install-package ebus-emitter -echo "Installing ebus-emitter from ${EBUS_EMITTER_PATH} (editable)…" -uv pip install --editable "${EBUS_EMITTER_PATH}" +# Install the emitter's *locked* runtime deps, then the emitter itself with +# --no-deps. A bare `uv pip install --editable ` re-resolves the emitter's +# constraints against PyPI and ignores its uv.lock, so a fresh bootstrap can pull +# a transitive dependency the emitter was never tested against — that is how +# ebus-sdk 0.12.0 (whose Device constructor is incompatible with the 0.1.x API +# the emitter targets) landed in this venv and broke `span-simulator` at startup. +# Sourcing from the lock keeps this venv on exactly what the emitter pins. +echo "Installing ebus-emitter's locked runtime deps…" +uv export --project "${EBUS_EMITTER_PATH}" --no-dev --no-emit-project --no-hashes \ + | uv pip install --requirements - + +echo "Installing ebus-emitter from ${EBUS_EMITTER_PATH} (editable, no re-resolution)…" +uv pip install --no-deps --editable "${EBUS_EMITTER_PATH}" echo "Done. Verify with: uv run python -c 'import ebus_emitter; print(ebus_emitter.__file__)'" diff --git a/span_panel_simulator/CHANGELOG.md b/span_panel_simulator/CHANGELOG.md index af6bea7..5e442f7 100644 --- a/span_panel_simulator/CHANGELOG.md +++ b/span_panel_simulator/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 1.0.12 — 2026-07-30 + +### Emitter & Homie Schema + +- MQTT publishing and per-tick property emission now run through the external `ebus-emitter` package. The simulator builds a device manifest per panel and drives + the emitter each tick, replacing the built-in publisher +- Published topics follow the emitter's live SPAN panel Homie 5 schema — flat node layout with topology and properties matching real hardware, so downstream + consumers (span-panel-api, span-card) see the same structure a physical panel emits +- Lugs node IDs renamed to `lugs-upstream` and `lugs-downstream` to match the emitter convention +- Device metadata key `software-version` renamed to `firmware-version` +- Multiple EVSE devices per panel are now published: every circuit whose template is tagged `device_type: evse` gets its own EVSE device (`evse`, `evse-2`, …) + driven by that circuit's power, instead of a single hardcoded EVSE +- BESS and PV device feeds and metadata are derived from circuit templates via stable circuit UUIDs rather than being configured independently +- Each panel now owns its own MQTT connection and subscribes to its own `/set` topics, replacing the single shared connection and the central topic router +- Stopping a panel clears its retained topics, so a shut-down clone no longer leaves stale state on the broker + +### Fixes + +- Panel startup no longer fails with `AttributeError: 'NoneType' object has no attribute 'get'`. The developer bootstrap script re-resolved the emitter's + dependencies against PyPI instead of its lock file and pulled an `ebus-sdk` release incompatible with the `Device` API the emitter targets; setup now installs + the emitter's locked dependency set + ## 1.0.11 — 2026-04-19 ### Fixes diff --git a/span_panel_simulator/Dockerfile b/span_panel_simulator/Dockerfile index 3c47664..26e9098 100644 --- a/span_panel_simulator/Dockerfile +++ b/span_panel_simulator/Dockerfile @@ -32,7 +32,7 @@ EXPOSE 18883 8081 18080 LABEL io.hass.name="SPAN Panel Simulator" \ io.hass.description="Simulates a SPAN electrical panel for testing and upgrade modeling" \ io.hass.type="addon" \ - io.hass.version="1.0.11" \ + io.hass.version="1.0.12" \ io.hass.arch="aarch64|amd64" CMD ["/run.sh"] diff --git a/span_panel_simulator/config.yaml b/span_panel_simulator/config.yaml index e96e05d..4c987e6 100644 --- a/span_panel_simulator/config.yaml +++ b/span_panel_simulator/config.yaml @@ -1,6 +1,6 @@ name: "SPAN Panel Simulator" description: "Simulates a SPAN electrical panel for testing and upgrade modeling" -version: "1.0.11" +version: "1.0.12" slug: "span_panel_simulator" url: "https://github.com/SpanPanel/simulator" image: "ghcr.io/spanpanel/simulator/{arch}" diff --git a/src/span_panel_simulator/__init__.py b/src/span_panel_simulator/__init__.py index 156f0f0..7c78be3 100644 --- a/src/span_panel_simulator/__init__.py +++ b/src/span_panel_simulator/__init__.py @@ -1,3 +1,3 @@ """Standalone eBus simulator for SPAN panels.""" -__version__ = "1.0.11" +__version__ = "1.0.12"