From 58202ab388bf13aba00fe07feb43391b902d8e64 Mon Sep 17 00:00:00 2001 From: AnDevX Date: Fri, 21 Aug 2026 09:05:44 +0100 Subject: [PATCH 1/3] feat(sensor): expose solar power, charge power and CT diagnostics sysSolarPower, sysSolarEnergyDelta, sysChargePower, sysSolarCT, sysGridCT and cfgCTConfig were already requested in the GraphQL device status query and present in every poll response, but never turned into entities - only their grid equivalents were. Adds six AndersenEvLiveSensor entities reusing the existing class unchanged. sysSolarPower lives in deviceStatus, not chargeStatus, so it reports continuously rather than only during a charge session, making this a whole-house solar monitor with no new API calls or config changes. --- custom_components/andersen_ev/sensor.py | 94 +++++++++++++++++++ .../andersen_ev/tests/test_sensor.py | 4 +- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/custom_components/andersen_ev/sensor.py b/custom_components/andersen_ev/sensor.py index 901300a..522d552 100644 --- a/custom_components/andersen_ev/sensor.py +++ b/custom_components/andersen_ev/sensor.py @@ -82,6 +82,12 @@ async def async_setup_entry( ) # Live status sensors + # NOTE: sysGridPower is declared KILO_WATT here while chargeStatus.gridPower + # below is declared WATT for what is plausibly the same physical quantity. + # At most one is right - unverified against real hardware (kettle test not + # yet run, see LOG.md Q10). sysSolarPower below deliberately mirrors this + # sensor's unit so the pair stays internally consistent whichever way that + # turns out. Do not change one without the other. entities.append( AndersenEvLiveSensor( coordinator, @@ -95,6 +101,94 @@ async def async_setup_entry( "mdi:transmission-tower", ) ) + # --- Solar live sensors ------------------------------------------------- + # sysSolarPower/sysSolarEnergyDelta/sysChargePower are already fetched in + # the GraphQL query (konnect/const.py) but were never exposed as entities. + # They live in deviceStatus, not chargeStatus, so they report continuously + # - not just during a charge session - making this a whole-house solar + # monitor for free. + entities.append( + AndersenEvLiveSensor( + coordinator, + device, + "sys_solar_power", + "System Solar Power", + "sysSolarPower", + SensorDeviceClass.POWER, + SensorStateClass.MEASUREMENT, + UnitOfPower.KILO_WATT, + "mdi:solar-power", + ) + ) + entities.append( + AndersenEvLiveSensor( + coordinator, + device, + "sys_charge_power", + "System Charge Power", + "sysChargePower", + SensorDeviceClass.POWER, + SensorStateClass.MEASUREMENT, + UnitOfPower.KILO_WATT, + "mdi:ev-station", + ) + ) + entities.append( + AndersenEvLiveSensor( + coordinator, + device, + "sys_solar_energy_delta", + "System Solar Energy Delta", + "sysSolarEnergyDelta", + SensorDeviceClass.ENERGY, + SensorStateClass.TOTAL, + UnitOfEnergy.KILO_WATT_HOUR, + "mdi:solar-power", + ) + ) + # --- CT clamp diagnostics ------------------------------------------------ + # Raw CT readings and configuration - not for the Energy dashboard. These + # exist to diagnose mis-clamped or mis-configured CTs, a common cause of + # solar generation reading suspiciously low. + entities.append( + AndersenEvLiveSensor( + coordinator, + device, + "sys_solar_ct", + "Solar CT", + "sysSolarCT", + None, + None, + None, + "mdi:gauge", + ) + ) + entities.append( + AndersenEvLiveSensor( + coordinator, + device, + "sys_grid_ct", + "Grid CT", + "sysGridCT", + None, + None, + None, + "mdi:gauge", + ) + ) + entities.append( + AndersenEvLiveSensor( + coordinator, + device, + "cfg_ct_config", + "CT Configuration", + "cfgCTConfig", + None, + None, + None, + "mdi:cog-outline", + ) + ) entities.append( AndersenEvLiveSensor( coordinator, diff --git a/custom_components/andersen_ev/tests/test_sensor.py b/custom_components/andersen_ev/tests/test_sensor.py index 7cdcf7a..2a2bdf7 100644 --- a/custom_components/andersen_ev/tests/test_sensor.py +++ b/custom_components/andersen_ev/tests/test_sensor.py @@ -60,12 +60,12 @@ async def test_creates_all_sensor_types_per_device(self): await async_setup_entry(MagicMock(), entry, async_add_entities) entities = async_add_entities.call_args.args[0] - assert len(entities) == 44 + assert len(entities) == 56 assert sum(isinstance(e, AndersenEvEnergySensor) for e in entities) == 8 assert sum(isinstance(e, AndersenEvCostSensor) for e in entities) == 8 assert sum(isinstance(e, AndersenEvConnectorSensor) for e in entities) == 2 assert sum(isinstance(e, AndersenEvChargeStatusSensor) for e in entities) == 16 - assert sum(isinstance(e, AndersenEvLiveSensor) for e in entities) == 10 + assert sum(isinstance(e, AndersenEvLiveSensor) for e in entities) == 22 @pytest.mark.asyncio async def test_no_devices_creates_no_entities(self): From 6cadc018befb7ee303fac4787108edfa50d31c9c Mon Sep 17 00:00:00 2001 From: AnDevX Date: Fri, 21 Aug 2026 10:33:28 +0100 Subject: [PATCH 2/3] docs(sensor): confirm sysGridPower/sysSolarPower unit via live test Kettle test against a live A3 (car unplugged, ~2.9kW kettle) showed sensor.*_system_grid_power spiking to ~2.9, confirming the declared KILO_WATT unit is correct rather than mislabelled watts. Replaces the prior "unverified" caveat comment with the confirmed result. --- custom_components/andersen_ev/sensor.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/custom_components/andersen_ev/sensor.py b/custom_components/andersen_ev/sensor.py index 522d552..2dd66a4 100644 --- a/custom_components/andersen_ev/sensor.py +++ b/custom_components/andersen_ev/sensor.py @@ -82,12 +82,10 @@ async def async_setup_entry( ) # Live status sensors - # NOTE: sysGridPower is declared KILO_WATT here while chargeStatus.gridPower - # below is declared WATT for what is plausibly the same physical quantity. - # At most one is right - unverified against real hardware (kettle test not - # yet run, see LOG.md Q10). sysSolarPower below deliberately mirrors this - # sensor's unit so the pair stays internally consistent whichever way that - # turns out. Do not change one without the other. + # sysGridPower/sysSolarPower are declared KILO_WATT; chargeStatus.gridPower + # below is declared WATT for the same physical quantity but only reports + # during a charge session. Verified correct via live kettle test - see + # LOG.md 2026-08-21. entities.append( AndersenEvLiveSensor( coordinator, From 0ef44e5b212d77539a2e7081cc1ab4c28cc0e4c2 Mon Sep 17 00:00:00 2001 From: AnDevX <61598648+AnDevX@users.noreply.github.com> Date: Sat, 29 Aug 2026 15:08:14 +0100 Subject: [PATCH 3/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- custom_components/andersen_ev/sensor.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/custom_components/andersen_ev/sensor.py b/custom_components/andersen_ev/sensor.py index 2dd66a4..dbf2fec 100644 --- a/custom_components/andersen_ev/sensor.py +++ b/custom_components/andersen_ev/sensor.py @@ -84,8 +84,7 @@ async def async_setup_entry( # Live status sensors # sysGridPower/sysSolarPower are declared KILO_WATT; chargeStatus.gridPower # below is declared WATT for the same physical quantity but only reports - # during a charge session. Verified correct via live kettle test - see - # LOG.md 2026-08-21. + # during a charge session. Unit verified via live load test (~2.9 kW kettle). entities.append( AndersenEvLiveSensor( coordinator,