diff --git a/homeassistant/components/sma/__init__.py b/homeassistant/components/sma/__init__.py index 06be21d7ac6..13c0e598a2d 100644 --- a/homeassistant/components/sma/__init__.py +++ b/homeassistant/components/sma/__init__.py @@ -6,29 +6,22 @@ import logging import pysma -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry, ConfigEntryNotReady +from homeassistant.config_entries import ConfigEntry, ConfigEntryNotReady from homeassistant.const import ( CONF_HOST, CONF_PASSWORD, - CONF_PATH, CONF_SCAN_INTERVAL, - CONF_SENSORS, CONF_SSL, CONF_VERIFY_SSL, EVENT_HOMEASSISTANT_STOP, ) from homeassistant.core import HomeAssistant -from homeassistant.helpers import entity_registry as er from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from .const import ( - CONF_CUSTOM, - CONF_FACTOR, CONF_GROUP, - CONF_KEY, - CONF_UNIT, DEFAULT_SCAN_INTERVAL, DOMAIN, PLATFORMS, @@ -42,94 +35,6 @@ from .const import ( _LOGGER = logging.getLogger(__name__) -def _parse_legacy_options( - entry: ConfigEntry, sensor_def: pysma.sensor.Sensors -) -> list[str]: - """Parse legacy configuration options. - - This will parse the legacy CONF_SENSORS and CONF_CUSTOM configuration options - to support deprecated yaml config from platform setup. - """ - - # Add sensors from the custom config - sensor_def.add( - [ - pysma.sensor.Sensor( - o[CONF_KEY], n, o[CONF_UNIT], o[CONF_FACTOR], o.get(CONF_PATH) - ) - for n, o in entry.data.get(CONF_CUSTOM).items() - ] - ) - - # Parsing of sensors configuration - if not (config_sensors := entry.data.get(CONF_SENSORS)): - return [] - - # Support import of legacy config that should have been removed from 0.99, but was still functional - # See also #25880 and #26306. Functional support was dropped in #48003 - if isinstance(config_sensors, dict): - config_sensors_list = [] - - for name, attr in config_sensors.items(): - config_sensors_list.append(name) - config_sensors_list.extend(attr) - - config_sensors = config_sensors_list - - # Find and replace sensors removed from pysma - # This only alters the config, the actual sensor migration takes place in _migrate_old_unique_ids - for sensor in config_sensors.copy(): - if sensor in pysma.const.LEGACY_MAP: - config_sensors.remove(sensor) - config_sensors.append(pysma.const.LEGACY_MAP[sensor]["new_sensor"]) - - # Only sensors from config should be enabled - for sensor in sensor_def: - sensor.enabled = sensor.name in config_sensors - - return config_sensors - - -def _migrate_old_unique_ids( - hass: HomeAssistant, - entry: ConfigEntry, - sensor_def: pysma.sensor.Sensors, - config_sensors: list[str], -) -> None: - """Migrate legacy sensor entity_id format to new format.""" - entity_registry = er.async_get(hass) - - # Create list of all possible sensor names - possible_sensors = set( - config_sensors + [s.name for s in sensor_def] + list(pysma.const.LEGACY_MAP) - ) - - for sensor in possible_sensors: - if sensor in sensor_def: - pysma_sensor = sensor_def[sensor] - original_key = pysma_sensor.key - elif sensor in pysma.const.LEGACY_MAP: - # If sensor was removed from pysma we will remap it to the new sensor - legacy_sensor = pysma.const.LEGACY_MAP[sensor] - pysma_sensor = sensor_def[legacy_sensor["new_sensor"]] - original_key = legacy_sensor["old_key"] - else: - _LOGGER.error("%s does not exist", sensor) - continue - - # Find entity_id using previous format of unique ID - entity_id = entity_registry.async_get_entity_id( - "sensor", "sma", f"sma-{original_key}-{sensor}" - ) - - if not entity_id: - continue - - # Change unique_id to new format using the device serial in entry.unique_id - new_unique_id = f"{entry.unique_id}-{pysma_sensor.key}_{pysma_sensor.key_idx}" - entity_registry.async_update_entity(entity_id, new_unique_id=new_unique_id) - - async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up sma from a config entry.""" # Init the SMA interface @@ -163,11 +68,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: sw_version=sma_device_info["sw_version"], ) - # Parse legacy options if initial setup was done from yaml - if entry.source == SOURCE_IMPORT: - config_sensors = _parse_legacy_options(entry, sensor_def) - _migrate_old_unique_ids(hass, entry, sensor_def, config_sensors) - # Define the coordinator async def async_update_data(): """Update the used SMA sensors.""" diff --git a/homeassistant/components/sma/config_flow.py b/homeassistant/components/sma/config_flow.py index b95e4e4fe06..c2fd48a00ca 100644 --- a/homeassistant/components/sma/config_flow.py +++ b/homeassistant/components/sma/config_flow.py @@ -8,18 +8,12 @@ import pysma import voluptuous as vol from homeassistant import config_entries, core -from homeassistant.const import ( - CONF_HOST, - CONF_PASSWORD, - CONF_SENSORS, - CONF_SSL, - CONF_VERIFY_SSL, -) +from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_SSL, CONF_VERIFY_SSL from homeassistant.data_entry_flow import FlowResult from homeassistant.helpers.aiohttp_client import async_get_clientsession import homeassistant.helpers.config_validation as cv -from .const import CONF_CUSTOM, CONF_GROUP, DOMAIN, GROUPS +from .const import CONF_GROUP, DOMAIN, GROUPS _LOGGER = logging.getLogger(__name__) @@ -56,8 +50,6 @@ class SmaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): CONF_VERIFY_SSL: True, CONF_GROUP: GROUPS[0], CONF_PASSWORD: vol.UNDEFINED, - CONF_SENSORS: [], - CONF_CUSTOM: {}, } async def async_step_user( @@ -108,18 +100,3 @@ class SmaConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ), errors=errors, ) - - async def async_step_import( - self, import_config: dict[str, Any] | None - ) -> FlowResult: - """Import a config flow from configuration.""" - device_info = await validate_input(self.hass, import_config) - - # If unique is configured import was already run - # This means remap was already done, so we can abort - await self.async_set_unique_id(device_info["serial"]) - self._abort_if_unique_id_configured(import_config) - - return self.async_create_entry( - title=import_config[CONF_HOST], data=import_config - ) diff --git a/homeassistant/components/sma/const.py b/homeassistant/components/sma/const.py index 1cef8dd72d2..d51b3f6d316 100644 --- a/homeassistant/components/sma/const.py +++ b/homeassistant/components/sma/const.py @@ -11,11 +11,7 @@ PYSMA_DEVICE_INFO = "device_info" PLATFORMS = [Platform.SENSOR] -CONF_CUSTOM = "custom" -CONF_FACTOR = "factor" CONF_GROUP = "group" -CONF_KEY = "key" -CONF_UNIT = "unit" DEFAULT_SCAN_INTERVAL = 5 diff --git a/homeassistant/components/sma/sensor.py b/homeassistant/components/sma/sensor.py index 429667ff0e4..ae7a0a64719 100644 --- a/homeassistant/components/sma/sensor.py +++ b/homeassistant/components/sma/sensor.py @@ -1,31 +1,16 @@ """SMA Solar Webconnect interface.""" from __future__ import annotations -import logging -from typing import Any - import pysma -import voluptuous as vol from homeassistant.components.sensor import ( - PLATFORM_SCHEMA, SensorDeviceClass, SensorEntity, SensorStateClass, ) -from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry -from homeassistant.const import ( - CONF_HOST, - CONF_PASSWORD, - CONF_PATH, - CONF_SENSORS, - CONF_SSL, - CONF_VERIFY_SSL, - ENERGY_KILO_WATT_HOUR, - POWER_WATT, -) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import ENERGY_KILO_WATT_HOUR, POWER_WATT from homeassistant.core import HomeAssistant -import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -34,91 +19,7 @@ from homeassistant.helpers.update_coordinator import ( DataUpdateCoordinator, ) -from .const import ( - CONF_CUSTOM, - CONF_FACTOR, - CONF_GROUP, - CONF_KEY, - CONF_UNIT, - DOMAIN, - GROUPS, - PYSMA_COORDINATOR, - PYSMA_DEVICE_INFO, - PYSMA_SENSORS, -) - -_LOGGER = logging.getLogger(__name__) - - -def _check_sensor_schema(conf: dict[str, Any]) -> dict[str, Any]: - """Check sensors and attributes are valid.""" - try: - valid = [s.name for s in pysma.sensor.Sensors()] - valid += pysma.const.LEGACY_MAP.keys() - except (ImportError, AttributeError): - return conf - - customs = list(conf[CONF_CUSTOM]) - - for sensor in conf[CONF_SENSORS]: - if sensor in customs: - _LOGGER.warning( - "All custom sensors will be added automatically, no need to include them in sensors: %s", - sensor, - ) - elif sensor not in valid: - raise vol.Invalid(f"{sensor} does not exist") - return conf - - -CUSTOM_SCHEMA = vol.Any( - { - vol.Required(CONF_KEY): vol.All(cv.string, vol.Length(min=13, max=15)), - vol.Required(CONF_UNIT): cv.string, - vol.Optional(CONF_FACTOR, default=1): vol.Coerce(float), - vol.Optional(CONF_PATH): vol.All(cv.ensure_list, [cv.string]), - } -) - -PLATFORM_SCHEMA = vol.All( - PLATFORM_SCHEMA.extend( - { - vol.Required(CONF_HOST): cv.string, - vol.Optional(CONF_SSL, default=False): cv.boolean, - vol.Optional(CONF_VERIFY_SSL, default=True): cv.boolean, - vol.Required(CONF_PASSWORD): cv.string, - vol.Optional(CONF_GROUP, default=GROUPS[0]): vol.In(GROUPS), - vol.Optional(CONF_SENSORS, default=[]): vol.Any( - cv.schema_with_slug_keys(cv.ensure_list), # will be deprecated - vol.All(cv.ensure_list, [str]), - ), - vol.Optional(CONF_CUSTOM, default={}): cv.schema_with_slug_keys( - CUSTOM_SCHEMA - ), - }, - extra=vol.PREVENT_EXTRA, - ), - _check_sensor_schema, -) - - -async def async_setup_platform( - hass: HomeAssistant, - config: ConfigEntry, - async_add_entities: AddEntitiesCallback, - discovery_info=None, -) -> None: - """Import the platform into a config entry.""" - _LOGGER.warning( - "Loading SMA via platform setup is deprecated. " - "Please remove it from your configuration" - ) - - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_IMPORT}, data=config - ) - ) +from .const import DOMAIN, PYSMA_COORDINATOR, PYSMA_DEVICE_INFO, PYSMA_SENSORS async def async_setup_entry( diff --git a/tests/components/sma/__init__.py b/tests/components/sma/__init__.py index 4210772420c..0c6c8a7ee67 100644 --- a/tests/components/sma/__init__.py +++ b/tests/components/sma/__init__.py @@ -16,85 +16,6 @@ MOCK_USER_INPUT = { "password": "password", } -MOCK_IMPORT = { - "platform": "sma", - "host": "1.1.1.1", - "ssl": True, - "verify_ssl": False, - "group": "user", - "password": "password", - "sensors": ["pv_power", "daily_yield", "total_yield", "not_existing_sensors"], - "custom": { - "yesterday_consumption": { - "factor": 1000.0, - "key": "6400_00543A01", - "unit": "kWh", - } - }, -} - -MOCK_IMPORT_DICT = { - "platform": "sma", - "host": "1.1.1.1", - "ssl": True, - "verify_ssl": False, - "group": "user", - "password": "password", - "sensors": { - "pv_power": [], - "pv_gen_meter": [], - "solar_daily": ["daily_yield", "total_yield"], - "status": ["grid_power", "frequency", "voltage_l1", "operating_time"], - }, - "custom": { - "operating_time": {"key": "6400_00462E00", "unit": "uur", "factor": 3600}, - "solar_daily": {"key": "6400_00262200", "unit": "kWh", "factor": 1000}, - }, -} - -MOCK_CUSTOM_SENSOR = { - "name": "yesterday_consumption", - "key": "6400_00543A01", - "unit": "kWh", - "factor": 1000, -} - -MOCK_CUSTOM_SENSOR2 = { - "name": "device_type_id", - "key": "6800_08822000", - "unit": "", - "path": '"1"[0].val[0].tag', -} - -MOCK_SETUP_DATA = dict( - { - "custom": {}, - "sensors": [], - }, - **MOCK_USER_INPUT, -) - -MOCK_CUSTOM_SETUP_DATA = dict( - { - "custom": { - MOCK_CUSTOM_SENSOR["name"]: { - "factor": MOCK_CUSTOM_SENSOR["factor"], - "key": MOCK_CUSTOM_SENSOR["key"], - "path": None, - "unit": MOCK_CUSTOM_SENSOR["unit"], - }, - MOCK_CUSTOM_SENSOR2["name"]: { - "factor": 1.0, - "key": MOCK_CUSTOM_SENSOR2["key"], - "path": MOCK_CUSTOM_SENSOR2["path"], - "unit": MOCK_CUSTOM_SENSOR2["unit"], - }, - }, - "sensors": [], - }, - **MOCK_USER_INPUT, -) - def _patch_async_setup_entry(return_value=True): return patch( diff --git a/tests/components/sma/conftest.py b/tests/components/sma/conftest.py index 80d9b38e28b..b953d8692a8 100644 --- a/tests/components/sma/conftest.py +++ b/tests/components/sma/conftest.py @@ -9,7 +9,7 @@ import pytest from homeassistant import config_entries from homeassistant.components.sma.const import DOMAIN -from . import MOCK_CUSTOM_SETUP_DATA, MOCK_DEVICE +from . import MOCK_DEVICE, MOCK_USER_INPUT from tests.common import MockConfigEntry @@ -21,7 +21,7 @@ def mock_config_entry(): domain=DOMAIN, title=MOCK_DEVICE["name"], unique_id=MOCK_DEVICE["serial"], - data=MOCK_CUSTOM_SETUP_DATA, + data=MOCK_USER_INPUT, source=config_entries.SOURCE_IMPORT, ) diff --git a/tests/components/sma/test_config_flow.py b/tests/components/sma/test_config_flow.py index 9194bc15d6f..8cf22b3634e 100644 --- a/tests/components/sma/test_config_flow.py +++ b/tests/components/sma/test_config_flow.py @@ -8,21 +8,14 @@ from pysma.exceptions import ( ) from homeassistant.components.sma.const import DOMAIN -from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER +from homeassistant.config_entries import SOURCE_USER from homeassistant.data_entry_flow import ( RESULT_TYPE_ABORT, RESULT_TYPE_CREATE_ENTRY, RESULT_TYPE_FORM, ) -from . import ( - MOCK_DEVICE, - MOCK_IMPORT, - MOCK_IMPORT_DICT, - MOCK_SETUP_DATA, - MOCK_USER_INPUT, - _patch_async_setup_entry, -) +from . import MOCK_DEVICE, MOCK_USER_INPUT, _patch_async_setup_entry async def test_form(hass): @@ -45,7 +38,7 @@ async def test_form(hass): assert result["type"] == RESULT_TYPE_CREATE_ENTRY assert result["title"] == MOCK_USER_INPUT["host"] - assert result["data"] == MOCK_SETUP_DATA + assert result["data"] == MOCK_USER_INPUT assert len(mock_setup_entry.mock_calls) == 1 @@ -148,43 +141,3 @@ async def test_form_already_configured(hass, mock_config_entry): assert result["type"] == RESULT_TYPE_ABORT assert result["reason"] == "already_configured" assert len(mock_setup_entry.mock_calls) == 0 - - -async def test_import(hass): - """Test we can import.""" - - with patch("pysma.SMA.new_session", return_value=True), patch( - "pysma.SMA.device_info", return_value=MOCK_DEVICE - ), patch( - "pysma.SMA.close_session", return_value=True - ), _patch_async_setup_entry() as mock_setup_entry: - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data=MOCK_IMPORT, - ) - - assert result["type"] == RESULT_TYPE_CREATE_ENTRY - assert result["title"] == MOCK_USER_INPUT["host"] - assert result["data"] == MOCK_IMPORT - assert len(mock_setup_entry.mock_calls) == 1 - - -async def test_import_sensor_dict(hass): - """Test we can import.""" - - with patch("pysma.SMA.new_session", return_value=True), patch( - "pysma.SMA.device_info", return_value=MOCK_DEVICE - ), patch( - "pysma.SMA.close_session", return_value=True - ), _patch_async_setup_entry() as mock_setup_entry: - result = await hass.config_entries.flow.async_init( - DOMAIN, - context={"source": SOURCE_IMPORT}, - data=MOCK_IMPORT_DICT, - ) - - assert result["type"] == RESULT_TYPE_CREATE_ENTRY - assert result["title"] == MOCK_USER_INPUT["host"] - assert result["data"] == MOCK_IMPORT_DICT - assert len(mock_setup_entry.mock_calls) == 1 diff --git a/tests/components/sma/test_sensor.py b/tests/components/sma/test_sensor.py index 129af154924..58fafe930c7 100644 --- a/tests/components/sma/test_sensor.py +++ b/tests/components/sma/test_sensor.py @@ -1,11 +1,5 @@ """Test the sma sensor platform.""" -from homeassistant.const import ( - ATTR_UNIT_OF_MEASUREMENT, - ENERGY_KILO_WATT_HOUR, - POWER_WATT, -) - -from . import MOCK_CUSTOM_SENSOR +from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, POWER_WATT async def test_sensors(hass, init_integration): @@ -13,7 +7,3 @@ async def test_sensors(hass, init_integration): state = hass.states.get("sensor.grid_power") assert state assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == POWER_WATT - - state = hass.states.get(f"sensor.{MOCK_CUSTOM_SENSOR['name']}") - assert state - assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR