From 7cbd55a8179b54a1559f5c6a21e66bfd365d8918 Mon Sep 17 00:00:00 2001 From: SukramJ Date: Mon, 4 Nov 2019 09:55:12 +0100 Subject: [PATCH] Add dump config service to HomematicIP Cloud (#28231) * Add dump config service to HomematicIP Cloud * Mock builin.open * Fix test * reduce SGTIN if anonymize * apply review feedback --- .../components/homematicip_cloud/__init__.py | 50 ++++++++++++++++++- .../homematicip_cloud/services.yaml | 13 ++++- .../components/homematicip_cloud/test_init.py | 15 +++++- 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/homematicip_cloud/__init__.py b/homeassistant/components/homematicip_cloud/__init__.py index 9a0eb65aa3f..d6ae05c463a 100644 --- a/homeassistant/components/homematicip_cloud/__init__.py +++ b/homeassistant/components/homematicip_cloud/__init__.py @@ -1,7 +1,9 @@ """Support for HomematicIP Cloud devices.""" import logging +from pathlib import Path from homematicip.aio.group import AsyncHeatingGroup +from homematicip.base.helpers import handle_config import voluptuous as vol from homeassistant import config_entries @@ -26,17 +28,23 @@ from .hap import HomematicipAuth, HomematicipHAP # noqa: F401 _LOGGER = logging.getLogger(__name__) +ATTR_ACCESSPOINT_ID = "accesspoint_id" +ATTR_ANONYMIZE = "anonymize" ATTR_CLIMATE_PROFILE_INDEX = "climate_profile_index" +ATTR_CONFIG_OUTPUT_FILE_PREFIX = "config_output_file_prefix" +ATTR_CONFIG_OUTPUT_PATH = "config_output_path" ATTR_DURATION = "duration" ATTR_ENDTIME = "endtime" ATTR_TEMPERATURE = "temperature" -ATTR_ACCESSPOINT_ID = "accesspoint_id" + +DEFAULT_CONFIG_FILE_PREFIX = "hmip-config" SERVICE_ACTIVATE_ECO_MODE_WITH_DURATION = "activate_eco_mode_with_duration" SERVICE_ACTIVATE_ECO_MODE_WITH_PERIOD = "activate_eco_mode_with_period" SERVICE_ACTIVATE_VACATION = "activate_vacation" SERVICE_DEACTIVATE_ECO_MODE = "deactivate_eco_mode" SERVICE_DEACTIVATE_VACATION = "deactivate_vacation" +SERVICE_DUMP_HAP_CONFIG = "dump_hap_config" SERVICE_SET_ACTIVE_CLIMATE_PROFILE = "set_active_climate_profile" CONFIG_SCHEMA = vol.Schema( @@ -96,6 +104,16 @@ SCHEMA_SET_ACTIVE_CLIMATE_PROFILE = vol.Schema( } ) +SCHEMA_DUMP_HAP_CONFIG = vol.Schema( + { + vol.Optional(ATTR_CONFIG_OUTPUT_PATH): cv.string, + vol.Optional( + ATTR_CONFIG_OUTPUT_FILE_PREFIX, default=DEFAULT_CONFIG_FILE_PREFIX + ): cv.string, + vol.Optional(ATTR_ANONYMIZE, default=True): cv.boolean, + } +) + async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool: """Set up the HomematicIP Cloud component.""" @@ -239,6 +257,36 @@ async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool: schema=SCHEMA_SET_ACTIVE_CLIMATE_PROFILE, ) + async def _async_dump_hap_config(service): + """Service to dump the configuration of a Homematic IP Access Point.""" + config_path = ( + service.data.get(ATTR_CONFIG_OUTPUT_PATH) or hass.config.config_dir + ) + config_file_prefix = service.data[ATTR_CONFIG_OUTPUT_FILE_PREFIX] + anonymize = service.data[ATTR_ANONYMIZE] + + for hap in hass.data[DOMAIN].values(): + hap_sgtin = hap.config_entry.title + + if anonymize: + hap_sgtin = hap_sgtin[-4:] + + file_name = f"{config_file_prefix}_{hap_sgtin}.json" + path = Path(config_path) + config_file = path / file_name + + json_state = await hap.home.download_configuration() + json_state = handle_config(json_state, anonymize) + + config_file.write_text(json_state, encoding="utf8") + + hass.services.async_register( + DOMAIN, + SERVICE_DUMP_HAP_CONFIG, + _async_dump_hap_config, + schema=SCHEMA_DUMP_HAP_CONFIG, + ) + def _get_home(hapid: str): """Return a HmIP home.""" hap = hass.data[DOMAIN].get(hapid) diff --git a/homeassistant/components/homematicip_cloud/services.yaml b/homeassistant/components/homematicip_cloud/services.yaml index f426c9b5d22..9a7d90eba9c 100644 --- a/homeassistant/components/homematicip_cloud/services.yaml +++ b/homeassistant/components/homematicip_cloud/services.yaml @@ -57,4 +57,15 @@ set_active_climate_profile: description: The index of the climate profile (1 based) example: 1 - +dump_hap_config: + description: Dump the configuration of the Homematic IP Access Point(s). + fields: + config_output_path: + description: (Default is 'Your home-assistant config directory') Path where to store the config. + example: '/config' + config_output_file_prefix: + description: (Default is 'hmip-config') Name of the config file. The SGTIN of the AP will always be appended. + example: 'hmip-config' + anonymize: + description: (Default is True) Should the Configuration be anonymized? + example: True diff --git a/tests/components/homematicip_cloud/test_init.py b/tests/components/homematicip_cloud/test_init.py index 894db2e691b..ba27a619e6a 100644 --- a/tests/components/homematicip_cloud/test_init.py +++ b/tests/components/homematicip_cloud/test_init.py @@ -5,7 +5,7 @@ from unittest.mock import patch from homeassistant.components import homematicip_cloud as hmipc from homeassistant.setup import async_setup_component -from tests.common import MockConfigEntry, mock_coro +from tests.common import Mock, MockConfigEntry, mock_coro async def test_config_with_accesspoint_passed_to_config_entry(hass): @@ -150,3 +150,16 @@ async def test_unload_entry(hass): assert await hmipc.async_unload_entry(hass, entry) assert len(mock_hap.return_value.async_reset.mock_calls) == 1 assert hass.data[hmipc.DOMAIN] == {} + + +async def test_hmip_dump_hap_config_services(hass, mock_hap_with_service): + """Test dump configuration services.""" + + with patch("pathlib.Path.write_text", return_value=Mock()) as write_mock: + await hass.services.async_call( + "homematicip_cloud", "dump_hap_config", {"anonymize": True}, blocking=True + ) + home = mock_hap_with_service.home + assert home.mock_calls[-1][0] == "download_configuration" + assert len(home.mock_calls) == 8 # pylint: disable=W0212 + assert len(write_mock.mock_calls) > 0