Files
ha-core/homeassistant/components/velbus/__init__.py
2024-12-12 20:48:01 +01:00

129 lines
4.1 KiB
Python

"""Support for Velbus devices."""
from __future__ import annotations
import asyncio
from dataclasses import dataclass
import logging
import os
import shutil
from velbusaio.controller import Velbus
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_PORT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.storage import STORAGE_DIR
from homeassistant.helpers.typing import ConfigType
from .const import DOMAIN
from .services import setup_services
_LOGGER = logging.getLogger(__name__)
PLATFORMS = [
Platform.BINARY_SENSOR,
Platform.BUTTON,
Platform.CLIMATE,
Platform.COVER,
Platform.LIGHT,
Platform.SELECT,
Platform.SENSOR,
Platform.SWITCH,
]
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
type VelbusConfigEntry = ConfigEntry[VelbusData]
@dataclass
class VelbusData:
"""Runtime data for the Velbus config entry."""
controller: Velbus
connect_task: asyncio.Task
async def velbus_connect_task(
controller: Velbus, hass: HomeAssistant, entry_id: str
) -> None:
"""Task to offload the long running connect."""
try:
await controller.connect()
except ConnectionError as ex:
raise PlatformNotReady(
f"Connection error while connecting to Velbus {entry_id}: {ex}"
) from ex
def _migrate_device_identifiers(hass: HomeAssistant, entry_id: str) -> None:
"""Migrate old device identifiers."""
dev_reg = dr.async_get(hass)
devices: list[dr.DeviceEntry] = dr.async_entries_for_config_entry(dev_reg, entry_id)
for device in devices:
old_identifier = list(next(iter(device.identifiers)))
if len(old_identifier) > 2:
new_identifier = {(old_identifier.pop(0), old_identifier.pop(0))}
_LOGGER.debug(
"migrate identifier '%s' to '%s'", device.identifiers, new_identifier
)
dev_reg.async_update_device(device.id, new_identifiers=new_identifier)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the actions for the Velbus component."""
setup_services(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: VelbusConfigEntry) -> bool:
"""Establish connection with velbus."""
controller = Velbus(
entry.data[CONF_PORT],
cache_dir=hass.config.path(STORAGE_DIR, f"velbuscache-{entry.entry_id}"),
)
task = hass.async_create_task(velbus_connect_task(controller, hass, entry.entry_id))
entry.runtime_data = VelbusData(controller=controller, connect_task=task)
_migrate_device_identifiers(hass, entry.entry_id)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: VelbusConfigEntry) -> bool:
"""Unload (close) the velbus connection."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
await entry.runtime_data.controller.stop()
return unload_ok
async def async_remove_entry(hass: HomeAssistant, entry: VelbusConfigEntry) -> None:
"""Remove the velbus entry, so we also have to cleanup the cache dir."""
await hass.async_add_executor_job(
shutil.rmtree,
hass.config.path(STORAGE_DIR, f"velbuscache-{entry.entry_id}"),
)
async def async_migrate_entry(
hass: HomeAssistant, config_entry: VelbusConfigEntry
) -> bool:
"""Migrate old entry."""
_LOGGER.debug("Migrating from version %s", config_entry.version)
cache_path = hass.config.path(STORAGE_DIR, f"velbuscache-{config_entry.entry_id}/")
if config_entry.version == 1:
# This is the config entry migration for adding the new program selection
# clean the velbusCache
if os.path.isdir(cache_path):
await hass.async_add_executor_job(shutil.rmtree, cache_path)
# set the new version
hass.config_entries.async_update_entry(config_entry, version=2)
_LOGGER.debug("Migration to version %s successful", config_entry.version)
return True