Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove deprecated yaml config from sma #62472

Merged
merged 4 commits into from Jan 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
102 changes: 1 addition & 101 deletions homeassistant/components/sma/__init__.py
Expand Up @@ -6,29 +6,22 @@

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,
Expand All @@ -42,94 +35,6 @@
_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
Expand Down Expand Up @@ -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."""
Expand Down
27 changes: 2 additions & 25 deletions homeassistant/components/sma/config_flow.py
Expand Up @@ -8,18 +8,12 @@
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__)

Expand Down Expand Up @@ -56,8 +50,6 @@ def __init__(self) -> None:
CONF_VERIFY_SSL: True,
CONF_GROUP: GROUPS[0],
CONF_PASSWORD: vol.UNDEFINED,
CONF_SENSORS: [],
CONF_CUSTOM: {},
}

async def async_step_user(
Expand Down Expand Up @@ -108,18 +100,3 @@ async def async_step_user(
),
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
)
4 changes: 0 additions & 4 deletions homeassistant/components/sma/const.py
Expand Up @@ -11,11 +11,7 @@

PLATFORMS = [Platform.SENSOR]

CONF_CUSTOM = "custom"
CONF_FACTOR = "factor"
CONF_GROUP = "group"
CONF_KEY = "key"
CONF_UNIT = "unit"

DEFAULT_SCAN_INTERVAL = 5

Expand Down
105 changes: 3 additions & 102 deletions 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
Expand All @@ -34,91 +19,7 @@
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(
Expand Down