diff --git a/homeassistant/components/lock/__init__.py b/homeassistant/components/lock/__init__.py index ec462c3b993..9a2466e22dd 100644 --- a/homeassistant/components/lock/__init__.py +++ b/homeassistant/components/lock/__init__.py @@ -24,6 +24,7 @@ from homeassistant.const import ( STATE_UNLOCKING, ) from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import ServiceValidationError import homeassistant.helpers.config_validation as cv from homeassistant.helpers.config_validation import ( # noqa: F401 PLATFORM_SCHEMA, @@ -152,8 +153,16 @@ class LockEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_ATTR_): if not code: code = self._lock_option_default_code if self.code_format_cmp and not self.code_format_cmp.match(code): - raise ValueError( - f"Code '{code}' for locking {self.entity_id} doesn't match pattern {self.code_format}" + if TYPE_CHECKING: + assert self.code_format + raise ServiceValidationError( + f"The code for {self.entity_id} doesn't match pattern {self.code_format}", + translation_domain=DOMAIN, + translation_key="add_default_code", + translation_placeholders={ + "entity_id": self.entity_id, + "code_format": self.code_format, + }, ) if code: data[ATTR_CODE] = code diff --git a/homeassistant/components/lock/strings.json b/homeassistant/components/lock/strings.json index d041d6ac61a..152a06f9e53 100644 --- a/homeassistant/components/lock/strings.json +++ b/homeassistant/components/lock/strings.json @@ -66,5 +66,10 @@ } } } + }, + "exceptions": { + "add_default_code": { + "message": "The code for {entity_id} doesn't match pattern {code_format}." + } } } diff --git a/tests/components/lock/test_init.py b/tests/components/lock/test_init.py index a03d975ed8a..c4337c367a9 100644 --- a/tests/components/lock/test_init.py +++ b/tests/components/lock/test_init.py @@ -1,6 +1,7 @@ """The tests for the lock component.""" from __future__ import annotations +import re from typing import Any import pytest @@ -21,6 +22,7 @@ from homeassistant.components.lock import ( LockEntityFeature, ) from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ServiceValidationError import homeassistant.helpers.entity_registry as er from homeassistant.helpers.typing import UNDEFINED, UndefinedType @@ -134,15 +136,15 @@ async def test_lock_open_with_code( state = hass.states.get(mock_lock_entity.entity_id) assert state.attributes["code_format"] == r"^\d{4}$" - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_OPEN ) - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_OPEN, code="" ) - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_OPEN, code="HELLO" ) @@ -170,15 +172,15 @@ async def test_lock_lock_with_code( mock_lock_entity.calls_unlock.assert_called_with(code="1234") assert mock_lock_entity.calls_lock.call_count == 0 - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_LOCK ) - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_LOCK, code="" ) - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_LOCK, code="HELLO" ) @@ -206,15 +208,15 @@ async def test_lock_unlock_with_code( mock_lock_entity.calls_lock.assert_called_with(code="1234") assert mock_lock_entity.calls_unlock.call_count == 0 - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_UNLOCK ) - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_UNLOCK, code="" ) - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_UNLOCK, code="HELLO" ) @@ -234,15 +236,15 @@ async def test_lock_with_illegal_code( ) -> None: """Test lock entity with default code that does not match the code format.""" - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_OPEN, code="123456" ) - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_LOCK, code="123456" ) - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_UNLOCK, code="123456" ) @@ -344,19 +346,30 @@ async def test_lock_with_illegal_default_code( assert mock_lock_entity.state_attributes == {"code_format": r"^\d{4}$"} assert mock_lock_entity._lock_option_default_code == "" - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_OPEN ) - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_LOCK ) - with pytest.raises(ValueError): + with pytest.raises( + ServiceValidationError, + match=re.escape( + rf"The code for lock.test_lock doesn't match pattern ^\d{{{4}}}$" + ), + ) as exc: await help_test_async_lock_service( hass, mock_lock_entity.entity_id, SERVICE_UNLOCK ) + assert ( + str(exc.value) + == rf"The code for lock.test_lock doesn't match pattern ^\d{{{4}}}$" + ) + assert exc.value.translation_key == "add_default_code" + @pytest.mark.parametrize(("enum"), list(LockEntityFeature)) def test_deprecated_constants( diff --git a/tests/components/matter/test_door_lock.py b/tests/components/matter/test_door_lock.py index 991d23f3353..51d48cddba7 100644 --- a/tests/components/matter/test_door_lock.py +++ b/tests/components/matter/test_door_lock.py @@ -14,6 +14,7 @@ from homeassistant.components.lock import ( ) from homeassistant.const import ATTR_CODE, STATE_UNKNOWN from homeassistant.core import HomeAssistant +from homeassistant.exceptions import ServiceValidationError import homeassistant.helpers.entity_registry as er from .common import set_node_attribute, trigger_subscription_callback @@ -113,7 +114,7 @@ async def test_lock_requires_pin( # set door state to unlocked set_node_attribute(door_lock, 1, 257, 0, 2) - with pytest.raises(ValueError): + with pytest.raises(ServiceValidationError): # Lock door using invalid code format await trigger_subscription_callback(hass, matter_client) await hass.services.async_call(