diff --git a/custom_components/goecharger_api2/service.py b/custom_components/goecharger_api2/service.py index 765c550..04da436 100644 --- a/custom_components/goecharger_api2/service.py +++ b/custom_components/goecharger_api2/service.py @@ -73,8 +73,10 @@ async def set_pv_data(self, call: ServiceCall): } _LOGGER.debug(f"Service-set_pv_data(): set PV data: {payload}") try: - resp = await coordinator.async_write_key(Tag.IDS.key, payload) + resp = await coordinator.async_write_key(Tag.IDS.key, payload, require_ack=call.return_response) if call.return_response: + if not isinstance(resp, dict) or resp.get(Tag.IDS.key) != payload: + return {"error": "The charger did not acknowledge PV data", "date": str(datetime.datetime.now().time())} return { "success": "true", "date": str(datetime.datetime.now().time()), diff --git a/tests/integration/test_service.py b/tests/integration/test_service.py index 3dbd837..82fb164 100644 --- a/tests/integration/test_service.py +++ b/tests/integration/test_service.py @@ -15,7 +15,7 @@ def svc(hass): service_module._COORDINATOR_PER_CONFIG_ENTRY_ID_MAP.clear() coordinator = SimpleNamespace( intg_type="charger", mode="lan", name="test charger", _serial="123456", data={}, - async_write_key=AsyncMock(return_value={Tag.IDS.key: True})) + async_write_key=AsyncMock(side_effect=lambda key, value, **kwargs: {key: value})) service = GoeChargerApiV2Service(hass, SimpleNamespace(entry_id="entry"), coordinator) yield service, coordinator service_module._COORDINATOR_PER_CONFIG_ENTRY_ID_MAP.clear() @@ -30,7 +30,7 @@ async def test_valid_values_are_written(svc): result = await service.set_pv_data(_call(pgrid=-500, ppv=1500, pakku=200)) assert result["success"] == "true" coordinator.async_write_key.assert_awaited_once_with( - Tag.IDS.key, {"pGrid": -500.0, "pPv": 1500.0, "pAkku": 200.0}) + Tag.IDS.key, {"pGrid": -500.0, "pPv": 1500.0, "pAkku": 200.0}, require_ack=True) @pytest.mark.parametrize("bad", [math.nan, math.inf, -math.inf]) @@ -46,4 +46,13 @@ async def test_non_finite_pv_and_battery_value_become_zero(svc, bad): service, coordinator = svc await service.set_pv_data(_call(pgrid=100, ppv=bad, pakku=bad)) coordinator.async_write_key.assert_awaited_once_with( - Tag.IDS.key, {"pGrid": 100.0, "pPv": 0.0, "pAkku": 0.0}) + Tag.IDS.key, {"pGrid": 100.0, "pPv": 0.0, "pAkku": 0.0}, require_ack=True) + + +@pytest.mark.parametrize("reply", [{}, None, {"err": "rejected"}, {"ids": True}, {"ids": {"pGrid": 1}}]) +async def test_pv_response_requires_matching_acknowledgement(svc, reply): + service, coordinator = svc + coordinator.async_write_key = AsyncMock(return_value=reply) + result = await service.set_pv_data(_call(pgrid=-500, ppv=1500, pakku=200)) + assert "error" in result and "success" not in result + assert coordinator.async_write_key.call_args.kwargs == {"require_ack": True} diff --git a/tests/integration/test_services_and_setup.py b/tests/integration/test_services_and_setup.py index e5b3552..107062a 100644 --- a/tests/integration/test_services_and_setup.py +++ b/tests/integration/test_services_and_setup.py @@ -45,7 +45,7 @@ def _call(return_response=True, **data): def _service(hass, mode="lan", car=2, **kwargs): coordinator = make_coordinator(hass, data={Tag.CAR_CONNECTED.key: car}, mode=mode, **kwargs) - coordinator.async_write_key = AsyncMock(return_value={Tag.IDS.key: True}) + coordinator.async_write_key = AsyncMock(return_value={Tag.IDS.key: {"pGrid": -500.0, "pPv": 0.0, "pAkku": 0.0}}) return GoeChargerApiV2Service(hass, coordinator._config_entry, coordinator), coordinator @@ -100,7 +100,7 @@ async def test_missing_grid_power_without_wanted_answer_returns_nothing(hass): async def test_successful_pv_data_returns_nothing_when_no_answer_is_wanted(hass): service, coordinator = _service(hass) assert await service.set_pv_data(_call(return_response=False, pgrid=-1, ppv=2, pakku=3)) is None - coordinator.async_write_key.assert_awaited_once_with(Tag.IDS.key, {"pGrid": -1.0, "pPv": 2.0, "pAkku": 3.0}) + coordinator.async_write_key.assert_awaited_once_with(Tag.IDS.key, {"pGrid": -1.0, "pPv": 2.0, "pAkku": 3.0}, require_ack=False) @pytest.mark.parametrize("error", [ValueError("down"), TypeError("odd")])