"""Test the homewizard config flow.""" from unittest.mock import MagicMock, patch from homewizard_energy.errors import DisabledError, RequestError, UnsupportedError from homeassistant import config_entries from homeassistant.components import zeroconf from homeassistant.components.homewizard.const import DOMAIN from homeassistant.const import CONF_IP_ADDRESS from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType from .generator import get_mock_device from tests.common import MockConfigEntry from tests.test_util.aiohttp import AiohttpClientMocker async def test_manual_flow_works( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test config flow accepts user configuration.""" device = get_mock_device() result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == "form" assert result["step_id"] == "user" with patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ), patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, ) as mock_setup_entry: result = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"} ) assert result["type"] == "create_entry" assert result["title"] == "P1 meter (aabbccddeeff)" assert result["data"][CONF_IP_ADDRESS] == "2.2.2.2" assert len(hass.config_entries.async_entries(DOMAIN)) == 1 assert len(device.close.mock_calls) == len(device.device.mock_calls) assert len(mock_setup_entry.mock_calls) == 1 async def test_discovery_flow_works( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test discovery setup flow works.""" service_info = zeroconf.ZeroconfServiceInfo( host="192.168.43.183", addresses=["192.168.43.183"], port=80, hostname="p1meter-ddeeff.local.", type="", name="", properties={ "api_enabled": "1", "path": "/api/v1", "product_name": "P1 meter", "product_type": "HWE-P1", "serial": "aabbccddeeff", }, ) with patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=get_mock_device(), ): flow = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_ZEROCONF}, data=service_info, ) with patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, ), patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=get_mock_device(), ): result = await hass.config_entries.flow.async_configure( flow["flow_id"], user_input=None ) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "discovery_confirm" with patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, ), patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=get_mock_device(), ): result = await hass.config_entries.flow.async_configure( flow["flow_id"], user_input={"ip_address": "192.168.43.183"} ) assert result["type"] == FlowResultType.CREATE_ENTRY assert result["title"] == "P1 meter (aabbccddeeff)" assert result["data"][CONF_IP_ADDRESS] == "192.168.43.183" assert result["result"] assert result["result"].unique_id == "HWE-P1_aabbccddeeff" async def test_discovery_flow_during_onboarding( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, mock_onboarding: MagicMock ) -> None: """Test discovery setup flow during onboarding.""" with patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, ) as mock_setup_entry, patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=get_mock_device(), ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_ZEROCONF}, data=zeroconf.ZeroconfServiceInfo( host="192.168.43.183", addresses=["192.168.43.183"], port=80, hostname="p1meter-ddeeff.local.", type="mock_type", name="mock_name", properties={ "api_enabled": "1", "path": "/api/v1", "product_name": "P1 meter", "product_type": "HWE-P1", "serial": "aabbccddeeff", }, ), ) assert result["type"] == FlowResultType.CREATE_ENTRY assert result["title"] == "P1 meter (aabbccddeeff)" assert result["data"][CONF_IP_ADDRESS] == "192.168.43.183" assert result["result"] assert result["result"].unique_id == "HWE-P1_aabbccddeeff" assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_onboarding.mock_calls) == 1 async def test_discovery_flow_during_onboarding_disabled_api( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, mock_onboarding: MagicMock ) -> None: """Test discovery setup flow during onboarding with a disabled API.""" def mock_initialize(): raise DisabledError device = get_mock_device() device.device.side_effect = mock_initialize with patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_ZEROCONF}, data=zeroconf.ZeroconfServiceInfo( host="192.168.43.183", addresses=["192.168.43.183"], port=80, hostname="p1meter-ddeeff.local.", type="mock_type", name="mock_name", properties={ "api_enabled": "0", "path": "/api/v1", "product_name": "P1 meter", "product_type": "HWE-P1", "serial": "aabbccddeeff", }, ), ) assert result["type"] == FlowResultType.FORM assert result["step_id"] == "discovery_confirm" assert result["errors"] == {"base": "api_not_enabled"} # We are onboarded, user enabled API again and picks up from discovery/config flow device.device.side_effect = None mock_onboarding.return_value = True with patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, ) as mock_setup_entry, patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={"ip_address": "192.168.43.183"} ) assert result["type"] == FlowResultType.CREATE_ENTRY assert result["title"] == "P1 meter (aabbccddeeff)" assert result["data"][CONF_IP_ADDRESS] == "192.168.43.183" assert result["result"] assert result["result"].unique_id == "HWE-P1_aabbccddeeff" assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_onboarding.mock_calls) == 1 async def test_discovery_disabled_api( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test discovery detecting disabled api.""" service_info = zeroconf.ZeroconfServiceInfo( host="192.168.43.183", addresses=["192.168.43.183"], port=80, hostname="p1meter-ddeeff.local.", type="", name="", properties={ "api_enabled": "0", "path": "/api/v1", "product_name": "P1 meter", "product_type": "HWE-P1", "serial": "aabbccddeeff", }, ) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_ZEROCONF}, data=service_info, ) assert result["type"] == FlowResultType.FORM def mock_initialize(): raise DisabledError device = get_mock_device() device.device.side_effect = mock_initialize with patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, ), patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ): result = await hass.config_entries.flow.async_configure( result["flow_id"], user_input={"ip_address": "192.168.43.183"} ) assert result["type"] == FlowResultType.FORM assert result["errors"] == {"base": "api_not_enabled"} async def test_discovery_missing_data_in_service_info( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test discovery detecting missing discovery info.""" service_info = zeroconf.ZeroconfServiceInfo( host="192.168.43.183", addresses=["192.168.43.183"], port=80, hostname="p1meter-ddeeff.local.", type="", name="", properties={ # "api_enabled": "1", --> removed "path": "/api/v1", "product_name": "P1 meter", "product_type": "HWE-P1", "serial": "aabbccddeeff", }, ) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_ZEROCONF}, data=service_info, ) assert result["type"] == FlowResultType.ABORT assert result["reason"] == "invalid_discovery_parameters" async def test_discovery_invalid_api( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test discovery detecting invalid_api.""" service_info = zeroconf.ZeroconfServiceInfo( host="192.168.43.183", addresses=["192.168.43.183"], port=80, hostname="p1meter-ddeeff.local.", type="", name="", properties={ "api_enabled": "1", "path": "/api/not_v1", "product_name": "P1 meter", "product_type": "HWE-P1", "serial": "aabbccddeeff", }, ) result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_ZEROCONF}, data=service_info, ) assert result["type"] == FlowResultType.ABORT assert result["reason"] == "unsupported_api_version" async def test_check_disabled_api( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test check detecting disabled api.""" def mock_initialize(): raise DisabledError device = get_mock_device() device.device.side_effect = mock_initialize result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == "form" assert result["step_id"] == "user" with patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ): result = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"} ) assert result["type"] == FlowResultType.FORM assert result["errors"] == {"base": "api_not_enabled"} async def test_check_error_handling_api( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test check detecting error with api.""" def mock_initialize(): raise Exception() device = get_mock_device() device.device.side_effect = mock_initialize result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == "form" assert result["step_id"] == "user" with patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ): result = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"} ) assert result["type"] == FlowResultType.ABORT assert result["reason"] == "unknown_error" async def test_check_detects_invalid_api( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test check detecting device endpoint failed fetching data.""" def mock_initialize(): raise UnsupportedError device = get_mock_device() device.device.side_effect = mock_initialize result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == "form" assert result["step_id"] == "user" with patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ): result = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"} ) assert result["type"] == FlowResultType.ABORT assert result["reason"] == "unsupported_api_version" async def test_check_requesterror( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test check detecting device endpoint failed fetching data due to a requesterror.""" def mock_initialize(): raise RequestError device = get_mock_device() device.device.side_effect = mock_initialize result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": config_entries.SOURCE_USER} ) assert result["type"] == "form" assert result["step_id"] == "user" with patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ): result = await hass.config_entries.flow.async_configure( result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"} ) assert result["type"] == FlowResultType.FORM assert result["errors"] == {"base": "network_error"} async def test_reauth_flow( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test reauth flow while API is enabled.""" mock_entry = MockConfigEntry( domain="homewizard_energy", data={CONF_IP_ADDRESS: "1.2.3.4"} ) mock_entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={ "source": config_entries.SOURCE_REAUTH, "entry_id": mock_entry.entry_id, }, ) assert result["type"] == "form" assert result["step_id"] == "reauth_confirm" device = get_mock_device() with patch( "homeassistant.components.homewizard.async_setup_entry", return_value=True, ), patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ): result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.ABORT assert result["reason"] == "reauth_successful" async def test_reauth_error( hass: HomeAssistant, aioclient_mock: AiohttpClientMocker ) -> None: """Test reauth flow while API is still disabled.""" def mock_initialize(): raise DisabledError() mock_entry = MockConfigEntry( domain="homewizard_energy", data={CONF_IP_ADDRESS: "1.2.3.4"} ) mock_entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( DOMAIN, context={ "source": config_entries.SOURCE_REAUTH, "entry_id": mock_entry.entry_id, }, ) assert result["type"] == "form" assert result["step_id"] == "reauth_confirm" device = get_mock_device() device.device.side_effect = mock_initialize with patch( "homeassistant.components.homewizard.config_flow.HomeWizardEnergy", return_value=device, ): result = await hass.config_entries.flow.async_configure(result["flow_id"], {}) assert result["type"] == FlowResultType.FORM assert result["errors"] == {"base": "api_not_enabled"}