"""Tests for the Radio Frequency integration setup.""" from unittest.mock import AsyncMock from freezegun.api import FrozenDateTimeFactory import pytest from rf_protocols import ModulationType from homeassistant.components.radio_frequency import ( DATA_COMPONENT, DOMAIN, async_get_transmitters, async_send_command, ) from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN from homeassistant.core import HomeAssistant, State from homeassistant.exceptions import HomeAssistantError from homeassistant.setup import async_setup_component from homeassistant.util import dt as dt_util from . import ENTITY_ID from .common import MockRadioFrequencyCommand, MockRadioFrequencyEntity from tests.common import mock_restore_cache async def test_get_transmitters_component_not_loaded(hass: HomeAssistant) -> None: """Test getting transmitters raises when the component is not loaded.""" with pytest.raises(HomeAssistantError, match="component_not_loaded"): async_get_transmitters(hass, 433_920_000, ModulationType.OOK) @pytest.mark.usefixtures("init_radio_frequency") async def test_get_transmitters_no_entities(hass: HomeAssistant) -> None: """Test getting transmitters raises when none are registered.""" with pytest.raises( HomeAssistantError, match="No Radio Frequency transmitters available", ): async_get_transmitters(hass, 433_920_000, ModulationType.OOK) @pytest.mark.usefixtures("mock_rf_entity") async def test_get_transmitters_with_frequency_ranges(hass: HomeAssistant) -> None: """Test transmitter with frequency ranges filters correctly.""" # 433.92 MHz is within 433-434 MHz range result = async_get_transmitters(hass, 433_920_000, ModulationType.OOK) assert result == [ENTITY_ID] # 868 MHz is outside the range result = async_get_transmitters(hass, 868_000_000, ModulationType.OOK) assert result == [] @pytest.mark.usefixtures("mock_rf_entity") async def test_get_transmitters_filters_by_modulation(hass: HomeAssistant) -> None: """Test transmitters are filtered by supported modulation.""" result = async_get_transmitters(hass, 433_920_000, "no_matching_modulation") # type: ignore[arg-type] assert result == [] @pytest.mark.usefixtures("mock_rf_entity") async def test_rf_entity_initial_state(hass: HomeAssistant) -> None: """Test radio frequency entity has no state before any command is sent.""" state = hass.states.get(ENTITY_ID) assert state is not None assert state.state == STATE_UNKNOWN async def test_async_send_command_success( hass: HomeAssistant, mock_rf_entity: MockRadioFrequencyEntity, freezer: FrozenDateTimeFactory, ) -> None: """Test sending command via async_send_command helper.""" now = dt_util.utcnow() freezer.move_to(now) command = MockRadioFrequencyCommand(frequency=433_920_000) await async_send_command(hass, ENTITY_ID, command) assert len(mock_rf_entity.send_command_calls) == 1 assert mock_rf_entity.send_command_calls[0].command is command state = hass.states.get(ENTITY_ID) assert state is not None assert state.state == now.isoformat(timespec="milliseconds") async def test_async_send_command_error_does_not_update_state( hass: HomeAssistant, mock_rf_entity: MockRadioFrequencyEntity, ) -> None: """Test that state is not updated when async_send_command raises an error.""" state = hass.states.get(ENTITY_ID) assert state is not None assert state.state == STATE_UNKNOWN command = MockRadioFrequencyCommand(frequency=433_920_000) mock_rf_entity.async_send_command = AsyncMock( side_effect=HomeAssistantError("Transmission failed") ) with pytest.raises(HomeAssistantError, match="Transmission failed"): await async_send_command(hass, ENTITY_ID, command) state = hass.states.get(ENTITY_ID) assert state is not None assert state.state == STATE_UNKNOWN @pytest.mark.usefixtures("init_radio_frequency") async def test_async_send_command_entity_not_found(hass: HomeAssistant) -> None: """Test async_send_command raises error when entity not found.""" command = MockRadioFrequencyCommand(frequency=433_920_000) with pytest.raises( HomeAssistantError, match="Radio Frequency entity `radio_frequency.nonexistent_entity` not found", ): await async_send_command(hass, "radio_frequency.nonexistent_entity", command) async def test_async_send_command_unsupported_frequency( hass: HomeAssistant, mock_rf_entity: MockRadioFrequencyEntity, ) -> None: """Test async_send_command raises when the frequency is not supported.""" command = MockRadioFrequencyCommand(frequency=868_000_000) with pytest.raises( HomeAssistantError, match=( f"Radio Frequency entity `{ENTITY_ID}` " "does not support frequency 868000000 Hz" ), ): await async_send_command(hass, ENTITY_ID, command) assert mock_rf_entity.send_command_calls == [] @pytest.mark.usefixtures("mock_rf_entity") async def test_async_send_command_unsupported_modulation( hass: HomeAssistant, mock_rf_entity: MockRadioFrequencyEntity, ) -> None: """Test async_send_command raises when the modulation is not supported.""" command = MockRadioFrequencyCommand( frequency=433_920_000, modulation="incorrect_modulation", # type: ignore[arg-type] ) with pytest.raises( HomeAssistantError, match=( f"Radio Frequency entity `{ENTITY_ID}` " "does not support modulation incorrect_modulation" ), ): await async_send_command(hass, ENTITY_ID, command) assert mock_rf_entity.send_command_calls == [] async def test_async_send_command_component_not_loaded(hass: HomeAssistant) -> None: """Test async_send_command raises error when component not loaded.""" command = MockRadioFrequencyCommand(frequency=433_920_000) with pytest.raises(HomeAssistantError, match="component_not_loaded"): await async_send_command(hass, "radio_frequency.some_entity", command) @pytest.mark.parametrize( ("restored_value", "expected_state"), [ ("2026-01-01T12:00:00.000+00:00", "2026-01-01T12:00:00.000+00:00"), (STATE_UNAVAILABLE, STATE_UNKNOWN), ], ) async def test_rf_entity_state_restore( hass: HomeAssistant, restored_value: str, expected_state: str, ) -> None: """Test radio frequency entity state restore.""" mock_restore_cache(hass, [State(ENTITY_ID, restored_value)]) assert await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done() component = hass.data[DATA_COMPONENT] await component.async_add_entities( [MockRadioFrequencyEntity("test_rf_transmitter")] ) state = hass.states.get(ENTITY_ID) assert state is not None assert state.state == expected_state