diff --git a/homeassistant/components/nibe_heatpump/coordinator.py b/homeassistant/components/nibe_heatpump/coordinator.py index 2451e2fbda9..71f87698792 100644 --- a/homeassistant/components/nibe_heatpump/coordinator.py +++ b/homeassistant/components/nibe_heatpump/coordinator.py @@ -10,12 +10,19 @@ from typing import Any from nibe.coil import Coil, CoilData from nibe.connection import Connection -from nibe.exceptions import CoilNotFoundException, ReadException +from nibe.exceptions import ( + CoilNotFoundException, + ReadException, + WriteDeniedException, + WriteException, + WriteTimeoutException, +) from nibe.heatpump import HeatPump, Series from propcache.api import cached_property from homeassistant.config_entries import ConfigEntry from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed @@ -134,7 +141,36 @@ class CoilCoordinator(ContextCoordinator[dict[int, CoilData], int]): async def async_write_coil(self, coil: Coil, value: float | str) -> None: """Write coil and update state.""" data = CoilData(coil, value) - await self.connection.write_coil(data) + try: + await self.connection.write_coil(data) + except WriteDeniedException as e: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="write_denied", + translation_placeholders={ + "address": str(coil.address), + "value": str(value), + }, + ) from e + except WriteTimeoutException as e: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="write_timeout", + translation_placeholders={ + "address": str(coil.address), + }, + ) from e + except WriteException as e: + LOGGER.debug("Failed to write", exc_info=True) + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="write_failed", + translation_placeholders={ + "address": str(coil.address), + "value": str(value), + "error": str(e), + }, + ) from e self.data[coil.address] = data diff --git a/homeassistant/components/nibe_heatpump/strings.json b/homeassistant/components/nibe_heatpump/strings.json index c65a76d3364..3312bc2287f 100644 --- a/homeassistant/components/nibe_heatpump/strings.json +++ b/homeassistant/components/nibe_heatpump/strings.json @@ -45,5 +45,16 @@ "unknown": "[%key:common::config_flow::error::unknown%]", "url": "The specified URL is not well formed nor supported" } + }, + "exceptions": { + "write_denied": { + "message": "Writing of coil {address} with value `{value}` was denied" + }, + "write_timeout": { + "message": "Timeout while writing coil {address}" + }, + "write_failed": { + "message": "Writing of coil {address} with value `{value}` failed with error `{error}`" + } } } diff --git a/tests/components/nibe_heatpump/test_number.py b/tests/components/nibe_heatpump/test_number.py index dc7faf0a80e..eeb9587f425 100644 --- a/tests/components/nibe_heatpump/test_number.py +++ b/tests/components/nibe_heatpump/test_number.py @@ -4,6 +4,7 @@ from typing import Any from unittest.mock import AsyncMock, patch from nibe.coil import CoilData +from nibe.exceptions import WriteDeniedException, WriteException, WriteTimeoutException from nibe.heatpump import Model import pytest from syrupy.assertion import SnapshotAssertion @@ -15,6 +16,7 @@ from homeassistant.components.number import ( ) from homeassistant.const import ATTR_ENTITY_ID, Platform from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from . import async_add_model @@ -108,3 +110,64 @@ async def test_set_value( assert isinstance(coil, CoilData) assert coil.coil.address == address assert coil.value == value + + +@pytest.mark.parametrize( + ("exception", "translation_key", "translation_placeholders"), + [ + ( + WriteDeniedException("denied"), + "write_denied", + {"address": "47398", "value": "25.0"}, + ), + ( + WriteTimeoutException("timeout writing"), + "write_timeout", + {"address": "47398"}, + ), + ( + WriteException("failed"), + "write_failed", + { + "address": "47398", + "value": "25.0", + "error": "failed", + }, + ), + ], +) +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_set_value_fail( + hass: HomeAssistant, + mock_connection: AsyncMock, + exception: Exception, + translation_key: str, + translation_placeholders: dict[str, Any], + coils: dict[int, Any], +) -> None: + """Test setting of value.""" + + value = 25 + model = Model.F1155 + address = 47398 + entity_id = "number.room_sensor_setpoint_s1_47398" + coils[address] = 0 + + await async_add_model(hass, model) + + await hass.async_block_till_done() + assert hass.states.get(entity_id) + + mock_connection.write_coil.side_effect = exception + + # Write value + with pytest.raises(HomeAssistantError) as exc_info: + await hass.services.async_call( + PLATFORM_DOMAIN, + SERVICE_SET_VALUE, + {ATTR_ENTITY_ID: entity_id, ATTR_VALUE: value}, + blocking=True, + ) + assert exc_info.value.translation_domain == "nibe_heatpump" + assert exc_info.value.translation_key == translation_key + assert exc_info.value.translation_placeholders == translation_placeholders