From ca70abe240e97083624955e271fa407c141e736a Mon Sep 17 00:00:00 2001 From: "A. Gideonse" Date: Fri, 24 Apr 2026 20:11:01 +0200 Subject: [PATCH] Refactor button platform to use indevolt-api 1.4.2 (#169063) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- homeassistant/components/indevolt/button.py | 5 ++-- homeassistant/components/indevolt/const.py | 3 +- .../components/indevolt/coordinator.py | 20 ++++++------- tests/components/indevolt/conftest.py | 3 ++ tests/components/indevolt/test_button.py | 28 ++++++++----------- 5 files changed, 27 insertions(+), 32 deletions(-) diff --git a/homeassistant/components/indevolt/button.py b/homeassistant/components/indevolt/button.py index 6abcf50048be..320c5ce4f54c 100644 --- a/homeassistant/components/indevolt/button.py +++ b/homeassistant/components/indevolt/button.py @@ -5,6 +5,8 @@ from __future__ import annotations from dataclasses import dataclass, field from typing import Final +from indevolt_api import IndevoltRealtimeAction + from homeassistant.components.button import ButtonEntity, ButtonEntityDescription from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback @@ -66,5 +68,4 @@ class IndevoltButtonEntity(IndevoltEntity, ButtonEntity): async def async_press(self) -> None: """Handle the button press.""" - - await self.coordinator.async_execute_realtime_action([0, 0, 0]) + await self.coordinator.async_realtime_action(IndevoltRealtimeAction.STOP) diff --git a/homeassistant/components/indevolt/const.py b/homeassistant/components/indevolt/const.py index 3b469282a643..cf815c56315f 100644 --- a/homeassistant/components/indevolt/const.py +++ b/homeassistant/components/indevolt/const.py @@ -16,8 +16,7 @@ ENERGY_MODE_READ_KEY: Final = "7101" ENERGY_MODE_WRITE_KEY: Final = "47005" PORTABLE_MODE: Final = 0 -# API write key and value for real-time control mode -REALTIME_ACTION_KEY: Final = "47015" +# Value for real-time control mode REALTIME_ACTION_MODE: Final = 4 # API key fields diff --git a/homeassistant/components/indevolt/coordinator.py b/homeassistant/components/indevolt/coordinator.py index 19320eec5441..a7f7844a575e 100644 --- a/homeassistant/components/indevolt/coordinator.py +++ b/homeassistant/components/indevolt/coordinator.py @@ -7,7 +7,7 @@ import logging from typing import Any, Final from aiohttp import ClientError -from indevolt_api import IndevoltAPI, TimeOutException +from indevolt_api import IndevoltAPI, IndevoltRealtimeAction, TimeOutException from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_MODEL @@ -24,7 +24,6 @@ from .const import ( ENERGY_MODE_READ_KEY, ENERGY_MODE_WRITE_KEY, PORTABLE_MODE, - REALTIME_ACTION_KEY, REALTIME_ACTION_MODE, SENSOR_KEYS, ) @@ -146,19 +145,16 @@ class IndevoltCoordinator(DataUpdateCoordinator[dict[str, Any]]): if refresh: await self.async_request_refresh() - async def async_execute_realtime_action(self, action: list[int]) -> None: + async def async_realtime_action( + self, + action_code: IndevoltRealtimeAction, + ) -> None: """Switch mode, execute action, and refresh for real-time control.""" - await self.async_switch_energy_mode(REALTIME_ACTION_MODE, refresh=False) - try: - success = await self.async_push_data(REALTIME_ACTION_KEY, action) - - except (DeviceTimeoutError, DeviceConnectionError) as err: - raise HomeAssistantError( - translation_domain=DOMAIN, - translation_key="failed_to_execute_realtime_action", - ) from err + match action_code: + case IndevoltRealtimeAction.STOP: + success = await self.api.stop() if not success: raise HomeAssistantError( diff --git a/tests/components/indevolt/conftest.py b/tests/components/indevolt/conftest.py index 384ecc469b80..324c1f7dc677 100644 --- a/tests/components/indevolt/conftest.py +++ b/tests/components/indevolt/conftest.py @@ -87,6 +87,9 @@ def mock_indevolt(generation: int) -> Generator[AsyncMock]: client = mock_client.return_value client.fetch_data.return_value = fixture_data client.set_data.return_value = True + client.stop.return_value = True + client.charge.return_value = True + client.discharge.return_value = True client.get_config.return_value = { "device": { "sn": device_info["sn"], diff --git a/tests/components/indevolt/test_button.py b/tests/components/indevolt/test_button.py index a5ea45c8d886..6844fa7a9528 100644 --- a/tests/components/indevolt/test_button.py +++ b/tests/components/indevolt/test_button.py @@ -1,8 +1,7 @@ """Tests for the Indevolt button platform.""" -from unittest.mock import AsyncMock, call, patch +from unittest.mock import AsyncMock, patch -from indevolt_api import TimeOutException import pytest from syrupy.assertion import SnapshotAssertion @@ -11,7 +10,6 @@ from homeassistant.components.indevolt.const import ( ENERGY_MODE_READ_KEY, ENERGY_MODE_WRITE_KEY, PORTABLE_MODE, - REALTIME_ACTION_KEY, REALTIME_ACTION_MODE, ) from homeassistant.const import ATTR_ENTITY_ID, Platform @@ -64,14 +62,11 @@ async def test_button_press_standby( blocking=True, ) - # Verify set_data was called twice with correct parameters - assert mock_indevolt.set_data.call_count == 2 - mock_indevolt.set_data.assert_has_calls( - [ - call(ENERGY_MODE_WRITE_KEY, REALTIME_ACTION_MODE), - call(REALTIME_ACTION_KEY, [0, 0, 0]), - ] + # Verify set_data was called for mode switch and stop() was called + mock_indevolt.set_data.assert_called_once_with( + ENERGY_MODE_WRITE_KEY, REALTIME_ACTION_MODE ) + mock_indevolt.stop.assert_called_once() @pytest.mark.parametrize("generation", [2], indirect=True) @@ -98,22 +93,23 @@ async def test_button_press_standby_already_in_realtime_mode( blocking=True, ) - # Verify set_data was called once with correct parameters - mock_indevolt.set_data.assert_called_once_with(REALTIME_ACTION_KEY, [0, 0, 0]) + # Verify stop() was called and no mode switch was needed + mock_indevolt.set_data.assert_not_called() + mock_indevolt.stop.assert_called_once() @pytest.mark.parametrize("generation", [2], indirect=True) -async def test_button_press_standby_timeout_error( +async def test_button_press_standby_rejected_command( hass: HomeAssistant, mock_indevolt: AsyncMock, mock_config_entry: MockConfigEntry, ) -> None: - """Test pressing standby raises HomeAssistantError when the device times out.""" + """Test pressing standby raises HomeAssistantError when the device rejects the command.""" with patch("homeassistant.components.indevolt.PLATFORMS", [Platform.BUTTON]): await setup_integration(hass, mock_config_entry) - # Simulate an API push failure - mock_indevolt.set_data.side_effect = TimeOutException("Timed out") + # Simulate stop() returning False (device rejected the command) + mock_indevolt.stop.return_value = False # Mock call to pause (dis)charging with pytest.raises(HomeAssistantError):