homeassistant/custom_components/hacs/__init__.py

225 lines
8.1 KiB
Python

"""
Custom element manager for community created elements.
For more details about this integration, please refer to the documentation at
https://hacs.xyz/
"""
import voluptuous as vol
from aiogithubapi import AIOGitHub
from homeassistant import config_entries
from homeassistant.const import EVENT_HOMEASSISTANT_START
from homeassistant.const import __version__ as HAVERSION
from homeassistant.components.lovelace import system_health_info
from homeassistant.exceptions import ConfigEntryNotReady, ServiceNotFound
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.event import async_call_later
from custom_components.hacs.configuration_schema import (
hacs_base_config_schema,
hacs_config_option_schema,
)
from custom_components.hacs.const import DOMAIN, ELEMENT_TYPES, STARTUP, VERSION
from custom_components.hacs.constrains import check_constans, check_requirements
from custom_components.hacs.hacsbase.configuration import Configuration
from custom_components.hacs.hacsbase.data import HacsData
from custom_components.hacs.setup import (
add_sensor,
load_hacs_repository,
setup_frontend,
)
from custom_components.hacs.globals import get_hacs
from custom_components.hacs.helpers.network import internet_connectivity_check
SCHEMA = hacs_base_config_schema()
SCHEMA[vol.Optional("options")] = hacs_config_option_schema()
CONFIG_SCHEMA = vol.Schema({DOMAIN: SCHEMA}, extra=vol.ALLOW_EXTRA)
async def async_setup(hass, config):
"""Set up this integration using yaml."""
hacs = get_hacs()
if DOMAIN not in config:
return True
hass.data[DOMAIN] = config
hacs.hass = hass
hacs.session = async_create_clientsession(hass)
hacs.configuration = Configuration.from_dict(
config[DOMAIN], config[DOMAIN].get("options")
)
hacs.configuration.config = config
hacs.configuration.config_type = "yaml"
await startup_wrapper_for_yaml()
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data={}
)
)
return True
async def async_setup_entry(hass, config_entry):
"""Set up this integration using UI."""
hacs = get_hacs()
conf = hass.data.get(DOMAIN)
if config_entry.source == config_entries.SOURCE_IMPORT:
if conf is None:
hass.async_create_task(
hass.config_entries.async_remove(config_entry.entry_id)
)
return False
hacs.hass = hass
hacs.session = async_create_clientsession(hass)
hacs.configuration = Configuration.from_dict(
config_entry.data, config_entry.options
)
hacs.configuration.config_type = "flow"
hacs.configuration.config_entry = config_entry
config_entry.add_update_listener(reload_hacs)
startup_result = await hacs_startup()
if not startup_result:
hacs.system.disabled = True
raise ConfigEntryNotReady
hacs.system.disabled = False
return startup_result
async def startup_wrapper_for_yaml():
"""Startup wrapper for yaml config."""
hacs = get_hacs()
startup_result = await hacs_startup()
if not startup_result:
hacs.system.disabled = True
hacs.hass.components.frontend.async_remove_panel(
hacs.configuration.sidepanel_title.lower()
.replace(" ", "_")
.replace("-", "_")
)
hacs.logger.info("Could not setup HACS, trying again in 15 min")
async_call_later(hacs.hass, 900, startup_wrapper_for_yaml())
return
hacs.system.disabled = False
async def hacs_startup():
"""HACS startup tasks."""
hacs = get_hacs()
if not check_requirements():
return False
if hacs.configuration.debug:
try:
await hacs.hass.services.async_call(
"logger", "set_level", {"hacs": "debug"}
)
except ServiceNotFound:
hacs.logger.error(
"Could not set logging level to debug, logger is not enabled"
)
lovelace_info = await system_health_info(hacs.hass)
hacs.logger.debug(f"Configuration type: {hacs.configuration.config_type}")
hacs.version = VERSION
hacs.logger.info(STARTUP)
hacs.system.config_path = hacs.hass.config.path()
hacs.system.ha_version = HAVERSION
hacs.system.lovelace_mode = lovelace_info.get("mode", "yaml")
hacs.system.disabled = False
hacs.github = AIOGitHub(
hacs.configuration.token, async_create_clientsession(hacs.hass)
)
hacs.data = HacsData()
# Check HACS Constrains
if not await hacs.hass.async_add_executor_job(check_constans):
if hacs.configuration.config_type == "flow":
if hacs.configuration.config_entry is not None:
await async_remove_entry(hacs.hass, hacs.configuration.config_entry)
return False
# Set up frontend
await setup_frontend()
if not await hacs.hass.async_add_executor_job(internet_connectivity_check):
hacs.logger.critical("No network connectivity")
return False
# Load HACS
if not await load_hacs_repository():
if hacs.configuration.config_type == "flow":
if hacs.configuration.config_entry is not None:
await async_remove_entry(hacs.hass, hacs.configuration.config_entry)
return False
# Restore from storefiles
if not await hacs.data.restore():
hacs_repo = hacs.get_by_name("hacs/integration")
hacs_repo.pending_restart = True
if hacs.configuration.config_type == "flow":
if hacs.configuration.config_entry is not None:
await async_remove_entry(hacs.hass, hacs.configuration.config_entry)
return False
# Add aditional categories
hacs.common.categories = ELEMENT_TYPES
if hacs.configuration.appdaemon:
hacs.common.categories.append("appdaemon")
if hacs.configuration.netdaemon:
hacs.common.categories.append("netdaemon")
if hacs.configuration.python_script:
hacs.configuration.python_script = False
if hacs.configuration.config_type == "yaml":
hacs.logger.warning(
"Configuration option 'python_script' is deprecated and you should remove it from your configuration, HACS will know if you use 'python_script' in your Home Assistant configuration, this option will be removed in a future release."
)
if hacs.configuration.theme:
hacs.configuration.theme = False
if hacs.configuration.config_type == "yaml":
hacs.logger.warning(
"Configuration option 'theme' is deprecated and you should remove it from your configuration, HACS will know if you use 'theme' in your Home Assistant configuration, this option will be removed in a future release."
)
# Setup startup tasks
if hacs.configuration.config_type == "yaml":
hacs.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, hacs.startup_tasks())
else:
async_call_later(hacs.hass, 5, hacs.startup_tasks())
# Show the configuration
hacs.configuration.print()
# Set up sensor
await hacs.hass.async_add_executor_job(add_sensor)
# Mischief managed!
return True
async def async_remove_entry(hass, config_entry):
"""Handle removal of an entry."""
hacs = get_hacs()
hacs.logger.info("Disabling HACS")
hacs.logger.info("Removing recuring tasks")
for task in hacs.recuring_tasks:
task()
hacs.logger.info("Removing sensor")
try:
await hass.config_entries.async_forward_entry_unload(config_entry, "sensor")
except ValueError:
pass
hacs.logger.info("Removing sidepanel")
try:
hass.components.frontend.async_remove_panel("hacs")
except AttributeError:
pass
hacs.system.disabled = True
hacs.logger.info("HACS is now disabled")
async def reload_hacs(hass, config_entry):
"""Reload HACS."""
await async_remove_entry(hass, config_entry)
await async_setup_entry(hass, config_entry)