diff --git a/homeassistant/components/siren/__init__.py b/homeassistant/components/siren/__init__.py index f301100fa6c..ed0e8b8645f 100644 --- a/homeassistant/components/siren/__init__.py +++ b/homeassistant/components/siren/__init__.py @@ -64,10 +64,29 @@ def process_turn_on_params( if not supported_features & SUPPORT_TONES: params.pop(ATTR_TONE, None) - elif (tone := params.get(ATTR_TONE)) is not None and ( - not siren.available_tones or tone not in siren.available_tones - ): - raise ValueError(f"Invalid tone received for entity {siren.entity_id}: {tone}") + elif (tone := params.get(ATTR_TONE)) is not None: + # Raise an exception if the specified tone isn't available + is_tone_dict_value = bool( + isinstance(siren.available_tones, dict) + and tone in siren.available_tones.values() + ) + if ( + not siren.available_tones + or tone not in siren.available_tones + and not is_tone_dict_value + ): + raise ValueError( + f"Invalid tone specified for entity {siren.entity_id}: {tone}, " + "check the available_tones attribute for valid tones to pass in" + ) + + # If available tones is a dict, and the tone provided is a dict value, we need + # to transform it to the corresponding dict key before returning + if is_tone_dict_value: + assert isinstance(siren.available_tones, dict) + params[ATTR_TONE] = next( + key for key, value in siren.available_tones.items() if value == tone + ) if not supported_features & SUPPORT_DURATION: params.pop(ATTR_DURATION, None) @@ -131,7 +150,7 @@ class SirenEntity(ToggleEntity): """Representation of a siren device.""" entity_description: SirenEntityDescription - _attr_available_tones: list[int | str] | None = None + _attr_available_tones: list[int | str] | dict[int, str] | None = None @final @property @@ -145,7 +164,7 @@ class SirenEntity(ToggleEntity): return None @property - def available_tones(self) -> list[int | str] | None: + def available_tones(self) -> list[int | str] | dict[int, str] | None: """ Return a list of available tones. diff --git a/tests/components/siren/test_init.py b/tests/components/siren/test_init.py index 729990ceaeb..e46fbbf8d5e 100644 --- a/tests/components/siren/test_init.py +++ b/tests/components/siren/test_init.py @@ -48,9 +48,32 @@ async def test_no_available_tones(hass): process_turn_on_params(siren, {"tone": "test"}) -async def test_missing_tones(hass): - """Test ValueError when setting a tone that is missing from available_tones.""" +async def test_available_tones_list(hass): + """Test that valid tones from tone list will get passed in.""" + siren = MockSirenEntity(SUPPORT_TONES, ["a", "b"]) + siren.hass = hass + assert process_turn_on_params(siren, {"tone": "a"}) == {"tone": "a"} + + +async def test_available_tones_dict(hass): + """Test that valid tones from available_tones dict will get passed in.""" + siren = MockSirenEntity(SUPPORT_TONES, {1: "a", 2: "b"}) + siren.hass = hass + assert process_turn_on_params(siren, {"tone": "a"}) == {"tone": 1} + assert process_turn_on_params(siren, {"tone": 1}) == {"tone": 1} + + +async def test_missing_tones_list(hass): + """Test ValueError when setting a tone that is missing from available_tones list.""" siren = MockSirenEntity(SUPPORT_TONES, ["a", "b"]) siren.hass = hass with pytest.raises(ValueError): process_turn_on_params(siren, {"tone": "test"}) + + +async def test_missing_tones_dict(hass): + """Test ValueError when setting a tone that is missing from available_tones dict.""" + siren = MockSirenEntity(SUPPORT_TONES, {1: "a", 2: "b"}) + siren.hass = hass + with pytest.raises(ValueError): + process_turn_on_params(siren, {"tone": 3})