diff --git a/homeassistant/components/homematicip_cloud/hap.py b/homeassistant/components/homematicip_cloud/hap.py index d64d05e72aa..0d6fc726050 100644 --- a/homeassistant/components/homematicip_cloud/hap.py +++ b/homeassistant/components/homematicip_cloud/hap.py @@ -94,6 +94,9 @@ class HomematicipHAP: ) except HmipcConnectionError: raise ConfigEntryNotReady + except Exception as err: # pylint: disable=broad-except + _LOGGER.error("Error connecting with HomematicIP Cloud: %s", err) + return False _LOGGER.info( "Connected to HomematicIP with HAP %s", self.config_entry.unique_id diff --git a/homeassistant/components/homematicip_cloud/services.py b/homeassistant/components/homematicip_cloud/services.py index 19d50893b9b..193cac94629 100644 --- a/homeassistant/components/homematicip_cloud/services.py +++ b/homeassistant/components/homematicip_cloud/services.py @@ -14,10 +14,9 @@ import homeassistant.helpers.config_validation as cv from homeassistant.helpers.config_validation import comp_entity_ids from homeassistant.helpers.typing import HomeAssistantType, ServiceCallType -from .const import DOMAIN +from .const import DOMAIN as HMIPC_DOMAIN _LOGGER = logging.getLogger(__name__) -HOMEMATICIP_CLOUD_SERVICES = "homematicip_cloud_services" ATTR_ACCESSPOINT_ID = "accesspoint_id" ATTR_ANONYMIZE = "anonymize" @@ -118,11 +117,9 @@ SCHEMA_RESET_ENERGY_COUNTER = vol.Schema( async def async_setup_services(hass: HomeAssistantType) -> None: """Set up the HomematicIP Cloud services.""" - if hass.data.get(HOMEMATICIP_CLOUD_SERVICES, False): + if hass.services.async_services().get(HMIPC_DOMAIN): return - hass.data[HOMEMATICIP_CLOUD_SERVICES] = True - async def async_call_hmipc_service(service: ServiceCallType): """Call correct HomematicIP Cloud service.""" service_name = service.service @@ -145,74 +142,69 @@ async def async_setup_services(hass: HomeAssistantType) -> None: await _set_active_climate_profile(hass, service) hass.services.async_register( - DOMAIN, + HMIPC_DOMAIN, SERVICE_ACTIVATE_ECO_MODE_WITH_DURATION, async_call_hmipc_service, schema=SCHEMA_ACTIVATE_ECO_MODE_WITH_DURATION, ) hass.services.async_register( - DOMAIN, + HMIPC_DOMAIN, SERVICE_ACTIVATE_ECO_MODE_WITH_PERIOD, async_call_hmipc_service, schema=SCHEMA_ACTIVATE_ECO_MODE_WITH_PERIOD, ) hass.services.async_register( - DOMAIN, + HMIPC_DOMAIN, SERVICE_ACTIVATE_VACATION, async_call_hmipc_service, schema=SCHEMA_ACTIVATE_VACATION, ) hass.services.async_register( - DOMAIN, + HMIPC_DOMAIN, SERVICE_DEACTIVATE_ECO_MODE, async_call_hmipc_service, schema=SCHEMA_DEACTIVATE_ECO_MODE, ) hass.services.async_register( - DOMAIN, + HMIPC_DOMAIN, SERVICE_DEACTIVATE_VACATION, async_call_hmipc_service, schema=SCHEMA_DEACTIVATE_VACATION, ) hass.services.async_register( - DOMAIN, + HMIPC_DOMAIN, SERVICE_SET_ACTIVE_CLIMATE_PROFILE, async_call_hmipc_service, schema=SCHEMA_SET_ACTIVE_CLIMATE_PROFILE, ) hass.services.async_register( - DOMAIN, + HMIPC_DOMAIN, SERVICE_DUMP_HAP_CONFIG, async_call_hmipc_service, schema=SCHEMA_DUMP_HAP_CONFIG, ) hass.helpers.service.async_register_admin_service( - DOMAIN, + HMIPC_DOMAIN, SERVICE_RESET_ENERGY_COUNTER, async_call_hmipc_service, schema=SCHEMA_RESET_ENERGY_COUNTER, ) -async def async_unload_services(hass): +async def async_unload_services(hass: HomeAssistantType): """Unload HomematicIP Cloud services.""" - if hass.data[DOMAIN]: + if hass.data[HMIPC_DOMAIN]: return - if not hass.data.get(HOMEMATICIP_CLOUD_SERVICES): - return - - hass.data[HOMEMATICIP_CLOUD_SERVICES] = False - for hmipc_service in HMIPC_SERVICES: - hass.services.async_remove(DOMAIN, hmipc_service) + hass.services.async_remove(HMIPC_DOMAIN, hmipc_service) async def _async_activate_eco_mode_with_duration( @@ -227,7 +219,7 @@ async def _async_activate_eco_mode_with_duration( if home: await home.activate_absence_with_duration(duration) else: - for hap in hass.data[DOMAIN].values(): + for hap in hass.data[HMIPC_DOMAIN].values(): await hap.home.activate_absence_with_duration(duration) @@ -243,7 +235,7 @@ async def _async_activate_eco_mode_with_period( if home: await home.activate_absence_with_period(endtime) else: - for hap in hass.data[DOMAIN].values(): + for hap in hass.data[HMIPC_DOMAIN].values(): await hap.home.activate_absence_with_period(endtime) @@ -260,7 +252,7 @@ async def _async_activate_vacation( if home: await home.activate_vacation(endtime, temperature) else: - for hap in hass.data[DOMAIN].values(): + for hap in hass.data[HMIPC_DOMAIN].values(): await hap.home.activate_vacation(endtime, temperature) @@ -275,7 +267,7 @@ async def _async_deactivate_eco_mode( if home: await home.deactivate_absence() else: - for hap in hass.data[DOMAIN].values(): + for hap in hass.data[HMIPC_DOMAIN].values(): await hap.home.deactivate_absence() @@ -290,7 +282,7 @@ async def _async_deactivate_vacation( if home: await home.deactivate_vacation() else: - for hap in hass.data[DOMAIN].values(): + for hap in hass.data[HMIPC_DOMAIN].values(): await hap.home.deactivate_vacation() @@ -301,7 +293,7 @@ async def _set_active_climate_profile( entity_id_list = service.data[ATTR_ENTITY_ID] climate_profile_index = service.data[ATTR_CLIMATE_PROFILE_INDEX] - 1 - for hap in hass.data[DOMAIN].values(): + for hap in hass.data[HMIPC_DOMAIN].values(): if entity_id_list != "all": for entity_id in entity_id_list: group = hap.hmip_device_by_entity_id.get(entity_id) @@ -321,7 +313,7 @@ async def _async_dump_hap_config( config_file_prefix = service.data[ATTR_CONFIG_OUTPUT_FILE_PREFIX] anonymize = service.data[ATTR_ANONYMIZE] - for hap in hass.data[DOMAIN].values(): + for hap in hass.data[HMIPC_DOMAIN].values(): hap_sgtin = hap.config_entry.unique_id if anonymize: @@ -343,7 +335,7 @@ async def _async_reset_energy_counter( """Service to reset the energy counter.""" entity_id_list = service.data[ATTR_ENTITY_ID] - for hap in hass.data[DOMAIN].values(): + for hap in hass.data[HMIPC_DOMAIN].values(): if entity_id_list != "all": for entity_id in entity_id_list: device = hap.hmip_device_by_entity_id.get(entity_id) @@ -357,7 +349,7 @@ async def _async_reset_energy_counter( def _get_home(hass: HomeAssistantType, hapid: str) -> Optional[AsyncHome]: """Return a HmIP home.""" - hap = hass.data[DOMAIN].get(hapid) + hap = hass.data[HMIPC_DOMAIN].get(hapid) if hap: return hap.home diff --git a/tests/components/homematicip_cloud/conftest.py b/tests/components/homematicip_cloud/conftest.py index be67e53c02c..502e9d1b73e 100644 --- a/tests/components/homematicip_cloud/conftest.py +++ b/tests/components/homematicip_cloud/conftest.py @@ -110,6 +110,7 @@ def simple_mock_home_fixture() -> AsyncHome: """Return a simple AsyncHome Mock.""" return Mock( spec=AsyncHome, + name="Demo", devices=[], groups=[], location=Mock(), diff --git a/tests/components/homematicip_cloud/helper.py b/tests/components/homematicip_cloud/helper.py index f240d136426..403dbd873be 100644 --- a/tests/components/homematicip_cloud/helper.py +++ b/tests/components/homematicip_cloud/helper.py @@ -45,7 +45,7 @@ def get_and_check_entity_basics(hass, mock_hap, entity_id, entity_name, device_m if isinstance(hmip_device, AsyncDevice): assert ha_state.attributes[ATTR_IS_GROUP] is False elif isinstance(hmip_device, AsyncGroup): - assert ha_state.attributes[ATTR_IS_GROUP] is True + assert ha_state.attributes[ATTR_IS_GROUP] return ha_state, hmip_device @@ -106,7 +106,7 @@ class HomeFactory: "homeassistant.components.homematicip_cloud.hap.HomematicipHAP.get_hap", return_value=mock_home, ): - assert await async_setup_component(self.hass, HMIPC_DOMAIN, {}) is True + assert await async_setup_component(self.hass, HMIPC_DOMAIN, {}) await self.hass.async_block_till_done() diff --git a/tests/components/homematicip_cloud/test_alarm_control_panel.py b/tests/components/homematicip_cloud/test_alarm_control_panel.py index 8aa101ca2f5..23e5beb40eb 100644 --- a/tests/components/homematicip_cloud/test_alarm_control_panel.py +++ b/tests/components/homematicip_cloud/test_alarm_control_panel.py @@ -40,14 +40,12 @@ async def _async_manipulate_security_zones( async def test_manually_configured_platform(hass): """Test that we do not set up an access point.""" - assert ( - await async_setup_component( - hass, - ALARM_CONTROL_PANEL_DOMAIN, - {ALARM_CONTROL_PANEL_DOMAIN: {"platform": HMIPC_DOMAIN}}, - ) - is True + assert await async_setup_component( + hass, + ALARM_CONTROL_PANEL_DOMAIN, + {ALARM_CONTROL_PANEL_DOMAIN: {"platform": HMIPC_DOMAIN}}, ) + assert not hass.data.get(HMIPC_DOMAIN) diff --git a/tests/components/homematicip_cloud/test_binary_sensor.py b/tests/components/homematicip_cloud/test_binary_sensor.py index 00e68b0c363..a66dd6d49ea 100644 --- a/tests/components/homematicip_cloud/test_binary_sensor.py +++ b/tests/components/homematicip_cloud/test_binary_sensor.py @@ -30,13 +30,8 @@ from .helper import async_manipulate_test_data, get_and_check_entity_basics async def test_manually_configured_platform(hass): """Test that we do not set up an access point.""" - assert ( - await async_setup_component( - hass, - BINARY_SENSOR_DOMAIN, - {BINARY_SENSOR_DOMAIN: {"platform": HMIPC_DOMAIN}}, - ) - is True + assert await async_setup_component( + hass, BINARY_SENSOR_DOMAIN, {BINARY_SENSOR_DOMAIN: {"platform": HMIPC_DOMAIN}}, ) assert not hass.data.get(HMIPC_DOMAIN) @@ -180,8 +175,8 @@ async def test_hmip_pluggable_mains_failure_surveillance_sensor( hass, default_mock_hap_factory ): """Test HomematicipPresenceDetector.""" - entity_id = "binary_sensor.netzausfall" - entity_name = "Netzausfall" + entity_id = "binary_sensor.netzausfalluberwachung" + entity_name = "Netzausfallüberwachung" device_model = "HmIP-PMFS" mock_hap = await default_mock_hap_factory.async_get_mock_hap( test_devices=[entity_name] @@ -219,6 +214,11 @@ async def test_hmip_smoke_detector(hass, default_mock_hap_factory): ) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_ON + await async_manipulate_test_data( + hass, hmip_device, "smokeDetectorAlarmType", None, + ) + ha_state = hass.states.get(entity_id) + assert ha_state.state == STATE_OFF async def test_hmip_water_detector(hass, default_mock_hap_factory): diff --git a/tests/components/homematicip_cloud/test_climate.py b/tests/components/homematicip_cloud/test_climate.py index d0bdaf85c00..52ca13aad62 100644 --- a/tests/components/homematicip_cloud/test_climate.py +++ b/tests/components/homematicip_cloud/test_climate.py @@ -19,6 +19,7 @@ from homeassistant.components.climate.const import ( PRESET_AWAY, PRESET_BOOST, PRESET_ECO, + PRESET_NONE, ) from homeassistant.components.homematicip_cloud import DOMAIN as HMIPC_DOMAIN from homeassistant.components.homematicip_cloud.climate import ( @@ -32,11 +33,8 @@ from .helper import HAPID, async_manipulate_test_data, get_and_check_entity_basi async def test_manually_configured_platform(hass): """Test that we do not set up an access point.""" - assert ( - await async_setup_component( - hass, CLIMATE_DOMAIN, {CLIMATE_DOMAIN: {"platform": HMIPC_DOMAIN}} - ) - is True + assert await async_setup_component( + hass, CLIMATE_DOMAIN, {CLIMATE_DOMAIN: {"platform": HMIPC_DOMAIN}} ) assert not hass.data.get(HMIPC_DOMAIN) @@ -47,7 +45,7 @@ async def test_hmip_heating_group_heat(hass, default_mock_hap_factory): entity_name = "Badezimmer" device_model = None mock_hap = await default_mock_hap_factory.async_get_mock_hap( - test_devices=["Wandthermostat", "Heizkörperthermostat"], + test_devices=["Wandthermostat", "Heizkörperthermostat3"], test_groups=[entity_name], ) @@ -378,6 +376,28 @@ async def test_hmip_heating_group_heat_with_switch(hass, default_mock_hap_factor assert ha_state.attributes[ATTR_PRESET_MODES] == [PRESET_BOOST, "STD", "P2"] +async def test_hmip_heating_group_heat_with_radiator(hass, default_mock_hap_factory): + """Test HomematicipHeatingGroup.""" + entity_id = "climate.vorzimmer" + entity_name = "Vorzimmer" + device_model = None + mock_hap = await default_mock_hap_factory.async_get_mock_hap( + test_devices=["Heizkörperthermostat2"], test_groups=[entity_name], + ) + ha_state, hmip_device = get_and_check_entity_basics( + hass, mock_hap, entity_id, entity_name, device_model + ) + + assert hmip_device + assert ha_state.state == HVAC_MODE_AUTO + assert ha_state.attributes["current_temperature"] == 20 + assert ha_state.attributes["min_temp"] == 5.0 + assert ha_state.attributes["max_temp"] == 30.0 + assert ha_state.attributes["temperature"] == 5.0 + assert ha_state.attributes[ATTR_PRESET_MODE] is None + assert ha_state.attributes[ATTR_PRESET_MODES] == [PRESET_NONE, PRESET_BOOST] + + async def test_hmip_climate_services(hass, mock_hap_with_service): """Test HomematicipHeatingGroup.""" diff --git a/tests/components/homematicip_cloud/test_cover.py b/tests/components/homematicip_cloud/test_cover.py index 000e859c08e..6980f6fd5d7 100644 --- a/tests/components/homematicip_cloud/test_cover.py +++ b/tests/components/homematicip_cloud/test_cover.py @@ -15,20 +15,17 @@ from .helper import async_manipulate_test_data, get_and_check_entity_basics async def test_manually_configured_platform(hass): """Test that we do not set up an access point.""" - assert ( - await async_setup_component( - hass, COVER_DOMAIN, {COVER_DOMAIN: {"platform": HMIPC_DOMAIN}} - ) - is True + assert await async_setup_component( + hass, COVER_DOMAIN, {COVER_DOMAIN: {"platform": HMIPC_DOMAIN}} ) assert not hass.data.get(HMIPC_DOMAIN) async def test_hmip_cover_shutter(hass, default_mock_hap_factory): """Test HomematicipCoverShutte.""" - entity_id = "cover.sofa_links" - entity_name = "Sofa links" - device_model = "HmIP-FBL" + entity_id = "cover.broll_1" + entity_name = "BROLL_1" + device_model = "HmIP-BROLL" mock_hap = await default_mock_hap_factory.async_get_mock_hap( test_devices=[entity_name] ) @@ -39,7 +36,6 @@ async def test_hmip_cover_shutter(hass, default_mock_hap_factory): assert ha_state.state == "closed" assert ha_state.attributes["current_position"] == 0 - assert ha_state.attributes["current_tilt_position"] == 0 service_call_counter = len(hmip_device.mock_calls) await hass.services.async_call( @@ -52,7 +48,6 @@ async def test_hmip_cover_shutter(hass, default_mock_hap_factory): ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OPEN assert ha_state.attributes[ATTR_CURRENT_POSITION] == 100 - assert ha_state.attributes[ATTR_CURRENT_TILT_POSITION] == 0 await hass.services.async_call( "cover", @@ -67,7 +62,6 @@ async def test_hmip_cover_shutter(hass, default_mock_hap_factory): ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_OPEN assert ha_state.attributes[ATTR_CURRENT_POSITION] == 50 - assert ha_state.attributes[ATTR_CURRENT_TILT_POSITION] == 0 await hass.services.async_call( "cover", "close_cover", {"entity_id": entity_id}, blocking=True @@ -79,7 +73,6 @@ async def test_hmip_cover_shutter(hass, default_mock_hap_factory): ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_CLOSED assert ha_state.attributes[ATTR_CURRENT_POSITION] == 0 - assert ha_state.attributes[ATTR_CURRENT_TILT_POSITION] == 0 await hass.services.async_call( "cover", "stop_cover", {"entity_id": entity_id}, blocking=True @@ -158,6 +151,10 @@ async def test_hmip_cover_slats(hass, default_mock_hap_factory): assert hmip_device.mock_calls[-1][0] == "set_shutter_stop" assert hmip_device.mock_calls[-1][1] == () + await async_manipulate_test_data(hass, hmip_device, "slatsLevel", None) + ha_state = hass.states.get(entity_id) + assert not ha_state.attributes.get(ATTR_CURRENT_TILT_POSITION) + await async_manipulate_test_data(hass, hmip_device, "shutterLevel", None) ha_state = hass.states.get(entity_id) assert ha_state.state == STATE_UNKNOWN diff --git a/tests/components/homematicip_cloud/test_hap.py b/tests/components/homematicip_cloud/test_hap.py index ae41bec1218..1dd5b2fc789 100644 --- a/tests/components/homematicip_cloud/test_hap.py +++ b/tests/components/homematicip_cloud/test_hap.py @@ -101,7 +101,7 @@ async def test_hap_setup_connection_error(): with patch.object(hap, "get_hap", side_effect=HmipcConnectionError), pytest.raises( ConfigEntryNotReady ): - await hap.async_setup() + assert not await hap.async_setup() assert not hass.async_add_job.mock_calls assert not hass.config_entries.flow.async_init.mock_calls @@ -132,21 +132,22 @@ async def test_hap_create(hass, hmip_config_entry, simple_mock_home): assert await hap.async_setup() -async def test_hap_create_exception(hass, hmip_config_entry, simple_mock_home): +async def test_hap_create_exception(hass, hmip_config_entry): """Mock AsyncHome to execute get_hap.""" hass.config.components.add(HMIPC_DOMAIN) + hap = HomematicipHAP(hass, hmip_config_entry) assert hap - with patch.object(hap, "get_hap", side_effect=HmipConnectionError), pytest.raises( - HmipConnectionError - ): - await hap.async_setup() - - simple_mock_home.init.side_effect = HmipConnectionError with patch( - "homeassistant.components.homematicip_cloud.hap.AsyncHome", - return_value=simple_mock_home, + "homeassistant.components.homematicip_cloud.hap.AsyncHome.get_current_state", + side_effect=Exception, + ): + assert not await hap.async_setup() + + with patch( + "homeassistant.components.homematicip_cloud.hap.AsyncHome.get_current_state", + side_effect=HmipConnectionError, ), pytest.raises(ConfigEntryNotReady): await hap.async_setup() diff --git a/tests/components/homematicip_cloud/test_init.py b/tests/components/homematicip_cloud/test_init.py index ee63dba3c97..ef7f5fa24ae 100644 --- a/tests/components/homematicip_cloud/test_init.py +++ b/tests/components/homematicip_cloud/test_init.py @@ -1,6 +1,7 @@ """Test HomematicIP Cloud setup process.""" from asynctest import CoroutineMock, Mock, patch +from homematicip.base.base_connection import HmipConnectionError from homeassistant.components.homematicip_cloud.const import ( CONF_ACCESSPOINT, @@ -11,7 +12,12 @@ from homeassistant.components.homematicip_cloud.const import ( HMIPC_NAME, ) from homeassistant.components.homematicip_cloud.hap import HomematicipHAP -from homeassistant.config_entries import ENTRY_STATE_LOADED, ENTRY_STATE_NOT_LOADED +from homeassistant.config_entries import ( + ENTRY_STATE_LOADED, + ENTRY_STATE_NOT_LOADED, + ENTRY_STATE_SETUP_ERROR, + ENTRY_STATE_SETUP_RETRY, +) from homeassistant.const import CONF_NAME from homeassistant.setup import async_setup_component @@ -31,10 +37,7 @@ async def test_config_with_accesspoint_passed_to_config_entry(hass): # no acccesspoint exists assert not hass.data.get(HMIPC_DOMAIN) - assert ( - await async_setup_component(hass, HMIPC_DOMAIN, {HMIPC_DOMAIN: entry_config}) - is True - ) + assert await async_setup_component(hass, HMIPC_DOMAIN, {HMIPC_DOMAIN: entry_config}) # config_entry created for access point config_entries = hass.config_entries.async_entries(HMIPC_DOMAIN) @@ -70,10 +73,7 @@ async def test_config_already_registered_not_passed_to_config_entry(hass): CONF_AUTHTOKEN: "123", CONF_NAME: "name", } - assert ( - await async_setup_component(hass, HMIPC_DOMAIN, {HMIPC_DOMAIN: entry_config}) - is True - ) + assert await async_setup_component(hass, HMIPC_DOMAIN, {HMIPC_DOMAIN: entry_config}) # no new config_entry created / still one config_entry config_entries = hass.config_entries.async_entries(HMIPC_DOMAIN) @@ -87,6 +87,34 @@ async def test_config_already_registered_not_passed_to_config_entry(hass): assert config_entries[0].unique_id == "ABC123" +async def test_load_entry_fails_due_to_connection_error(hass, hmip_config_entry): + """Test load entry fails due to connection error.""" + hmip_config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homematicip_cloud.hap.AsyncHome.get_current_state", + side_effect=HmipConnectionError, + ): + assert await async_setup_component(hass, HMIPC_DOMAIN, {}) + + assert hass.data[HMIPC_DOMAIN][hmip_config_entry.unique_id] + assert hmip_config_entry.state == ENTRY_STATE_SETUP_RETRY + + +async def test_load_entry_fails_due_to_generic_exception(hass, hmip_config_entry): + """Test load entry fails due to generic exception.""" + hmip_config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.homematicip_cloud.hap.AsyncHome.get_current_state", + side_effect=Exception, + ): + assert await async_setup_component(hass, HMIPC_DOMAIN, {}) + + assert hass.data[HMIPC_DOMAIN][hmip_config_entry.unique_id] + assert hmip_config_entry.state == ENTRY_STATE_SETUP_ERROR + + async def test_unload_entry(hass): """Test being able to unload an entry.""" mock_config = {HMIPC_AUTHTOKEN: "123", HMIPC_HAPID: "ABC123", HMIPC_NAME: "name"} @@ -101,7 +129,7 @@ async def test_unload_entry(hass): instance.home.currentAPVersion = "mock-ap-version" instance.async_reset = CoroutineMock(return_value=True) - assert await async_setup_component(hass, HMIPC_DOMAIN, {}) is True + assert await async_setup_component(hass, HMIPC_DOMAIN, {}) assert mock_hap.return_value.mock_calls[0][0] == "async_setup" @@ -127,3 +155,71 @@ async def test_hmip_dump_hap_config_services(hass, mock_hap_with_service): assert home.mock_calls[-1][0] == "download_configuration" assert home.mock_calls assert write_mock.mock_calls + + +async def test_setup_services_and_unload_services(hass): + """Test setup services and unload services.""" + mock_config = {HMIPC_AUTHTOKEN: "123", HMIPC_HAPID: "ABC123", HMIPC_NAME: "name"} + MockConfigEntry(domain=HMIPC_DOMAIN, data=mock_config).add_to_hass(hass) + + with patch("homeassistant.components.homematicip_cloud.HomematicipHAP") as mock_hap: + instance = mock_hap.return_value + instance.async_setup = CoroutineMock(return_value=True) + instance.home.id = "1" + instance.home.modelType = "mock-type" + instance.home.name = "mock-name" + instance.home.currentAPVersion = "mock-ap-version" + instance.async_reset = CoroutineMock(return_value=True) + + assert await async_setup_component(hass, HMIPC_DOMAIN, {}) + + # Check services are created + hmipc_services = hass.services.async_services()[HMIPC_DOMAIN] + assert len(hmipc_services) == 8 + + config_entries = hass.config_entries.async_entries(HMIPC_DOMAIN) + assert len(config_entries) == 1 + + await hass.config_entries.async_unload(config_entries[0].entry_id) + # Check services are removed + assert not hass.services.async_services().get(HMIPC_DOMAIN) + + +async def test_setup_two_haps_unload_one_by_one(hass): + """Test setup two access points and unload one by one and check services.""" + + # Setup AP1 + mock_config = {HMIPC_AUTHTOKEN: "123", HMIPC_HAPID: "ABC123", HMIPC_NAME: "name"} + MockConfigEntry(domain=HMIPC_DOMAIN, data=mock_config).add_to_hass(hass) + # Setup AP2 + mock_config2 = {HMIPC_AUTHTOKEN: "123", HMIPC_HAPID: "ABC1234", HMIPC_NAME: "name2"} + MockConfigEntry(domain=HMIPC_DOMAIN, data=mock_config2).add_to_hass(hass) + + with patch("homeassistant.components.homematicip_cloud.HomematicipHAP") as mock_hap: + instance = mock_hap.return_value + instance.async_setup = CoroutineMock(return_value=True) + instance.home.id = "1" + instance.home.modelType = "mock-type" + instance.home.name = "mock-name" + instance.home.currentAPVersion = "mock-ap-version" + instance.async_reset = CoroutineMock(return_value=True) + + assert await async_setup_component(hass, HMIPC_DOMAIN, {}) + + hmipc_services = hass.services.async_services()[HMIPC_DOMAIN] + assert len(hmipc_services) == 8 + + config_entries = hass.config_entries.async_entries(HMIPC_DOMAIN) + assert len(config_entries) == 2 + # unload the first AP + await hass.config_entries.async_unload(config_entries[0].entry_id) + + # services still exists + hmipc_services = hass.services.async_services()[HMIPC_DOMAIN] + assert len(hmipc_services) == 8 + + # unload the second AP + await hass.config_entries.async_unload(config_entries[1].entry_id) + + # Check services are removed + assert not hass.services.async_services().get(HMIPC_DOMAIN) diff --git a/tests/components/homematicip_cloud/test_light.py b/tests/components/homematicip_cloud/test_light.py index 31919c5e84a..8909e469ee9 100644 --- a/tests/components/homematicip_cloud/test_light.py +++ b/tests/components/homematicip_cloud/test_light.py @@ -19,11 +19,8 @@ from .helper import async_manipulate_test_data, get_and_check_entity_basics async def test_manually_configured_platform(hass): """Test that we do not set up an access point.""" - assert ( - await async_setup_component( - hass, LIGHT_DOMAIN, {LIGHT_DOMAIN: {"platform": HMIPC_DOMAIN}} - ) - is True + assert await async_setup_component( + hass, LIGHT_DOMAIN, {LIGHT_DOMAIN: {"platform": HMIPC_DOMAIN}} ) assert not hass.data.get(HMIPC_DOMAIN) diff --git a/tests/components/homematicip_cloud/test_sensor.py b/tests/components/homematicip_cloud/test_sensor.py index 46d26a3a8df..be5a7423463 100644 --- a/tests/components/homematicip_cloud/test_sensor.py +++ b/tests/components/homematicip_cloud/test_sensor.py @@ -30,11 +30,8 @@ from .helper import async_manipulate_test_data, get_and_check_entity_basics async def test_manually_configured_platform(hass): """Test that we do not set up an access point.""" - assert ( - await async_setup_component( - hass, SENSOR_DOMAIN, {SENSOR_DOMAIN: {"platform": HMIPC_DOMAIN}} - ) - is True + assert await async_setup_component( + hass, SENSOR_DOMAIN, {SENSOR_DOMAIN: {"platform": HMIPC_DOMAIN}} ) assert not hass.data.get(HMIPC_DOMAIN) diff --git a/tests/components/homematicip_cloud/test_switch.py b/tests/components/homematicip_cloud/test_switch.py index 245d407d103..0cd01154753 100644 --- a/tests/components/homematicip_cloud/test_switch.py +++ b/tests/components/homematicip_cloud/test_switch.py @@ -16,11 +16,8 @@ from .helper import async_manipulate_test_data, get_and_check_entity_basics async def test_manually_configured_platform(hass): """Test that we do not set up an access point.""" - assert ( - await async_setup_component( - hass, SWITCH_DOMAIN, {SWITCH_DOMAIN: {"platform": HMIPC_DOMAIN}} - ) - is True + assert await async_setup_component( + hass, SWITCH_DOMAIN, {SWITCH_DOMAIN: {"platform": HMIPC_DOMAIN}} ) assert not hass.data.get(HMIPC_DOMAIN) @@ -152,7 +149,12 @@ async def test_hmip_multi_switch(hass, default_mock_hap_factory): entity_name = "Jalousien - 1 KiZi, 2 SchlaZi Channel1" device_model = "HmIP-PCBS2" mock_hap = await default_mock_hap_factory.async_get_mock_hap( - test_devices=["Jalousien - 1 KiZi, 2 SchlaZi"] + test_devices=[ + "Jalousien - 1 KiZi, 2 SchlaZi", + "Multi IO Box", + "Heizungsaktor", + "ioBroker", + ] ) ha_state, hmip_device = get_and_check_entity_basics( diff --git a/tests/components/homematicip_cloud/test_weather.py b/tests/components/homematicip_cloud/test_weather.py index 3f755ecee2b..e3370e77ffe 100644 --- a/tests/components/homematicip_cloud/test_weather.py +++ b/tests/components/homematicip_cloud/test_weather.py @@ -15,11 +15,8 @@ from .helper import async_manipulate_test_data, get_and_check_entity_basics async def test_manually_configured_platform(hass): """Test that we do not set up an access point.""" - assert ( - await async_setup_component( - hass, WEATHER_DOMAIN, {WEATHER_DOMAIN: {"platform": HMIPC_DOMAIN}} - ) - is True + assert await async_setup_component( + hass, WEATHER_DOMAIN, {WEATHER_DOMAIN: {"platform": HMIPC_DOMAIN}} ) assert not hass.data.get(HMIPC_DOMAIN) diff --git a/tests/fixtures/homematicip_cloud.json b/tests/fixtures/homematicip_cloud.json index 5ca5f2810d3..068470313fc 100644 --- a/tests/fixtures/homematicip_cloud.json +++ b/tests/fixtures/homematicip_cloud.json @@ -66,7 +66,7 @@ }, "homeId": "00000000-0000-0000-0000-000000000001", "id": "3014F7110000000000ABCD50", - "label": "Netzausfall", + "label": "Netzausfall\u00fcberwachung", "lastStatusUpdate": 1577487207542, "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", "manufacturerCode": 1, @@ -3020,7 +3020,7 @@ }, "homeId": "00000000-0000-0000-0000-000000000001", "id": "3014F7110000000000000013", - "label": "Heizkörperthermostat", + "label": "Heizkörperthermostat2", "lastStatusUpdate": 1524514007132, "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", "manufacturerCode": 1, @@ -3188,7 +3188,7 @@ }, "homeId": "00000000-0000-0000-0000-000000000001", "id": "3014F7110000000000000016", - "label": "Heizkörperthermostat", + "label": "Heizkörperthermostat3", "lastStatusUpdate": 1524514626157, "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", "manufacturerCode": 1, @@ -3348,7 +3348,7 @@ }, "homeId": "00000000-0000-0000-0000-000000000001", "id": "3014F7110000000000000019", - "label": "Rauchwarnmelder", + "label": "Rauchwarnmelder2", "lastStatusUpdate": 1524480981494, "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", "manufacturerCode": 1, @@ -3400,7 +3400,7 @@ }, "homeId": "00000000-0000-0000-0000-000000000001", "id": "3014F7110000000000000020", - "label": "Rauchwarnmelder", + "label": "Rauchwarnmelder3", "lastStatusUpdate": 1524456324824, "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", "manufacturerCode": 1, @@ -3452,7 +3452,7 @@ }, "homeId": "00000000-0000-0000-0000-000000000001", "id": "3014F7110000000000000021", - "label": "Rauchwarnmelder", + "label": "Rauchwarnmelder4", "lastStatusUpdate": 1524443129876, "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", "manufacturerCode": 1, @@ -3566,7 +3566,7 @@ }, "homeId": "00000000-0000-0000-0000-000000000001", "id": "3014F7110000000000000023", - "label": "Wandthermostat", + "label": "Wandthermostat4", "lastStatusUpdate": 1524516454116, "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", "manufacturerCode": 1, @@ -3623,7 +3623,7 @@ }, "homeId": "00000000-0000-0000-0000-000000000001", "id": "3014F7110000000000000024", - "label": "Wandthermostat", + "label": "Wandthermostat2", "lastStatusUpdate": 1524516436601, "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", "manufacturerCode": 1, @@ -3680,7 +3680,7 @@ }, "homeId": "00000000-0000-0000-0000-000000000001", "id": "3014F7110000000000000025", - "label": "Wandthermostat", + "label": "Wandthermostat3", "lastStatusUpdate": 1524516556479, "liveUpdateState": "LIVE_UPDATE_NOT_SUPPORTED", "manufacturerCode": 1,