diff --git a/homeassistant/components/reolink/siren.py b/homeassistant/components/reolink/siren.py index 4f493cd448b6e8..cfd1f5f82f0864 100644 --- a/homeassistant/components/reolink/siren.py +++ b/homeassistant/components/reolink/siren.py @@ -43,6 +43,7 @@ class ReolinkHostSirenEntityDescription( SIREN_ENTITIES = ( ReolinkSirenEntityDescription( key="siren", + cmd_id=547, translation_key="siren", supported=lambda api, ch: api.supported(ch, "siren_play"), ), @@ -100,6 +101,11 @@ def __init__( self.entity_description = entity_description super().__init__(reolink_data, channel) + @property + def is_on(self) -> bool | None: + """State of the siren.""" + return self._host.api.baichuan.siren_state(self._channel) + @raise_translated_error async def async_turn_on(self, **kwargs: Any) -> None: """Turn on the siren.""" diff --git a/tests/components/devolo_home_control/conftest.py b/tests/components/devolo_home_control/conftest.py index 55e072d075c959..33655c8cf83af4 100644 --- a/tests/components/devolo_home_control/conftest.py +++ b/tests/components/devolo_home_control/conftest.py @@ -1,41 +1,25 @@ """Fixtures for tests.""" from collections.abc import Generator +from itertools import cycle from unittest.mock import MagicMock, patch import pytest -@pytest.fixture -def credentials_valid() -> bool: - """Mark test as credentials invalid.""" - return True - - -@pytest.fixture -def maintenance() -> bool: - """Mark test as maintenance mode on.""" - return False - - @pytest.fixture(autouse=True) -def patch_mydevolo(credentials_valid: bool, maintenance: bool) -> Generator[None]: +def mydevolo() -> Generator[None]: """Fixture to patch mydevolo into a desired state.""" - with ( - patch( - "homeassistant.components.devolo_home_control.Mydevolo.credentials_valid", - return_value=credentials_valid, - ), - patch( - "homeassistant.components.devolo_home_control.Mydevolo.maintenance", - return_value=maintenance, - ), - patch( - "homeassistant.components.devolo_home_control.Mydevolo.get_gateway_ids", - return_value=["1400000000000001", "1400000000000002"], - ), + mydevolo = MagicMock() + mydevolo.uuid.return_value = "123456" + mydevolo.credentials_valid.return_value = True + mydevolo.maintenance.return_value = False + mydevolo.get_gateway_ids.return_value = ["1400000000000001", "1400000000000002"] + with patch( + "homeassistant.components.devolo_home_control.Mydevolo", + side_effect=cycle([mydevolo]), ): - yield + yield mydevolo @pytest.fixture(autouse=True) diff --git a/tests/components/devolo_home_control/test_config_flow.py b/tests/components/devolo_home_control/test_config_flow.py index 9367d746d2ef9c..c872bc5c65b7c4 100644 --- a/tests/components/devolo_home_control/test_config_flow.py +++ b/tests/components/devolo_home_control/test_config_flow.py @@ -1,13 +1,12 @@ """Test the devolo_home_control config flow.""" -from unittest.mock import patch - -import pytest +from unittest.mock import MagicMock from homeassistant import config_entries from homeassistant.components.devolo_home_control.const import DOMAIN +from homeassistant.const import CONF_PASSWORD, CONF_USERNAME from homeassistant.core import HomeAssistant -from homeassistant.data_entry_flow import FlowResult, FlowResultType +from homeassistant.data_entry_flow import FlowResultType from .const import ( DISCOVERY_INFO, @@ -20,7 +19,6 @@ async def test_form(hass: HomeAssistant) -> None: """Test we get the form.""" - result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) @@ -28,42 +26,56 @@ async def test_form(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.FORM assert result["errors"] == {} - await _setup(hass, result) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_USERNAME: "test-username", CONF_PASSWORD: "test-password"}, + ) + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == "devolo Home Control" + assert result["data"] == { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + } -@pytest.mark.parametrize("credentials_valid", [False]) -async def test_form_invalid_credentials_user(hass: HomeAssistant) -> None: +async def test_form_invalid_credentials_user( + hass: HomeAssistant, mydevolo: MagicMock +) -> None: """Test if we get the error message on invalid credentials.""" - + mydevolo.credentials_valid.return_value = False result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) - assert result["step_id"] == "user" - assert result["type"] is FlowResultType.FORM - assert result["errors"] == {} result = await hass.config_entries.flow.async_configure( result["flow_id"], - {"username": "test-username", "password": "test-password"}, + {CONF_USERNAME: "test-username", CONF_PASSWORD: "wrong-password"}, ) - + assert result["type"] is FlowResultType.FORM assert result["errors"] == {"base": "invalid_auth"} + mydevolo.credentials_valid.return_value = True + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_USERNAME: "test-username", CONF_PASSWORD: "correct-password"}, + ) + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["data"] == { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "correct-password", + } + async def test_form_already_configured(hass: HomeAssistant) -> None: """Test if we get the error message on already configured.""" - with patch( - "homeassistant.components.devolo_home_control.Mydevolo.uuid", - return_value="123456", - ): - MockConfigEntry(domain=DOMAIN, unique_id="123456", data={}).add_to_hass(hass) - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": config_entries.SOURCE_USER}, - data={"username": "test-username", "password": "test-password"}, - ) - assert result["type"] is FlowResultType.ABORT - assert result["reason"] == "already_configured" + MockConfigEntry(domain=DOMAIN, unique_id="123456", data={}).add_to_hass(hass) + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": config_entries.SOURCE_USER}, + data={CONF_USERNAME: "test-username", CONF_PASSWORD: "test-password"}, + ) + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "already_configured" async def test_form_zeroconf(hass: HomeAssistant) -> None: @@ -73,32 +85,45 @@ async def test_form_zeroconf(hass: HomeAssistant) -> None: context={"source": config_entries.SOURCE_ZEROCONF}, data=DISCOVERY_INFO, ) - assert result["step_id"] == "zeroconf_confirm" assert result["type"] is FlowResultType.FORM - await _setup(hass, result) + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_USERNAME: "test-username", CONF_PASSWORD: "test-password"}, + ) + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == "devolo Home Control" + assert result["data"] == { + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", + } -@pytest.mark.parametrize("credentials_valid", [False]) -async def test_form_invalid_credentials_zeroconf(hass: HomeAssistant) -> None: +async def test_form_invalid_credentials_zeroconf( + hass: HomeAssistant, mydevolo: MagicMock +) -> None: """Test if we get the error message on invalid credentials.""" - + mydevolo.credentials_valid.return_value = False result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_ZEROCONF}, data=DISCOVERY_INFO, ) - assert result["step_id"] == "zeroconf_confirm" + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_USERNAME: "test-username", CONF_PASSWORD: "test-password"}, + ) assert result["type"] is FlowResultType.FORM + assert result["errors"] == {"base": "invalid_auth"} + mydevolo.credentials_valid.return_value = True result = await hass.config_entries.flow.async_configure( result["flow_id"], - {"username": "test-username", "password": "test-password"}, + {CONF_USERNAME: "test-username", CONF_PASSWORD: "correct-password"}, ) - - assert result["errors"] == {"base": "invalid_auth"} + assert result["type"] is FlowResultType.CREATE_ENTRY async def test_zeroconf_wrong_device(hass: HomeAssistant) -> None: @@ -108,7 +133,6 @@ async def test_zeroconf_wrong_device(hass: HomeAssistant) -> None: context={"source": config_entries.SOURCE_ZEROCONF}, data=DISCOVERY_INFO_WRONG_DEVOLO_DEVICE, ) - assert result["reason"] == "Not a devolo Home Control gateway." assert result["type"] is FlowResultType.ABORT @@ -128,8 +152,8 @@ async def test_form_reauth(hass: HomeAssistant) -> None: domain=DOMAIN, unique_id="123456", data={ - "username": "test-username", - "password": "test-password", + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", }, ) mock_config.add_to_hass(hass) @@ -137,35 +161,25 @@ async def test_form_reauth(hass: HomeAssistant) -> None: assert result["step_id"] == "reauth_confirm" assert result["type"] is FlowResultType.FORM - with ( - patch( - "homeassistant.components.devolo_home_control.async_setup_entry", - return_value=True, - ) as mock_setup_entry, - patch( - "homeassistant.components.devolo_home_control.Mydevolo.uuid", - return_value="123456", - ), - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"username": "test-username-new", "password": "test-password-new"}, - ) - await hass.async_block_till_done() - - assert result2["type"] is FlowResultType.ABORT - assert len(mock_setup_entry.mock_calls) == 1 - - -@pytest.mark.parametrize("credentials_valid", [False]) -async def test_form_invalid_credentials_reauth(hass: HomeAssistant) -> None: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_USERNAME: "test-username-new", CONF_PASSWORD: "test-password-new"}, + ) + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reauth_successful" + + +async def test_form_invalid_credentials_reauth( + hass: HomeAssistant, mydevolo: MagicMock +) -> None: """Test if we get the error message on invalid credentials.""" + mydevolo.credentials_valid.return_value = False mock_config = MockConfigEntry( domain=DOMAIN, unique_id="123456", data={ - "username": "test-username", - "password": "test-password", + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", }, ) mock_config.add_to_hass(hass) @@ -173,71 +187,38 @@ async def test_form_invalid_credentials_reauth(hass: HomeAssistant) -> None: result = await hass.config_entries.flow.async_configure( result["flow_id"], - {"username": "test-username", "password": "test-password"}, + {CONF_USERNAME: "test-username", CONF_PASSWORD: "wrong-password"}, ) - assert result["errors"] == {"base": "invalid_auth"} + assert result["type"] is FlowResultType.FORM + + mydevolo.credentials_valid.return_value = True + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_USERNAME: "test-username-new", CONF_PASSWORD: "correct-password"}, + ) + assert result["reason"] == "reauth_successful" + assert result["type"] is FlowResultType.ABORT async def test_form_uuid_change_reauth(hass: HomeAssistant) -> None: """Test that the reauth confirmation form is served.""" mock_config = MockConfigEntry( domain=DOMAIN, - unique_id="123456", + unique_id="123457", data={ - "username": "test-username", - "password": "test-password", + CONF_USERNAME: "test-username", + CONF_PASSWORD: "test-password", }, ) mock_config.add_to_hass(hass) result = await mock_config.start_reauth_flow(hass) - assert result["step_id"] == "reauth_confirm" assert result["type"] is FlowResultType.FORM - with ( - patch( - "homeassistant.components.devolo_home_control.async_setup_entry", - return_value=True, - ), - patch( - "homeassistant.components.devolo_home_control.Mydevolo.uuid", - return_value="789123", - ), - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"username": "test-username-new", "password": "test-password-new"}, - ) - await hass.async_block_till_done() - - assert result2["type"] is FlowResultType.FORM - assert result2["errors"] == {"base": "reauth_failed"} - - -async def _setup(hass: HomeAssistant, result: FlowResult) -> None: - """Finish configuration steps.""" - with ( - patch( - "homeassistant.components.devolo_home_control.async_setup_entry", - return_value=True, - ) as mock_setup_entry, - patch( - "homeassistant.components.devolo_home_control.Mydevolo.uuid", - return_value="123456", - ), - ): - result2 = await hass.config_entries.flow.async_configure( - result["flow_id"], - {"username": "test-username", "password": "test-password"}, - ) - await hass.async_block_till_done() - - assert result2["type"] is FlowResultType.CREATE_ENTRY - assert result2["title"] == "devolo Home Control" - assert result2["data"] == { - "username": "test-username", - "password": "test-password", - } - - assert len(mock_setup_entry.mock_calls) == 1 + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_USERNAME: "test-username-new", CONF_PASSWORD: "test-password-new"}, + ) + assert result["type"] is FlowResultType.FORM + assert result["errors"] == {"base": "reauth_failed"} diff --git a/tests/components/devolo_home_control/test_init.py b/tests/components/devolo_home_control/test_init.py index fb97447264d165..c9b39366cdd641 100644 --- a/tests/components/devolo_home_control/test_init.py +++ b/tests/components/devolo_home_control/test_init.py @@ -1,9 +1,8 @@ """Tests for the devolo Home Control integration.""" -from unittest.mock import patch +from unittest.mock import MagicMock, patch from devolo_home_control_api.exceptions.gateway import GatewayOfflineError -import pytest from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN from homeassistant.components.devolo_home_control.const import DOMAIN @@ -27,17 +26,21 @@ async def test_setup_entry(hass: HomeAssistant) -> None: assert entry.state is ConfigEntryState.LOADED -@pytest.mark.parametrize("credentials_valid", [False]) -async def test_setup_entry_credentials_invalid(hass: HomeAssistant) -> None: +async def test_setup_entry_credentials_invalid( + hass: HomeAssistant, mydevolo: MagicMock +) -> None: """Test setup entry fails if credentials are invalid.""" + mydevolo.credentials_valid.return_value = False entry = configure_integration(hass) await hass.config_entries.async_setup(entry.entry_id) assert entry.state is ConfigEntryState.SETUP_ERROR -@pytest.mark.parametrize("maintenance", [True]) -async def test_setup_entry_maintenance(hass: HomeAssistant) -> None: +async def test_setup_entry_maintenance( + hass: HomeAssistant, mydevolo: MagicMock +) -> None: """Test setup entry fails if mydevolo is in maintenance mode.""" + mydevolo.maintenance.return_value = True entry = configure_integration(hass) await hass.config_entries.async_setup(entry.entry_id) assert entry.state is ConfigEntryState.SETUP_RETRY diff --git a/tests/components/reolink/conftest.py b/tests/components/reolink/conftest.py index f40bfa8398521d..d501b146b7d9dc 100644 --- a/tests/components/reolink/conftest.py +++ b/tests/components/reolink/conftest.py @@ -171,6 +171,7 @@ def _init_host_mock(host_mock: MagicMock) -> None: host_mock.baichuan.mac_address.return_value = TEST_MAC_CAM host_mock.baichuan.privacy_mode.return_value = False host_mock.baichuan.day_night_state.return_value = "day" + host_mock.baichuan.siren_state.return_value = True host_mock.baichuan.subscribe_events.side_effect = ReolinkError("Test error") host_mock.baichuan.active_scene = "off" host_mock.baichuan.scene_names = ["off", "home"] diff --git a/tests/components/reolink/test_siren.py b/tests/components/reolink/test_siren.py index 0f69ecf87ea1b8..c3ed7708f526e7 100644 --- a/tests/components/reolink/test_siren.py +++ b/tests/components/reolink/test_siren.py @@ -16,6 +16,7 @@ ATTR_ENTITY_ID, SERVICE_TURN_OFF, SERVICE_TURN_ON, + STATE_ON, STATE_UNKNOWN, Platform, ) @@ -39,7 +40,7 @@ async def test_siren( assert config_entry.state is ConfigEntryState.LOADED entity_id = f"{Platform.SIREN}.{TEST_CAM_NAME}_siren" - assert hass.states.get(entity_id).state == STATE_UNKNOWN + assert hass.states.get(entity_id).state == STATE_ON # test siren turn on await hass.services.async_call(