diff --git a/.codecov.yml b/.codecov.yml index 64d13414a..8cc664c99 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -17,7 +17,7 @@ coverage: paths: - custom_components/hacs/validate/ tasks: - target: 100% + target: 70% paths: - custom_components/hacs/tasks/ repositories: diff --git a/custom_components/hacs/api/__init__.py b/custom_components/hacs/api/__init__.py deleted file mode 100644 index f8017b365..000000000 --- a/custom_components/hacs/api/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Initialize HACS API""" diff --git a/custom_components/hacs/api/acknowledge_critical_repository.py b/custom_components/hacs/api/acknowledge_critical_repository.py deleted file mode 100644 index f0d69bd9c..000000000 --- a/custom_components/hacs/api/acknowledge_critical_repository.py +++ /dev/null @@ -1,25 +0,0 @@ -"""API Handler for acknowledge_critical_repository""" -from homeassistant.components import websocket_api -import homeassistant.helpers.config_validation as cv -import voluptuous as vol - -from custom_components.hacs.helpers.functions.store import ( - async_load_from_store, - async_save_to_store, -) - - -@websocket_api.async_response -@websocket_api.websocket_command( - {vol.Required("type"): "hacs/critical", vol.Optional("repository"): cv.string} -) -async def acknowledge_critical_repository(hass, connection, msg): - """Handle get media player cover command.""" - repository = msg["repository"] - - critical = await async_load_from_store(hass, "critical") - for repo in critical: - if repository == repo["repository"]: - repo["acknowledged"] = True - await async_save_to_store(hass, "critical", critical) - connection.send_message(websocket_api.result_message(msg["id"], critical)) diff --git a/custom_components/hacs/api/check_local_path.py b/custom_components/hacs/api/check_local_path.py deleted file mode 100644 index d45526bbb..000000000 --- a/custom_components/hacs/api/check_local_path.py +++ /dev/null @@ -1,24 +0,0 @@ -"""API Handler for check_local_path""" -from homeassistant.components import websocket_api -import homeassistant.helpers.config_validation as cv -import voluptuous as vol - -from custom_components.hacs.helpers.functions.path_exsist import async_path_exsist - - -@websocket_api.async_response -@websocket_api.websocket_command( - {vol.Required("type"): "hacs/check_path", vol.Optional("path"): cv.string} -) -async def check_local_path(_hass, connection, msg): - """Handle get media player cover command.""" - path = msg.get("path") - exist = {"exist": False} - - if path is None: - return - - if await async_path_exsist(path): - exist["exist"] = True - - connection.send_message(websocket_api.result_message(msg["id"], exist)) diff --git a/custom_components/hacs/api/get_critical_repositories.py b/custom_components/hacs/api/get_critical_repositories.py deleted file mode 100644 index 23fdcf4da..000000000 --- a/custom_components/hacs/api/get_critical_repositories.py +++ /dev/null @@ -1,15 +0,0 @@ -"""API Handler for get_critical_repositories""" -from homeassistant.components import websocket_api -import voluptuous as vol - -from custom_components.hacs.helpers.functions.store import async_load_from_store - - -@websocket_api.async_response -@websocket_api.websocket_command({vol.Required("type"): "hacs/get_critical"}) -async def get_critical_repositories(hass, connection, msg): - """Handle get media player cover command.""" - critical = await async_load_from_store(hass, "critical") - if not critical: - critical = [] - connection.send_message(websocket_api.result_message(msg["id"], critical)) diff --git a/custom_components/hacs/api/hacs_config.py b/custom_components/hacs/api/hacs_config.py deleted file mode 100644 index 35609a6df..000000000 --- a/custom_components/hacs/api/hacs_config.py +++ /dev/null @@ -1,28 +0,0 @@ -"""API Handler for hacs_config""" -from homeassistant.components import websocket_api -import voluptuous as vol - -from custom_components.hacs.share import get_hacs - - -@websocket_api.async_response -@websocket_api.websocket_command({vol.Required("type"): "hacs/config"}) -async def hacs_config(_hass, connection, msg): - """Handle get media player cover command.""" - hacs = get_hacs() - config = hacs.configuration - - content = {} - content["frontend_mode"] = config.frontend_mode - content["frontend_compact"] = config.frontend_compact - content["onboarding_done"] = config.onboarding_done - content["version"] = hacs.version - content["frontend_expected"] = hacs.frontend.version_expected - content["frontend_running"] = hacs.frontend.version_running - content["dev"] = config.dev - content["debug"] = config.debug - content["country"] = config.country - content["experimental"] = config.experimental - content["categories"] = hacs.common.categories - - connection.send_message(websocket_api.result_message(msg["id"], content)) diff --git a/custom_components/hacs/api/hacs_removed.py b/custom_components/hacs/api/hacs_removed.py deleted file mode 100644 index 7ede24e93..000000000 --- a/custom_components/hacs/api/hacs_removed.py +++ /dev/null @@ -1,15 +0,0 @@ -"""API Handler for hacs_removed""" -from homeassistant.components import websocket_api -import voluptuous as vol - -from custom_components.hacs.share import list_removed_repositories - - -@websocket_api.async_response -@websocket_api.websocket_command({vol.Required("type"): "hacs/removed"}) -async def hacs_removed(_hass, connection, msg): - """Get information about removed repositories.""" - content = [] - for repo in list_removed_repositories(): - content.append(repo.to_json()) - connection.send_message(websocket_api.result_message(msg["id"], content)) diff --git a/custom_components/hacs/api/hacs_repositories.py b/custom_components/hacs/api/hacs_repositories.py deleted file mode 100644 index d4d79f5d9..000000000 --- a/custom_components/hacs/api/hacs_repositories.py +++ /dev/null @@ -1,65 +0,0 @@ -"""API Handler for hacs_repositories""" -from homeassistant.components import websocket_api -import voluptuous as vol - -from custom_components.hacs.share import get_hacs - - -@websocket_api.async_response -@websocket_api.websocket_command({vol.Required("type"): "hacs/repositories"}) -async def hacs_repositories(_hass, connection, msg): - """Handle get media player cover command.""" - hacs = get_hacs() - repositories = hacs.repositories - content = [] - for repo in repositories: - if ( - repo.data.category in hacs.common.categories - and not repo.ignored_by_country_configuration - ): - data = { - "additional_info": repo.information.additional_info, - "authors": repo.data.authors, - "available_version": repo.display_available_version, - "beta": repo.data.show_beta, - "can_install": repo.can_install, - "category": repo.data.category, - "country": repo.data.country, - "config_flow": repo.data.config_flow, - "custom": repo.custom, - "default_branch": repo.data.default_branch, - "description": repo.data.description, - "domain": repo.data.domain, - "downloads": repo.data.downloads, - "file_name": repo.data.file_name, - "first_install": repo.status.first_install, - "full_name": repo.data.full_name, - "hide": repo.data.hide, - "hide_default_branch": repo.data.hide_default_branch, - "homeassistant": repo.data.homeassistant, - "id": repo.data.id, - "info": repo.information.info, - "installed_version": repo.display_installed_version, - "installed": repo.data.installed, - "issues": repo.data.open_issues, - "javascript_type": repo.information.javascript_type, - "last_updated": repo.data.last_updated, - "local_path": repo.content.path.local, - "main_action": repo.main_action, - "name": repo.display_name, - "new": repo.data.new, - "pending_upgrade": repo.pending_upgrade, - "releases": repo.data.published_tags, - "selected_tag": repo.data.selected_tag, - "stars": repo.data.stargazers_count, - "state": repo.state, - "status_description": repo.display_status_description, - "status": repo.display_status, - "topics": repo.data.topics, - "updated_info": repo.status.updated_info, - "version_or_commit": repo.display_version_or_commit, - } - - content.append(data) - - connection.send_message(websocket_api.result_message(msg["id"], content)) diff --git a/custom_components/hacs/api/hacs_repository.py b/custom_components/hacs/api/hacs_repository.py deleted file mode 100644 index bfa002345..000000000 --- a/custom_components/hacs/api/hacs_repository.py +++ /dev/null @@ -1,113 +0,0 @@ -"""API Handler for hacs_repository""" -from aiogithubapi import AIOGitHubAPIException -from homeassistant.components import websocket_api -import homeassistant.helpers.config_validation as cv -import voluptuous as vol - -from custom_components.hacs.share import get_hacs -from custom_components.hacs.utils.logger import getLogger - - -@websocket_api.async_response -@websocket_api.websocket_command( - { - vol.Required("type"): "hacs/repository", - vol.Optional("action"): cv.string, - vol.Optional("repository"): cv.string, - } -) -async def hacs_repository(hass, connection, msg): - """Handle get media player cover command.""" - hacs = get_hacs() - logger = getLogger() - data = {} - repository = None - - repo_id = msg.get("repository") - action = msg.get("action") - if repo_id is None or action is None: - return - - try: - repository = hacs.get_by_id(repo_id) - logger.debug(f"Running {action} for {repository.data.full_name}") - - if action == "update": - await repository.update_repository(ignore_issues=True, force=True) - repository.status.updated_info = True - - elif action == "install": - repository.data.new = False - was_installed = repository.data.installed - await repository.async_install() - if not was_installed: - hass.bus.async_fire("hacs/reload", {"force": True}) - - elif action == "not_new": - repository.data.new = False - - elif action == "uninstall": - repository.data.new = False - await repository.update_repository(True) - await repository.uninstall() - - elif action == "hide": - repository.data.hide = True - - elif action == "unhide": - repository.data.hide = False - - elif action == "show_beta": - repository.data.show_beta = True - await repository.update_repository() - - elif action == "hide_beta": - repository.data.show_beta = False - await repository.update_repository() - - elif action == "toggle_beta": - repository.data.show_beta = not repository.data.show_beta - await repository.update_repository() - - elif action == "delete": - repository.data.show_beta = False - repository.remove() - - elif action == "release_notes": - data = [ - { - "name": x.attributes["name"], - "body": x.attributes["body"], - "tag": x.attributes["tag_name"], - } - for x in repository.releases.objects - ] - - elif action == "set_version": - if msg["version"] == repository.data.default_branch: - repository.data.selected_tag = None - else: - repository.data.selected_tag = msg["version"] - await repository.update_repository() - - hass.bus.async_fire("hacs/reload", {"force": True}) - - else: - logger.error(f"WS action '{action}' is not valid") - - await hacs.data.async_write() - message = None - except AIOGitHubAPIException as exception: - message = exception - except AttributeError as exception: - message = f"Could not use repository with ID {repo_id} ({exception})" - except (Exception, BaseException) as exception: # pylint: disable=broad-except - message = exception - - if message is not None: - logger.error(message) - hass.bus.async_fire("hacs/error", {"message": str(message)}) - - if repository: - repository.state = None - connection.send_message(websocket_api.result_message(msg["id"], data)) diff --git a/custom_components/hacs/api/hacs_repository_data.py b/custom_components/hacs/api/hacs_repository_data.py deleted file mode 100644 index c061e2881..000000000 --- a/custom_components/hacs/api/hacs_repository_data.py +++ /dev/null @@ -1,124 +0,0 @@ -"""API Handler for hacs_repository_data""" -import sys - -from aiogithubapi import AIOGitHubAPIException -from homeassistant.components import websocket_api -import homeassistant.helpers.config_validation as cv -import voluptuous as vol - -from custom_components.hacs.exceptions import HacsException -from custom_components.hacs.helpers.functions.misc import extract_repository_from_url -from custom_components.hacs.helpers.functions.register_repository import ( - register_repository, -) -from custom_components.hacs.share import get_hacs -from custom_components.hacs.utils.logger import getLogger - -_LOGGER = getLogger() - - -@websocket_api.async_response -@websocket_api.websocket_command( - { - vol.Required("type"): "hacs/repository/data", - vol.Optional("action"): cv.string, - vol.Optional("repository"): cv.string, - vol.Optional("data"): cv.string, - } -) -async def hacs_repository_data(hass, connection, msg): - """Handle get media player cover command.""" - hacs = get_hacs() - repo_id = msg.get("repository") - action = msg.get("action") - data = msg.get("data") - - if repo_id is None: - return - - if action == "add": - repo_id = extract_repository_from_url(repo_id) - if repo_id is None: - return - - if repo_id in hacs.common.skip: - hacs.common.skip.remove(repo_id) - - if hacs.common.renamed_repositories.get(repo_id): - repo_id = hacs.common.renamed_repositories[repo_id] - - if not hacs.get_by_name(repo_id): - try: - registration = await register_repository(repo_id, data.lower()) - if registration is not None: - raise HacsException(registration) - except ( - Exception, - BaseException, - ) as exception: # pylint: disable=broad-except - hass.bus.async_fire( - "hacs/error", - { - "action": "add_repository", - "exception": str(sys.exc_info()[0].__name__), - "message": str(exception), - }, - ) - else: - hass.bus.async_fire( - "hacs/error", - { - "action": "add_repository", - "message": f"Repository '{repo_id}' exists in the store.", - }, - ) - - repository = hacs.get_by_name(repo_id) - else: - repository = hacs.get_by_id(repo_id) - - if repository is None: - hass.bus.async_fire("hacs/repository", {}) - return - - _LOGGER.debug("Running %s for %s", action, repository.data.full_name) - try: - if action == "set_state": - repository.state = data - - elif action == "set_version": - repository.data.selected_tag = data - await repository.update_repository() - - repository.state = None - - elif action == "install": - was_installed = repository.data.installed - repository.data.selected_tag = data - await repository.update_repository() - await repository.async_install() - repository.state = None - if not was_installed: - hass.bus.async_fire("hacs/reload", {"force": True}) - - elif action == "add": - repository.state = None - - else: - repository.state = None - _LOGGER.error("WS action '%s' is not valid", action) - - message = None - except AIOGitHubAPIException as exception: - message = exception - except AttributeError as exception: - message = f"Could not use repository with ID {repo_id} ({exception})" - except (Exception, BaseException) as exception: # pylint: disable=broad-except - message = exception - - if message is not None: - _LOGGER.error(message) - hass.bus.async_fire("hacs/error", {"message": str(message)}) - - await hacs.data.async_write() - connection.send_message(websocket_api.result_message(msg["id"], {})) diff --git a/custom_components/hacs/api/hacs_settings.py b/custom_components/hacs/api/hacs_settings.py deleted file mode 100644 index 7af3468c7..000000000 --- a/custom_components/hacs/api/hacs_settings.py +++ /dev/null @@ -1,54 +0,0 @@ -"""API Handler for hacs_settings""" -from homeassistant.components import websocket_api -import homeassistant.helpers.config_validation as cv -import voluptuous as vol - -from custom_components.hacs.share import get_hacs -from custom_components.hacs.utils.logger import getLogger - -_LOGGER = getLogger() - - -@websocket_api.async_response -@websocket_api.websocket_command( - { - vol.Required("type"): "hacs/settings", - vol.Optional("action"): cv.string, - vol.Optional("categories"): cv.ensure_list, - } -) -async def hacs_settings(hass, connection, msg): - """Handle get media player cover command.""" - hacs = get_hacs() - - action = msg["action"] - _LOGGER.debug("WS action '%s'", action) - - if action == "set_fe_grid": - hacs.configuration.frontend_mode = "Grid" - - elif action == "onboarding_done": - hacs.configuration.onboarding_done = True - - elif action == "set_fe_table": - hacs.configuration.frontend_mode = "Table" - - elif action == "set_fe_compact_true": - hacs.configuration.frontend_compact = False - - elif action == "set_fe_compact_false": - hacs.configuration.frontend_compact = True - - elif action == "clear_new": - for repo in hacs.repositories: - if repo.data.new and repo.data.category in msg.get("categories", []): - _LOGGER.debug( - "Clearing new flag from '%s'", - repo.data.full_name, - ) - repo.data.new = False - else: - _LOGGER.error("WS action '%s' is not valid", action) - hass.bus.async_fire("hacs/config", {}) - await hacs.data.async_write() - connection.send_message(websocket_api.result_message(msg["id"], {})) diff --git a/custom_components/hacs/api/hacs_status.py b/custom_components/hacs/api/hacs_status.py deleted file mode 100644 index 15ce85cb0..000000000 --- a/custom_components/hacs/api/hacs_status.py +++ /dev/null @@ -1,24 +0,0 @@ -"""API Handler for hacs_status""" -from homeassistant.components import websocket_api -import voluptuous as vol - -from custom_components.hacs.share import get_hacs - - -@websocket_api.async_response -@websocket_api.websocket_command({vol.Required("type"): "hacs/status"}) -async def hacs_status(_hass, connection, msg): - """Handle get media player cover command.""" - hacs = get_hacs() - content = { - "startup": hacs.status.startup, - "background_task": hacs.status.background_task, - "lovelace_mode": hacs.core.lovelace_mode, - "reloading_data": hacs.status.reloading_data, - "upgrading_all": hacs.status.upgrading_all, - "disabled": hacs.system.disabled, - "disabled_reason": hacs.system.disabled_reason, - "has_pending_tasks": hacs.queue.has_pending_tasks, - "stage": hacs.stage, - } - connection.send_message(websocket_api.result_message(msg["id"], content)) diff --git a/custom_components/hacs/tasks/setup_websocket_api.py b/custom_components/hacs/tasks/setup_websocket_api.py index f776c10e3..3faa3fc8f 100644 --- a/custom_components/hacs/tasks/setup_websocket_api.py +++ b/custom_components/hacs/tasks/setup_websocket_api.py @@ -1,21 +1,22 @@ """Register WS API endpoints for HACS.""" from __future__ import annotations +import sys + +from aiogithubapi import AIOGitHubAPIException +from homeassistant.components import websocket_api from homeassistant.components.websocket_api import async_register_command from homeassistant.core import HomeAssistant +import homeassistant.helpers.config_validation as cv +import voluptuous as vol -from ..api.acknowledge_critical_repository import acknowledge_critical_repository -from ..api.check_local_path import check_local_path -from ..api.get_critical_repositories import get_critical_repositories -from ..api.hacs_config import hacs_config -from ..api.hacs_removed import hacs_removed -from ..api.hacs_repositories import hacs_repositories -from ..api.hacs_repository import hacs_repository -from ..api.hacs_repository_data import hacs_repository_data -from ..api.hacs_settings import hacs_settings -from ..api.hacs_status import hacs_status from ..base import HacsBase from ..enums import HacsStage +from ..exceptions import HacsException +from ..helpers.functions.misc import extract_repository_from_url +from ..helpers.functions.register_repository import register_repository +from ..helpers.functions.store import async_load_from_store, async_save_to_store +from ..share import get_hacs, list_removed_repositories from .base import HacsTask @@ -30,13 +31,441 @@ class Task(HacsTask): stages = [HacsStage.SETUP] async def async_execute(self) -> None: + """Execute this task.""" async_register_command(self.hass, hacs_settings) async_register_command(self.hass, hacs_config) async_register_command(self.hass, hacs_repositories) async_register_command(self.hass, hacs_repository) async_register_command(self.hass, hacs_repository_data) - async_register_command(self.hass, check_local_path) async_register_command(self.hass, hacs_status) async_register_command(self.hass, hacs_removed) async_register_command(self.hass, acknowledge_critical_repository) async_register_command(self.hass, get_critical_repositories) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "hacs/critical", + vol.Optional("repository"): cv.string, + } +) +@websocket_api.require_admin +@websocket_api.async_response +async def acknowledge_critical_repository(hass, connection, msg): + """Handle get media player cover command.""" + repository = msg["repository"] + + critical = await async_load_from_store(hass, "critical") + for repo in critical: + if repository == repo["repository"]: + repo["acknowledged"] = True + await async_save_to_store(hass, "critical", critical) + connection.send_message(websocket_api.result_message(msg["id"], critical)) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "hacs/get_critical", + } +) +@websocket_api.require_admin +@websocket_api.async_response +async def get_critical_repositories(hass, connection, msg): + """Handle get media player cover command.""" + critical = await async_load_from_store(hass, "critical") + if not critical: + critical = [] + connection.send_message(websocket_api.result_message(msg["id"], critical)) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "hacs/config", + } +) +@websocket_api.require_admin +@websocket_api.async_response +async def hacs_config(_hass, connection, msg): + """Handle get media player cover command.""" + hacs = get_hacs() + connection.send_message( + websocket_api.result_message( + msg["id"], + { + "frontend_mode": hacs.configuration.frontend_mode, + "frontend_compact": hacs.configuration.frontend_compact, + "onboarding_done": hacs.configuration.onboarding_done, + "version": hacs.version, + "frontend_expected": hacs.frontend.version_expected, + "frontend_running": hacs.frontend.version_running, + "dev": hacs.configuration.dev, + "debug": hacs.configuration.debug, + "country": hacs.configuration.country, + "experimental": hacs.configuration.experimental, + "categories": hacs.common.categories, + }, + ) + ) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "hacs/removed", + } +) +@websocket_api.require_admin +@websocket_api.async_response +async def hacs_removed(_hass, connection, msg): + """Get information about removed repositories.""" + content = [] + for repo in list_removed_repositories(): + content.append(repo.to_json()) + connection.send_message(websocket_api.result_message(msg["id"], content)) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "hacs/repositories", + vol.Optional("categories"): [str], + } +) +@websocket_api.require_admin +@websocket_api.async_response +async def hacs_repositories(_hass, connection, msg): + """Handle get media player cover command.""" + hacs = get_hacs() + connection.send_message( + websocket_api.result_message( + msg["id"], + [ + { + "additional_info": repo.information.additional_info, + "authors": repo.data.authors, + "available_version": repo.display_available_version, + "beta": repo.data.show_beta, + "can_install": repo.can_install, + "category": repo.data.category, + "config_flow": repo.data.config_flow, + "country": repo.data.country, + "custom": repo.custom, + "default_branch": repo.data.default_branch, + "description": repo.data.description, + "domain": repo.data.domain, + "downloads": repo.data.downloads, + "file_name": repo.data.file_name, + "first_install": repo.status.first_install, + "full_name": repo.data.full_name, + "hide_default_branch": repo.data.hide_default_branch, + "hide": repo.data.hide, + "homeassistant": repo.data.homeassistant, + "id": repo.data.id, + "info": repo.information.info, + "installed_version": repo.display_installed_version, + "installed": repo.data.installed, + "issues": repo.data.open_issues, + "javascript_type": repo.information.javascript_type, + "last_updated": repo.data.last_updated, + "local_path": repo.content.path.local, + "main_action": repo.main_action, + "name": repo.display_name, + "new": repo.data.new, + "pending_upgrade": repo.pending_upgrade, + "releases": repo.data.published_tags, + "selected_tag": repo.data.selected_tag, + "stars": repo.data.stargazers_count, + "state": repo.state, + "status_description": repo.display_status_description, + "status": repo.display_status, + "topics": repo.data.topics, + "updated_info": repo.status.updated_info, + "version_or_commit": repo.display_version_or_commit, + } + for repo in hacs.repositories + if repo.data.category in (msg.get("categories") or hacs.common.categories) + and not repo.ignored_by_country_configuration + ], + ) + ) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "hacs/repository/data", + vol.Optional("action"): cv.string, + vol.Optional("repository"): cv.string, + vol.Optional("data"): cv.string, + } +) +@websocket_api.require_admin +@websocket_api.async_response +async def hacs_repository_data(hass, connection, msg): + """Handle get media player cover command.""" + hacs = get_hacs() + repo_id = msg.get("repository") + action = msg.get("action") + data = msg.get("data") + + if repo_id is None: + return + + if action == "add": + repo_id = extract_repository_from_url(repo_id) + if repo_id is None: + return + + if repo_id in hacs.common.skip: + hacs.common.skip.remove(repo_id) + + if hacs.common.renamed_repositories.get(repo_id): + repo_id = hacs.common.renamed_repositories[repo_id] + + if not hacs.get_by_name(repo_id): + try: + registration = await register_repository(repo_id, data.lower()) + if registration is not None: + raise HacsException(registration) + except ( + Exception, + BaseException, + ) as exception: # pylint: disable=broad-except + hass.bus.async_fire( + "hacs/error", + { + "action": "add_repository", + "exception": str(sys.exc_info()[0].__name__), + "message": str(exception), + }, + ) + else: + hass.bus.async_fire( + "hacs/error", + { + "action": "add_repository", + "message": f"Repository '{repo_id}' exists in the store.", + }, + ) + + repository = hacs.get_by_name(repo_id) + else: + repository = hacs.get_by_id(repo_id) + + if repository is None: + hass.bus.async_fire("hacs/repository", {}) + return + + hacs.log.debug("Running %s for %s", action, repository.data.full_name) + try: + if action == "set_state": + repository.state = data + + elif action == "set_version": + repository.data.selected_tag = data + await repository.update_repository() + + repository.state = None + + elif action == "install": + was_installed = repository.data.installed + repository.data.selected_tag = data + await repository.update_repository() + await repository.async_install() + repository.state = None + if not was_installed: + hass.bus.async_fire("hacs/reload", {"force": True}) + + elif action == "add": + repository.state = None + + else: + repository.state = None + hacs.log.error("WS action '%s' is not valid", action) + + message = None + except AIOGitHubAPIException as exception: + message = exception + except AttributeError as exception: + message = f"Could not use repository with ID {repo_id} ({exception})" + except (Exception, BaseException) as exception: # pylint: disable=broad-except + message = exception + + if message is not None: + hacs.log.error(message) + hass.bus.async_fire("hacs/error", {"message": str(message)}) + + await hacs.data.async_write() + connection.send_message(websocket_api.result_message(msg["id"], {})) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "hacs/repository", + vol.Optional("action"): cv.string, + vol.Optional("repository"): cv.string, + } +) +@websocket_api.require_admin +@websocket_api.async_response +async def hacs_repository(hass, connection, msg): + """Handle get media player cover command.""" + hacs = get_hacs() + data = {} + repository = None + + repo_id = msg.get("repository") + action = msg.get("action") + if repo_id is None or action is None: + return + + try: + repository = hacs.get_by_id(repo_id) + hacs.log.debug(f"Running {action} for {repository.data.full_name}") + + if action == "update": + await repository.update_repository(ignore_issues=True, force=True) + repository.status.updated_info = True + + elif action == "install": + repository.data.new = False + was_installed = repository.data.installed + await repository.async_install() + if not was_installed: + hass.bus.async_fire("hacs/reload", {"force": True}) + + elif action == "not_new": + repository.data.new = False + + elif action == "uninstall": + repository.data.new = False + await repository.update_repository(True) + await repository.uninstall() + + elif action == "hide": + repository.data.hide = True + + elif action == "unhide": + repository.data.hide = False + + elif action == "show_beta": + repository.data.show_beta = True + await repository.update_repository() + + elif action == "hide_beta": + repository.data.show_beta = False + await repository.update_repository() + + elif action == "toggle_beta": + repository.data.show_beta = not repository.data.show_beta + await repository.update_repository() + + elif action == "delete": + repository.data.show_beta = False + repository.remove() + + elif action == "release_notes": + data = [ + { + "name": x.attributes["name"], + "body": x.attributes["body"], + "tag": x.attributes["tag_name"], + } + for x in repository.releases.objects + ] + + elif action == "set_version": + if msg["version"] == repository.data.default_branch: + repository.data.selected_tag = None + else: + repository.data.selected_tag = msg["version"] + await repository.update_repository() + + hass.bus.async_fire("hacs/reload", {"force": True}) + + else: + hacs.log.error(f"WS action '{action}' is not valid") + + await hacs.data.async_write() + message = None + except AIOGitHubAPIException as exception: + message = exception + except AttributeError as exception: + message = f"Could not use repository with ID {repo_id} ({exception})" + except (Exception, BaseException) as exception: # pylint: disable=broad-except + message = exception + + if message is not None: + hacs.log.error(message) + hass.bus.async_fire("hacs/error", {"message": str(message)}) + + if repository: + repository.state = None + connection.send_message(websocket_api.result_message(msg["id"], data)) + + +@websocket_api.websocket_command( + { + vol.Required("type"): "hacs/settings", + vol.Optional("action"): cv.string, + vol.Optional("categories"): cv.ensure_list, + } +) +@websocket_api.require_admin +@websocket_api.async_response +async def hacs_settings(hass, connection, msg): + """Handle get media player cover command.""" + hacs = get_hacs() + + action = msg["action"] + hacs.log.debug("WS action '%s'", action) + + if action == "set_fe_grid": + hacs.configuration.frontend_mode = "Grid" + + elif action == "onboarding_done": + hacs.configuration.onboarding_done = True + + elif action == "set_fe_table": + hacs.configuration.frontend_mode = "Table" + + elif action == "set_fe_compact_true": + hacs.configuration.frontend_compact = False + + elif action == "set_fe_compact_false": + hacs.configuration.frontend_compact = True + + elif action == "clear_new": + for repo in hacs.repositories: + if repo.data.new and repo.data.category in msg.get("categories", []): + hacs.log.debug( + "Clearing new flag from '%s'", + repo.data.full_name, + ) + repo.data.new = False + else: + hacs.log.error("WS action '%s' is not valid", action) + hass.bus.async_fire("hacs/config", {}) + await hacs.data.async_write() + connection.send_message(websocket_api.result_message(msg["id"], {})) + + +@websocket_api.websocket_command({vol.Required("type"): "hacs/status"}) +@websocket_api.require_admin +@websocket_api.async_response +async def hacs_status(_hass, connection, msg): + """Handle get media player cover command.""" + hacs = get_hacs() + connection.send_message( + websocket_api.result_message( + msg["id"], + { + "startup": hacs.status.startup, + "background_task": hacs.status.background_task, + "lovelace_mode": hacs.core.lovelace_mode, + "reloading_data": hacs.status.reloading_data, + "upgrading_all": hacs.status.upgrading_all, + "disabled": hacs.system.disabled, + "disabled_reason": hacs.system.disabled_reason, + "has_pending_tasks": hacs.queue.has_pending_tasks, + "stage": hacs.stage, + }, + ) + ) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index b3475d992..39a9af156 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -3,28 +3,23 @@ import os from homeassistant.core import HomeAssistant import pytest -from custom_components.hacs.api.acknowledge_critical_repository import ( +from custom_components.hacs.tasks.setup_websocket_api import ( acknowledge_critical_repository, -) -from custom_components.hacs.api.check_local_path import check_local_path -from custom_components.hacs.api.get_critical_repositories import ( get_critical_repositories, + hacs_config, + hacs_removed, + hacs_repositories, + hacs_repository, + hacs_repository_data, + hacs_settings, + hacs_status, ) -from custom_components.hacs.api.hacs_config import hacs_config -from custom_components.hacs.api.hacs_removed import hacs_removed -from custom_components.hacs.api.hacs_repositories import hacs_repositories -from custom_components.hacs.api.hacs_repository import hacs_repository -from custom_components.hacs.api.hacs_repository_data import hacs_repository_data -from custom_components.hacs.api.hacs_settings import hacs_settings -from custom_components.hacs.api.hacs_status import hacs_status @pytest.mark.asyncio async def test_check_local_path(hacs, connection, tmpdir): hacs.hass = HomeAssistant() os.makedirs(tmpdir, exist_ok=True) - check_local_path(hacs.hass, connection, {"path": tmpdir, "id": 1}) - check_local_path(hacs.hass, connection, {"id": 1}) get_critical_repositories(hacs.hass, connection, {"id": 1}) hacs_config(hacs.hass, connection, {"id": 1}) hacs_removed(hacs.hass, connection, {"id": 1}) diff --git a/tests/tasks/test_setup_websocket_api.py b/tests/tasks/test_setup_websocket_api.py index feccbc4fa..d575aede3 100644 --- a/tests/tasks/test_setup_websocket_api.py +++ b/tests/tasks/test_setup_websocket_api.py @@ -17,4 +17,4 @@ async def test_setup_websocket_api(hacs: HacsBase): "custom_components.hacs.tasks.setup_websocket_api.async_register_command" ) as mock_async_register_command: await task.execute_task() - assert mock_async_register_command.call_count == 10 + assert mock_async_register_command.call_count == 9