diff --git a/homeassistant/components/onewire/entity.py b/homeassistant/components/onewire/entity.py index afee4a73366f..66d077bae380 100644 --- a/homeassistant/components/onewire/entity.py +++ b/homeassistant/components/onewire/entity.py @@ -6,9 +6,12 @@ from typing import Any, override from aio_ownet.exceptions import OWServerError from aio_ownet.proxy import OWServerStatelessProxy +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.device_registry import DeviceInfo from homeassistant.helpers.entity import Entity, EntityDescription +from .const import DOMAIN + _LOGGER = logging.getLogger(__name__) @@ -44,7 +47,14 @@ class OneWireEntity(Entity): async def _write_value(self, value: bytes) -> None: """Write a value to the server.""" - await self._owproxy.write(self._device_file, value) + try: + await self._owproxy.write(self._device_file, value) + except OWServerError as exc: + raise HomeAssistantError( + translation_domain=DOMAIN, + translation_key="write_failed", + translation_placeholders={"device_file": self._device_file}, + ) from exc async def async_update(self) -> None: """Get the latest data from the device.""" diff --git a/homeassistant/components/onewire/quality_scale.yaml b/homeassistant/components/onewire/quality_scale.yaml index a7fd675f658c..fc0cf17b3061 100644 --- a/homeassistant/components/onewire/quality_scale.yaml +++ b/homeassistant/components/onewire/quality_scale.yaml @@ -39,9 +39,7 @@ rules: config-entry-unloading: done log-when-unavailable: done entity-unavailable: done - action-exceptions: - status: exempt - comment: No service actions currently available + action-exceptions: done reauthentication-flow: status: exempt comment: Local polling without authentication diff --git a/homeassistant/components/onewire/strings.json b/homeassistant/components/onewire/strings.json index 709fcbb03c0e..8b0a5a260036 100644 --- a/homeassistant/components/onewire/strings.json +++ b/homeassistant/components/onewire/strings.json @@ -128,6 +128,11 @@ } } }, + "exceptions": { + "write_failed": { + "message": "Error writing to {device_file}" + } + }, "options": { "abort": { "no_configurable_devices": "No configurable devices found" diff --git a/tests/components/onewire/test_select.py b/tests/components/onewire/test_select.py index 495366bd8b0c..b08ebd9f18f4 100644 --- a/tests/components/onewire/test_select.py +++ b/tests/components/onewire/test_select.py @@ -3,6 +3,7 @@ from collections.abc import Generator from unittest.mock import MagicMock, patch +from aio_ownet.exceptions import OWServerConnectionError from freezegun.api import FrozenDateTimeFactory import pytest from syrupy.assertion import SnapshotAssertion @@ -14,6 +15,7 @@ from homeassistant.components.select import ( ) from homeassistant.const import ATTR_ENTITY_ID, ATTR_OPTION, Platform from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from . import setup_owproxy_mock_devices @@ -95,3 +97,30 @@ async def test_selection_option_service( blocking=True, ) assert hass.states.get(entity_id).state == "9" + + +@pytest.mark.parametrize("device_id", ["28.111111111111"]) +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_selection_option_write_failure( + hass: HomeAssistant, + config_entry: MockConfigEntry, + owproxy: MagicMock, + device_id: str, +) -> None: + """Test 1-Wire select option action raises on write failure.""" + setup_owproxy_mock_devices(owproxy, [device_id]) + await hass.config_entries.async_setup(config_entry.entry_id) + + owproxy.return_value.write.side_effect = OWServerConnectionError + with pytest.raises(HomeAssistantError) as exc_info: + await hass.services.async_call( + SELECT_DOMAIN, + SERVICE_SELECT_OPTION, + { + ATTR_ENTITY_ID: "select.28_111111111111_temperature_resolution", + ATTR_OPTION: "9", + }, + blocking=True, + ) + assert exc_info.value.translation_key == "write_failed" + assert str(exc_info.value) == "Error writing to /28.111111111111/tempres" diff --git a/tests/components/onewire/test_switch.py b/tests/components/onewire/test_switch.py index ca13a69e2dac..d57c32233563 100644 --- a/tests/components/onewire/test_switch.py +++ b/tests/components/onewire/test_switch.py @@ -3,6 +3,7 @@ from collections.abc import Generator from unittest.mock import MagicMock, patch +from aio_ownet.exceptions import OWServerConnectionError from freezegun.api import FrozenDateTimeFactory import pytest from syrupy.assertion import SnapshotAssertion @@ -12,11 +13,14 @@ from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN from homeassistant.const import ( ATTR_ENTITY_ID, SERVICE_TOGGLE, + SERVICE_TURN_OFF, + SERVICE_TURN_ON, STATE_OFF, STATE_ON, Platform, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er from . import setup_owproxy_mock_devices @@ -110,3 +114,30 @@ async def test_switch_toggle( ) await hass.async_block_till_done() assert hass.states.get(entity_id).state == STATE_ON + + +@pytest.mark.parametrize("device_id", ["05.111111111111"]) +@pytest.mark.parametrize("service", [SERVICE_TURN_ON, SERVICE_TURN_OFF]) +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_switch_write_failure( + hass: HomeAssistant, + config_entry: MockConfigEntry, + owproxy: MagicMock, + device_id: str, + service: str, +) -> None: + """Test 1-Wire switch action raises on write failure.""" + setup_owproxy_mock_devices(owproxy, [device_id]) + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() + + owproxy.return_value.write.side_effect = OWServerConnectionError + with pytest.raises(HomeAssistantError) as exc_info: + await hass.services.async_call( + SWITCH_DOMAIN, + service, + {ATTR_ENTITY_ID: "switch.05_111111111111_programmed_input_output"}, + blocking=True, + ) + assert exc_info.value.translation_key == "write_failed" + assert str(exc_info.value) == "Error writing to /05.111111111111/PIO"