Full Code of hacs/integration for AI

main cb488796faf1 cached
701 files
2.3 MB
642.5k tokens
655 symbols
1 requests
Download .txt
Showing preview only (2,559K chars total). Download the full file or copy to clipboard to get everything.
Repository: hacs/integration
Branch: main
Commit: cb488796faf1
Files: 701
Total size: 2.3 MB

Directory structure:
gitextract_ec9nfn8q/

├── .codeclimate.yml
├── .codecov.yml
├── .coveragerc
├── .devcontainer.json
├── .dockerignore
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── a_integration.yml
│   │   ├── b_frontend.yml
│   │   ├── c_bot.yml
│   │   ├── config.yml
│   │   ├── d_documentation.yml
│   │   ├── e_action.yml
│   │   ├── f_addon.yml
│   │   └── removal.yml
│   ├── dependabot.yml
│   ├── pre-commit-config.yaml
│   ├── release.yml
│   └── workflows/
│       ├── action-container.yml
│       ├── generate-hacs-data.yml
│       ├── lint.yaml
│       ├── lock.yml
│       ├── publish.yml
│       ├── pull_requests_labels.yml
│       ├── pytest.yml
│       ├── stale.yml
│       └── validate.yml
├── .gitignore
├── .pylintrc
├── LICENSE
├── README.md
├── action/
│   ├── Dockerfile
│   └── action.py
├── constraints.txt
├── custom_components/
│   └── hacs/
│       ├── __init__.py
│       ├── base.py
│       ├── config_flow.py
│       ├── const.py
│       ├── coordinator.py
│       ├── data_client.py
│       ├── diagnostics.py
│       ├── entity.py
│       ├── enums.py
│       ├── exceptions.py
│       ├── frontend.py
│       ├── icons.json
│       ├── iconset.js
│       ├── manifest.json
│       ├── repairs.py
│       ├── repositories/
│       │   ├── __init__.py
│       │   ├── appdaemon.py
│       │   ├── base.py
│       │   ├── integration.py
│       │   ├── plugin.py
│       │   ├── python_script.py
│       │   ├── template.py
│       │   └── theme.py
│       ├── switch.py
│       ├── system_health.py
│       ├── types.py
│       ├── update.py
│       ├── utils/
│       │   ├── __init__.py
│       │   ├── backup.py
│       │   ├── configuration_schema.py
│       │   ├── data.py
│       │   ├── decode.py
│       │   ├── decorator.py
│       │   ├── file_system.py
│       │   ├── filters.py
│       │   ├── github_graphql_query.py
│       │   ├── json.py
│       │   ├── logger.py
│       │   ├── path.py
│       │   ├── queue_manager.py
│       │   ├── regex.py
│       │   ├── store.py
│       │   ├── url.py
│       │   ├── validate.py
│       │   ├── version.py
│       │   └── workarounds.py
│       ├── validate/
│       │   ├── README.md
│       │   ├── __init__.py
│       │   ├── archived.py
│       │   ├── base.py
│       │   ├── brands.py
│       │   ├── description.py
│       │   ├── hacsjson.py
│       │   ├── images.py
│       │   ├── information.py
│       │   ├── integration_manifest.py
│       │   ├── issues.py
│       │   ├── manager.py
│       │   └── topics.py
│       └── websocket/
│           ├── __init__.py
│           ├── critical.py
│           ├── repositories.py
│           └── repository.py
├── hacs.json
├── info.md
├── pyproject.toml
├── requirements_action.txt
├── requirements_base.txt
├── requirements_core_min.txt
├── requirements_generate_data.txt
├── requirements_lint.txt
├── requirements_test.txt
├── scripts/
│   ├── __init__.py
│   ├── clear_storage
│   ├── coverage
│   ├── data/
│   │   ├── __init__.py
│   │   ├── common.py
│   │   ├── generate_category_data.py
│   │   └── validate_category_data.py
│   ├── develop
│   ├── install/
│   │   ├── core
│   │   ├── core_dev
│   │   ├── frontend
│   │   ├── pip_packages
│   │   └── uv_packages
│   ├── lgtm.js
│   ├── lint
│   ├── setup
│   ├── snapshot-update
│   ├── test
│   └── update/
│       ├── __init__.py
│       ├── default_repositories.py
│       └── manifest.py
└── tests/
    ├── __init__.py
    ├── action/
    │   └── test_hacs_action_integration.py
    ├── common.py
    ├── conftest.py
    ├── fixtures/
    │   ├── proxy/
    │   │   ├── api.github.com/
    │   │   │   ├── rate_limit.json
    │   │   │   └── repos/
    │   │   │       ├── hacs/
    │   │   │       │   ├── default/
    │   │   │       │   │   └── contents/
    │   │   │       │   │       ├── appdaemon.json
    │   │   │       │   │       ├── integration.json
    │   │   │       │   │       ├── plugin.json
    │   │   │       │   │       ├── python_script.json
    │   │   │       │   │       ├── template.json
    │   │   │       │   │       └── theme.json
    │   │   │       │   ├── integration/
    │   │   │       │   │   ├── contents/
    │   │   │       │   │   │   ├── custom_components/
    │   │   │       │   │   │   │   └── hacs/
    │   │   │       │   │   │   │       └── manifest.json
    │   │   │       │   │   │   └── hacs.json
    │   │   │       │   │   └── git/
    │   │   │       │   │       └── trees/
    │   │   │       │   │           └── main.json
    │   │   │       │   └── integration.json
    │   │   │       └── hacs-test-org/
    │   │   │           ├── addon-basic/
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── addon-basic.json
    │   │   │           ├── appdaemon-basic/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   ├── apps/
    │   │   │           │   │   │   └── example.json
    │   │   │           │   │   ├── apps.json
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── appdaemon-basic.json
    │   │   │           ├── integration-basic/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   ├── custom_components/
    │   │   │           │   │   │   └── example/
    │   │   │           │   │   │       └── manifest.json
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   ├── releases/
    │   │   │           │   │   └── latest.json
    │   │   │           │   └── releases.json
    │   │   │           ├── integration-basic-custom/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   ├── custom_components/
    │   │   │           │   │   │   └── example/
    │   │   │           │   │   │       └── manifest.json
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── integration-basic-custom.json
    │   │   │           ├── integration-basic.json
    │   │   │           ├── integration-invalid/
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── integration-invalid.json
    │   │   │           ├── plugin-basic/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── plugin-basic.json
    │   │   │           ├── plugin-custom-dist/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── plugin-custom-dist.json
    │   │   │           ├── python_script-basic/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── python_script-basic.json
    │   │   │           ├── template-basic/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── template-basic.json
    │   │   │           ├── theme-basic/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           └── theme-basic.json
    │   │   ├── brands.home-assistant.io/
    │   │   │   └── domains.json
    │   │   ├── data-v2.hacs.xyz/
    │   │   │   ├── appdaemon/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   ├── critical/
    │   │   │   │   └── data.json
    │   │   │   ├── integration/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   ├── netdaemon/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   ├── plugin/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   ├── python_script/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   ├── removed/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   ├── template/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   └── theme/
    │   │   │       ├── data.json
    │   │   │       └── repositories.json
    │   │   ├── github.com/
    │   │   │   └── hacs-test-org/
    │   │   │       ├── appdaemon-basic/
    │   │   │       │   └── _base/
    │   │   │       │       ├── README.md
    │   │   │       │       └── apps/
    │   │   │       │           └── example/
    │   │   │       │               └── __init__.py
    │   │   │       └── integration-basic/
    │   │   │           └── _base/
    │   │   │               ├── README.md
    │   │   │               └── custom_components/
    │   │   │                   └── example/
    │   │   │                       └── manifest.json
    │   │   └── raw.githubusercontent.com/
    │   │       └── hacs-test-org/
    │   │           ├── appdaemon-basic/
    │   │           │   ├── 1.0.0/
    │   │           │   │   ├── README.md
    │   │           │   │   └── hacs.json
    │   │           │   └── 2.0.0/
    │   │           │       ├── README.md
    │   │           │       └── hacs.json
    │   │           ├── integration-basic/
    │   │           │   ├── 1.0.0/
    │   │           │   │   ├── README.md
    │   │           │   │   └── hacs.json
    │   │           │   ├── 2.0.0/
    │   │           │   │   ├── README.md
    │   │           │   │   └── hacs.json
    │   │           │   └── main/
    │   │           │       └── hacs.json
    │   │           ├── plugin-basic/
    │   │           │   ├── 1.0.0/
    │   │           │   │   ├── hacs.json
    │   │           │   │   └── plugin-basic.js
    │   │           │   └── 2.0.0/
    │   │           │       ├── hacs.json
    │   │           │       └── plugin-basic.js
    │   │           ├── python_script-basic/
    │   │           │   ├── 1.0.0/
    │   │           │   │   ├── README.md
    │   │           │   │   ├── hacs.json
    │   │           │   │   └── python_scripts/
    │   │           │   │       └── example.py
    │   │           │   └── 2.0.0/
    │   │           │       ├── README.md
    │   │           │       ├── hacs.json
    │   │           │       └── python_scripts/
    │   │           │           └── example.py
    │   │           ├── template-basic/
    │   │           │   ├── 1.0.0/
    │   │           │   │   ├── example.jinja
    │   │           │   │   └── hacs.json
    │   │           │   └── 2.0.0/
    │   │           │       ├── example.jinja
    │   │           │       └── hacs.json
    │   │           └── theme-basic/
    │   │               ├── 1.0.0/
    │   │               │   ├── hacs.json
    │   │               │   └── themes/
    │   │               │       └── example.yaml
    │   │               └── 2.0.0/
    │   │                   ├── hacs.json
    │   │                   └── themes/
    │   │                       └── example.yaml
    │   ├── repository_data.json
    │   ├── stored_repositories.json
    │   ├── v2-appdaemon-data.json
    │   ├── v2-critical-data.json
    │   ├── v2-integration-data.json
    │   ├── v2-plugin-data.json
    │   ├── v2-python_script-data.json
    │   ├── v2-removed-data.json
    │   ├── v2-template-data.json
    │   └── v2-theme-data.json
    ├── hacsbase/
    │   ├── test_backup.py
    │   ├── test_configuration.py
    │   ├── test_hacs.py
    │   └── test_hacsbase_data.py
    ├── helpers/
    │   ├── classes/
    │   │   ├── test_repository_data.py
    │   │   └── test_validate_class.py
    │   ├── download/
    │   │   ├── test_gather_files_to_download.py
    │   │   └── test_should_try_releases.py
    │   ├── filters/
    │   │   ├── test_filter_content_return_one_of_type.py
    │   │   └── test_get_first_directory_in_directory.py
    │   └── functions/
    │       └── test_extract_repository_from_url.py
    ├── homeassistantfixtures/
    │   ├── __init__.py
    │   ├── common.py
    │   ├── dev.py
    │   └── min.py
    ├── integration/
    │   └── test_integration_setup.py
    ├── patch_time.py
    ├── repositories/
    │   ├── helpers/
    │   │   ├── __init__.py
    │   │   └── test_properties.py
    │   ├── test_can_install.py
    │   ├── test_display_status.py
    │   ├── test_download_repository.py
    │   ├── test_get_documentation.py
    │   ├── test_get_hacs_json.py
    │   ├── test_get_hacs_json_raw.py
    │   ├── test_get_reposiotry_releases.py
    │   ├── test_hacs_manifest.py
    │   ├── test_plugin_repository.py
    │   ├── test_register_repository.py
    │   ├── test_remove_repository.py
    │   ├── test_removed_repository.py
    │   └── test_update_repository.py
    ├── ruff.toml
    ├── scripts/
    │   └── data/
    │       └── test_generate_category_data.py
    ├── snapshots/
    │   ├── action/
    │   │   └── test_hacs_action_integration/
    │   │       ├── bad_documentation.log
    │   │       ├── bad_issue_tracker.log
    │   │       ├── no_releases.log
    │   │       ├── releases_without_assets.log
    │   │       └── valid_manifest.log
    │   ├── api-usage/
    │   │   └── tests/
    │   │       ├── action/
    │   │       │   ├── test_hacs_action_integrationtest-hacs-action-integration-bad-documentation.json
    │   │       │   ├── test_hacs_action_integrationtest-hacs-action-integration-bad-issue-tracker.json
    │   │       │   ├── test_hacs_action_integrationtest-hacs-action-integration-no-releases.json
    │   │       │   ├── test_hacs_action_integrationtest-hacs-action-integration-releases-without-assets.json
    │   │       │   └── test_hacs_action_integrationtest-hacs-action-integration-valid-manifest.json
    │   │       ├── hacsbase/
    │   │       │   ├── test_backuptest-directory.json
    │   │       │   ├── test_backuptest-file.json
    │   │       │   ├── test_backuptest-muilti.json
    │   │       │   ├── test_hacsbase_datatest-hacs-data-async-write1.json
    │   │       │   ├── test_hacsbase_datatest-hacs-data-async-write2.json
    │   │       │   ├── test_hacsbase_datatest-hacs-data-restore-write-not-new.json
    │   │       │   ├── test_hacstest-add-remove-repository.json
    │   │       │   └── test_hacstest-hacs.json
    │   │       ├── helpers/
    │   │       │   └── download/
    │   │       │       ├── test_gather_files_to_downloadtest-gather-appdaemon-files-base.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-appdaemon-files-with-subdir.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-content-in-root-theme.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-files-to-download.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-different-card-name.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-files-from-dist.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-files-from-release-multiple.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-files-from-release.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-files-from-root.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-multiple-files-in-root.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-multiple-plugin-files-from-dist.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-zip-release.json
    │   │       │       ├── test_gather_files_to_downloadtest-single-file-repo.json
    │   │       │       ├── test_should_try_releasestest-base.json
    │   │       │       ├── test_should_try_releasestest-category-is-wrong.json
    │   │       │       ├── test_should_try_releasestest-no-releases.json
    │   │       │       ├── test_should_try_releasestest-ref-is-default.json
    │   │       │       └── test_should_try_releasestest-zip-release.json
    │   │       ├── integration/
    │   │       │   └── test_integration_setuptest-integration-setup.json
    │   │       ├── repositories/
    │   │       │   ├── helpers/
    │   │       │   │   ├── test_propertiestest-repository-helpers-properties-can-be-installed.json
    │   │       │   │   └── test_propertiestest-repository-helpers-properties-pending-update.json
    │   │       │   ├── test_can_installtest-hacs-can-install.json
    │   │       │   ├── test_display_statustest-display-status.json
    │   │       │   ├── test_download_repositorytest-download-repository-hacs-test-org-appdaemon-basic.json
    │   │       │   ├── test_download_repositorytest-download-repository-hacs-test-org-integration-basic.json
    │   │       │   ├── test_download_repositorytest-download-repository-hacs-test-org-plugin-basic.json
    │   │       │   ├── test_download_repositorytest-download-repository-hacs-test-org-python-script-basic.json
    │   │       │   ├── test_download_repositorytest-download-repository-hacs-test-org-template-basic.json
    │   │       │   ├── test_download_repositorytest-download-repository-hacs-test-org-theme-basic.json
    │   │       │   ├── test_get_documentationtest-repository-get-documentation-data0.json
    │   │       │   ├── test_get_documentationtest-repository-get-documentation-data1.json
    │   │       │   ├── test_get_documentationtest-repository-get-documentation-data2.json
    │   │       │   ├── test_get_documentationtest-repository-get-documentation-data3.json
    │   │       │   ├── test_get_hacs_json_rawtest-get-hacs-json-raw-1-0-0-expected0.json
    │   │       │   ├── test_get_hacs_json_rawtest-get-hacs-json-raw-99-99-99-none.json
    │   │       │   ├── test_get_hacs_json_rawtest-get-hacs-json-raw-with-exception.json
    │   │       │   ├── test_get_hacs_jsontest-get-hacs-json-with-exception.json
    │   │       │   ├── test_get_hacs_jsontest-validate-repository-1-0-0-integration-basic-1-0-0.json
    │   │       │   ├── test_get_hacs_jsontest-validate-repository-99-99-99-none.json
    │   │       │   ├── test_get_reposiotry_releasestest-get-reposiotry-releases-hacs-test-org-appdaemon-basic.json
    │   │       │   ├── test_get_reposiotry_releasestest-get-reposiotry-releases-hacs-test-org-integration-basic.json
    │   │       │   ├── test_get_reposiotry_releasestest-get-reposiotry-releases-hacs-test-org-plugin-basic.json
    │   │       │   ├── test_get_reposiotry_releasestest-get-reposiotry-releases-hacs-test-org-python-script-basic.json
    │   │       │   ├── test_get_reposiotry_releasestest-get-reposiotry-releases-hacs-test-org-template-basic.json
    │   │       │   ├── test_get_reposiotry_releasestest-get-reposiotry-releases-hacs-test-org-theme-basic.json
    │   │       │   ├── test_plugin_repositorytest-add-dashboard-resource-with-invalid-file-name.json
    │   │       │   ├── test_plugin_repositorytest-add-dashboard-resource.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-hacstag-1-0-0-none-none-100.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-hacstag-1-7-dev09-r2-none-none-17092.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-hacstag-none-2-0-1-none-201.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-hacstag-none-none-3-4-2-342.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-hacstag-none-none-none.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-namespace-hacs-test-org-awesome-plugin-hacsfiles-awesome-plugin.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-namespace-hacs-test-org-plugin-advanced-hacsfiles-plugin-advanced.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-namespace-hacs-test-org-plugin-basic-hacsfiles-plugin-basic.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-url.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-no-hass-data.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-no-lovelace-data.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-no-lovelace-resources.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-no-store.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-none-store.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-wrong-key.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-wrong-version.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler.json
    │   │       │   ├── test_plugin_repositorytest-remove-dashboard-resource.json
    │   │       │   ├── test_plugin_repositorytest-update-dashboard-resource.json
    │   │       │   ├── test_register_repositorytest-register-repository-failures-hacs-test-org-addon-basic-the-repository-does-not-seem-to-be-a-integration-but-an-add-on-repository-hacs-does-not-manage-add-ons.json
    │   │       │   ├── test_register_repositorytest-register-repository-failures-hacs-test-org-integration-invalid-integration-hacs-test-org-integration-invalid-repository-structure-for-main-is-not-compliant.json
    │   │       │   ├── test_register_repositorytest-register-repository-failures-hassio-addons-example-the-repository-does-not-seem-to-be-a-integration-but-an-add-on-repository-hacs-does-not-manage-add-ons.json
    │   │       │   ├── test_register_repositorytest-register-repository-failures-home-assistant-addons-the-repository-does-not-seem-to-be-a-integration-but-an-add-on-repository-hacs-does-not-manage-add-ons.json
    │   │       │   ├── test_register_repositorytest-register-repository-failures-home-assistant-core-you-can-not-add-homeassistant-core-to-use-core-integrations-check-the-home-assistant-documentation-for-how-to-add-them.json
    │   │       │   ├── test_register_repositorytest-register-repository-hacs-test-org-integration-basic-custom-integration.json
    │   │       │   ├── test_register_repositorytest-register-repository-hacs-test-org-plugin-custom-dist-plugin.json
    │   │       │   ├── test_remove_repositorytest-remove-repository-hacs-test-org-appdaemon-basic.json
    │   │       │   ├── test_remove_repositorytest-remove-repository-hacs-test-org-integration-basic.json
    │   │       │   ├── test_remove_repositorytest-remove-repository-hacs-test-org-plugin-basic.json
    │   │       │   ├── test_remove_repositorytest-remove-repository-hacs-test-org-python-script-basic.json
    │   │       │   ├── test_remove_repositorytest-remove-repository-hacs-test-org-template-basic.json
    │   │       │   ├── test_remove_repositorytest-remove-repository-hacs-test-org-theme-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-download-failure.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-hacs-test-org-appdaemon-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-hacs-test-org-integration-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-hacs-test-org-plugin-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-hacs-test-org-python-script-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-hacs-test-org-template-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-hacs-test-org-theme-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-no-manifest.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-no-update.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-old-core-version.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-old-hacs-version.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-same-provided-version.json
    │   │       │   ├── test_update_repositorytest-update-repository-websocket-hacs-test-org-appdaemon-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-websocket-hacs-test-org-integration-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-websocket-hacs-test-org-plugin-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-websocket-hacs-test-org-python-script-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-websocket-hacs-test-org-template-basic.json
    │   │       │   └── test_update_repositorytest-update-repository-websocket-hacs-test-org-theme-basic.json
    │   │       ├── scripts/
    │   │       │   └── data/
    │   │       │       ├── test_generate_category_datatest-generate-category-data-error-status-release-304-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-error-status-release-404-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-errors-release-cancellederror-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-errors-release-error2-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-errors-release-timeouterror-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-hacs-test-org-appdaemon-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-hacs-test-org-plugin-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-hacs-test-org-python-script-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-hacs-test-org-template-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-hacs-test-org-theme-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-single-repository-hacs-test-org-appdaemon-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-single-repository-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-single-repository-hacs-test-org-plugin-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-single-repository-hacs-test-org-python-script-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-single-repository-hacs-test-org-template-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-single-repository-hacs-test-org-theme-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-with-30plus-prereleases-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-with-prior-content-hacs-test-org-appdaemon-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-with-prior-content-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-with-prior-content-hacs-test-org-plugin-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-with-prior-content-hacs-test-org-python-script-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-with-prior-content-hacs-test-org-template-basic.json
    │   │       │       └── test_generate_category_datatest-generate-category-data-with-prior-content-hacs-test-org-theme-basic.json
    │   │       ├── test_config_flowtest-flow-with-activation-failure.json
    │   │       ├── test_config_flowtest-flow-with-registration-failure.json
    │   │       ├── test_config_flowtest-flow-with-remove-while-activating.json
    │   │       ├── test_config_flowtest-full-user-flow-implementation.json
    │   │       ├── test_config_flowtest-options-flow.json
    │   │       ├── test_data_clienttest-basic-functionality-data-hacs-test-org-appdaemon-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-data-hacs-test-org-integration-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-data-hacs-test-org-plugin-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-data-hacs-test-org-python-script-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-data-hacs-test-org-template-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-data-hacs-test-org-theme-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-appdaemon-data0.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-critical-data6.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-integration-data1.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-plugin-data2.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-python-script-data3.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-removed-data7.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-template-data4.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-theme-data5.json
    │   │       ├── test_data_clienttest-basic-functionality-repositories-hacs-test-org-appdaemon-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-repositories-hacs-test-org-integration-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-repositories-hacs-test-org-plugin-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-repositories-hacs-test-org-python-script-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-repositories-hacs-test-org-template-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-repositories-hacs-test-org-theme-basic.json
    │   │       ├── test_data_clienttest-discard-invalid-repo-data-appdaemon-data0.json
    │   │       ├── test_data_clienttest-discard-invalid-repo-data-integration-data1.json
    │   │       ├── test_data_clienttest-discard-invalid-repo-data-plugin-data2.json
    │   │       ├── test_data_clienttest-discard-invalid-repo-data-python-script-data3.json
    │   │       ├── test_data_clienttest-discard-invalid-repo-data-template-data4.json
    │   │       ├── test_data_clienttest-discard-invalid-repo-data-theme-data5.json
    │   │       ├── test_data_clienttest-exception-handling-exception-error-fetching-data-from-hacs-test.json
    │   │       ├── test_data_clienttest-exception-handling-timeouterror-timeout-of-60s-reached.json
    │   │       ├── test_data_clienttest-status-handling-1009-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-200-does-not-raise.json
    │   │       ├── test_data_clienttest-status-handling-201-does-not-raise.json
    │   │       ├── test_data_clienttest-status-handling-301-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-302-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-304-hacsnotmodifiedexception.json
    │   │       ├── test_data_clienttest-status-handling-400-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-401-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-403-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-418-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-429-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-500-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-529-hacsexception.json
    │   │       ├── test_diagnosticstest-diagnostics-with-exception.json
    │   │       ├── test_diagnosticstest-diagnostics.json
    │   │       ├── test_sensor_cleanuptest-sensor-cleanup.json
    │   │       ├── test_switchtest-switch-entity-state-hacs-test-org-appdaemon-basic.json
    │   │       ├── test_switchtest-switch-entity-state-hacs-test-org-integration-basic.json
    │   │       ├── test_switchtest-switch-entity-state-hacs-test-org-plugin-basic.json
    │   │       ├── test_switchtest-switch-entity-state-hacs-test-org-python-script-basic.json
    │   │       ├── test_switchtest-switch-entity-state-hacs-test-org-template-basic.json
    │   │       ├── test_switchtest-switch-entity-state-hacs-test-org-theme-basic.json
    │   │       ├── test_system_healthtest-system-health-after-unload.json
    │   │       ├── test_system_healthtest-system-health.json
    │   │       ├── test_updatetest-update-entity-state-hacs-test-org-appdaemon-basic.json
    │   │       ├── test_updatetest-update-entity-state-hacs-test-org-integration-basic.json
    │   │       ├── test_updatetest-update-entity-state-hacs-test-org-plugin-basic.json
    │   │       ├── test_updatetest-update-entity-state-hacs-test-org-python-script-basic.json
    │   │       ├── test_updatetest-update-entity-state-hacs-test-org-template-basic.json
    │   │       ├── test_updatetest-update-entity-state-hacs-test-org-theme-basic.json
    │   │       ├── utils/
    │   │       │   ├── test_pathtest-is-safe.json
    │   │       │   ├── test_queue_managertest-queue-manager.json
    │   │       │   └── test_versiontest-version-to-download.json
    │   │       └── validate/
    │   │           ├── test_async_run_repository_checkstest-async-run-repository-checks.json
    │   │           ├── test_brands_checktest-added-to-brands.json
    │   │           ├── test_brands_checktest-local-brands-asset-content-in-root.json
    │   │           ├── test_brands_checktest-local-brands-asset-missing-falls-back-to-remote.json
    │   │           ├── test_brands_checktest-local-brands-asset-not-in-root.json
    │   │           ├── test_brands_checktest-not-added-to-brands.json
    │   │           ├── test_hacsjson_checktest-hacs-manifest-integration-zip-release-with-filename.json
    │   │           ├── test_hacsjson_checktest-hacs-manifest-no-manifest.json
    │   │           ├── test_hacsjson_checktest-hacs-manifest-with-invalid-manifest.json
    │   │           ├── test_hacsjson_checktest-hacs-manifest-with-missing-filename.json
    │   │           ├── test_hacsjson_checktest-hacs-manifest-with-valid-manifest.json
    │   │           ├── test_images_checktest-repository-has-images.json
    │   │           ├── test_images_checktest-repository-has-not-images.json
    │   │           ├── test_integration_manifest_checktest-hacs-manifest-with-invalid-manifest.json
    │   │           ├── test_integration_manifest_checktest-integration-manifest-with-valid-manifest.json
    │   │           ├── test_integration_manifest_checktest-integration-no-manifest.json
    │   │           ├── test_repository_archived_checktest-repository-archived.json
    │   │           ├── test_repository_archived_checktest-repository-not-archived.json
    │   │           ├── test_repository_description_checktest-repository-hacs-description.json
    │   │           ├── test_repository_description_checktest-repository-no-description.json
    │   │           ├── test_repository_information_file_checktest-has-info-file.json
    │   │           ├── test_repository_information_file_checktest-has-info-md-file.json
    │   │           ├── test_repository_information_file_checktest-has-readme-file.json
    │   │           ├── test_repository_information_file_checktest-has-readme-md-file.json
    │   │           ├── test_repository_information_file_checktest-no-info-file.json
    │   │           ├── test_repository_information_file_checktest-no-readme-file.json
    │   │           ├── test_repository_issues_checktest-repository-issues-enabled.json
    │   │           ├── test_repository_issues_checktest-repository-issues-not-enabled.json
    │   │           ├── test_repository_topics_checktest-repository-hacs-topics.json
    │   │           └── test_repository_topics_checktest-repository-no-topics.json
    │   ├── config_flow/
    │   │   ├── test_already_configured.json
    │   │   ├── test_flow_with_activation_failure.json
    │   │   ├── test_flow_with_registration_failure.json
    │   │   └── test_full_user_flow_implementation.json
    │   ├── data_client/
    │   │   └── base/
    │   │       ├── data/
    │   │       │   ├── appdaemon.json
    │   │       │   ├── integration.json
    │   │       │   ├── plugin.json
    │   │       │   ├── python_script.json
    │   │       │   ├── template.json
    │   │       │   └── theme.json
    │   │       ├── data_validate/
    │   │       │   ├── appdaemon.json
    │   │       │   ├── critical.json
    │   │       │   ├── integration.json
    │   │       │   ├── plugin.json
    │   │       │   ├── python_script.json
    │   │       │   ├── removed.json
    │   │       │   ├── template.json
    │   │       │   └── theme.json
    │   │       └── repositories/
    │   │           ├── appdaemon.json
    │   │           ├── integration.json
    │   │           ├── plugin.json
    │   │           ├── python_script.json
    │   │           ├── template.json
    │   │           └── theme.json
    │   ├── diagnostics/
    │   │   ├── base.json
    │   │   └── exception.json
    │   ├── hacs-test-org/
    │   │   ├── appdaemon-basic/
    │   │   │   ├── test_discard_invalid_repo_data.json
    │   │   │   ├── test_download_repository.json
    │   │   │   ├── test_get_reposiotry_releases.json
    │   │   │   ├── test_remove_repository_post.json
    │   │   │   ├── test_remove_repository_pre.json
    │   │   │   ├── test_switch/
    │   │   │   │   └── entity_states.json
    │   │   │   ├── test_update_entity_state.json
    │   │   │   ├── test_update_repository_entity.json
    │   │   │   └── test_update_repository_websocket.json
    │   │   ├── integration-basic/
    │   │   │   ├── get_documentation/
    │   │   │   │   ├── installed_false_last_version_2_0_0.md
    │   │   │   │   ├── installed_false_last_version_99_99_99.md
    │   │   │   │   ├── installed_true_installed_version_1_0_0.md
    │   │   │   │   └── installed_true_installed_version_1_0_0_last_version_2_0_0.md
    │   │   │   ├── test_discard_invalid_repo_data.json
    │   │   │   ├── test_download_repository.json
    │   │   │   ├── test_get_reposiotry_releases.json
    │   │   │   ├── test_remove_repository_post.json
    │   │   │   ├── test_remove_repository_pre.json
    │   │   │   ├── test_switch/
    │   │   │   │   └── entity_states.json
    │   │   │   ├── test_update_entity_state.json
    │   │   │   ├── test_update_repository_entity.json
    │   │   │   └── test_update_repository_websocket.json
    │   │   ├── integration-basic-custom/
    │   │   │   └── test_register_repository.json
    │   │   ├── plugin-basic/
    │   │   │   ├── test_discard_invalid_repo_data.json
    │   │   │   ├── test_download_repository.json
    │   │   │   ├── test_get_reposiotry_releases.json
    │   │   │   ├── test_remove_repository_post.json
    │   │   │   ├── test_remove_repository_pre.json
    │   │   │   ├── test_switch/
    │   │   │   │   └── entity_states.json
    │   │   │   ├── test_update_entity_state.json
    │   │   │   ├── test_update_repository_entity.json
    │   │   │   └── test_update_repository_websocket.json
    │   │   ├── plugin-custom-dist/
    │   │   │   └── test_register_repository.json
    │   │   ├── python_script-basic/
    │   │   │   ├── test_discard_invalid_repo_data.json
    │   │   │   ├── test_download_repository.json
    │   │   │   ├── test_get_reposiotry_releases.json
    │   │   │   ├── test_remove_repository_post.json
    │   │   │   ├── test_remove_repository_pre.json
    │   │   │   ├── test_switch/
    │   │   │   │   └── entity_states.json
    │   │   │   ├── test_update_entity_state.json
    │   │   │   ├── test_update_repository_entity.json
    │   │   │   └── test_update_repository_websocket.json
    │   │   ├── template-basic/
    │   │   │   ├── test_discard_invalid_repo_data.json
    │   │   │   ├── test_download_repository.json
    │   │   │   ├── test_get_reposiotry_releases.json
    │   │   │   ├── test_remove_repository_post.json
    │   │   │   ├── test_remove_repository_pre.json
    │   │   │   ├── test_switch/
    │   │   │   │   └── entity_states.json
    │   │   │   ├── test_update_entity_state.json
    │   │   │   ├── test_update_repository_entity.json
    │   │   │   └── test_update_repository_websocket.json
    │   │   └── theme-basic/
    │   │       ├── test_discard_invalid_repo_data.json
    │   │       ├── test_download_repository.json
    │   │       ├── test_get_reposiotry_releases.json
    │   │       ├── test_remove_repository_post.json
    │   │       ├── test_remove_repository_pre.json
    │   │       ├── test_switch/
    │   │       │   └── entity_states.json
    │   │       ├── test_update_entity_state.json
    │   │       ├── test_update_repository_entity.json
    │   │       └── test_update_repository_websocket.json
    │   ├── scripts/
    │   │   └── data/
    │   │       ├── generate_category_data/
    │   │       │   ├── appdaemon/
    │   │       │   │   ├── data.json
    │   │       │   │   ├── repositories.json
    │   │       │   │   └── summary.json
    │   │       │   ├── integration/
    │   │       │   │   ├── data.json
    │   │       │   │   ├── repositories.json
    │   │       │   │   └── summary.json
    │   │       │   ├── plugin/
    │   │       │   │   ├── data.json
    │   │       │   │   ├── repositories.json
    │   │       │   │   └── summary.json
    │   │       │   ├── python_script/
    │   │       │   │   ├── data.json
    │   │       │   │   ├── repositories.json
    │   │       │   │   └── summary.json
    │   │       │   ├── single/
    │   │       │   │   ├── appdaemon/
    │   │       │   │   │   └── hacs-test-org/
    │   │       │   │   │       └── appdaemon-basic/
    │   │       │   │   │           ├── data.json
    │   │       │   │   │           ├── repositories.json
    │   │       │   │   │           └── summary.json
    │   │       │   │   ├── integration/
    │   │       │   │   │   └── hacs-test-org/
    │   │       │   │   │       └── integration-basic/
    │   │       │   │   │           ├── data.json
    │   │       │   │   │           ├── repositories.json
    │   │       │   │   │           └── summary.json
    │   │       │   │   ├── plugin/
    │   │       │   │   │   └── hacs-test-org/
    │   │       │   │   │       └── plugin-basic/
    │   │       │   │   │           ├── data.json
    │   │       │   │   │           ├── repositories.json
    │   │       │   │   │           └── summary.json
    │   │       │   │   ├── python_script/
    │   │       │   │   │   └── hacs-test-org/
    │   │       │   │   │       └── python_script-basic/
    │   │       │   │   │           ├── data.json
    │   │       │   │   │           ├── repositories.json
    │   │       │   │   │           └── summary.json
    │   │       │   │   ├── template/
    │   │       │   │   │   └── hacs-test-org/
    │   │       │   │   │       └── template-basic/
    │   │       │   │   │           ├── data.json
    │   │       │   │   │           ├── repositories.json
    │   │       │   │   │           └── summary.json
    │   │       │   │   └── theme/
    │   │       │   │       └── hacs-test-org/
    │   │       │   │           └── theme-basic/
    │   │       │   │               ├── data.json
    │   │       │   │               ├── repositories.json
    │   │       │   │               └── summary.json
    │   │       │   ├── template/
    │   │       │   │   ├── data.json
    │   │       │   │   ├── repositories.json
    │   │       │   │   └── summary.json
    │   │       │   └── theme/
    │   │       │       ├── data.json
    │   │       │       ├── repositories.json
    │   │       │       └── summary.json
    │   │       ├── test_generate_category_data_error_status_release/
    │   │       │   └── integration/
    │   │       │       ├── 304.json
    │   │       │       └── 404.json
    │   │       ├── test_generate_category_data_errors_release/
    │   │       │   └── integration/
    │   │       │       ├── CancelledError.json
    │   │       │       ├── TimeoutError.json
    │   │       │       └── error2.json
    │   │       ├── test_generate_category_data_with_30plus_prereleases/
    │   │       │   └── integration.json
    │   │       └── test_generate_category_data_with_prior_content/
    │   │           ├── appdaemon.json
    │   │           ├── integration.json
    │   │           ├── plugin.json
    │   │           ├── python_script.json
    │   │           ├── template.json
    │   │           └── theme.json
    │   ├── system_health/
    │   │   ├── system_health.json
    │   │   └── system_health_after_unload.json
    │   ├── test_integration_setup.json
    │   └── test_integration_setup_with_custom_updater.json
    ├── test_config_flow.py
    ├── test_data_client.py
    ├── test_diagnostics.py
    ├── test_emuns.py
    ├── test_sensor_cleanup.py
    ├── test_switch.py
    ├── test_system_health.py
    ├── test_update.py
    ├── utils/
    │   ├── test_decorator.py
    │   ├── test_fs_util.py
    │   ├── test_path.py
    │   ├── test_queue_manager.py
    │   ├── test_store.py
    │   ├── test_url.py
    │   ├── test_validate.py
    │   ├── test_version.py
    │   └── test_workarounds.py
    └── validate/
        ├── test_async_run_repository_checks.py
        ├── test_brands_check.py
        ├── test_hacsjson_check.py
        ├── test_images_check.py
        ├── test_integration_manifest_check.py
        ├── test_repository_archived_check.py
        ├── test_repository_description_check.py
        ├── test_repository_information_file_check.py
        ├── test_repository_issues_check.py
        └── test_repository_topics_check.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .codeclimate.yml
================================================
---
engines:
  duplication:
    enabled: true
    config:
      languages:
        - python
  fixme:
    enabled: true
  radon:
    enabled: true
ratings:
  paths:
    - "**.py"
exclude_paths:
  - tests/
  - action/
  - scripts/

================================================
FILE: .codecov.yml
================================================
comment: false
codecov:
  branch: main

coverage:
  precision: 2
  round: down
  range: "60...100"

  status:
    patch: off
    project:
      default:
        target: 50%
      validate:
        target: 100%
        paths:
          - custom_components/hacs/validate/
      repositories:
        target: 50%
        paths:
          - custom_components/hacs/repositories/

parsers:
  gcov:
    branch_detection:
      conditional: yes
      loop: yes
      method: no
      macro: no

ignore:
  - "tests"


================================================
FILE: .coveragerc
================================================
[run]
source = custom_components

omit =
    # omit tests
    tests/*

    # omit scripts
    scripts/update/*

[report]
exclude_lines =
    if TYPE_CHECKING:

================================================
FILE: .devcontainer.json
================================================
{
  "name": "hacs/integration",
  "image": "mcr.microsoft.com/devcontainers/python:1-3.13",
  "postCreateCommand": "scripts/setup",
  "forwardPorts": [
    8123
  ],
  "portsAttributes": {
    "8123": {
      "label": "Home Assistant"
    },
    "0-8122": {
      "label": "Auto-Forwarded - Other",
      "onAutoForward": "ignore"
    },
    "8124-999999": {
      "label": "Auto-Forwarded - Other",
      "onAutoForward": "ignore"
    }
  },
  "customizations": {
    "extensions": [
      "charliermarsh.ruff",
      "ms-python.python",
      "github.vscode-pull-request-github",
      "ryanluker.vscode-coverage-gutters",
      "ms-python.vscode-pylance",
      "GitHub.copilot"
    ],
    "vscode": {
      "settings": {
        "python.pythonPath": "/usr/local/bin/python",
        "python.formatting.provider": "ruff",
        "editor.formatOnPaste": false,
        "editor.formatOnSave": true,
        "editor.formatOnType": true,
        "editor.defaultFormatter": "charliermarsh.ruff",
        "editor.rulers": [
          100
        ],
        "editor.codeActionsOnSave": {
          "source.fixAll": "always",
          "source.organizeImports": "always"
        },
        "files.trimTrailingWhitespace": true
      },
      "extensions": [
        "GitHub.copilot",
        "github.vscode-pull-request-github",
        "ms-python.python",
        "ms-python.vscode-pylance",
        "ms-vscode.makefile-tools",
        "ryanluker.vscode-coverage-gutters"
      ]
    }
  },
  "remoteUser": "vscode",
  "features": {
    "ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {},
    "ghcr.io/devcontainers/features/github-cli:1": {},
    "ghcr.io/devcontainers/features/node:1": {},
    "ghcr.io/devcontainers/features/rust:1": {}
  }
}

================================================
FILE: .dockerignore
================================================
*
!custom_components/hacs
!scripts
!action
!constraints.txt
!requirements_action.txt


================================================
FILE: .gitattributes
================================================
text eol=lf

================================================
FILE: .github/ISSUE_TEMPLATE/a_integration.yml
================================================
---
name: "Backend/Integration"
description: You use this when something is not doing what it's supposed to do.
labels: "issue:backend"
body:
- type: markdown
  attributes:
    value: |
      Learn how to submit an issue here https://hacs.xyz/docs/help/issues/
      Before you open a new issue, search through the existing issues to see if others have had the same problem.

      The issue template is not a suggestion, fill out everything that is asked.
- type: textarea
  attributes:
    label: "System Health details"
    description: "Paste the data from the System Health card in Home Assistant (https://www.home-assistant.io//more-info/system-health#github-issues)"
  validations:
    required: true
- type: checkboxes
  attributes:
    label: Checklist
    options:
      - label: I'm running the newest version of HACS <https://github.com/hacs/integration/releases/latest>
        required: true
      - label: I have enabled debug logging for my installation.
        required: true
      - label: I have filled out the issue template to the best of my ability.
        required: true
      - label: I have read <https://hacs.xyz/docs/help/issues/>
        required: true
      - label:  This issue is related to the backend (integration part) of HACS.
        required: true
      - label: This issue only contains 1 issue (if you have multiple issues, open one issue for each issue).
        required: true
      - label: This is a bug and not a feature request.
        required: true
      - label: This issue is not a duplicate issue of currently [open](https://github.com/hacs/integration/issues) or issues [pending release](https://github.com/hacs/integration/issues?q=is%3Aissue+is%3Aclosed+sort%3Aupdated-desc+milestone%3Anext).
        required: true
- type: textarea
  attributes:
    label: "Describe the issue"
    description: "A clear and concise description of what the issue is."
  validations:
    required: true
- type: textarea
  attributes:
    label: Reproduction steps
    description: "Without steps to reproduce, it will be hard to fix, it is very important that you fill out this part, issues without it will be closed"
    value: |
      1.
      2.
      3.
      ...
  validations:
    required: true
- type: textarea
  attributes:
    label: "Debug logs"
    description: "To enable debug logs check this https://hacs.xyz/docs/use/troubleshooting/logs/, this **needs** to include _everything_ from startup of Home Assistant to the point where you encounter the issue."
    render: text
  validations:
    required: true
- type: textarea
  attributes:
    label: "Diagnostics dump"
    description: "Drag or paste the diagnostics dump file here. (see https://hacs.xyz/docs/use/troubleshooting/diagnostics/ for info)"
  validations:
    required: true


================================================
FILE: .github/ISSUE_TEMPLATE/b_frontend.yml
================================================
---
name: "Frontend"
description: You use this when elements in the UI are not working correctly.
labels: "issue:frontend"
body:
- type: markdown
  attributes:
    value: |
      Learn how to submit an issue here https://hacs.xyz/docs/help/issues/
      Before you open a new issue, search through the existing issues to see if others have had the same problem.

      The issue template is not a suggestion, fill out everything that is asked.
- type: markdown
  attributes:
    value: "## Installation details"
- type: input
  attributes:
    label: Web browser
    description: The type of browser you are using
  validations:
    required: true
- type: input
  attributes:
    label: Web browser version
    description: The version of the browser you are using
  validations:
    required: true
- type: textarea
  attributes:
    label: "System Health details"
    description: "Paste the data from the System Health card in Home Assistant (https://www.home-assistant.io//more-info/system-health#github-issues)"
  validations:
    required: true
- type: checkboxes
  attributes:
    label: Checklist
    options:
      - label: I'm running the newest version of HACS <https://github.com/hacs/integration/releases/latest>
        required: true
      - label: I have filled out the issue template to the best of my ability.
        required: true
      - label: I have read <https://hacs.xyz/docs/help/issues/>
        required: true
      - label:  This issue is related to the frontend of HACS.
        required: true
      - label: This issue only contains 1 issue (if you have multiple issues, open one issue for each issue).
        required: true
      - label: This is a bug and not a feature request.
        required: true
      - label: This issue is not a duplicate issue of currently [open](https://github.com/hacs/integration/issues) or issues [pending release](https://github.com/hacs/integration/issues?q=is%3Aissue+is%3Aclosed+sort%3Aupdated-desc+milestone%3Anext).
        required: true
- type: textarea
  attributes:
    label: Describe the issue
    placeholder: "A clear and concise description of what the issue is."
  validations:
    required: true
- type: textarea
  attributes:
    label: Reproduction steps
    description: "Without steps to reproduce, it will be hard to fix, it is very important that you fill out this part, issues without it will be closed"
    value: |
      1.
      2.
      3.
      ...
  validations:
    required: true
- type: textarea
  attributes:
    label: Screenshots
    placeholder: "Here you paste screenshots to showcase the issue."
  validations:
    required: true
- type: textarea
  attributes:
    label: "Javascript logs from your browser console"
    render: text
  validations:
    required: true
- type: textarea
  attributes:
    label: "Debug logs"
    description: "To enable debug logs check this https://hacs.xyz/docs/use/troubleshooting/logs/, this **needs** to include _everything_ from startup of Home Assistant to the point where you encounter the issue."
    render: text
  validations:
    required: true
- type: textarea
  attributes:
    label: "Diagnostics dump"
    description: "Drag or paste the diagnostics dump file here. (see https://hacs.xyz/docs/use/troubleshooting/diagnostics/ for info)"
  validations:
    required: true


================================================
FILE: .github/ISSUE_TEMPLATE/c_bot.yml
================================================
---
name: "hacs-bot"
description: You use this when hacs-bot did something wrong.
labels: "issue:bot"
body:
- type: markdown
  attributes:
    value: |
      Learn how to submit an issue here https://hacs.xyz/docs/help/issues/
      Before you open a new issue, search through the existing issues to see if others have had the same problem.

      The issue template is not a suggestion, fill out everything that is asked.
- type: textarea
  attributes:
    label: Describe the issue
    placeholder: "A clear and concise description of what the issue is."
  validations:
    required: true
- type: checkboxes
  attributes:
    label: Checklist
    options:
      - label: I have filled out the issue template to the best of my ability.
        required: true
      - label: I have read <https://hacs.xyz/docs/help/issues/>
        required: true
      - label:  This issue is related to the HACS bot.
        required: true
      - label: This issue only contains 1 issue (if you have multiple issues, open one issue for each issue).
        required: true
      - label: This is a bug and not a feature request.
        required: true
      - label: This issue is not a duplicate issue of currently [open](https://github.com/hacs/integration/issues) or issues [pending release](https://github.com/hacs/integration/issues?q=is%3Aissue+is%3Aclosed+sort%3Aupdated-desc+milestone%3Anext).
        required: true

================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: HACS Looks different
    url: https://experimental.hacs.xyz/docs/use/dashboard/
    about: HACS does not look like others/HACS not showing menu/HACS does not look like guides/youtube Im following
  - name: Closed issues for next release
    url: https://github.com/hacs/integration/issues?q=is%3Aissue+is%3Aclosed+sort%3Aupdated-desc+milestone%3Anext
    about: Se closed issues that will be a part of the next release
  - name: How to file an issue
    url: https://hacs.xyz/docs/issues
    about: Describes what an issue should contain
  - name: HACS Documentation
    url: https://hacs.xyz/
    about: The documentation for HACS
  - name: HACS Discord server
    url: https://discord.gg/apgchf8
    about: For questions about HACS


================================================
FILE: .github/ISSUE_TEMPLATE/d_documentation.yml
================================================
---
name: "Documentation"
description: You use this when something is wrong with the documentation.
labels: "issue:documentation"
body:
- type: markdown
  attributes:
    value: |
      Learn how to submit an issue here https://hacs.xyz/docs/help/issues/
      Before you open a new issue, search through the existing issues to see if others have had the same problem.

      The issue template is not a suggestion, fill out everything that is asked.
- type: textarea
  attributes:
    label: Describe the issue
    placeholder: "A clear and concise description of what the issue is."
  validations:
    required: true
- type: textarea
  attributes:
    label: Screenshots
    placeholder: "Here you paste screenshots to showcase the issue."


================================================
FILE: .github/ISSUE_TEMPLATE/e_action.yml
================================================
---
name: "HACS Action"
description: You use this when there is an issue with the HACS action.
labels: "issue:action"
body:
- type: markdown
  attributes:
    value: |
      Learn how to submit an issue here https://hacs.xyz/docs/help/issues/
      Before you open a new issue, search through the existing issues to see if others have had the same problem.

      The issue template is not a suggestion, fill out everything that is asked.
- type: textarea
  attributes:
    label: Describe the issue
    placeholder: "A clear and concise description of what the issue is."
  validations:
    required: true
- type: input
  attributes:
    label: Link to action run
  validations:
    required: true
- type: input
  attributes:
    label: Link to action configuration
  validations:
    required: true
- type: checkboxes
  attributes:
    label: Checklist
    options:
      - label: I have filled out the issue template to the best of my ability.
        required: true
      - label: I have read <https://hacs.xyz/docs/help/issues/>
        required: true
      - label:  This issue is related to the HACS action.
        required: true
      - label: This is a bug and not a feature request.
        required: true
      - label: This issue only contains 1 issue (if you have multiple issues, open one issue for each issue).
        required: true

================================================
FILE: .github/ISSUE_TEMPLATE/f_addon.yml
================================================
---
name: "HACS Add-ons"
description: You use this when there is an issue with one of the HACS Add-ons
labels: "issue:addon"
body:
- type: markdown
  attributes:
    value: |
      Learn how to submit an issue here https://hacs.xyz/docs/help/issues/
      Before you open a new issue, search through the existing issues to see if others have had the same problem.

      The issue template is not a suggestion, fill out everything that is asked.
- type: checkboxes
  attributes:
    label: Checklist
    options:
      - label: I have filled out the issue template to the best of my ability.
        required: true
      - label: I have read <https://hacs.xyz/docs/help/issues/>
        required: true
      - label:  This issue is related to one the HACS add-ons.
        required: true
      - label: This is a bug and not a feature request.
        required: true
      - label: This issue only contains 1 issue (if you have multiple issues, open one issue for each issue).
        required: true
- type: dropdown
  validations:
    required: true
  attributes:
    label: Which Add-on are you reporting an issue for?
    options:
      - get
- type: textarea
  attributes:
    label: Describe the issue
    placeholder: "A clear and concise description of what the issue is."
  validations:
    required: true

- type: textarea
  attributes:
    label: Add-on logs
  validations:
    required: true

- type: textarea
  attributes:
    label: Supervisor logs
  validations:
    required: true

- type: textarea
  attributes:
    label: "Diagnostics dump"
    description: "Drag or paste the diagnostics dump file here. (see https://hacs.xyz/docs/use/troubleshooting/diagnostics/ for info)"
  validations:
    required: true


================================================
FILE: .github/ISSUE_TEMPLATE/removal.yml
================================================
---
name: Request for repository removal
description: Flagging of repository that should be removed from HACS
labels: flag
body:
- type: markdown
  attributes:
    value: |
      Learn how to submit an issue here https://hacs.xyz/docs/help/issues/
      Before you open a new issue, search through the existing issues to see if others have had the same problem.

      The issue template is not a suggestion, fill out everything that is asked.
- type: input
  id: repo
  attributes:
    label: Repository
    description: The repository that is requested to be removed (owner/repository)
  validations:
    required: true
- type: checkboxes
  attributes:
    label: Checklist
    options:
      - label: I understand that this form should only be used for repositories that needs to be removed from HACS
        required: true
      - label: I understand that a bug is not reason enough to have a repository removed
        required: true
      - label: The repository is currently shipped as a default repository in HACS
        required: true
      - label: I have tried to get the authors attention to the reason for removal
        required: true
- type: textarea
  attributes:
    label: Why should this be removed?
    placeholder: "Describe why the repository should be removed from HACS. If you are flagging a repository for removal without a good reason/description, the request will be closed"
  validations:
    required: true
- type: input
  attributes:
    label: Link to issue
    description: The URL to the issue that shows an attempt to contact the author has been made
  validations:
    required: true


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "devcontainers"
    directory: "/"
    labels:
      - "pr: dependency-update"
    schedule:
      interval: weekly
      time: "06:00"
    open-pull-requests-limit: 10
  - package-ecosystem: "github-actions"
    directory: "/"
    labels:
      - "pr: dependency-update"
    schedule:
      interval: weekly
      time: "06:00"
    open-pull-requests-limit: 10
  - package-ecosystem: pip
    directory: "/"
    labels:
      - "pr: dependency-update"
    schedule:
      interval: weekly
      time: "06:00"
    open-pull-requests-limit: 10


================================================
FILE: .github/pre-commit-config.yaml
================================================
repos:
  - repo: local
    hooks:
      - id: codespell
        name: Check code for common misspellings
        language: system
        types: [text]
        stages: [pre-commit, pre-push, manual]
        entry: codespell
        args:
          - --quiet-level=2
          - --ignore-words-list=hass,ba,fo
          - --skip=tests/fixtures/*

      - id: isort
        name: Sort imports
        language: system
        types: [text]
        stages: [pre-commit, pre-push, manual]
        entry: isort

      - id: pyupgrade
        name: Run pyupgrade
        language: system
        types: [text]
        stages: [pre-commit, pre-push, manual]
        entry: pyupgrade
        files: ^.*.py$
        args:
          - "--py39-plus"

      - id: ruff-check
        name: Run ruff check
        language: system
        types: [text]
        stages: [pre-commit, pre-push, manual]
        entry: ruff
        args:
          - check
        files: ^((action|custom_components|script|tests)/.+)?[^/]+\.py$

      - id: ruff-format
        name: Run ruff format
        language: system
        types: [text]
        stages: [pre-commit, pre-push, manual]
        entry: ruff
        args:
          - format
        files: ^((action|custom_components|script)/.+)?[^/]+\.py$

      - id: check-executables-have-shebangs
        name: Check that executables have shebangs
        language: system
        types: [text, executable]
        entry: check-executables-have-shebangs
        stages: [pre-commit, pre-push, manual]

      - id: check-json
        name: Check JSON files
        language: system
        types: [json]
        stages: [pre-commit, pre-push, manual]
        entry: check-json

      - id: requirements-txt-fixer
        name: Check requirements files
        language: system
        types: [text]
        stages: [pre-commit, pre-push, manual]
        entry: requirements-txt-fixer
        files: ^requirements_.*.txt$

      - id: check-ast
        name: Check Python AST
        language: system
        types: [python]
        stages: [pre-commit, pre-push, manual]
        entry: check-ast

      - id: mixed-line-ending
        name: Check line nedings
        language: system
        types: [text]
        stages: [pre-commit, pre-push, manual]
        entry: mixed-line-ending
        args:
          - --fix=lf


================================================
FILE: .github/release.yml
================================================
changelog:
  categories:
    - title: '💥 Breaking changes'
      labels:
        - 'Breaking Change'

    - title: '🛎️ Experimental'
      labels:
        - 'Experimental'

    - title: '✨ New features'
      labels:
        - 'pr: new-feature'

    - title: '⚡ Enhancements'
      labels:
        - 'pr: enhancement'

    - title: '♻️ Refactor'
      labels:
        - 'pr: refactor'

    - title: '🐛 Bug fixes'
      labels:
        - 'pr: bugfix'

    - title: '🎨 Frontend updates'
      labels:
        - 'pr: frontend-update'


================================================
FILE: .github/workflows/action-container.yml
================================================
name: "Build the action container"

on:
  release:
    types:
      - published
  push:
    branches:
      - main
    paths:
      - '.github/workflows/action-container.yml'
      - 'custom_components/**'
      - 'action/**'
  pull_request:
    branches:
      - main
    paths:
      - '.github/workflows/action-container.yml'
      - 'custom_components/**'
      - 'action/**'

concurrency:
  group: container-build-${{ github.ref }}
  cancel-in-progress: true

permissions: {}

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    permissions:
      packages: write
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Login to GitHub Container Registry
        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
        if: ${{ github.event_name != 'pull_request' }}
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Docker metadata
        id: meta
        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6.0.0
        with:
          images: |
            ghcr.io/${{ github.repository_owner }}/action
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=sha

      - name: Build and push
        uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
        with:
          context: .
          file: action/Dockerfile
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}


================================================
FILE: .github/workflows/generate-hacs-data.yml
================================================
name: Generate HACS Data

on:
  workflow_dispatch:
    inputs:
      forceRepositoryUpdate:
        description: 'Force repository update'
        required: false
        default: 'False'
        type: choice
        options:
          - "False"
          - "True"
      category:
        description: 'Select a category'
        required: false
        type: choice
        options:
          - None
          - appdaemon
          - integration
          - plugin
          - python_script
          - template
          - theme
  schedule:
    - cron: "0 */2 * * *"

concurrency:
  group: category-data

permissions: {}

jobs:
  generate-matrix:
    name: Generate matrix
    runs-on: ubuntu-latest
    if: github.repository == 'hacs/integration'
    outputs:
      categories: ${{ steps.set-matrix.outputs.categories }}
    steps:
      - id: set-matrix
        run: |
          if [[ "${{ github.event_name }}" == "workflow_dispatch"  ]] && [[ "${{ inputs.category }}" != "None"  ]] && [[ "${{ inputs.category }}" != ""  ]]; then
            echo "categories=['${{ inputs.category }}']" >> $GITHUB_OUTPUT
          else
            echo "categories=['appdaemon','integration','plugin','python_script','template','theme']" >> $GITHUB_OUTPUT
          fi

  category-data:
    runs-on: ubuntu-latest
    needs: generate-matrix
    if: github.repository == 'hacs/integration'
    name: Generate ${{ matrix.category }} data
    strategy:
      fail-fast: false
      matrix:
        category: ${{ fromJSON( needs.generate-matrix.outputs.categories )}}
    steps:
      - name: Checkout the repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        id: python
        with:
          python-version: "3.13"
          cache: 'pip'
          cache-dependency-path: |
            requirements_base.txt
            requirements_generate_data.txt

      - name: Install dependencies
        run:  |
          scripts/install/frontend
          scripts/install/pip_packages --requirement requirements_generate_data.txt

      - name: Generate ${{ matrix.category }} data
        run: python3 -m scripts.data.generate_category_data ${{ matrix.category }}
        env:
          DATA_GENERATOR_TOKEN: ${{ secrets.DATA_GENERATOR_TOKEN }}
          FORCE_REPOSITORY_UPDATE: ${{ inputs.forceRepositoryUpdate }}

      - name: Validate output with JQ
        run: |
          jq -c . outputdata/${{ matrix.category }}/data.json
          jq -c . outputdata/${{ matrix.category }}/repositories.json

      - name: Validate output with schema
        run: |
          python3 -m scripts.data.validate_category_data ${{ matrix.category }} outputdata/${{ matrix.category }}/data.json

      - name: Generate diff
        run: |
          diff -U 8 outputdata/diff/${{ matrix.category }}_before.json outputdata/diff/${{ matrix.category }}_after.json > outputdata/diff/${{ matrix.category }}.diff || true
          cat outputdata/diff/${{ matrix.category }}.diff

      - name: Upload diff
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
        env:
          CATEGORY: ${{ matrix.category }}
        with:
          script: |
            const fs = require('fs');
            const diffContents = fs.readFileSync(`outputdata/diff/${process.env.CATEGORY}.diff`);

            core.summary.addDetails(`${process.env.CATEGORY}.diff contents`, `\n\n\`\`\`diff\n${diffContents}\`\`\`\n\n`)
            core.summary.write()

      - name: Upload artifacts
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: ${{ matrix.category }}
          path: |
            outputdata/${{ matrix.category }}
            outputdata/summary.json
            outputdata/diff
          if-no-files-found: error
          retention-days: 7

  summarize:
    name: Summarize
    runs-on: ubuntu-latest
    needs: category-data
    if: github.repository == 'hacs/integration'
    outputs:
      changedCategories: ${{ steps.combined.outputs.changedCategories }}
      environments: ${{ steps.combined.outputs.environments }}
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          path: outputdata

      - name: Generate combined summary
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
        id: combined
        env:
          HACS_CHANGED_PCT_TARGET: ${{ vars.HACS_CHANGED_PCT_TARGET }}
          HACS_DIFF_TARGET: ${{ vars.HACS_DIFF_TARGET }}
        with:
          script: |
            const fs = require('fs');
            const summaries = {};
            const changedCategories = [];
            const environments = {};
            const diffTarget = Number(process.env.HACS_DIFF_TARGET || 1)

            core.info(`[global] diffTarget: ${diffTarget}`);

            const subDirectories = fs.readdirSync("outputdata", { withFileTypes: true })
                .filter(entry => entry.isDirectory())
                .map(entry => entry.name)

            for (const directory of subDirectories) {
              let changedPctTarget = Number(process.env.HACS_CHANGED_PCT_TARGET)
              const parsed = JSON.parse(fs.readFileSync(`outputdata/${directory}/summary.json`))

              if (!changedPctTarget) {
                if (!parsed.new_count || parsed.new_count <= 0) {
                  changedPctTarget = 0;
                } else if (parsed.new_count > 750) {
                    changedPctTarget = 7;
                } else if (parsed.new_count > 500) {
                    changedPctTarget = 8;
                } else if (parsed.new_count > 250) {
                    changedPctTarget = 9;
                } else if (parsed.new_count > 100) {
                    changedPctTarget = 10;
                } else if (parsed.new_count > 75) {
                    changedPctTarget = 15;
                } else if (parsed.new_count > 50) {
                    changedPctTarget = 20;
                } else if (parsed.new_count > 25) {
                    changedPctTarget = 25;
                } else if (parsed.new_count > 10) {
                    changedPctTarget = 28;
                } else {
                    changedPctTarget = 50;
                }
              }

              core.info(`[${directory}] changedPctTarget: ${changedPctTarget}`);

              if (parsed.changed >= 1 || parsed.diff >= 1) {
                changedCategories.push(directory)
              }

              if (parsed.changed_pct >= changedPctTarget) {
                core.warning(`${directory} changed ${parsed.changed_pct}%!`)
                environments[directory] = `publish-${directory}-verify`;
              }

              if (parsed.diff >= diffTarget) {
                core.warning(`${directory} changed ${parsed.diff}!`)
                environments[directory] = `publish-${directory}-verify`;
              }

              summaries[directory] = JSON.parse(fs.readFileSync(`outputdata/${directory}/summary.json`));
            }

            core.summary.addCodeBlock(JSON.stringify({summaries, environments, changedCategories}, null, 4), "json")
            core.summary.write()
            core.setOutput("changedCategories", JSON.stringify(changedCategories))
            core.setOutput("environments", environments)

      - name: Send notification
        if: ${{ steps.combined.outputs.environments != '{}' }}
        run: |
          curl \
            -H "Content-Type: application/json" \
            -d '{"username": "GitHub action", "content": "[Attention needed!](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}})"}' \
            ${{ secrets.DISCORD_WEBHOOK_ACTION_FAILURE }}

  publish:
    runs-on: ubuntu-latest
    needs: summarize
    if: github.repository == 'hacs/integration'
    name: Publish ${{ matrix.category }} data
    environment: ${{ fromJSON(needs.summarize.outputs.environments)[matrix.category] }}
    strategy:
      fail-fast: false
      matrix:
        category: ${{ fromJSON(needs.summarize.outputs.changedCategories) }}
    steps:
      - name: Checkout the repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        id: python
        with:
          python-version: "3.13"
          cache: 'pip'
          cache-dependency-path: |
            requirements_base.txt
            requirements_generate_data.txt

      - name: Install dependencies
        run:  |
          scripts/install/frontend
          scripts/install/pip_packages --requirement requirements_generate_data.txt

      - name: Download artifacts
        uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
        with:
          name: ${{ matrix.category }}
          path: outputdata

      - name: Validate output with JQ
        run: |
          jq -c . outputdata/${{ matrix.category }}/data.json
          jq -c . outputdata/${{ matrix.category }}/repositories.json

      - name: Validate output with schema
        run: |
          python3 -m scripts.data.validate_category_data ${{ matrix.category }} outputdata/${{ matrix.category }}/data.json

      - name: Upload to R2
        run: |
          aws s3 sync \
            outputdata/${{ matrix.category }} \
            s3://data-v2/${{ matrix.category }} \
            --endpoint-url ${{ secrets.CF_R2_ENDPOINT_DATA }}
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_SECRET_ACCESS_KEY }}

      - name: Bust Cloudflare cache
        run: |
          curl --silent --show-error --fail -X POST \
            "https://api.cloudflare.com/client/v4/zones/${{ secrets.CF_ZONE_ID }}/purge_cache" \
            -H "Authorization: Bearer ${{ secrets.CF_BUST_CACHE_TOKEN }}" \
            -H "Content-Type: application/json" \
            --data '{"files": ["https:\/\/data-v2.hacs.xyz\/${{ matrix.category }}\/data.json", "https:\/\/data-v2.hacs.xyz\/${{ matrix.category }}\/repositories.json"]}'

  notify_on_failure:
    runs-on: ubuntu-latest
    name: Trigger Discord notification when jobs fail
    needs: ["generate-matrix", "category-data", "summarize", "publish"]
    steps:
      - name: Send notification
        if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.event_name == 'schedule' }}
        run: |
          curl \
            -H "Content-Type: application/json" \
            -d '{"username": "GitHub action failure", "content": "[Scheduled action failed!](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}})"}' \
            ${{ secrets.DISCORD_WEBHOOK_ACTION_FAILURE }}


================================================
FILE: .github/workflows/lint.yaml
================================================
name: Lint

on:
  pull_request:
    branches:
      - main
  push:
    branches:
      - main

concurrency:
  group: lint-${{ github.ref }}
  cancel-in-progress: true

permissions: {}

jobs:
  matrix:
    runs-on: ubuntu-latest
    name: Run ${{ matrix.check }}
    strategy:
      matrix:
        check:
          - check-ast
          - check-executables-have-shebangs
          - check-json
          - codespell
          - isort
          - mixed-line-ending
          - pyupgrade
          - requirements-txt-fixer
          - ruff-check
          - ruff-format
    steps:
      - name: Checkout the repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        id: python
        with:
          python-version: "3.13"
          cache: 'pip'
          cache-dependency-path: |
            requirements_base.txt
            requirements_lint.txt

      - name: Install dependencies
        run: |
          scripts/install/pip_packages --requirement requirements_lint.txt
          pre-commit install-hooks --config .github/pre-commit-config.yaml

      - name: Run the check (${{ matrix.check }})
        run: pre-commit run --show-diff-on-failure --hook-stage manual ${{ matrix.check }} --all-files --config .github/pre-commit-config.yaml

  lint-json:
    runs-on: ubuntu-latest
    name: With JQ
    steps:
    - name: Checkout the repository
      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

    - name: Run validation
      run: jq -r -e -c . tests/fixtures/*.json

================================================
FILE: .github/workflows/lock.yml
================================================
name: "Lock closed issues and PR's"

on:
  schedule:
    - cron: "0 * * * *"

concurrency:
  group: lock-${{ github.ref }}
  cancel-in-progress: true

permissions: {}

jobs:
  lock:
    runs-on: ubuntu-latest
    if: github.repository == 'hacs/integration'
    permissions:
      issues: write
      pull-requests: write
    steps:
      - name: 🔒 Lock closed issues and PRs
        uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0
        with:
          issue-inactive-days: "14"
          issue-lock-reason: ""
          pr-inactive-days: "1"
          pr-lock-reason: ""

================================================
FILE: .github/workflows/publish.yml
================================================
name: Publish

on:
  release:
    types:
      - published
  push:
    branches:
      - main

concurrency:
  group: publish-${{ github.ref }}
  cancel-in-progress: true

permissions: {}

jobs:
  release_zip_file:
    name: Publish HACS zip file asset
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - name: 📥 Checkout the repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: 🛠️ Set up Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: "3.13"

      - name: 🔢 Get version
        if: ${{ github.event_name == 'release' }}
        id: version
        uses: home-assistant/actions/helpers/version@dce0e860c68256ef2902ece06afa5401eb4674e1 # master

      - name: 🔢 Set version number
        if: ${{ github.event_name == 'release' }}
        run: |
          sed -i "/MINIMUM_HA_VERSION = /c\MINIMUM_HA_VERSION = \"$(jq .homeassistant -r ${{ github.workspace }}/hacs.json)\"" ${{ github.workspace }}/custom_components/hacs/const.py
          python3 ${{ github.workspace }}/scripts/update/manifest.py --version ${{ steps.version.outputs.version }}

      - name: ⏬ Download HACS frontend
        run: ${{ github.workspace }}/scripts/install/frontend

      - name: 📤 Upload zip to action
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        if: ${{ github.event_name == 'push' }}
        with:
          name: hacs
          path: ${{ github.workspace }}/custom_components/hacs
          retention-days: 7

      # Pack the HACS dir as a zip and upload to the release
      - name: 📦 ZIP HACS Dir
        if: ${{ github.event_name == 'release' }}
        run: |
          cd ${{ github.workspace }}/custom_components/hacs
          zip hacs.zip -r ./

      - name: 📤 Upload zip to release
        uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2.6.1
        if: ${{ github.event_name == 'release' }}
        with:
          files: ${{ github.workspace }}/custom_components/hacs/hacs.zip



================================================
FILE: .github/workflows/pull_requests_labels.yml
================================================
name: "Check Pull Request labels"

on:
  pull_request:
    types:
      - labeled
      - opened
      - synchronize
      - unlabeled
    branches:
      - main

permissions: {}

jobs:
  check_labels:
    name: "Check Pull Request labels"
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Check the labels
        uses: ludeeus/action-require-labels@7ef0dba93830452589680da7cdea2e2c4c0f8dff # 1.1.0
        with:
          labels: >-
              Breaking Change, Experimental, pr: new-feature,
              pr: enhancement, pr: refactor, pr: bugfix,
              pr: dependency-update, pr: action, pr: test,
              pr: repository

================================================
FILE: .github/workflows/pytest.yml
================================================
name: Test

on:
  pull_request:
    branches:
      - main
  push:
    branches:
      - main

concurrency:
  group: test-${{ github.ref }}
  cancel-in-progress: true

permissions: {}

jobs:
  legacy:
    name: With pytest with Home Assistant (min. supported version)
    runs-on: ubuntu-latest
    steps:
      - name: 📥 Checkout the repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: 🛠️ Set up Python 3.13
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: "3.13"
          cache: 'pip'
          cache-dependency-path: |
            requirements_core_min.txt
            requirements_base.txt
            requirements_test.txt

      - name: 📦 Install dependencies
        run: |
          scripts/install/pip_packages \
            --requirement requirements_core_min.txt \
            --requirement requirements_test.txt
          scripts/install/frontend

      - name: ⏲️ Set time zone
        uses: szenius/set-timezone@1f9716b0f7120e344f0c62bb7b1ee98819aefd42 # v2.0
        with:
          timezoneLinux: 'Asia/Singapore'

      - name: 🏃 Run tests
        env:
          PYTEST: true
        run: scripts/test

  dev:
    name: With pytest with Home Assistant (${{ matrix.homeassistant-version }}) & Python (${{ matrix.python-version }})
    runs-on: ubuntu-latest
    strategy:
      matrix:
        homeassistant-version:
          - "dev"
        python-version:
          - "3.14"
    steps:
      - name: 📥 Checkout the repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: 🛠️ Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: ${{ matrix.python-version }}
          cache: 'pip'
          cache-dependency-path: |
            requirements_core_min.txt
            requirements_base.txt
            requirements_test.txt

      - name: 📦 Install dependencies
        run: |
          scripts/install/pip_packages --requirement requirements_test.txt
          scripts/install/core_dev
          scripts/install/frontend

      - name: ⏲️ Set time zone
        uses: szenius/set-timezone@1f9716b0f7120e344f0c62bb7b1ee98819aefd42 # v2.0
        with:
          timezoneLinux: 'Asia/Singapore'

      - name: 🏃 Run tests
        env:
          PYTEST: true
        run: scripts/test

      - name: 📤 Upload coverage to Codecov
        if: ${{ matrix.python-version == '3.14' }}
        run: |
          scripts/coverage
          curl -sfSL https://codecov.io/bash | bash -

================================================
FILE: .github/workflows/stale.yml
================================================
name: 'Close stale issues'

on:
  workflow_dispatch:
  schedule:
    - cron: '30 10 * * *'

permissions: {}

jobs:
  issues_missing_required_information:
    runs-on: ubuntu-latest
    permissions:
      actions: write
      issues: write
      pull-requests: write
    steps:
      - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f  # v10.2.0
        with:
          days-before-stale: -1
          days-before-close: 7
          only-labels: 'Missing required issue information'
          stale-issue-label: 'Missing required issue information'


================================================
FILE: .github/workflows/validate.yml
================================================
name: Validate

on:
  pull_request:
    branches:
      - main
  push:
    branches:
      - main
  schedule:
    - cron: "0 12 * * *"

concurrency:
  cancel-in-progress: true
  group: validate-${{ github.ref }}

permissions: {}

jobs:
  preflight:
    if: ${{ github.repository == 'hacs/integration' }}
    runs-on: ubuntu-latest
    name: Preflight
    steps:
      - name: Validation preflight
        env:
          ACTOR: ${{ github.actor }}
          EVENT_NAME: ${{ github.event_name }}
          REF_NAME: ${{ github.ref_name }}
          REF: ${{ github.ref }}
          SHA: ${{ github.sha }}
        run: |
          echo "**Start:** $(date)" >> $GITHUB_STEP_SUMMARY
          echo "**Actor:** $ACTOR" >> $GITHUB_STEP_SUMMARY
          echo "**Event:** $EVENT_NAME" >> $GITHUB_STEP_SUMMARY
          echo "**Ref name:** $REF_NAME" >> $GITHUB_STEP_SUMMARY
          echo "**Ref:** $REF" >> $GITHUB_STEP_SUMMARY
          echo "**SHA:** $SHA" >> $GITHUB_STEP_SUMMARY

  validate-hassfest:
    needs:
      - "preflight"
    runs-on: ubuntu-latest
    name: With hassfest
    steps:
      - name: Checkout the repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      # Test files conflict with running hassfest
      - name: Remove tests
        run: rm -rf tests

      - name: Hassfest validation
        uses: home-assistant/actions/hassfest@dce0e860c68256ef2902ece06afa5401eb4674e1 # master

  validate-hacs:
    needs:
      - "preflight"
    runs-on: ubuntu-latest
    name: With HACS Action
    steps:
      - name: HACS validation
        uses: hacs/action@dcb30e72781db3f207d5236b861172774ab0b485 # main
        with:
          category: integration

  validate-hacs-local:
    if: ${{ github.event_name != 'schedule' }}
    needs:
      - "preflight"
    runs-on: ubuntu-latest
    name: Check ${{matrix.entry.category}} ${{matrix.entry.repository}} with HACS Action (local)
    strategy:
      matrix:
        entry:
          - repository: "hacs/integration"
            category: "integration"
          - repository: "piitaya/lovelace-mushroom"
            category: "plugin"
    steps:
      - name: Checkout the repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Build Container
        run: |
          docker build . -t hacs/action:local -f action/Dockerfile

      - name: Run Action
        run: |
          docker run --name hacs_action_local \
            --env INPUT_GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }} \
            --env INPUT_REPOSITORY=${{matrix.entry.repository}} \
            --env INPUT_CATEGORY=${{matrix.entry.category}} \
            hacs/action:local

  validata-hacs-data:
    if: ${{ github.event_name != 'schedule' }}
    needs:
      - "preflight"
    runs-on: ubuntu-latest
    name: Check ${{matrix.entry.category}} ${{matrix.entry.repository}} with HACS data generation
    strategy:
      matrix:
        entry:
          - repository: "hacs/integration"
            category: "integration"
          - repository: "piitaya/lovelace-mushroom"
            category: "plugin"
    steps:
      - name: Checkout the repository
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Set up Python
        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
        with:
          python-version: "3.13"
          cache: 'pip'
          cache-dependency-path: |
            requirements_base.txt
            requirements_generate_data.txt

      - name: Install dependencies
        run:  |
          scripts/install/frontend
          scripts/install/pip_packages --requirement requirements_generate_data.txt

      - name: Generate data
        run: |
          python3 -m scripts.data.generate_category_data \
            ${{ matrix.entry.category }} \
            ${{ matrix.entry.repository }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Generate diff
        run: |
          diff -U 8 outputdata/diff/${{ matrix.entry.category }}_before.json outputdata/diff/${{ matrix.entry.category }}_after.json > outputdata/diff/${{ matrix.entry.category }}.diff || true
          cat outputdata/diff/${{ matrix.entry.category }}.diff

      - name: Upload diff
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
        env:
          CATEGORY: ${{ matrix.entry.category }}
        with:
          script: |
            const fs = require('fs');
            const diffContents = fs.readFileSync(`outputdata/diff/${process.env.CATEGORY}.diff`);

            core.summary.addDetails(`${process.env.CATEGORY}.diff contents`, `\n\n\`\`\`diff\n${diffContents}\`\`\`\n\n`)
            core.summary.write()

      - name: Validate output with JQ
        run: |
          jq -c . outputdata/${{ matrix.entry.category }}/data.json
          jq -c . outputdata/${{ matrix.entry.category }}/repositories.json

      - name: Validate output with schema
        run: |
          python3 -m scripts.data.validate_category_data ${{  matrix.entry.category }} outputdata/${{ matrix.entry.category }}/data.json

      - name: Upload artifact
        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
        with:
          name: "${{ matrix.entry.category }}_${{ strategy.job-index }}"
          path: |
            outputdata/summary.json
            outputdata/${{ matrix.entry.category }}
            outputdata/diff
          if-no-files-found: error
          retention-days: 3


  notify_on_failure:
    runs-on: ubuntu-latest
    name: Trigger Discord notification when jobs fail
    needs: ["preflight","validate-hassfest", "validate-hacs"]
    steps:
      - name: Send notification
        if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.event_name == 'schedule' }}
        run: |
          curl \
            -H "Content-Type: application/json" \
            -d '{"username": "GitHub action failure", "content": "[Scheduled action failed!](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}})"}' \
            ${{ secrets.DISCORD_WEBHOOK_ACTION_FAILURE }}


================================================
FILE: .gitignore
================================================
# artifacts
__pycache__
.pytest*
*.egg-info
*/build/*
*/dist/*


# misc
.claude
.coverage
.python-version
.venv
.vscode
coverage.xml
htmlcov
outputdata
settings.json
venv


# Frontend are downloaded on release
custom_components/hacs/hacs_frontend

# Translation files
custom_components/hacs/translations
!custom_components/hacs/translations/en.json

# Home Assistant configuration
config


================================================
FILE: .pylintrc
================================================
[MESSAGES CONTROL]
# pylint issue with Python 3.9 https://github.com/PyCQA/pylint/issues/3882
disable=unsubscriptable-object

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 - 2023 Joakim Sørensen (@ludeeus)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# HACS (Home Assistant Community Store)

_Manage (Install, track, upgrade) and discover custom elements for Home Assistant directly from the UI._

## What?

HACS is an integration that gives the user a powerful UI to handle downloads of custom needs.

**Highlights of what HACS can do:**

- Help you discover new custom elements.
- Help you download new custom elements.
- Help you keep track of your custom elements.
  - Manage(download/update/remove)
  - Shortcuts to repositories/issue tracker

## Useful links

- [General documentation](https://hacs.xyz/)
- [Configuration](https://hacs.xyz/docs/use/configuration/basic)
- [FAQ](https://hacs.xyz/docs/faq)
- [GitHub](https://github.com/hacs)
- [Discord](https://discord.gg/apgchf8)
- [Become a GitHub sponsor? ❤️](https://github.com/sponsors/ludeeus)
- [BuyMe~~Coffee~~Beer? 🍺🙈](https://buymeacoffee.com/ludeeus)


## Issues

~~If~~ When you experience issues/bugs with this the best way to report them is to open an issue in **this** repo.

[Issue link](https://hacs.xyz/docs/help/issues)


================================================
FILE: action/Dockerfile
================================================
FROM python:3.13-alpine
WORKDIR /hacs

COPY . /hacs

ENV \
    UV_SYSTEM_PYTHON=true \
    UV_EXTRA_INDEX_URL="https://wheels.home-assistant.io/musllinux-index/"

RUN \
    apk add --no-cache --virtual .build-deps \
        bash \
    \
    && bash /hacs/scripts/install/pip_packages \
        uv==0.9.6 \
    \
    && bash /hacs/scripts/install/uv_packages \
        -r requirements_action.txt \
    \
    && bash /hacs/scripts/install/frontend \
    \
    && apk del --no-cache .build-deps > /dev/null 2>&1 \
    \
    && rm -rf /var/cache/apk/* \
    \
    && find /usr/local \( -type d -a -name test -o -name tests -o -name '__pycache__' \) -o \( -type f -a -name '*.pyc' -o -name '*.pyo' \) -exec rm -rf '{}' \; \
    \
    && mv /hacs/action/action.py /hacs/action.py \
    \
    && rm -rf /hacs/scripts /hacs/action  \
    \
    && rm /hacs/requirements_action.txt  /hacs/constraints.txt

ENTRYPOINT ["python3", "/hacs/action.py"]


================================================
FILE: action/action.py
================================================
"""Validate a GitHub repository to be used with HACS."""

from __future__ import annotations

import asyncio
import json
import logging
import os

from aiogithubapi import GitHub, GitHubAPI
import aiohttp
from homeassistant.core import HomeAssistant

from custom_components.hacs.base import HacsBase
from custom_components.hacs.const import HACS_ACTION_GITHUB_API_HEADERS
from custom_components.hacs.enums import HacsGitHubRepo
from custom_components.hacs.exceptions import HacsException
from custom_components.hacs.utils.decode import decode_content
from custom_components.hacs.utils.logger import LOGGER
from custom_components.hacs.validate.manager import ValidationManager

TOKEN = os.getenv("INPUT_GITHUB_TOKEN")
GITHUB_WORKSPACE = os.getenv("GITHUB_WORKSPACE")
GITHUB_ACTOR = os.getenv("GITHUB_ACTOR")
GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH")
GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY")
CHANGED_FILES = os.getenv("CHANGED_FILES", "")


REPOSITORY = os.getenv("REPOSITORY", os.getenv("INPUT_REPOSITORY"))
CATEGORY = os.getenv("CATEGORY", os.getenv("INPUT_CATEGORY", ""))


CATEGORIES = [
    "appdaemon",
    "integration",
    "plugin",
    "python_script",
    "template",
    "theme",
]


logging.basicConfig(
    format="::%(levelname)s:: %(message)s",
    level=logging.DEBUG,
)


def error(error: str):
    LOGGER.error(error)
    exit(1)


def output_in_group(group: str, content: str):
    print(f"::group::{group}")  # noqa: T201
    print(content)  # noqa: T201
    print("::endgroup::")  # noqa: T201


def get_event_data():
    if GITHUB_EVENT_PATH is None or not os.path.exists(GITHUB_EVENT_PATH):
        return {}
    with open(GITHUB_EVENT_PATH) as ev:
        return json.loads(ev.read())


async def choose_repository(githubapi: GitHubAPI, category: str):
    if category is None:
        return None

    response = await githubapi.repos.contents.get(HacsGitHubRepo.DEFAULT, category)
    current = json.loads(decode_content(response.data.content))

    with open(f"{GITHUB_WORKSPACE}/{category}") as cat_file:  # noqa: ASYNC230
        new = json.loads(cat_file.read())

    for repo in current:
        if repo in new:
            new.remove(repo)

    if len(new) != 1:
        error(f"{new} is not a single repository")

    return new[0]


def choose_category():
    for name in CHANGED_FILES.split(" "):
        if name in CATEGORIES:
            return name


async def preflight():
    """Preflight checks."""
    event_data = get_event_data()
    ref: str | None = None

    hacs = HacsBase()
    hacs.hass = HomeAssistant("")

    hacs.system.action = True
    hacs.configuration.token = TOKEN
    hacs.core.config_path = None

    async with aiohttp.ClientSession() as session:
        hacs.session = session
        hacs.validation = ValidationManager(hacs=hacs, hass=hacs.hass)
        hacs.githubapi = GitHubAPI(
            token=hacs.configuration.token,
            session=session,
            client_name="HACS/Action",
        )

        if REPOSITORY and CATEGORY:
            repository = REPOSITORY
            category = CATEGORY
        elif GITHUB_REPOSITORY == HacsGitHubRepo.DEFAULT:
            category = choose_category()
            repository = await choose_repository(hacs.githubapi, category)
            LOGGER.info(f"Actor: {GITHUB_ACTOR}")
        else:
            category = CATEGORY.lower()
            if event_data.get("pull_request") is not None:
                head = event_data["pull_request"]["head"]
                ref = head["ref"]
                repository = head["repo"]["full_name"]
            else:
                repository = GITHUB_REPOSITORY
                if event_data.get("ref") is not None:
                    # For push events
                    ref = event_data["ref"]

                    # For tag events
                    if ref.startswith("refs/tags/"):
                        ref = ref.split("/")[-1]

        if TOKEN is None:
            error("No GitHub token found, use env GITHUB_TOKEN to set this.")

        if repository is None:
            error("No repository found, use env REPOSITORY to set this.")

        if category is None:
            error("No category found, use env CATEGORY to set this.")

        if category not in CATEGORIES:
            error(f"Category {category} is not valid.")

        if (repository_ref := os.getenv("REPOSITORY_REF")) is not None:
            ref = repository_ref

        if ref is None and GITHUB_REPOSITORY != HacsGitHubRepo.DEFAULT:
            repo = await hacs.githubapi.repos.get(repository)
            ref = repo.data.default_branch

        LOGGER.info(f"Category: {category}")
        LOGGER.info(f"Repository: {repository}{f'@{ref}' if ref else ''}")

        await validate_repository(hacs, repository, category, ref)


async def validate_repository(hacs: HacsBase, repository: str, category: str, ref=None):
    """Validate."""
    # Legacy GitHub client
    hacs.github = GitHub(
        hacs.configuration.token,
        hacs.session,
        headers=HACS_ACTION_GITHUB_API_HEADERS,
    )

    try:
        await hacs.async_register_repository(
            repository_full_name=repository,
            category=category,
            ref=ref,
        )
    except HacsException as exception:
        LOGGER.error(exception)

    if (repo := hacs.repositories.get_by_full_name(repository)) is None:
        error(f"Repository {repository} not loaded properly in HACS.")

    output_in_group(
        "data",
        json.dumps(
            {
                "data": repo.data.to_json(),
                "manifest": repo.repository_manifest.to_dict(),
                "release": (
                    {
                        "tag": matching_release.tag_name,
                        "assets": [asset.name for asset in matching_release.assets],
                    }
                    if repo.releases.objects
                    and len(repo.releases.objects) > 0
                    and (
                        matching_release := next(
                            (
                                release
                                for release in repo.releases.objects
                                if release.tag_name == repo.data.last_version
                            ),
                            repo.releases.objects[0],
                        )
                    )
                    else None
                ),
                "category": category,
                "ref": ref,
            },
            indent=4,
        ),
    )


if __name__ == "__main__":
    asyncio.run(preflight())


================================================
FILE: constraints.txt
================================================


================================================
FILE: custom_components/hacs/__init__.py
================================================
"""HACS gives you a powerful UI to handle downloads of all your custom needs.

For more details about this integration, please refer to the documentation at
https://hacs.xyz/
"""

from __future__ import annotations

from aiogithubapi import AIOGitHubAPIException, GitHub, GitHubAPI
from aiogithubapi.const import ACCEPT_HEADERS
from awesomeversion import AwesomeVersion
from homeassistant.components.frontend import async_remove_panel
from homeassistant.components.lovelace.system_health import system_health_info
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import Platform, __version__ as HAVERSION
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.entity_registry import async_get as async_get_entity_registry
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.start import async_at_start
from homeassistant.loader import async_get_integration

from .base import HacsBase
from .const import DOMAIN, HACS_SYSTEM_ID, MINIMUM_HA_VERSION
from .data_client import HacsDataClient
from .enums import HacsDisabledReason, HacsStage, LovelaceMode
from .frontend import async_register_frontend
from .utils.data import HacsData
from .utils.queue_manager import QueueManager
from .utils.version import version_left_higher_or_equal_then_right
from .websocket import async_register_websocket_commands

PLATFORMS = [Platform.SWITCH, Platform.UPDATE]


async def _async_initialize_integration(
    hass: HomeAssistant,
    config_entry: ConfigEntry,
) -> bool:
    """Initialize the integration"""
    hass.data[DOMAIN] = hacs = HacsBase()
    hacs.enable_hacs()

    if config_entry.source == SOURCE_IMPORT:
        # Import is not supported
        hass.async_create_task(hass.config_entries.async_remove(config_entry.entry_id))
        return False

    hacs.configuration.update_from_dict(
        {
            "config_entry": config_entry,
            **config_entry.data,
            **config_entry.options,
        },
    )

    integration = await async_get_integration(hass, DOMAIN)

    hacs.set_stage(None)

    hacs.log.info("Starting HACS[%s]", integration.version)

    clientsession = async_get_clientsession(hass)

    hacs.integration = integration
    hacs.version = integration.version
    hacs.configuration.dev = integration.version == "0.0.0"
    hacs.hass = hass
    hacs.queue = QueueManager(hass=hass)
    hacs.data = HacsData(hacs=hacs)
    hacs.data_client = HacsDataClient(
        session=clientsession,
        client_name=f"HACS/{integration.version}",
    )
    hacs.system.running = True
    hacs.session = clientsession

    hacs.core.lovelace_mode = LovelaceMode.YAML
    try:
        lovelace_info = await system_health_info(hacs.hass)
        hacs.core.lovelace_mode = LovelaceMode(lovelace_info.get("mode", "yaml"))
    except BaseException:  # lgtm [py/catch-base-exception] pylint: disable=broad-except
        # If this happens, the users YAML is not valid, we assume YAML mode
        pass
    hacs.core.config_path = hacs.hass.config.path()

    if hacs.core.ha_version is None:
        hacs.core.ha_version = AwesomeVersion(HAVERSION)

    ## Legacy GitHub client
    hacs.github = GitHub(
        hacs.configuration.token,
        clientsession,
        headers={
            "User-Agent": f"HACS/{hacs.version}",
            "Accept": ACCEPT_HEADERS["preview"],
        },
    )

    ## New GitHub client
    hacs.githubapi = GitHubAPI(
        token=hacs.configuration.token,
        session=clientsession,
        **{"client_name": f"HACS/{hacs.version}"},
    )

    async def async_startup():
        """HACS startup tasks."""
        hacs.enable_hacs()

        try:
            import custom_components.custom_updater
        except ImportError:
            pass
        else:
            hacs.log.critical(
                "HACS cannot be used with custom_updater. "
                "To use HACS you need to remove custom_updater from `custom_components`",
            )

            hacs.disable_hacs(HacsDisabledReason.CONSTRAINS)
            return False

        if not version_left_higher_or_equal_then_right(
            hacs.core.ha_version.string,
            MINIMUM_HA_VERSION,
        ):
            hacs.log.critical(
                "You need HA version %s or newer to use this integration.",
                MINIMUM_HA_VERSION,
            )
            hacs.disable_hacs(HacsDisabledReason.CONSTRAINS)
            return False

        if not await hacs.data.restore():
            hacs.disable_hacs(HacsDisabledReason.RESTORE)
            return False

        hacs.set_active_categories()

        async_register_websocket_commands(hass)
        await async_register_frontend(hass, hacs)

        await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)

        hacs.set_stage(HacsStage.SETUP)
        if hacs.system.disabled:
            return False

        hacs.set_stage(HacsStage.WAITING)
        hacs.log.info("Setup complete, waiting for Home Assistant before startup tasks starts")

        # Schedule startup tasks
        async_at_start(hass=hass, at_start_cb=hacs.startup_tasks)

        return not hacs.system.disabled

    async def async_try_startup(_=None):
        """Startup wrapper for yaml config."""
        try:
            startup_result = await async_startup()
        except AIOGitHubAPIException:
            startup_result = False
        if not startup_result:
            if hacs.system.disabled_reason != HacsDisabledReason.INVALID_TOKEN:
                hacs.log.info("Could not setup HACS, trying again in 15 min")
                async_call_later(hass, 900, async_try_startup)
            return
        hacs.enable_hacs()

    await async_try_startup()

    # Remove old (v0-v1) sensor if it exists, can be removed in v3
    er = async_get_entity_registry(hass)
    if old_sensor := er.async_get_entity_id("sensor", DOMAIN, HACS_SYSTEM_ID):
        er.async_remove(old_sensor)

    # Mischief managed!
    return True


async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
    """Set up this integration using UI."""
    config_entry.async_on_unload(config_entry.add_update_listener(async_reload_entry))
    setup_result = await _async_initialize_integration(hass=hass, config_entry=config_entry)
    hacs: HacsBase = hass.data[DOMAIN]
    return setup_result and not hacs.system.disabled


async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
    """Handle removal of an entry."""
    hacs: HacsBase = hass.data[DOMAIN]

    if hacs.queue.has_pending_tasks:
        hacs.log.warning("Pending tasks, can not unload, try again later.")
        return False

    # Clear out pending queue
    hacs.queue.clear()

    for task in hacs.recurring_tasks:
        # Cancel all pending tasks
        task()

    # Store data
    await hacs.data.async_write(force=True)

    try:
        if hass.data.get("frontend_panels", {}).get("hacs"):
            hacs.log.info("Removing sidepanel")
            async_remove_panel(hass, "hacs")
    except AttributeError:
        pass

    unload_ok = await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)

    hacs.set_stage(None)
    hacs.disable_hacs(HacsDisabledReason.REMOVED)

    hass.data.pop(DOMAIN, None)

    return unload_ok


async def async_reload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
    """Reload the HACS config entry."""
    if not await async_unload_entry(hass, config_entry):
        return
    await async_setup_entry(hass, config_entry)


================================================
FILE: custom_components/hacs/base.py
================================================
"""Base HACS class."""

from __future__ import annotations

import asyncio
from collections.abc import Awaitable, Callable
from dataclasses import asdict, dataclass, field
from datetime import timedelta
import gzip
import math
import os
import pathlib
import shutil
from typing import TYPE_CHECKING, Any

from aiogithubapi import (
    AIOGitHubAPIException,
    GitHub,
    GitHubAPI,
    GitHubAuthenticationException,
    GitHubException,
    GitHubNotModifiedException,
    GitHubRatelimitException,
)
from aiogithubapi.objects.repository import AIOGitHubAPIRepository
from aiohttp.client import ClientSession, ClientTimeout
from awesomeversion import AwesomeVersion
from homeassistant.components.persistent_notification import (
    async_create as async_create_persistent_notification,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EVENT_HOMEASSISTANT_FINAL_WRITE, Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_track_time_interval
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.loader import Integration
from homeassistant.util import dt

from .const import DOMAIN, TV, URL_BASE
from .coordinator import HacsUpdateCoordinator
from .data_client import HacsDataClient
from .enums import (
    HacsCategory,
    HacsDisabledReason,
    HacsDispatchEvent,
    HacsGitHubRepo,
    HacsStage,
    LovelaceMode,
)
from .exceptions import (
    AddonRepositoryException,
    HacsException,
    HacsExecutionStillInProgress,
    HacsExpectedException,
    HacsNotModifiedException,
    HacsRepositoryArchivedException,
    HacsRepositoryExistException,
    HomeAssistantCoreRepositoryException,
)
from .repositories import REPOSITORY_CLASSES
from .repositories.base import HACS_MANIFEST_KEYS_TO_EXPORT, REPOSITORY_KEYS_TO_EXPORT
from .utils.file_system import async_exists
from .utils.json import json_loads
from .utils.logger import LOGGER
from .utils.queue_manager import QueueManager
from .utils.store import async_load_from_store, async_save_to_store
from .utils.workarounds import async_register_static_path

if TYPE_CHECKING:
    from .repositories.base import HacsRepository
    from .utils.data import HacsData
    from .validate.manager import ValidationManager


@dataclass
class RemovedRepository:
    """Removed repository."""

    repository: str | None = None
    reason: str | None = None
    link: str | None = None
    removal_type: str = None  # archived, not_compliant, critical, dev, broken
    acknowledged: bool = False

    def update_data(self, data: dict):
        """Update data of the repository."""
        for key in data:
            if data[key] is None:
                continue
            if key in (
                "reason",
                "link",
                "removal_type",
                "acknowledged",
            ):
                self.__setattr__(key, data[key])

    def to_json(self):
        """Return a JSON representation of the data."""
        return {
            "repository": self.repository,
            "reason": self.reason,
            "link": self.link,
            "removal_type": self.removal_type,
            "acknowledged": self.acknowledged,
        }


@dataclass
class HacsConfiguration:
    """HacsConfiguration class."""

    appdaemon_path: str = "appdaemon/apps/"
    appdaemon: bool = False
    config: dict[str, Any] = field(default_factory=dict)
    config_entry: ConfigEntry | None = None
    country: str = "ALL"
    debug: bool = False
    dev: bool = False
    frontend_repo_url: str = ""
    frontend_repo: str = ""
    plugin_path: str = "www/community/"
    python_script_path: str = "python_scripts/"
    python_script: bool = False
    release_limit: int = 5
    sidepanel_icon: str = "hacs:hacs"
    sidepanel_title: str = "HACS"
    theme_path: str = "themes/"
    theme: bool = False
    token: str = None

    def to_json(self) -> str:
        """Return a json string."""
        return asdict(self)

    def update_from_dict(self, data: dict) -> None:
        """Set attributes from dicts."""
        if not isinstance(data, dict):
            raise HacsException("Configuration is not valid.")

        for key in data:
            if key in {"experimental", "netdaemon", "release_limit", "debug"}:
                continue
            self.__setattr__(key, data[key])


@dataclass
class HacsCore:
    """HACS Core info."""

    config_path: pathlib.Path | None = None
    ha_version: AwesomeVersion | None = None
    lovelace_mode = LovelaceMode("yaml")


@dataclass
class HacsCommon:
    """Common for HACS."""

    categories: set[str] = field(default_factory=set)
    renamed_repositories: dict[str, str] = field(default_factory=dict)
    archived_repositories: set[str] = field(default_factory=set)
    ignored_repositories: set[str] = field(default_factory=set)
    skip: set[str] = field(default_factory=set)


@dataclass
class HacsStatus:
    """HacsStatus."""

    startup: bool = True
    new: bool = False
    active_frontend_endpoint_plugin: bool = False
    active_frontend_endpoint_theme: bool = False
    inital_fetch_done: bool = False


@dataclass
class HacsSystem:
    """HACS System info."""

    disabled_reason: HacsDisabledReason | None = None
    running: bool = False
    stage = HacsStage.SETUP
    action: bool = False
    generator: bool = False

    @property
    def disabled(self) -> bool:
        """Return if HACS is disabled."""
        return self.disabled_reason is not None


@dataclass
class HacsRepositories:
    """HACS Repositories."""

    _default_repositories: set[str] = field(default_factory=set)
    _repositories: set[HacsRepository] = field(default_factory=set)
    _repositories_by_full_name: dict[str, HacsRepository] = field(default_factory=dict)
    _repositories_by_id: dict[str, HacsRepository] = field(default_factory=dict)
    _removed_repositories_by_full_name: dict[str, RemovedRepository] = field(default_factory=dict)

    @property
    def list_all(self) -> list[HacsRepository]:
        """Return a list of repositories."""
        return list(self._repositories)

    @property
    def list_removed(self) -> list[RemovedRepository]:
        """Return a list of removed repositories."""
        return list(self._removed_repositories_by_full_name.values())

    @property
    def list_downloaded(self) -> list[HacsRepository]:
        """Return a list of downloaded repositories."""
        return [repo for repo in self._repositories if repo.data.installed]

    def category_downloaded(self, category: HacsCategory) -> bool:
        """Check if a given category has been downloaded."""
        for repository in self.list_downloaded:
            if repository.data.category == category:
                return True
        return False

    def register(self, repository: HacsRepository, default: bool = False) -> None:
        """Register a repository."""
        repo_id = str(repository.data.id)

        if repo_id == "0":
            return

        if registered_repo := self._repositories_by_id.get(repo_id):
            if registered_repo.data.full_name == repository.data.full_name:
                return

            self.unregister(registered_repo)

            registered_repo.data.full_name = repository.data.full_name
            registered_repo.data.new = False
            repository = registered_repo

        if repository not in self._repositories:
            self._repositories.add(repository)

        self._repositories_by_id[repo_id] = repository
        self._repositories_by_full_name[repository.data.full_name_lower] = repository

        if default:
            self.mark_default(repository)

    def unregister(self, repository: HacsRepository) -> None:
        """Unregister a repository."""
        repo_id = str(repository.data.id)

        if repo_id == "0":
            return

        if not self.is_registered(repository_id=repo_id):
            return

        if self.is_default(repo_id):
            self._default_repositories.remove(repo_id)

        if repository in self._repositories:
            self._repositories.remove(repository)

        self._repositories_by_id.pop(repo_id, None)
        self._repositories_by_full_name.pop(repository.data.full_name_lower, None)

    def mark_default(self, repository: HacsRepository) -> None:
        """Mark a repository as default."""
        repo_id = str(repository.data.id)

        if repo_id == "0":
            return

        if not self.is_registered(repository_id=repo_id):
            return

        self._default_repositories.add(repo_id)

    def set_repository_id(self, repository: HacsRepository, repo_id: str):
        """Update a repository id."""
        existing_repo_id = str(repository.data.id)
        if existing_repo_id == repo_id:
            return
        if existing_repo_id != "0":
            raise ValueError(
                f"The repo id for {repository.data.full_name_lower} "
                f"is already set to {existing_repo_id}"
            )
        repository.data.id = repo_id
        self.register(repository)

    def is_default(self, repository_id: str | None = None) -> bool:
        """Check if a repository is default."""
        if not repository_id:
            return False
        return repository_id in self._default_repositories

    def is_registered(
        self,
        repository_id: str | None = None,
        repository_full_name: str | None = None,
    ) -> bool:
        """Check if a repository is registered."""
        if repository_id is not None:
            return repository_id in self._repositories_by_id
        if repository_full_name is not None:
            return repository_full_name in self._repositories_by_full_name
        return False

    def is_downloaded(
        self,
        repository_id: str | None = None,
        repository_full_name: str | None = None,
    ) -> bool:
        """Check if a repository is registered."""
        if repository_id is not None:
            repo = self.get_by_id(repository_id)
        if repository_full_name is not None:
            repo = self.get_by_full_name(repository_full_name)
        if repo is None:
            return False
        return repo.data.installed

    def get_by_id(self, repository_id: str | None) -> HacsRepository | None:
        """Get repository by id."""
        if not repository_id:
            return None
        return self._repositories_by_id.get(str(repository_id))

    def get_by_full_name(self, repository_full_name: str | None) -> HacsRepository | None:
        """Get repository by full name."""
        if not repository_full_name:
            return None
        return self._repositories_by_full_name.get(repository_full_name.lower())

    def is_removed(self, repository_full_name: str) -> bool:
        """Check if a repository is removed."""
        return repository_full_name in self._removed_repositories_by_full_name

    def removed_repository(self, repository_full_name: str) -> RemovedRepository:
        """Get repository by full name."""
        if removed := self._removed_repositories_by_full_name.get(repository_full_name):
            return removed

        removed = RemovedRepository(repository=repository_full_name)
        self._removed_repositories_by_full_name[repository_full_name] = removed
        return removed


class HacsBase:
    """Base HACS class."""

    data: HacsData | None = None
    data_client: HacsDataClient | None = None
    frontend_version: str | None = None
    github: GitHub | None = None
    githubapi: GitHubAPI | None = None
    hass: HomeAssistant | None = None
    integration: Integration | None = None
    queue: QueueManager | None = None
    repository: AIOGitHubAPIRepository | None = None
    session: ClientSession | None = None
    stage: HacsStage | None = None
    validation: ValidationManager | None = None
    version: AwesomeVersion | None = None

    def __init__(self) -> None:
        """Initialize."""
        self.common = HacsCommon()
        self.configuration = HacsConfiguration()
        self.coordinators: dict[HacsCategory, HacsUpdateCoordinator] = {}
        self.core = HacsCore()
        self.log = LOGGER
        self.recurring_tasks: list[Callable[[], None]] = []
        self.repositories = HacsRepositories()
        self.status = HacsStatus()
        self.system = HacsSystem()

    @property
    def integration_dir(self) -> pathlib.Path:
        """Return the HACS integration dir."""
        return self.integration.file_path

    def set_stage(self, stage: HacsStage | None) -> None:
        """Set HACS stage."""
        if stage and self.stage == stage:
            return

        self.stage = stage
        if stage is not None:
            self.log.info("Stage changed: %s", self.stage)
            self.async_dispatch(HacsDispatchEvent.STAGE, {"stage": self.stage})

    def disable_hacs(self, reason: HacsDisabledReason) -> None:
        """Disable HACS."""
        if self.system.disabled_reason == reason:
            return

        self.system.disabled_reason = reason
        if reason != HacsDisabledReason.REMOVED:
            self.log.error("HACS is disabled - %s", reason)

        if reason == HacsDisabledReason.INVALID_TOKEN:
            self.hass.add_job(self.configuration.config_entry.async_start_reauth, self.hass)

    def enable_hacs(self) -> None:
        """Enable HACS."""
        if self.system.disabled_reason is not None:
            self.system.disabled_reason = None
            self.log.info("HACS is enabled")

    def enable_hacs_category(self, category: HacsCategory) -> None:
        """Enable HACS category."""
        if category not in self.common.categories:
            self.log.info("Enable category: %s", category)
            self.common.categories.add(category)
            self.coordinators[category] = HacsUpdateCoordinator()

    def disable_hacs_category(self, category: HacsCategory) -> None:
        """Disable HACS category."""
        if category in self.common.categories:
            self.log.info("Disabling category: %s", category)
            self.common.categories.pop(category)
            self.coordinators.pop(category)

    async def async_save_file(self, file_path: str, content: Any) -> bool:
        """Save a file."""

        def _write_file():
            with open(
                file_path,
                mode="w" if isinstance(content, str) else "wb",
                encoding="utf-8" if isinstance(content, str) else None,
                errors="ignore" if isinstance(content, str) else None,
            ) as file_handler:
                file_handler.write(content)

            # Create gz for .js files
            if os.path.isfile(file_path):
                if file_path.endswith(".js"):
                    with open(file_path, "rb") as f_in:
                        with gzip.open(file_path + ".gz", "wb") as f_out:
                            shutil.copyfileobj(f_in, f_out)

            # LEGACY! Remove with 2.0
            if "themes" in file_path and file_path.endswith(".yaml"):
                filename = file_path.split("/")[-1]
                base = file_path.split("/themes/")[0]
                combined = f"{base}/themes/{filename}"
                if os.path.exists(combined):
                    self.log.info("Removing old theme file %s", combined)
                    os.remove(combined)

        try:
            await self.hass.async_add_executor_job(_write_file)
        except (
            # lgtm [py/catch-base-exception] pylint: disable=broad-except
            BaseException
        ) as error:
            self.log.error("Could not write data to %s - %s", file_path, error)
            return False

        return await async_exists(self.hass, file_path)

    async def async_can_update(self) -> int:
        """Helper to calculate the number of repositories we can fetch data for."""
        try:
            response = await self.async_github_api_method(self.githubapi.rate_limit)
            if ((limit := response.data.resources.core.remaining or 0) - 1000) >= 10:
                return math.floor((limit - 1000) / 10)
            reset = dt.as_local(dt.utc_from_timestamp(response.data.resources.core.reset))
            self.log.info(
                "GitHub API ratelimited - %s remaining (%s)",
                response.data.resources.core.remaining,
                f"{reset.hour}:{reset.minute}:{reset.second}",
            )
            self.disable_hacs(HacsDisabledReason.RATE_LIMIT)
        except (
            # lgtm [py/catch-base-exception] pylint: disable=broad-except
            BaseException
        ) as exception:
            self.log.exception(exception)

        return 0

    async def async_github_api_method(
        self,
        method: Callable[[], Awaitable[TV]],
        *args,
        raise_exception: bool = True,
        **kwargs,
    ) -> TV | None:
        """Call a GitHub API method"""
        _exception = None

        try:
            return await method(*args, **kwargs)
        except GitHubAuthenticationException as exception:
            self.disable_hacs(HacsDisabledReason.INVALID_TOKEN)
            _exception = exception
        except GitHubRatelimitException as exception:
            self.disable_hacs(HacsDisabledReason.RATE_LIMIT)
            _exception = exception
        except GitHubNotModifiedException as exception:
            raise exception
        except GitHubException as exception:
            _exception = exception
        except (
            # lgtm [py/catch-base-exception] pylint: disable=broad-except
            BaseException
        ) as exception:
            self.log.exception(exception)
            _exception = exception

        if raise_exception and _exception is not None:
            raise HacsException(_exception)
        return None

    async def async_register_repository(
        self,
        repository_full_name: str,
        category: HacsCategory,
        *,
        check: bool = True,
        ref: str | None = None,
        repository_id: str | None = None,
        default: bool = False,
    ) -> None:
        """Register a repository."""
        if repository_full_name in self.common.skip:
            if repository_full_name != HacsGitHubRepo.INTEGRATION:
                raise HacsExpectedException(f"Skipping {repository_full_name}")

        if repository_full_name == "home-assistant/core":
            raise HomeAssistantCoreRepositoryException()

        if repository_full_name == "home-assistant/addons" or repository_full_name.startswith(
            "hassio-addons/"
        ):
            raise AddonRepositoryException()

        if category not in REPOSITORY_CLASSES:
            self.log.warning(
                "%s is not a valid repository category, %s will not be registered.",
                category,
                repository_full_name,
            )
            return

        if (renamed := self.common.renamed_repositories.get(repository_full_name)) is not None:
            repository_full_name = renamed

        repository: HacsRepository = REPOSITORY_CLASSES[category](self, repository_full_name)
        if check:
            try:
                await repository.async_registration(ref)
                if repository.validate.errors:
                    self.common.skip.add(repository.data.full_name)
                    if not self.status.startup:
                        self.log.error("Validation for %s failed.", repository_full_name)
                    if self.system.action:
                        raise HacsException(
                            f"::error:: Validation for {repository_full_name} failed."
                        )
                    return repository.validate.errors
                if self.system.action:
                    repository.logger.info("%s Validation completed", repository.string)
                else:
                    repository.logger.info("%s Registration completed", repository.string)
            except (HacsRepositoryExistException, HacsRepositoryArchivedException) as exception:
                if self.system.generator:
                    repository.logger.error(
                        "%s Registration Failed - %s", repository.string, exception
                    )
                return
            except AIOGitHubAPIException as exception:
                self.common.skip.add(repository.data.full_name)
                raise HacsException(
                    f"Validation for {repository_full_name} failed with {exception}."
                ) from exception

        if self.status.new:
            repository.data.new = False

        if repository_id is not None:
            repository.data.id = repository_id

        else:
            if self.hass is not None and check and repository.data.new:
                self.async_dispatch(
                    HacsDispatchEvent.REPOSITORY,
                    {
                        "action": "registration",
                        "repository": repository.data.full_name,
                        "repository_id": repository.data.id,
                    },
                )

        self.repositories.register(repository, default)

    async def startup_tasks(self, _=None) -> None:
        """Tasks that are started after setup."""
        self.set_stage(HacsStage.STARTUP)
        await self.async_load_hacs_from_github()

        if critical := await async_load_from_store(self.hass, "critical"):
            for repo in critical:
                if not repo["acknowledged"]:
                    self.log.critical("URGENT!: Check the HACS panel!")
                    async_create_persistent_notification(
                        self.hass, title="URGENT!", message="**Check the HACS panel!**"
                    )
                    break

        self.recurring_tasks.append(
            async_track_time_interval(
                self.hass,
                self.async_load_hacs_from_github,
                timedelta(hours=48),
            )
        )

        self.recurring_tasks.append(
            async_track_time_interval(
                self.hass, self.async_update_downloaded_custom_repositories, timedelta(hours=48)
            )
        )

        self.recurring_tasks.append(
            async_track_time_interval(
                self.hass, self.async_get_all_category_repositories, timedelta(hours=6)
            )
        )

        self.recurring_tasks.append(
            async_track_time_interval(self.hass, self.async_check_rate_limit, timedelta(minutes=5))
        )
        self.recurring_tasks.append(
            async_track_time_interval(self.hass, self.async_process_queue, timedelta(minutes=10))
        )

        self.recurring_tasks.append(
            async_track_time_interval(
                self.hass, self.async_handle_critical_repositories, timedelta(hours=6)
            )
        )

        unsub = self.hass.bus.async_listen_once(
            EVENT_HOMEASSISTANT_FINAL_WRITE, self.data.async_force_write
        )
        if config_entry := self.configuration.config_entry:
            config_entry.async_on_unload(unsub)

        self.log.debug("There are %s scheduled recurring tasks", len(self.recurring_tasks))

        self.status.startup = False
        self.async_dispatch(HacsDispatchEvent.STATUS, {})

        await self.async_handle_removed_repositories()
        await self.async_get_all_category_repositories()

        self.set_stage(HacsStage.RUNNING)

        self.async_dispatch(HacsDispatchEvent.RELOAD, {"force": True})

        await self.async_handle_critical_repositories()
        await self.async_process_queue()

        self.async_dispatch(HacsDispatchEvent.STATUS, {})

    async def async_download_file(
        self,
        url: str,
        *,
        headers: dict | None = None,
        keep_url: bool = False,
        nolog: bool = False,
        handle_rate_limit: bool = False,
        **_,
    ) -> bytes | None:
        """Download files, and return the content."""
        if url is None:
            return None

        if not keep_url and "tags/" in url:
            url = url.replace("tags/", "")

        self.log.debug("Trying to download %s", url)
        attempt_count = 0

        while attempt_count < 5:
            try:
                request = await self.session.get(
                    url=url,
                    timeout=ClientTimeout(total=60),
                    headers=headers,
                )

                # Make sure that we got a valid result
                if request.status == 200:
                    return await request.read()

                # Handle rate-limits
                if handle_rate_limit and request.status == 429:
                    header = int(request.headers.get("retry-after") or 10)
                    retry_after = min(header, 60)  # Limit to 60 seconds

                    self.log.warning(
                        "GitHub has imposed a ratelimit on the request for %s, "
                        "retrying after %s seconds",
                        url,
                        retry_after,
                    )
                    attempt_count += 1
                    await asyncio.sleep(retry_after)
                    continue

                raise HacsException(
                    f"Got status code {request.status} when trying to download {url}"
                )
            except TimeoutError:
                self.log.warning(
                    "A timeout of 60! seconds was encountered while downloading %s, "
                    "using over 60 seconds to download a single file is not normal. "
                    "This is not a problem with HACS but how your host communicates with GitHub. "
                    "Retrying up to 5 times to mask/hide your host/network problems to "
                    "stop the flow of issues opened about it. "
                    "Tries left %s",
                    url,
                    (4 - attempt_count),
                )
                attempt_count += 1
                await asyncio.sleep(1)
                continue

            except (
                # lgtm [py/catch-base-exception] pylint: disable=broad-except
                BaseException
            ) as exception:
                if not nolog:
                    self.log.exception("Download failed - %s", exception)

            return None

    async def async_recreate_entities(self) -> None:
        """Recreate entities."""
        platforms = [Platform.UPDATE]

        # Workaround for core versions without https://github.com/home-assistant/core/pull/117084
        if self.core.ha_version < AwesomeVersion("2024.6.0"):
            unload_platforms_lock = asyncio.Lock()
            async with unload_platforms_lock:
                on_unload = self.configuration.config_entry._on_unload
                self.configuration.config_entry._on_unload = []
                await self.hass.config_entries.async_unload_platforms(
                    entry=self.configuration.config_entry,
                    platforms=platforms,
                )
                self.configuration.config_entry._on_unload = on_unload
        else:
            await self.hass.config_entries.async_unload_platforms(
                entry=self.configuration.config_entry,
                platforms=platforms,
            )
        await self.hass.config_entries.async_forward_entry_setups(
            self.configuration.config_entry, platforms
        )

    @callback
    def async_dispatch(self, signal: HacsDispatchEvent, data: dict | None = None) -> None:
        """Dispatch a signal with data."""
        async_dispatcher_send(self.hass, signal, data)

    def set_active_categories(self) -> None:
        """Set the active categories."""
        self.common.categories = set()
        for category in (HacsCategory.INTEGRATION, HacsCategory.PLUGIN, HacsCategory.TEMPLATE):
            self.enable_hacs_category(HacsCategory(category))

        if (
            HacsCategory.PYTHON_SCRIPT in self.hass.config.components
            or self.repositories.category_downloaded(HacsCategory.PYTHON_SCRIPT)
        ):
            self.enable_hacs_category(HacsCategory.PYTHON_SCRIPT)

        if self.hass.services.has_service(
            "frontend", "reload_themes"
        ) or self.repositories.category_downloaded(HacsCategory.THEME):
            self.enable_hacs_category(HacsCategory.THEME)

        if self.configuration.appdaemon:
            self.enable_hacs_category(HacsCategory.APPDAEMON)

    async def async_load_hacs_from_github(self, _=None) -> None:
        """Load HACS from GitHub."""
        if self.status.inital_fetch_done:
            return

        try:
            repository = self.repositories.get_by_full_name(HacsGitHubRepo.INTEGRATION)
            should_recreate_entities = False
            if repository is None:
                should_recreate_entities = True
                await self.async_register_repository(
                    repository_full_name=HacsGitHubRepo.INTEGRATION,
                    category=HacsCategory.INTEGRATION,
                    default=True,
                )
                repository = self.repositories.get_by_full_name(HacsGitHubRepo.INTEGRATION)
            elif not self.status.startup:
                self.log.error("Scheduling update of hacs/integration")
                self.queue.add(repository.common_update())
            if repository is None:
                raise HacsException("Unknown error")

            repository.data.installed = True
            repository.data.installed_version = self.integration.version.string
            repository.data.new = False
            repository.data.releases = True

            if should_recreate_entities:
                await self.async_recreate_entities()

            self.repository = repository.repository_object
            self.repositories.mark_default(repository)
        except HacsException as exception:
            if "403" in str(exception):
                self.log.critical(
                    "GitHub API is ratelimited, or the token is wrong.",
                )
            else:
                self.log.critical("Could not load HACS! - %s", exception)
            self.disable_hacs(HacsDisabledReason.LOAD_HACS)

    async def async_get_all_category_repositories(self, _=None) -> None:
        """Get all category repositories."""
        if self.system.disabled:
            return
        self.log.info("Loading known repositories")
        await asyncio.gather(
            *[
                self.async_get_category_repositories_experimental(category)
                for category in self.common.categories or []
            ]
        )

    async def async_get_category_repositories_experimental(self, category: str) -> None:
        """Update all category repositories."""
        self.log.debug("Fetching updated content for %s", category)
        try:
            category_data = await self.data_client.get_data(category, validate=True)
        except HacsNotModifiedException:
            self.log.debug("No updates for %s", category)
            return
        except HacsException as exception:
            self.log.error("Could not update %s - %s", category, exception)
            return

        await self.data.register_unknown_repositories(category_data, category)

        for repo_id, repo_data in category_data.items():
            repo_name = repo_data["full_name"]
            if self.common.renamed_repositories.get(repo_name):
                repo_name = self.common.renamed_repositories[repo_name]
            if self.repositories.is_removed(repo_name):
                continue
            if repo_name in self.common.archived_repositories:
                continue
            if repository := self.repositories.get_by_full_name(repo_name):
                self.repositories.set_repository_id(repository, repo_id)
                self.repositories.mark_default(repository)
                if repository.data.last_fetched is None or (
                    repository.data.last_fetched.timestamp() < repo_data["last_fetched"]
                ):
                    repository.data.update_data({**dict(REPOSITORY_KEYS_TO_EXPORT), **repo_data})
                    if (manifest := repo_data.get("manifest")) is not None:
                        repository.repository_manifest.update_data(
                            {**dict(HACS_MANIFEST_KEYS_TO_EXPORT), **manifest}
                        )

        if category == "integration":
            self.status.inital_fetch_done = True

        if self.stage == HacsStage.STARTUP:
            for repository in self.repositories.list_all:
                if (
                    repository.data.category == category
                    and not repository.data.installed
                    and not self.repositories.is_default(repository.data.id)
                ):
                    repository.logger.debug(
                        "%s Unregister stale custom repository", repository.string
                    )
                    self.repositories.unregister(repository)

        self.async_dispatch(HacsDispatchEvent.REPOSITORY, {})
        self.coordinators[category].async_update_listeners()

    async def async_check_rate_limit(self, _=None) -> None:
        """Check rate limit."""
        if not self.system.disabled or self.system.disabled_reason != HacsDisabledReason.RATE_LIMIT:
            return

        self.log.debug("Checking if ratelimit has lifted")
        can_update = await self.async_can_update()
        self.log.debug("Ratelimit indicate we can update %s", can_update)
        if can_update > 0:
            self.enable_hacs()
            await self.async_process_queue()

    async def async_process_queue(self, _=None) -> None:
        """Process the queue."""
        if self.system.disabled:
            self.log.debug("HACS is disabled")
            return
        if not self.queue.has_pending_tasks:
            self.log.debug("Nothing in the queue")
            return
        if self.queue.running:
            self.log.debug("Queue is already running")
            return

        async def _handle_queue():
            if not self.queue.has_pending_tasks:
                await self.data.async_write()
                return
            can_update = await self.async_can_update()
            self.log.debug(
                "Can update %s repositories, items in queue %s",
                can_update,
                self.queue.pending_tasks,
            )
            if can_update != 0:
                try:
                    await self.queue.execute(can_update)
                except HacsExecutionStillInProgress:
                    return

                await _handle_queue()

        await _handle_queue()

    async def async_handle_removed_repositories(self, _=None) -> None:
        """Handle removed repositories."""
        if self.system.disabled:
            return
        need_to_save = False
        self.log.info("Loading removed repositories")

        try:
            removed_repositories = await self.data_client.get_data("removed", validate=True)
        except HacsException:
            return

        for item in removed_repositories:
            removed = self.repositories.removed_repository(item["repository"])
            removed.update_data(item)

        for removed in self.repositories.list_removed:
            if (repository := self.repositories.get_by_full_name(removed.repository)) is None:
                continue
            if repository.data.full_name in self.common.ignored_repositories:
                continue
            if repository.data.installed:
                if removed.removal_type != "critical":
                    async_create_issue(
                        hass=self.hass,
                        domain=DOMAIN,
                        issue_id=f"removed_{repository.data.id}",
                        is_fixable=False,
                        issue_domain=DOMAIN,
                        severity=IssueSeverity.WARNING,
                        translation_key="removed",
                        translation_placeholders={
                            "name": repository.data.full_name,
                            "reason": removed.reason,
                            "repositry_id": repository.data.id,
                        },
                    )
                    self.log.warning(
                        "You have '%s' installed with HACS "
                        "this repository has been removed from HACS, please consider removing it. "
                        "Removal reason (%s)",
                        repository.data.full_name,
                        removed.reason,
                    )
            else:
                need_to_save = True
                repository.remove()

        if need_to_save:
            await self.data.async_write()

    async def async_update_downloaded_custom_repositories(self, _=None) -> None:
        """Execute the task."""
        if self.system.disabled:
            return
        self.log.info("Starting recurring background task for downloaded custom repositories")

        repositories_to_update = 0
        repositories_updated = asyncio.Event()

        async def update_repository(repository: HacsRepository) -> None:
            """Update a repository"""
            nonlocal repositories_to_update
            await repository.update_repository(ignore_issues=True)
            repositories_to_update -= 1
            if not repositories_to_update:
                repositories_updated.set()

        for repository in self.repositories.list_downloaded:
            if (
                repository.data.category in self.common.categories
                and not self.repositories.is_default(repository.data.id)
            ):
                repositories_to_update += 1
                self.queue.add(update_repository(repository))

        async def update_coordinators() -> None:
            """Update all coordinators."""
            await repositories_updated.wait()
            for coordinator in self.coordinators.values():
                coordinator.async_update_listeners()

        if config_entry := self.configuration.config_entry:
            config_entry.async_create_background_task(
                self.hass, update_coordinators(), "update_coordinators"
            )
        else:
            self.hass.async_create_background_task(update_coordinators(), "update_coordinators")

        self.log.debug("Recurring background task for downloaded custom repositories done")

    async def async_handle_critical_repositories(self, _=None) -> None:
        """Handle critical repositories."""
        critical_queue = QueueManager(hass=self.hass)
        instored = []
        critical = []
        was_installed = False

        try:
            critical = await self.data_client.get_data("critical", validate=True)
        except (GitHubNotModifiedException, HacsNotModifiedException):
            return
        except HacsException:
            pass

        if not critical:
            self.log.debug("No critical repositories")
            return

        stored_critical = await async_load_from_store(self.hass, "critical")

        for stored in stored_critical or []:
            instored.append(stored["repository"])

        stored_critical = []

        for repository in critical:
            removed_repo = self.repositories.removed_repository(repository["repository"])
            removed_repo.removal_type = "critical"
            repo = self.repositories.get_by_full_name(repository["repository"])

            stored = {
                "repository": repository["repository"],
                "reason": repository["reason"],
                "link": repository["link"],
                "acknowledged": True,
            }
            if repository["repository"] not in instored:
                if repo is not None and repo.data.installed:
                    self.log.critical(
                        "Removing repository %s, it is marked as critical",
                        repository["repository"],
                    )
                    was_installed = True
                    stored["acknowledged"] = False
                    # Remove from HACS
                    critical_queue.add(repo.uninstall())
                    repo.remove()

            stored_critical.append(stored)
            removed_repo.update_data(stored)

        # Uninstall
        await critical_queue.execute()

        # Save to FS
        await async_save_to_store(self.hass, "critical", stored_critical)

        # Restart HASS
        if was_installed:
            self.log.critical("Restarting Home Assistant")
            self.hass.async_create_task(self.hass.async_stop(100))

    async def async_setup_frontend_endpoint_plugin(self) -> None:
        """Setup the http endpoints for plugins if its not already handled."""
        if self.status.active_frontend_endpoint_plugin or not await async_exists(
            self.hass, self.hass.config.path("www/community")
        ):
            return

        self.log.info("Setting up plugin endpoint")
        use_cache = self.core.lovelace_mode == "storage"
        self.log.info(
            "<HacsFrontend> %s mode, cache for /hacsfiles/: %s",
            self.core.lovelace_mode,
            use_cache,
        )

        await async_register_static_path(
            self.hass,
            URL_BASE,
            self.hass.config.path("www/community"),
            cache_headers=use_cache,
        )

        self.status.active_frontend_endpoint_plugin = True


================================================
FILE: custom_components/hacs/config_flow.py
================================================
"""Adds config flow for HACS."""

from __future__ import annotations

import asyncio
from contextlib import suppress
from typing import TYPE_CHECKING

from aiogithubapi import (
    GitHubDeviceAPI,
    GitHubException,
    GitHubLoginDeviceModel,
    GitHubLoginOauthModel,
)
from aiogithubapi.common.const import OAUTH_USER_LOGIN
from awesomeversion import AwesomeVersion
from homeassistant.config_entries import ConfigFlow, OptionsFlow
from homeassistant.const import __version__ as HAVERSION
from homeassistant.core import callback
from homeassistant.data_entry_flow import UnknownFlow
from homeassistant.helpers import aiohttp_client
from homeassistant.loader import async_get_integration
import voluptuous as vol

from .base import HacsBase
from .const import CLIENT_ID, DOMAIN, LOCALE, MINIMUM_HA_VERSION
from .utils.configuration_schema import (
    APPDAEMON,
    COUNTRY,
    SIDEPANEL_ICON,
    SIDEPANEL_TITLE,
)
from .utils.logger import LOGGER

if TYPE_CHECKING:
    from homeassistant.core import HomeAssistant


class HacsFlowHandler(ConfigFlow, domain=DOMAIN):
    """Config flow for HACS."""

    VERSION = 1

    hass: HomeAssistant
    activation_task: asyncio.Task | None = None
    device: GitHubDeviceAPI | None = None

    _registration: GitHubLoginDeviceModel | None = None
    _activation: GitHubLoginOauthModel | None = None
    _reauth: bool = False

    def __init__(self) -> None:
        """Initialize."""
        self._errors = {}
        self._user_input = {}

    async def async_step_user(self, user_input):
        """Handle a flow initialized by the user."""
        self._errors = {}
        if self._async_current_entries():
            return self.async_abort(reason="single_instance_allowed")
        if self.hass.data.get(DOMAIN):
            return self.async_abort(reason="single_instance_allowed")

        if user_input:
            if [x for x in user_input if x.startswith("acc_") and not user_input[x]]:
                self._errors["base"] = "acc"
                return await self._show_config_form(user_input)

            self._user_input = user_input

            return await self.async_step_device(user_input)

        # Initial form
        return await self._show_config_form(user_input)

    async def async_step_device(self, _user_input):
        """Handle device steps."""

        async def _wait_for_activation() -> None:
            try:
                response = await self.device.activation(device_code=self._registration.device_code)
                self._activation = response.data
            finally:

                async def _progress():
                    with suppress(UnknownFlow):
                        await self.hass.config_entries.flow.async_configure(flow_id=self.flow_id)

        if not self.device:
            integration = await async_get_integration(self.hass, DOMAIN)
            self.device = GitHubDeviceAPI(
                client_id=CLIENT_ID,
                session=aiohttp_client.async_get_clientsession(self.hass),
                **{"client_name": f"HACS/{integration.version}"},
            )
            try:
                response = await self.device.register()
                self._registration = response.data
            except GitHubException as exception:
                LOGGER.exception(exception)
                return self.async_abort(reason="could_not_register")

        if self.activation_task is None:
            self.activation_task = self.hass.async_create_task(_wait_for_activation())

        if self.activation_task.done():
            if (exception := self.activation_task.exception()) is not None:
                LOGGER.exception(exception)
                return self.async_show_progress_done(next_step_id="could_not_register")
            return self.async_show_progress_done(next_step_id="device_done")

        show_progress_kwargs = {
            "step_id": "device",
            "progress_action": "wait_for_device",
            "description_placeholders": {
                "url": OAUTH_USER_LOGIN,
                "code": self._registration.user_code,
            },
            "progress_task": self.activation_task,
        }
        return self.async_show_progress(**show_progress_kwargs)

    async def _show_config_form(self, user_input):
        """Show the configuration form to edit location data."""

        if not user_input:
            user_input = {}

        if AwesomeVersion(HAVERSION) < MINIMUM_HA_VERSION:
            return self.async_abort(
                reason="min_ha_version",
                description_placeholders={"version": MINIMUM_HA_VERSION},
            )
        return self.async_show_form(
            step_id="user",
            data_schema=vol.Schema(
                {
                    vol.Required("acc_logs", default=user_input.get("acc_logs", False)): bool,
                    vol.Required("acc_addons", default=user_input.get("acc_addons", False)): bool,
                    vol.Required(
                        "acc_untested", default=user_input.get("acc_untested", False)
                    ): bool,
                    vol.Required("acc_disable", default=user_input.get("acc_disable", False)): bool,
                }
            ),
            errors=self._errors,
        )

    async def async_step_device_done(self, user_input: dict[str, bool] | None = None):
        """Handle device steps"""
        if self._reauth:
            existing_entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
            self.hass.config_entries.async_update_entry(
                existing_entry, data={**existing_entry.data, "token": self._activation.access_token}
            )
            await self.hass.config_entries.async_reload(existing_entry.entry_id)
            return self.async_abort(reason="reauth_successful")

        return self.async_create_entry(
            title="",
            data={
                "token": self._activation.access_token,
            },
            options={
                "experimental": True,
            },
        )

    async def async_step_could_not_register(self, _user_input=None):
        """Handle issues that need transition await from progress step."""
        return self.async_abort(reason="could_not_register")

    async def async_step_reauth(self, _user_input=None):
        """Perform reauth upon an API authentication error."""
        return await self.async_step_reauth_confirm()

    async def async_step_reauth_confirm(self, user_input=None):
        """Dialog that informs the user that reauth is required."""
        if user_input is None:
            return self.async_show_form(
                step_id="reauth_confirm",
                data_schema=vol.Schema({}),
            )
        self._reauth = True
        return await self.async_step_device(None)

    @staticmethod
    @callback
    def async_get_options_flow(config_entry):
        return HacsOptionsFlowHandler(config_entry)


class HacsOptionsFlowHandler(OptionsFlow):
    """HACS config flow options handler."""

    def __init__(self, config_entry):
        """Initialize HACS options flow."""
        if AwesomeVersion(HAVERSION) < "2024.11.99":
            self.config_entry = config_entry

    async def async_step_init(self, _user_input=None):
        """Manage the options."""
        return await self.async_step_user()

    async def async_step_user(self, user_input=None):
        """Handle a flow initialized by the user."""
        hacs: HacsBase = self.hass.data.get(DOMAIN)
        if user_input is not None:
            return self.async_create_entry(title="", data={**user_input, "experimental": True})

        if hacs is None or hacs.configuration is None:
            return self.async_abort(reason="not_setup")

        if hacs.queue.has_pending_tasks:
            return self.async_abort(reason="pending_tasks")

        schema = {
            vol.Optional(SIDEPANEL_TITLE, default=hacs.configuration.sidepanel_title): str,
            vol.Optional(SIDEPANEL_ICON, default=hacs.configuration.sidepanel_icon): str,
            vol.Optional(COUNTRY, default=hacs.configuration.country): vol.In(LOCALE),
            vol.Optional(APPDAEMON, default=hacs.configuration.appdaemon): bool,
        }

        return self.async_show_form(step_id="user", data_schema=vol.Schema(schema))


================================================
FILE: custom_components/hacs/const.py
================================================
"""Constants for HACS"""

from typing import TypeVar

from aiogithubapi.common.const import ACCEPT_HEADERS

NAME_SHORT = "HACS"
DOMAIN = "hacs"
CLIENT_ID = "395a8e669c5de9f7c6e8"
MINIMUM_HA_VERSION = "0.0.0"

URL_BASE = "/hacsfiles"

TV = TypeVar("TV")

PACKAGE_NAME = "custom_components.hacs"

DEFAULT_CONCURRENT_TASKS = 15
DEFAULT_CONCURRENT_BACKOFF_TIME = 1

HACS_REPOSITORY_ID = "172733314"

HACS_ACTION_GITHUB_API_HEADERS = {
    "User-Agent": "HACS/action",
    "Accept": ACCEPT_HEADERS["preview"],
}

VERSION_STORAGE = "6"
STORENAME = "hacs"

HACS_SYSTEM_ID = "0717a0cd-745c-48fd-9b16-c8534c9704f9-bc944b0f-fd42-4a58-a072-ade38d1444cd"

LOCALE = [
    "ALL",
    "AF",
    "AL",
    "DZ",
    "AS",
    "AD",
    "AO",
    "AI",
    "AQ",
    "AG",
    "AR",
    "AM",
    "AW",
    "AU",
    "AT",
    "AZ",
    "BS",
    "BH",
    "BD",
    "BB",
    "BY",
    "BE",
    "BZ",
    "BJ",
    "BM",
    "BT",
    "BO",
    "BQ",
    "BA",
    "BW",
    "BV",
    "BR",
    "IO",
    "BN",
    "BG",
    "BF",
    "BI",
    "KH",
    "CM",
    "CA",
    "CV",
    "KY",
    "CF",
    "TD",
    "CL",
    "CN",
    "CX",
    "CC",
    "CO",
    "KM",
    "CG",
    "CD",
    "CK",
    "CR",
    "HR",
    "CU",
    "CW",
    "CY",
    "CZ",
    "CI",
    "DK",
    "DJ",
    "DM",
    "DO",
    "EC",
    "EG",
    "SV",
    "GQ",
    "ER",
    "EE",
    "ET",
    "FK",
    "FO",
    "FJ",
    "FI",
    "FR",
    "GF",
    "PF",
    "TF",
    "GA",
    "GM",
    "GE",
    "DE",
    "GH",
    "GI",
    "GR",
    "GL",
    "GD",
    "GP",
    "GU",
    "GT",
    "GG",
    "GN",
    "GW",
    "GY",
    "HT",
    "HM",
    "VA",
    "HN",
    "HK",
    "HU",
    "IS",
    "IN",
    "ID",
    "IR",
    "IQ",
    "IE",
    "IM",
    "IL",
    "IT",
    "JM",
    "JP",
    "JE",
    "JO",
    "KZ",
    "KE",
    "KI",
    "KP",
    "KR",
    "KW",
    "KG",
    "LA",
    "LV",
    "LB",
    "LS",
    "LR",
    "LY",
    "LI",
    "LT",
    "LU",
    "MO",
    "MK",
    "MG",
    "MW",
    "MY",
    "MV",
    "ML",
    "MT",
    "MH",
    "MQ",
    "MR",
    "MU",
    "YT",
    "MX",
    "FM",
    "MD",
    "MC",
    "MN",
    "ME",
    "MS",
    "MA",
    "MZ",
    "MM",
    "NA",
    "NR",
    "NP",
    "NL",
    "NC",
    "NZ",
    "NI",
    "NE",
    "NG",
    "NU",
    "NF",
    "MP",
    "NO",
    "OM",
    "PK",
    "PW",
    "PS",
    "PA",
    "PG",
    "PY",
    "PE",
    "PH",
    "PN",
    "PL",
    "PT",
    "PR",
    "QA",
    "RO",
    "RU",
    "RW",
    "RE",
    "BL",
    "SH",
    "KN",
    "LC",
    "MF",
    "PM",
    "VC",
    "WS",
    "SM",
    "ST",
    "SA",
    "SN",
    "RS",
    "SC",
    "SL",
    "SG",
    "SX",
    "SK",
    "SI",
    "SB",
    "SO",
    "ZA",
    "GS",
    "SS",
    "ES",
    "LK",
    "SD",
    "SR",
    "SJ",
    "SZ",
    "SE",
    "CH",
    "SY",
    "TW",
    "TJ",
    "TZ",
    "TH",
    "TL",
    "TG",
    "TK",
    "TO",
    "TT",
    "TN",
    "TR",
    "TM",
    "TC",
    "TV",
    "UG",
    "UA",
    "AE",
    "GB",
    "US",
    "UM",
    "UY",
    "UZ",
    "VU",
    "VE",
    "VN",
    "VG",
    "VI",
    "WF",
    "EH",
    "YE",
    "ZM",
    "ZW",
]


================================================
FILE: custom_components/hacs/coordinator.py
================================================
"""Coordinator to trigger entity updates."""

from __future__ import annotations

from collections.abc import Callable
from typing import Any

from homeassistant.core import CALLBACK_TYPE, callback
from homeassistant.helpers.update_coordinator import BaseDataUpdateCoordinatorProtocol


class HacsUpdateCoordinator(BaseDataUpdateCoordinatorProtocol):
    """Dispatch updates to update entities."""

    def __init__(self) -> None:
        """Initialize."""
        self._listeners: dict[CALLBACK_TYPE, tuple[CALLBACK_TYPE, object | None]] = {}

    @callback
    def async_add_listener(
        self, update_callback: CALLBACK_TYPE, context: Any = None
    ) -> Callable[[], None]:
        """Listen for data updates."""

        @callback
        def remove_listener() -> None:
            """Remove update listener."""
            self._listeners.pop(remove_listener)

        self._listeners[remove_listener] = (update_callback, context)

        return remove_listener

    @callback
    def async_update_listeners(self) -> None:
        """Update all registered listeners."""
        for update_callback, _ in list(self._listeners.values()):
            update_callback()


================================================
FILE: custom_components/hacs/data_client.py
================================================
"""HACS Data client."""

from __future__ import annotations

import asyncio
from typing import Any

from aiohttp import ClientSession, ClientTimeout
import voluptuous as vol

from .exceptions import HacsException, HacsNotModifiedException
from .utils.logger import LOGGER
from .utils.validate import (
    VALIDATE_FETCHED_V2_CRITICAL_REPO_SCHEMA,
    VALIDATE_FETCHED_V2_REMOVED_REPO_SCHEMA,
    VALIDATE_FETCHED_V2_REPO_DATA,
)

CRITICAL_REMOVED_VALIDATORS = {
    "critical": VALIDATE_FETCHED_V2_CRITICAL_REPO_SCHEMA,
    "removed": VALIDATE_FETCHED_V2_REMOVED_REPO_SCHEMA,
}


class HacsDataClient:
    """HACS Data client."""

    def __init__(self, session: ClientSession, client_name: str) -> None:
        """Initialize."""
        self._client_name = client_name
        self._etags = {}
        self._session = session

    async def _do_request(
        self,
        filename: str,
        section: str | None = None,
    ) -> dict[str, dict[str, Any]] | list[str]:
        """Do request."""
        endpoint = "/".join([v for v in [section, filename] if v is not None])
        try:
            response = await self._session.get(
                f"https://data-v2.hacs.xyz/{endpoint}",
                timeout=ClientTimeout(total=60),
                headers={
                    "User-Agent": self._client_name,
                    "If-None-Match": self._etags.get(endpoint, ""),
                },
            )
            if response.status == 304:
                raise HacsNotModifiedException() from None
            response.raise_for_status()
        except HacsNotModifiedException:
            raise
        except TimeoutError:
            raise HacsException("Timeout of 60s reached") from None
        except Exception as exception:
            raise HacsException(f"Error fetching data from HACS: {exception}") from exception

        self._etags[endpoint] = response.headers.get("etag")

        return await response.json()

    async def get_data(self, section: str | None, *, validate: bool) -> dict[str, dict[str, Any]]:
        """Get data."""
        data = await self._do_request(filename="data.json", section=section)
        if not validate:
            return data

        if section in VALIDATE_FETCHED_V2_REPO_DATA:
            validated = {}
            for key, repo_data in data.items():
                try:
                    validated[key] = VALIDATE_FETCHED_V2_REPO_DATA[section](repo_data)
                except vol.Invalid as exception:
                    LOGGER.info(
                        "Got invalid data for %s (%s)", repo_data.get("full_name", key), exception
                    )
                    continue

            return validated

        if not (validator := CRITICAL_REMOVED_VALIDATORS.get(section)):
            raise ValueError(f"Do not know how to validate {section}")

        validated = []
        for repo_data in data:
            try:
                validated.append(validator(repo_data))
            except vol.Invalid as exception:
                LOGGER.info("Got invalid data for %s (%s)", section, exception)
                continue

        return validated

    async def get_repositories(self, section: str) -> list[str]:
        """Get repositories."""
        return await self._do_request(filename="repositories.json", section=section)


================================================
FILE: custom_components/hacs/diagnostics.py
================================================
"""Diagnostics support for HACS."""

from __future__ import annotations

from typing import Any

from aiogithubapi import GitHubException
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant

from .base import HacsBase
from .const import DOMAIN


async def async_get_config_entry_diagnostics(
    hass: HomeAssistant,
    entry: ConfigEntry,
) -> dict[str, Any]:
    """Return diagnostics for a config entry."""
    hacs: HacsBase = hass.data[DOMAIN]

    data = {
        "entry": entry.as_dict(),
        "hacs": {
            "stage": hacs.stage,
            "version": hacs.version,
            "disabled_reason": hacs.system.disabled_reason,
            "new": hacs.status.new,
            "startup": hacs.status.startup,
            "categories": hacs.common.categories,
            "renamed_repositories": hacs.common.renamed_repositories,
            "archived_repositories": hacs.common.archived_repositories,
            "ignored_repositories": hacs.common.ignored_repositories,
            "lovelace_mode": hacs.core.lovelace_mode,
            "configuration": {},
        },
        "custom_repositories": [
            repo.data.full_name
            for repo in hacs.repositories.list_all
            if not hacs.repositories.is_default(str(repo.data.id))
        ],
        "repositories": [],
    }

    for key in (
        "appdaemon",
        "country",
        "debug",
        "dev",
        "python_script",
        "release_limit",
        "theme",
    ):
        data["hacs"]["configuration"][key] = getattr(hacs.configuration, key, None)

    for repository in hacs.repositories.list_downloaded:
        data["repositories"].append(
            {
                "data": repository.data.to_json(),
                "integration_manifest": repository.integration_manifest,
                "repository_manifest": repository.repository_manifest.to_dict(),
                "ref": repository.ref,
                "paths": {
                    "localpath": repository.localpath.replace(hacs.core.config_path, "/config"),
                    "local": repository.content.path.local.replace(
                        hacs.core.config_path, "/config"
                    ),
                    "remote": repository.content.path.remote,
                },
            }
        )

    try:
        rate_limit_response = await hacs.githubapi.rate_limit()
        data["rate_limit"] = rate_limit_response.data.as_dict
    except GitHubException as exception:
        data["rate_limit"] = str(exception)

    return async_redact_data(data, ("token",))


================================================
FILE: custom_components/hacs/entity.py
================================================
"""HACS Base entities."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from homeassistant.core import callback
from homeassistant.helpers.device_registry import DeviceEntryType
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.update_coordinator import BaseCoordinatorEntity

from .const import DOMAIN, HACS_SYSTEM_ID, NAME_SHORT
from .coordinator import HacsUpdateCoordinator
from .enums import HacsDispatchEvent, HacsGitHubRepo

if TYPE_CHECKING:
    from .base import HacsBase
    from .repositories.base import HacsRepository


def system_info(hacs: HacsBase) -> dict:
    """Return system info."""
    return {
        "identifiers": {(DOMAIN, HACS_SYSTEM_ID)},
        "name": NAME_SHORT,
        "manufacturer": "hacs.xyz",
        "model": "",
        "sw_version": str(hacs.version),
        "configuration_url": "homeassistant://hacs",
        "entry_type": DeviceEntryType.SERVICE,
    }


class HacsBaseEntity(Entity):
    """Base HACS entity."""

    repository: HacsRepository | None = None
    _attr_should_poll = False

    def __init__(self, hacs: HacsBase) -> None:
        """Initialize."""
        self.hacs = hacs


class HacsDispatcherEntity(HacsBaseEntity):
    """Base HACS entity listening to dispatcher signals."""

    async def async_added_to_hass(self) -> None:
        """Register for status events."""
        self.async_on_remove(
            async_dispatcher_connect(
                self.hass,
                HacsDispatchEvent.REPOSITORY,
                self._update_and_write_state,
            )
        )

    @callback
    def _update(self) -> None:
        """Update the sensor."""

    async def async_update(self) -> None:
        """Manual updates of the sensor."""
        self._update()

    @callback
    def _update_and_write_state(self, _: Any) -> None:
        """Update the entity and write state."""
        self._update()
        self.async_write_ha_state()


class HacsSystemEntity(HacsDispatcherEntity):
    """Base system entity."""

    _attr_icon = "hacs:hacs"
    _attr_unique_id = HACS_SYSTEM_ID

    @property
    def device_info(self) -> dict[str, any]:
        """Return device information about HACS."""
        return system_info(self.hacs)


class HacsRepositoryEntity(BaseCoordinatorEntity[HacsUpdateCoordinator], HacsBaseEntity):
    """Base repository entity."""

    def __init__(
        self,
        hacs: HacsBase,
        repository: HacsRepository,
    ) -> None:
        """Initialize."""
        BaseCoordinatorEntity.__init__(self, hacs.coordinators[repository.data.category])
        HacsBaseEntity.__init__(self, hacs=hacs)
        self.repository = repository
        self._attr_unique_id = str(repository.data.id)
        self._repo_last_fetched = repository.data.last_fetched

    @property
    def available(self) -> bool:
        """Return True if entity is available."""
        return self.hacs.repositories.is_downloaded(repository_id=str(self.repository.data.id))

    @property
    def device_info(self) -> dict[str, any]:
        """Return device information about HACS."""
        if self.repository.data.full_name == HacsGitHubRepo.INTEGRATION:
            return system_info(self.hacs)

        def _manufacturer():
            if authors := self.repository.data.authors:
                return ", ".join(author.replace("@", "") for author in authors)
            return self.repository.data.full_name.split("/")[0]

        return {
            "identifiers": {(DOMAIN, str(self.repository.data.id))},
            "name": self.repository.display_name,
            "model": self.repository.data.category,
            "manufacturer": _manufacturer(),
            "configuration_url": f"homeassistant://hacs/repository/{self.repository.data.id}",
            "entry_type": DeviceEntryType.SERVICE,
        }

    @callback
    def _handle_coordinator_update(self) -> None:
        """Handle updated data from the coordinator."""
        if (
            self._repo_last_fetched is not None
            and self.repository.data.last_fetched is not None
            and self._repo_last_fetched >= self.repository.data.last_fetched
        ):
            return

        self._repo_last_fetched = self.repository.data.last_fetched
        self.async_write_ha_state()

    async def async_update(self) -> None:
        """Update the entity.

        Only used by the generic entity update service.
        """


================================================
FILE: custom_components/hacs/enums.py
================================================
"""Helper constants."""

# pylint: disable=missing-class-docstring
from enum import StrEnum


class HacsGitHubRepo(StrEnum):
    """HacsGitHubRepo."""

    DEFAULT = "hacs/default"
    INTEGRATION = "hacs/integration"


class HacsCategory(StrEnum):
    APPDAEMON = "appdaemon"
    INTEGRATION = "integration"
    LOVELACE = "lovelace"
    PLUGIN = "plugin"  # Kept for legacy purposes
    PYTHON_SCRIPT = "python_script"
    TEMPLATE = "template"
    THEME = "theme"
    REMOVED = "removed"

    def __str__(self):
        return str(self.value)


class HacsDispatchEvent(StrEnum):
    """HacsDispatchEvent."""

    CONFIG = "hacs_dispatch_config"
    ERROR = "hacs_dispatch_error"
    RELOAD = "hacs_dispatch_reload"
    REPOSITORY = "hacs_dispatch_repository"
    REPOSITORY_DOWNLOAD_PROGRESS = "hacs_dispatch_repository_download_progress"
    STAGE = "hacs_dispatch_stage"
    STARTUP = "hacs_dispatch_startup"
    STATUS = "hacs_dispatch_status"


class RepositoryFile(StrEnum):
    """Repository file names."""

    HACS_JSON = "hacs.json"
    MAINIFEST_JSON = "manifest.json"


class LovelaceMode(StrEnum):
    """Lovelace Modes."""

    STORAGE = "storage"
    AUTO = "auto"
    AUTO_GEN = "auto-gen"
    YAML = "yaml"


class HacsStage(StrEnum):
    SETUP = "setup"
    STARTUP = "startup"
    WAITING = "waiting"
    RUNNING = "running"
    BACKGROUND = "background"


class HacsDisabledReason(StrEnum):
    RATE_LIMIT = "rate_limit"
    REMOVED = "removed"
    INVALID_TOKEN = "invalid_token"
    CONSTRAINS = "constrains"
    LOAD_HACS = "load_hacs"
    RESTORE = "restore"


================================================
FILE: custom_components/hacs/exceptions.py
================================================
"""Custom Exceptions for HACS."""


class HacsException(Exception):
    """Super basic."""


class HacsRepositoryArchivedException(HacsException):
    """For repositories that are archived."""


class HacsNotModifiedException(HacsException):
    """For responses that are not modified."""


class HacsExpectedException(HacsException):
    """For stuff that are expected."""


class HacsRepositoryExistException(HacsException):
    """For repositories that are already exist."""


class HacsExecutionStillInProgress(HacsException):
    """Exception to raise if execution is still in progress."""


class AddonRepositoryException(HacsException):
    """Exception to raise when user tries to add add-on repository."""

    exception_message = (
        "The repository does not seem to be a integration, "
        "but an add-on repository. HACS does not manage add-ons."
    )

    def __init__(self) -> None:
        super().__init__(self.exception_message)


class HomeAssistantCoreRepositoryException(HacsException):
    """Exception to raise when user tries to add the home-assistant/core repository."""

    exception_message = (
        "You can not add homeassistant/core, to use core integrations "
        "check the Home Assistant documentation for how to add them."
    )

    def __init__(self) -> None:
        super().__init__(self.exception_message)


================================================
FILE: custom_components/hacs/frontend.py
================================================
"""Starting setup task: Frontend."""

from __future__ import annotations

import os
from typing import TYPE_CHECKING

from homeassistant.components.frontend import (
    add_extra_js_url,
    async_register_built_in_panel,
)

from .const import DOMAIN, URL_BASE
from .hacs_frontend import VERSION as FE_VERSION, locate_dir
from .utils.workarounds import async_register_static_path

if TYPE_CHECKING:
    from homeassistant.core import HomeAssistant

    from .base import HacsBase


async def async_register_frontend(hass: HomeAssistant, hacs: HacsBase) -> None:
    """Register the frontend."""

    # Register frontend
    if hacs.configuration.dev and (frontend_path := os.getenv("HACS_FRONTEND_DIR")):
        hacs.log.warning(
            "<HacsFrontend> Frontend development mode enabled. Do not run in production!"
        )
        await async_register_static_path(
            hass, f"{URL_BASE}/frontend", f"{frontend_path}/hacs_frontend", cache_headers=False
        )
        hacs.frontend_version = "dev"
    else:
        await async_register_static_path(
            hass, f"{URL_BASE}/frontend", locate_dir(), cache_headers=False
        )
        hacs.frontend_version = FE_VERSION

    # Custom iconset
    await async_register_static_path(
        hass, f"{URL_BASE}/iconset.js", str(hacs.integration_dir / "iconset.js")
    )
    add_extra_js_url(hass, f"{URL_BASE}/iconset.js")

    # Add to sidepanel if needed
    if DOMAIN not in hass.data.get("frontend_panels", {}):
        async_register_built_in_panel(
            hass,
            component_name="custom",
            sidebar_title=hacs.configuration.sidepanel_title,
            sidebar_icon=hacs.configuration.sidepanel_icon,
            frontend_url_path=DOMAIN,
            config={
                "_panel_custom": {
                    "name": "hacs-frontend",
                    "embed_iframe": True,
                    "trust_external": False,
                    "js_url": f"/hacsfiles/frontend/entrypoint.js?hacstag={hacs.frontend_version}",
                }
            },
            require_admin=True,
        )

    # Setup plugin endpoint if needed
    await hacs.async_setup_frontend_endpoint_plugin()


================================================
FILE: custom_components/hacs/icons.json
================================================
{
  "entity": {
    "switch": {
      "pre-release": {
        "state": {
          "on": "mdi:test-tube",
          "off": "mdi:test-tube-off"
        }
      }
    }
  }
}

================================================
FILE: custom_components/hacs/iconset.js
================================================
const hacsIcons = {
  hacs: {
    path: "m 20.064849,22.306912 c -0.0319,0.369835 -0.280561,0.707789 -0.656773,0.918212 -0.280572,0.153036 -0.605773,0.229553 -0.950094,0.229553 -0.0765,0 -0.146661,-0.0064 -0.216801,-0.01275 -0.605774,-0.05739 -1.135016,-0.344329 -1.402827,-0.7588 l 0.784304,-0.516495 c 0.0893,0.146659 0.344331,0.312448 0.707793,0.34433 0.235931,0.02551 0.471852,-0.01913 0.637643,-0.108401 0.101998,-0.05101 0.172171,-0.127529 0.17854,-0.191295 0.0065,-0.08289 -0.0255,-0.369835 -0.733293,-0.439975 -1.013854,-0.09565 -1.645127,-0.688661 -1.568606,-1.460214 0.0319,-0.382589 0.280561,-0.714165 0.663153,-0.930965 0.331571,-0.172165 0.752423,-0.25506 1.166895,-0.210424 0.599382,0.05739 1.128635,0.344329 1.402816,0.7588 l -0.784304,0.510118 c -0.0893,-0.140282 -0.344331,-0.299694 -0.707782,-0.331576 -0.235932,-0.02551 -0.471863,0.01913 -0.637654,0.10202 -0.0956,0.05739 -0.165791,0.133906 -0.17216,0.191295 -0.0255,0.293317 0.465482,0.420847 0.726913,0.439976 v 0.0064 c 1.020234,0.09565 1.638757,0.66953 1.562237,1.460213 z m -7.466854,-0.988354 c 0,-1.192401 0.962855,-2.155249 2.15525,-2.155249 0.599393,0 1.179645,0.25506 1.594117,0.707789 l -0.695033,0.624895 c -0.235931,-0.25506 -0.561133,-0.401718 -0.899084,-0.401718 -0.675903,0 -1.217906,0.542 -1.217906,1.217906 0,0.66953 0.542003,1.217908 1.217906,1.217908 0.337951,0 0.663153,-0.140283 0.899084,-0.401718 l 0.695033,0.631271 c -0.414472,0.452729 -0.988355,0.707788 -1.594117,0.707788 -1.192395,0 -2.15525,-0.969224 -2.15525,-2.148872 z M 8.6573365,23.461054 10.353474,19.14418 h 0.624893 l 1.568618,4.316874 H 11.52037 L 11.265308,22.734136 H 9.964513 l -0.274192,0.726918 z m 1.6833885,-1.68339 h 0.580263 L 10.646796,21.012487 Z M 8.1089536,19.156932 v 4.297745 H 7.1461095 v -1.645131 h -1.606867 v 1.645131 H 4.5763876 v -4.297745 h 0.9628549 v 1.696143 h 1.606867 V 19.156932 Z M 20.115859,4.2997436 C 20.090359,4.159461 19.969198,4.0574375 19.822548,4.0574375 H 14.141102 10.506516 4.8250686 c -0.14665,0 -0.2678112,0.1020202 -0.2933108,0.2423061 L 3.690064,8.8461703 c -0.00651,0.01913 -0.00651,0.03826 -0.00651,0.057391 v 1.5239797 c 0,0.165789 0.133911,0.299694 0.2996911,0.299694 H 4.5762579 20.0711 20.664112 c 0.165781,0 0.299691,-0.133905 0.299691,-0.299694 V 8.8971848 c 0,-0.01913 0,-0.03826 -0.0065,-0.05739 z M 4.5763876,17.358767 c 0,0.184917 0.1466608,0.331577 0.3315819,0.331577 h 5.5985465 3.634586 0.924594 c 0.184911,0 0.331571,-0.14666 0.331571,-0.331577 v -4.744098 c 0,-0.184918 0.146661,-0.331577 0.331582,-0.331577 h 2.894913 c 0.184921,0 0.331582,0.146659 0.331582,0.331577 v 4.744098 c 0,0.184917 0.146661,0.331577 0.331571,0.331577 h 0.446363 c 0.18491,0 0.331571,-0.14666 0.331571,-0.331577 v -5.636804 c 0,-0.184918 -0.146661,-0.331577 -0.331571,-0.331577 H 4.9079695 c -0.1849211,0 -0.3315819,0.146659 -0.3315819,0.331577 z m 1.6578879,-4.852498 h 5.6495565 c 0.15303,0 0.280561,0.12753 0.280561,0.280564 v 3.513438 c 0,0.153036 -0.127531,0.280566 -0.280561,0.280566 H 6.2342755 c -0.1530412,0 -0.2805719,-0.12753 -0.2805719,-0.280566 v -3.513438 c 0,-0.159411 0.1275307,-0.280564 0.2805719,-0.280564 z M 19.790657,3.3879075 H 4.8569594 c -0.1530412,0 -0.2805718,-0.1275296 -0.2805718,-0.2805642 V 1.3665653 C 4.5763876,1.2135296 4.7039182,1.086 4.8569594,1.086 H 19.790657 c 0.153041,0 0.280572,0.1275296 0.280572,0.2805653 v 1.740778 c 0,0.1530346 -0.127531,0.2805642 -0.280572,0.2805642 z",
    keywords: ["hacs", "home assistant community store"],
  },
};

window.customIcons = window.customIcons || {};
window.customIconsets = window.customIconsets || {};

window.customIcons["hacs"] = {
  getIcon: async (iconName) => (
    { path: hacsIcons[iconName]?.path }
  ),
  getIconList: async () =>
    Object.entries(hacsIcons).map(([icon, content]) => ({
      name: icon,
      keywords: content.keywords,
    })
    )
};


================================================
FILE: custom_components/hacs/manifest.json
================================================
{
    "domain": "hacs",
    "name": "HACS",
    "after_dependencies": [
        "python_script"
    ],
    "codeowners": [
        "@ludeeus"
    ],
    "config_flow": true,
    "dependencies": [
        "http",
        "websocket_api",
        "frontend",
        "persistent_notification",
        "lovelace",
        "repairs"
    ],
    "documentation": "https://hacs.xyz/docs/use/",
    "iot_class": "cloud_polling",
    "issue_tracker": "https://github.com/hacs/integration/issues",
    "requirements": [
        "aiogithubapi>=22.10.1"
    ],
    "version": "0.0.0"
}


================================================
FILE: custom_components/hacs/repairs.py
================================================
"""Repairs platform for HACS."""

from __future__ import annotations

from typing import Any

from homeassistant import data_entry_flow
from homeassistant.components.repairs import RepairsFlow
from homeassistant.core import HomeAssistant
import voluptuous as vol

from custom_components.hacs.base import HacsBase

from .const import DOMAIN


class RestartRequiredFixFlow(RepairsFlow):
    """Handler for an issue fixing flow."""

    def __init__(self, issue_id: str) -> None:
        self.issue_id = issue_id

    async def async_step_init(
        self, user_input: dict[str, str] | None = None
    ) -> data_entry_flow.FlowResult:
        """Handle the first step of a fix flow."""

        return await self.async_step_confirm_restart()

    async def async_step_confirm_restart(
        self, user_input: dict[str, str] | None = None
    ) -> data_entry_flow.FlowResult:
        """Handle the confirm step of a fix flow."""
        if user_input is not None:
            await self.hass.services.async_call("homeassistant", "restart")
            return self.async_create_entry(title="", data={})

        hacs: HacsBase = self.hass.data[DOMAIN]
        integration = hacs.repositories.get_by_id(self.issue_id.split("_")[2])

        return self.async_show_form(
            step_id="confirm_restart",
            data_schema=vol.Schema({}),
            description_placeholders={"name": integration.display_name},
        )


async def async_create_fix_flow(
    hass: HomeAssistant,
    issue_id: str,
    data: dict[str, str | int | float | None] | None = None,
    *args: Any,
    **kwargs: Any,
) -> RepairsFlow | None:
    """Create flow."""
    if issue_id.startswith("restart_required"):
        return RestartRequiredFixFlow(issue_id)
    return None


================================================
FILE: custom_components/hacs/repositories/__init__.py
================================================
"""Initialize repositories."""

from __future__ import annotations

from ..enums import HacsCategory
from .appdaemon import HacsAppdaemonRepository
from .base import HacsRepository
from .integration import HacsIntegrationRepository
from .plugin import HacsPluginRepository
from .python_script import HacsPythonScriptRepository
from .template import HacsTemplateRepository
from .theme import HacsThemeRepository

REPOSITORY_CLASSES: dict[HacsCategory, HacsRepository] = {
    HacsCategory.THEME: HacsThemeRepository,
    HacsCategory.INTEGRATION: HacsIntegrationRepository,
    HacsCategory.PYTHON_SCRIPT: HacsPythonScriptRepository,
    HacsCategory.APPDAEMON: HacsAppdaemonRepository,
    HacsCategory.PLUGIN: HacsPluginRepository,
    HacsCategory.TEMPLATE: HacsTemplateRepository,
}


================================================
FILE: custom_components/hacs/repositories/appdaemon.py
================================================
"""Class for appdaemon apps in HACS."""

from __future__ import annotations

from typing import TYPE_CHECKING

from aiogithubapi import AIOGitHubAPIException

from ..enums import HacsCategory, HacsDispatchEvent
from ..exceptions import HacsException
from ..utils.decorator import concurrent
from .base import HacsRepository

if TYPE_CHECKING:
    from ..base import HacsBase


class HacsAppdaemonRepository(HacsRepository):
    """Appdaemon apps in HACS."""

    def __init__(self, hacs: HacsBase, full_name: str):
        """Initialize."""
        super().__init__(hacs=hacs)
        self.data.full_name = full_name
        self.data.full_name_lower = full_name.lower()
        self.data.category = HacsCategory.APPDAEMON
        self.content.path.local = self.localpath
        self.content.path.remote = "apps"

    @property
    def localpath(self):
        """Return localpath."""
        return f"{self.hacs.core.config_path}/appdaemon/apps/{self.data.name}"

    async def validate_repository(self):
        """Validate."""
        await self.common_validate()

        # Custom step 1: Validate content.
        try:
            addir = await self.repository_object.get_contents("apps", self.ref)
        except AIOGitHubAPIException:
            raise HacsException(
                f"{self.string} Repository structure for {self.ref.replace('tags/', '')} is not compliant"
            ) from None

        if not isinstance(addir, list):
            self.validate.errors.append(f"{self.string} Repository structure not compliant")

        self.content.path.remote = addir[0].path
        self.content.objects = await self.repository_object.get_contents(
            self.content.path.remote, self.ref
        )

        # Handle potential errors
        if self.validate.errors:
            for error in self.validate.errors:
                if not self.hacs.status.startup:
                    self.logger.error("%s %s", self.string, error)
        return self.validate.success

    @concurrent(concurrenttasks=10, backoff_time=5)
    async def update_repository(self, ignore_issues=False, force=False):
        """Update."""
        if not await self.common_update(ignore_issues, force) and not force:
            return

        # Get appdaemon objects.
        if self.repository_manifest:
            if self.repository_manifest.content_in_root:
                self.content.path.remote = ""

        if self.content.path.remote == "apps":
            addir = await self.repository_object.get_contents(self.content.path.remote, self.ref)
            self.content.path.remote = addir[0].path
        self.content.objects = await self.repository_object.get_contents(
            self.content.path.remote, self.ref
        )

        # Set local path
        self.content.path.local = self.localpath

        # Signal frontend to refresh
        if self.data.installed:
            self.hacs.async_dispatch(
                HacsDispatchEvent.REPOSITORY,
                {
                    "id": 1337,
                    "action": "update",
                    "repository": self.data.full_name,
                    "repository_id": self.data.id,
                },
            )


================================================
FILE: custom_components/hacs/repositories/base.py
================================================
"""Repository."""

from __future__ import annotations

from asyncio import sleep
from datetime import UTC, datetime
import os
import pathlib
import shutil
import tempfile
from typing import TYPE_CHECKING, Any
import zipfile

from aiogithubapi import (
    AIOGitHubAPIException,
    AIOGitHubAPINotModifiedException,
    GitHubReleaseModel,
)
from aiogithubapi.objects.repository import AIOGitHubAPIRepository
import attr
from homeassistant.helpers import device_registry as dr, issue_registry as ir

from ..const import DOMAIN
from ..enums import HacsDispatchEvent, RepositoryFile
from ..exceptions import (
    HacsException,
    HacsNotModifiedException,
    HacsRepositoryArchivedException,
    HacsRepositoryExistException,
)
from ..types import DownloadableContent
from ..utils.backup import Backup
from ..utils.decode import decode_content
from ..utils.decorator import concurrent, return_none_on_exception
from ..utils.file_system import async_exists, async_remove, async_remove_directory
from ..utils.filters import filter_content_return_one_of_type
from ..utils.github_graphql_query import GET_REPOSITORY_RELEASES
from ..utils.json import json_loads
from ..utils.logger import LOGGER
from ..utils.path import is_safe
from ..utils.queue_manager import QueueManager
from ..utils.store import async_remove_store
from ..utils.url import github_archive, github_release_asset
from ..utils.validate import Validate
from ..utils.version import (
    version_left_higher_or_equal_then_right,
    version_left_higher_then_right,
)
from ..utils.workarounds import DOMAIN_OVERRIDES

if TYPE_CHECKING:
    from ..base import HacsBase


TOPIC_FILTER = (
    "add-on",
    "addon",
    "app",
    "appdaemon-apps",
    "appdaemon",
    "custom-card",
    "custom-cards",
    "custom-component",
    "custom-components",
    "customcomponents",
    "hacktoberfest",
    "hacs-default",
    "hacs-integration",
    "hacs-repository",
    "hacs",
    "hass",
    "hassio",
    "home-assistant-custom",
    "home-assistant-frontend",
    "home-assistant-hacs",
    "home-assistant-sensor",
    "home-assistant",
    "home-automation",
    "homeassistant-components",
    "homeassistant-integration",
    "homeassistant-sensor",
    "homeassistant",
    "homeautomation",
    "integration",
    "lovelace-ui",
    "lovelace",
    "media-player",
    "mediaplayer",
    "plugin",
    "python_script",
    "python-script",
    "python",
    "sensor",
    "smart-home",
    "smarthome",
    "template",
    "templates",
    "theme",
    "themes",
)


REPOSITORY_KEYS_TO_EXPORT = (
    # Keys can not be removed from this list until v3
    # If keys are added, the action need to be re-run with force
    ("description", ""),
    ("downloads", 0),
    ("domain", None),
    ("etag_releases", None),
    ("etag_repository", None),
    ("full_name", ""),
    ("last_commit", None),
    ("last_updated", 0),
    ("last_version", None),
    ("manifest_name", None),
    ("open_issues", 0),
    ("prerelease", None),
    ("stargazers_count", 0),
    ("topics", []),
)

HACS_MANIFEST_KEYS_TO_EXPORT = (
    # Keys can not be removed from this list until v3
    # If keys are added, the action need to be re-run with force
    ("country", []),
    ("name", None),
)


class FileInformation:
    """FileInformation."""

    def __init__(self, url, path, name):
        self.download_url = url
        self.path = path
        self.name = name


@attr.s(auto_attribs=True)
class RepositoryData:
    """RepositoryData class."""

    archived: bool = False
    authors: list[str] = []
    category: str = ""
    config_flow: bool = False
    default_branch: str = None
    description: str = ""
    domain: str = None
    downloads: int = 0
    etag_repository: str = None
    etag_releases: str = None
    file_name: str = ""
    first_install: bool = False
    full_name: str = ""
    hide: bool = False
    has_issues: bool = True
    id: int = 0
    installed_commit: str = None
    installed_version: str = None
    installed: bool = False
    last_commit: str = None
    last_fetched: datetime = None
    last_updated: str = 0
    last_version: str = None
    manifest_name: str = None
    new: bool = True
    open_issues: int = 0
    prerelease: str = None
    published_tags: list[str] = []
    releases: bool = False
    selected_tag: str = None
    show_beta: bool = False
    stargazers_count: int = 0
    topics: list[str] = []

    @property
    def name(self):
        """Return the name."""
        if self.category == "integration":
            return self.domain
        return self.full_name.split("/")[-1]

    def to_json(self):
        """Export to json."""
        return attr.asdict(self, filter=lambda attr, value: attr.name != "last_fetched")

    @staticmethod
    def create_from_dict(source: dict, action: bool = False) -> RepositoryData:
        """Set attributes from dicts."""
        data = RepositoryData()
        data.update_data(source, action)
        return data

    def update_data(self, data: dict, action: bool = False) -> None:
        """Update data of the repository."""
        for key, value in data.items():
            if key not in self.__dict__:
                continue

            if key == "last_fetched" and isinstance(value, float):
                setattr(self, key, datetime.fromtimestamp(value, UTC))
            elif key == "id":
                setattr(self, key, str(value))
            elif key == "country":
                if isinstance(value, str):
                    setattr(self, key, [value])
                else:
                    setattr(self, key, value)
            elif key == "topics" and not action:
                setattr(self, key, [topic for topic in value if topic not in TOPIC_FILTER])

            else:
                setattr(self, key, value)


@attr.s(auto_attribs=True)
class HacsManifest:
    """HacsManifest class."""

    content_in_root: bool = False
    country: list[str] = []
    filename: str = None
    hacs: str = None  # Minimum HACS version
    hide_default_branch: bool = False
    homeassistant: str = None  # Minimum Home Assistant version
    manifest: dict = {}
    name: str = None
    persistent_directory: str = None
    render_readme: bool = False
    zip_release: bool = False

    def to_dict(self):
        """Export to json."""
        return attr.asdict(self)

    @staticmethod
    def from_dict(manifest: dict):
        """Set attributes from dicts."""
        if manifest is None:
            raise HacsException("Missing manifest data")

        manifest_data = HacsManifest()
        manifest_data.manifest = {
            k: v
            for k, v in manifest.items()
            if k in manifest_data.__dict__ and v != manifest_data.__getattribute__(k)
        }

        for key, value in manifest_data.manifest.items():
            if key == "country" and isinstance(value, str):
                setattr(manifest_data, key, [value])
            elif key in manifest_data.__dict__:
                setattr(manifest_data, key, value)
        return manifest_data

    def update_data(self, data: dict) -> None:
        """Update the manifest data."""
        for key, value in data.items():
            if key not in self.__dict__:
                continue

            if key == "country":
                if isinstance(value, str):
                    setattr(self, key, [value])
                else:
                    setattr(self, key, value)
            else:
                setattr(self, key, value)


class RepositoryReleases:
    """RepositoyReleases."""

    last_release = None
    last_release_object = None
    published_tags = []
    objects: list[GitHubReleaseModel] = []
    releases = False
    downloads = None


class RepositoryPath:
    """RepositoryPath."""

    local: str | None = None
    remote: str | None = None


class RepositoryContent:
    """RepositoryContent."""

    path: RepositoryPath | None = None
    files = []
    objects = []
    single = False


class HacsRepository:
    """HacsRepository."""

    def __init__(self, hacs: HacsBase) -> None:
        """Set up HacsRepository."""
        self.hacs = hacs
        self.additional_info = ""
        self.data = RepositoryData()
        self.content = RepositoryContent()
        self.content.path = RepositoryPath()
        self.repository_object: AIOGitHubAPIRepository | None = None
        self.updated_info = False
        self.state = None
        self.force_branch = False
        self.integration_manifest = {}
        self.repository_manifest = HacsManifest.from_dict({})
        self.validate = Validate()
        self.releases = RepositoryReleases()
        self.pending_restart = False
        self.tree = []
        self.treefiles = []
        self.ref = None
        self.logger = LOGGER

    def __str__(self) -> str:
        """Return a string representation of the repository."""
        return self.string

    @property
    def string(self) -> str:
        """Return a string representation of the repository."""
        return f"<{self.data.category.title()} {self.data.full_name}>"

    @property
    def display_name(self) -> str:
        """Return display name."""
        if self.repository_manifest.name is not None:
            return self.repository_manifest.name

        if self.data.category == "integration":
            if self.data.manifest_name is not None:
                return self.data.manifest_name
            if "name" in self.integration_manifest:
                return self.integration_manifest["name"]

        return self.data.full_name.split("/")[-1].replace("-", " ").replace("_", " ").title()

    @property
    def ignored_by_country_configuration(self) -> bool:
        """Return True if hidden by country."""
        if self.data.installed:
            return False
        configuration = self.hacs.configuration.country.lower()
        if configuration == "all":
            return False

        manifest = [entry.lower() for entry in self.repository_manifest.country or []]
        if not manifest:
            return False
        return configuration not in manifest

    @property
    def display_status(self) -> str:
        """Return display_status."""
        if self.data.new:
            status = "new"
        elif self.pending_restart:
            status = "pending-restart"
        elif self.pending_update:
            status = "pending-upgrade"
        elif self.data.installed:
            status = "installed"
        else:
            status = "default"
        return status

    @property
    def display_installed_version(self) -> str:
        """Return display_authors"""
        if self.data.installed_version is not None:
            installed = self.data.installed_version
        else:
            if self.data.installed_commit is not None:
                installed = self.data.installed_commit
            else:
                installed = ""
        return str(installed)

    @property
    def display_available_version(self) -> str:
        """Return display_authors"""
        if self.data.show_beta and self.data.prerelease is not None:
            available = self.data.prerelease
        elif self.data.last_version is not None:
            available = self.data.last_version
        else:
            if self.data.last_commit is not None:
                available = self.data.last_commit
            else:
                available = ""
        return str(available)

    @property
    def display_version_or_commit(self) -> str:
        """Does the repositoriy use releases or commits?"""
        if self.data.releases:
            version_or_commit = "version"
        else:
            version_or_commit = "commit"
        return version_or_commit

    @property
    def pending_update(self) -> bool:
        """Return True if pending update."""
        if self.data.installed:
            if self.data.selected_tag is not None:
                if self.data.selected_tag == self.data.default_branch:
                    if self.data.installed_commit != self.data.last_commit:
                        return True
                    return False
            if self.display_version_or_commit == "version":
                if (
                    result := version_left_higher_then_right(
                        self.display_available_version,
                        self.display_installed_version,
                    )
                ) is not None:
                    return result
            if self.display_installed_version != self.display_available_version:
                return True

        return False

    @property
    def can_download(self) -> bool:
        """Return True if we can download."""
        if self.repository_manifest.homeassistant is not None:
            if self.data.releases:
                if not version_left_higher_or_equal_then_right(
                    self.hacs.core.ha_version.string,
                    self.repository_manifest.homeassistant,
                ):
                    return False
        return True

    @property
    def localpath(self) -> str | None:
        """Return localpath."""
        return None

    @property
    def should_try_releases(self) -> bool:
        """Return a boolean indicating whether to download releases or not."""
        if self.repository_manifest.zip_release:
            if self.repository_manifest.filename.endswith(".zip"):
                if self.ref != self.data.default_branch:
                    return True
        if self.ref == self.data.default_branch:
            return False
        if self.data.category not in ["plugin", "theme"]:
            return False
        if not self.data.releases:
            return False
        return True

    async def validate_repository(self) -> None:
        """Validate."""

    @concurrent(concurrenttasks=10, backoff_time=5)
    async def update_repository(self, ignore_issues=False, force=False) -> None:
        """Update the repository"""

    async def common_validate(self, ignore_issues: bool = False) -> None:
        """Common validation steps of the repository."""
        self.validate.errors.clear()

        # Make sure the repository exist.
        self.logger.debug("%s Checking repository.", self.string)
        await self.common_update_data(ignore_issues=ignore_issues)

        # Get the content of hacs.json
        if RepositoryFile.HACS_JSON in [x.filename for x in self.tree]:
            if manifest := await self.async_get_hacs_json():
                self.repository_manifest = HacsManifest.from_dict(manifest)
                self.data.update_data(
                    self.repository_manifest.to_dict(),
                    action=self.hacs.system.action,
                )

    async def common_registration(self) -> None:
        """Common registration steps of the repository."""
        # Attach repository
        if self.repository_object is None:
            try:
                self.repository_object, etag = await self.async_get_legacy_repository_object(
                    etag=None if self.data.installed else self.data.etag_repository,
                )
                self.data.update_data(
                    self.repository_object.attributes,
                    action=self.hacs.system.action,
                )
                self.data.etag_repository = etag
            except HacsNotModifiedException:
                self.logger.debug("%s Did not update, content was not modified", self.string)
                return

        if self.repository_object:
            self.data.last_updated = self.repository_object.attributes.get("pushed_at", 0)
            self.data.last_fetched = datetime.now(UTC)

    @concurrent(concurrenttasks=10, backoff_time=5)
    async def common_update(self, ignore_issues=False, force=False, skip_releases=False) -> bool:
        """Common information update steps of the repository."""
        self.logger.debug("%s Getting repository information", self.string)

        # Attach repository
        current_etag = self.data.etag_repository
        try:
            await self.common_update_data(
                ignore_issues=ignore_issues,
                force=force,
                skip_releases=skip_releases,
            )
        except HacsRepositoryExistException:
            self.data.full_name = self.hacs.common.renamed_repositories[self.data.full_name]
            await self.common_update_data(ignore_issues=ignore_issues, force=force)

        except HacsException:
            if not ignore_issues and not force:
                return False

        if not self.data.installed and (current_etag == self.data.etag_repository) and not force:
            self.logger.debug("%s Did not update, content was not modified", self.string)
            return False

        # Update last updated
        if self.repository_object:
            self.data.last_updated = self.repository_object.attributes.get("pushed_at", 0)

            # Update last available commit
            await self.repository_object.set_last_commit()
            self.data.last_commit = self.repository_object.last_commit

        # Get the content of hacs.json
        if RepositoryFile.HACS_JSON in [x.filename for x in self.tree]:
            if manifest := await self.async_get_hacs_json():
                self.repository_manifest = HacsManifest.from_dict(manifest)
                self.data.update_data(
                    self.repository_manifest.to_dict(),
                    action=self.hacs.system.action,
                )

        # Update "info.md"
        self.additional_info = await self.async_get_info_file_contents()

        # Set last fetch attribute
        self.data.last_fetched = datetime.now(UTC)

        return True

    async def download_zip_files(self, validate: Validate) -> None:
        """Download ZIP archive from repository release."""

        try:
            await self.async_download_zip_file(
                DownloadableContent(
                    name=self.repository_manifest.filename,
                    url=github_release_asset(
                        repository=self.data.full_name,
                        version=self.ref,
                        filename=self.repository_manifest.filename,
                    ),
                ),
                validate,
            )
        # lgtm [py/catch-base-exception] pylint: disable=broad-except
        except BaseException:
            validate.errors.append(
                f"Download of {self.repository_manifest.filename} was not completed"
            )

    async def async_download_zip_file(
        self,
        content: DownloadableContent,
        validate: Validate,
    ) -> None:
        """Download ZIP archive from repository release."""
        try:
            filecontent = await self.hacs.async_download_file(content["url"])

            if filecontent is None:
                validate.errors.append(f"Failed to download {content['url']}")
                return

            temp_dir = await self.hacs.hass.async_add_executor_job(tempfile.mkdtemp)
            temp_file = f"{temp_dir}/{self.repository_manifest.filename}"

            result = await self.hacs.async_save_file(temp_file, filecontent)

            def _extract_zip_file():
                with zipfile.ZipFile(temp_file, "r") as zip_file:
                    zip_file.extractall(self.content.path.local)

            await self.hacs.hass.async_add_executor_job(_extract_zip_file)

            def cleanup_temp_dir():
                """Cleanup temp_dir."""
                if os.path.exists(temp_dir):
                    self.logger.debug("%s Cleaning up %s", self.string, temp_dir)
                    shutil.rmtree(temp_dir)

            if result:
                self.logger.info("%s Download of %s completed", self.string, content["name"])
                await self.hacs.hass.async_add_executor_job(cleanup_temp_dir)
                return

            validate.errors.append(f"[{content['name']}] was not downloaded")
        # lgtm [py/catch-base-exception] pylint: disable=broad-except
        except BaseException:
            validate.errors.append("Download was not completed")

    async def download_content(self, version: string | None = None) -> None:
        """Download the content of a directory."""
        contents: list[FileInformation] | None = None
        if (
            not self.repository_manifest.zip_release
            and not self.data.file_name
            and self.content.path.remote is not None
        ):
            self.logger.info("%s Downloading repository archive", self.string)
            try:
                await self.download_repository_zip()
                return
            except HacsException as exception:
                self.logger.exception(exception)

        if self.repository_manifest.filename:
            self.logger.debug("%s %s", self.string, self.repository_manifest.filename)

        if self.content.path.remote == "release" and version is not None:
            contents = await self.release_contents(version)

        if not contents:
            contents = self.gather_files_to_download()

        if not contents:
            raise HacsException("No content to download")

        download_queue = QueueManager(hass=self.hacs.hass)

        for content in contents:
            if self.repository_manifest.content_in_root and self.repository_manifest.filename:
                if content.name != self.repository_manifest.filename:
                    continue
            download_queue.add(self.dowload_repository_content(content))

        await download_queue.execute()

    async def download_repository_zip(self):
        """Download the zip archive of the repository."""
        ref = f"{self.ref}".replace("tags/", "")

        if not ref:
            raise HacsException("Missing required elements.")

        filecontent = await self.hacs.async_download_file(
            github_archive(repository=self.data.full_name, version=ref, variant="tags"),
            keep_url=True,
            nolog=True,
        )

        if filecontent is None:
            filecontent = await self.hacs.async_download_file(
                github_archive(repository=self.data.full_name, version=ref, variant="heads"),
                keep_url=True,
            )
        if filecontent is None:
            raise HacsException(f"[{self}] Failed to download zipball")

        temp_dir = await self.hacs.hass.async_add_executor_job(tempfile.mkdtemp)
        temp_file = f"{temp_dir}/{self.repository_manifest.filename}"
        result = await self.hacs.async_save_file(temp_file, filecontent)
        if not result:
            raise HacsException("Could not save ZIP file")

        def _extract_zip_file():
            with zipfile.ZipFile(temp_file, "r") as zip_file:
                extractable = []
                for path in zip_file.filelist:
                    filename = "/".join(path.filename.split("/")[1:])
                    if (
                        filename.startswith(self.content.path.remote)
                        and filename != self.content.path.remote
                    ):
                        path.filename = filename.replace(self.content.path.remote, "")
                        if path.filename == "/":
                            # Blank files is not valid, and will start to throw in Python 3.12
                            continue
                        extractable.append(path)

                if len(extractable) == 0:
                    raise HacsException("No content to extract")
                zip_file.extractall(self.content.path.local, extractable)

        await self.hacs.hass.async_add_executor_job(_extract_zip_file)

        def cleanup_temp_dir():
            """Cleanup temp_dir."""
            if os.path.exists(temp_dir):
                self.logger.debug("%s Cleaning up %s", self.string, temp_dir)
                shutil.rmtree(temp_dir)

        await self.hacs.hass.async_add_executor_job(cleanup_temp_dir)
        self.logger.info("%s Content was extracted to %s", self.string, self.content.path.local)

    async def async_get_hacs_json(self, ref: str = None) -> dict[str, Any] | None:
        """Get the content of the hacs.json file."""
        try:
            response = await self.hacs.async_github_api_method(
                method=self.hacs.githubapi.repos.contents.get,
                raise_exception=False,
                repository=self.data.full_name,
                path=RepositoryFile.HACS_JSON,
                **{"params": {"ref": ref or self.version_to_download()}},
            )
            if response:
                return json_loads(decode_content(response.data.content))
        # lgtm [py/catch-base-exception] pylint: disable=broad-except
        except BaseException:
            pass

    async def async_get_info_file_contents(self, *, version: str | None = None, **kwargs) -> str:
        """Get the content of the info.md file."""

        def _info_file_variants() -> tuple[str, ...]:
            name: str = "readme"
            return (
                f"{name.upper()}.md",
                f"{name}.md",
                f"{name}.MD",
                f"{name.upper()}.MD",
                name.upper(),
                name,
            )

        info_files = [filename for filename in _info_file_variants() if filename in self.treefiles]

        if not info_files:
            return ""

        return await self.get_documentation(filename=info_files[0], version=version) or ""

    def remove(self) -> None:
        """Run remove tasks."""
        if self.hacs.repositories.is_registered(repository_id=str(self.data.id)):
            self.logger.info("%s Starting removal", self.string)
            self.hacs.repositories.unregister(self)

    async def uninstall(self) -> None:
        """Run uninstall tasks."""
        self.logger.info("%s Removing", self.string)
        if not await self.remove_local_directory():
            raise HacsException("Could not uninstall")
        self.data.installed = False
        await self._async_post_uninstall()
        await async_remove_store(self.hacs.hass, f"hacs/{self.data.id}.hacs")

        self.data.installed_version = None
        self.data.installed_commit = None
        self.hacs.async_dispatch(
            HacsDispatchEvent.REPOSITORY,
            {
                "id": 1337,
                "action": "uninstall",
                "repository": self.data.full_name,
                "repository_id": self.data.id,
            },
        )

        await self.async_remove_entity_device()
        ir.async_delete_issue(self.hacs.hass, DOMAIN, f"removed_{self.data.id}")

    async def remove_local_directory(self) -> None:
        """Check the local directory."""

        try:
            if self.data.category == "python_script":
                local_path = f"{self.content.path.local}/{self.data.file_name}"
            elif self.data.category == "template":
                local_path = f"{self.content.path.local}/{self.data.file_name}"
            elif self.data.category == "theme":
                path = (
                    f"{self.hacs.core.config_path}/"
                    f"{self.hacs.configuration.theme_path}/"
                    f"{self.data.name}.yaml"
                )
                await async_remove(self.hacs.hass, path, missing_ok=True)
                local_path = self.content.path.local
            elif self.data.category == "integration":
                if not self.data.domain:
                    if domain := DOMAIN_OVERRIDES.get(self.data.full_name):
                        self.data.domain = domain
                        self.content.path.local = self.localpath
                    else:
                        self.logger.error("%s Missing domain", self.string)
                        return False
                local_path = self.content.path.local
            else:
                local_path = self.content.path.local

            if await async_exists(self.hacs.hass, local_path):
                if not is_safe(self.hacs, local_path):
                    self.logger.error("%s Path %s is blocked from removal", self.string, local_path)
                    return False
                self.logger.debug("%s Removing %s", self.string, local_path)

                if self.data.category in ["python_script", "template"]:
                    await async_remove(self.hacs.hass, local_path)
                else:
                    await async_remove_directory(self.hacs.hass, local_path)

                while await async_exists(self.hacs.hass, local_path):
                    await sleep(1)
            else:
                self.logger.debug(
                    "%s Presumed local content path %s does not exist", self.string, local_path
                )

        except (
            # lgtm [py/catch-base-exception] pylint: disable=broad-except
            BaseException
        ) as exception:
            self.logger.debug("%s Removing %s failed with %s", self.string, local_path, exception)
            return False
        return True

    async def async_pre_registration(self) -> None:
        """Run pre registration steps."""

    @concurrent(concurrenttasks=10)
    async def async_registration(self, ref=None) -> None:
        """Run registration steps."""
        await self.async_pre_registration()

        if ref is not None:
            self.data.selected_tag = ref
            self.ref = ref
            self.force_branch = True

        if not await self.validate_repository():
            return False

        # Run common registration steps.
        await self.common_registration()

        # Set correct local path
        self.content.path.local = self.localpath

        # Run local post registration steps.
        await self.async_post_registration()

    async def async_post_registration(self) -> None:
        """Run post registration steps."""
        if not self.hacs.system.action:
            return
        await self.hacs.validation.async_run_repository_checks(self)

    async def async_pre_install(self) -> None:
        """Run pre install steps."""

    async def _async_pre_install(self) -> None:
        """Run pre install steps."""
        self.logger.info("%s Running pre installation steps", self.string)
        await self.async_pre_install()
        self.logger.info("%s Pre installation steps completed", self.string)

    async def async_install(self, *, version: str | None = None, **_) -> None:
        """Run install steps."""
        await self._async_pre_install()
        self.hacs.async_dispatch(
            HacsDispatchEvent.REPOSITORY_DOWNLOAD_PROGRESS,
            {"repository": self.data.full_name, "progress": 30},
        )
        self.logger.info("%s Running installation steps", self.string)
        await self.async_install_repository(version=version)
        self.hacs.async_dispatch(
            HacsDispatchEvent.REPOSITORY_DOWNLOAD_PROGRESS,
            {"repository": self.data.full_name, "progress": 90},
        )
        self.logger.info("%s Installation steps completed", self.string)
        await self._async_post_install()
        self.hacs.async_dispatch(
            HacsDispatchEvent.REPOSITORY_DOWNLOAD_PROGRESS,
            {"repository": self.data.full_name, "progress": False},
        )

    async def async_post_installation(self) -> None:
        """Run post install steps."""

    async def async_post_uninstall(self):
        """Run post uninstall steps."""

    async def _async_post_uninstall(self):
        """Run post uninstall steps."""
        await self.async_post_uninstall()

    async def _async_post_install(self) -> None:
        """Run post install steps."""
        self.logger.info("%s Running post installation steps", self.string)
        await self.async_post_installation()
        self.data.new = False
        self.hacs.async_dispatch(
            HacsDispatchEvent.REPOSITORY,
            {
                "id": 1337,
                "action": "install",
                "repository": self.data.full_name,
                "repository_id": self.data.id,
            },
        )
        self.logger.info("%s Post installation steps completed", self.string)

    async def async_install_repository(self, *, version: str | None = None, **_) -> None:
        """Common installation steps of the repository."""
        persistent_directory = None
        force_update = version is None or (
            self.data.last_version is not None and version != self.data.last_version
        )
        await self.update_repository(force=force_update)
        if self.content.path.local is None:
            raise HacsException("repository.content.path.local is None")
        self.validate.errors.clear()

        version_to_install = version or self.version_to_download()
        if version_to_install == self.data.default_branch:
            self.ref = version_to_install
        else:
            self.ref = f"tags/{version_to_install}"

        self.hacs.async_dispatch(
            HacsDispatchEvent.REPOSITORY_DOWNLOAD_PROGRESS,
            {"repository": self.data.full_name, "progress": 40},
        )

        if self.repository_manifest.persistent_directory:
            if await async_exists(
                self.hacs.hass,
                f"{self.content.path.local}/{self.repository_manifest.persistent_directory}",
            ):
                persistent_directory = Backup(
                    hacs=self.hacs,
                    local_path=f"{self.content.path.local}/{self.repository_manifest.persistent_directory}",
                    backup_path=tempfile.gettempdir() + "/hacs_persistent_directory/",
                )
                await self.hacs.hass.async_add_executor_job(persistent_directory.create)

        if self.data.installed and not self.content.single:
            backup = Backup(hacs=self.hacs, local_path=self.content.path.local)
            await self.hacs.hass.async_add_executor_job(backup.create)

        self.hacs.log.debug("%s Local path is set to %s", self.string, self.content.path.local)
        self.hacs.log.debug("%s Remote path is set to %s", self.string, self.content.path.remote)
        self.hacs.log.debug("%s Version to install: %s", self.string, version_to_install)

        self.hacs.async_dispatch(
            HacsDispatchEvent.REPOSITORY_DOWNLOAD_PROGRESS,
            {"repository": self.data.full_name, "progress": 50},
        )

        if self.repository_manifest.zip_release and self.repository_manifest.filename:
            await self.download_zip_files(self.validate)
        else:
            await self.download_content(version_to_install)

        self.hacs.async_dispatch(
            HacsDispatchEvent.REPOSITORY_DOWNLOAD_PROGRESS,
            {"repository": self.data.full_name, "progress": 70},
        )

        if self.validate.errors:
            for error in self.validate.errors:
                self.logger.error("%s %s", self.string, error)
            if self.data.installed and not self.content.single:
                await self.hacs.hass.async_add_executor_job(backup.restore)
                await self.hacs.hass.async_add_executor_job(backup.cleanup)
            raise HacsException("Could not download, see log for details")

        self.hacs.async_dispatch(
            HacsDispatchEvent.REPOSITORY_DOWNLOAD_PROGRESS,
            {"repository": self.data.full_name, "progress": 80},
        )

        if self.data.installed and not self.content.single:
            await self.hacs.hass.async_add_executor_job(backup.cleanup)

        if persistent_directory is not None:
            await self.hacs.hass.async_add_executor_job(persistent_directory.restore)
            await self.hacs.hass.async_add_executor_job(persistent_directory.cleanup)

        if self.validate.success:
            self.data.installed = True
            self.data.installed_commit = self.data.last_commit

            if version_to_install == self.data.default_branch:
                self.data.installed_version = None
            else:
                self.data.installed_version = version_to_install

    async def async_get_legacy_repository_object(
        self,
        etag: str | None = None,
    ) -> tuple[AIOGitHubAPIRepository, Any | None]:
        """Return a repository object."""
        try:
            repository = await self.hacs.github.get_repo(self.data.full_name, etag)
            return repository, self.hacs.github.client.last_response.etag
        except AIOGitHubAPINotModifiedException as exception:
            raise HacsNotModifiedException(exception) from exception
        except (ValueError, AIOGitHubAPIException, Exception) as exception:
            raise HacsException(exception) from exception

    def update_filenames(self) -> None:
        """Get the filename to target."""

    async def get_tree(self, ref: str):
        """Return the repository tree."""
        if self.repository_object is None:
            raise HacsException("No repository_object")
        try:
            tree = await self.repository_object.get_tree(ref)
            return tree
        except (ValueError, AIOGitHubAPIException) as exception:
            raise HacsException(exception) from exception

    async def get_releases(self, prerelease=False, returnlimit=5) -> list[GitHubReleaseModel]:
        """Return the repository releases."""
        response = await self.hacs.async_github_api_method(
            method=self.hacs.githubapi.repos.releases.list,
            repository=self.data.full_name,
        )
        releases = []
        for release in response.data or []:
            if len(releases) == returnlimit:
                break
            if release.draft or (release.prerelease and not prerelease):
                continue
            releases.append(release)
        return releases

    async def common_update_data(
        self,
        ignore_issues: bool = False,
        force: bool = False,
        retry=False,
        skip_releases=False,
    ) -> None:
        """Common update data."""
        releases = []
        try:
            repository_object, etag = await self.async_get_legacy_repository_object(
                etag=None if force or self.data.installed else self.data.etag_repository,
            )
            self.repository_object = repository_object
            if self.data.full_name.lower() != repository_object.full_name.lower():
                self.hacs.common.renamed_repositories[self.data.full_name] = (
                    repository_object.full_name
                )
                if not self.hacs.system.generator:
                    raise HacsRepositoryExistException
                self.logger.error(
                    "%s Repository has been renamed - %s", self.string, repository_object.full_name
                )
            self.data.update_data(
                repository_object.attributes,
                action=self.hacs.system.action,
            )
            self.data.etag_repository = etag
        except HacsNotModifiedException:
            return
        except HacsRepositoryExistException:
            raise HacsRepositoryExistException from None
        except (AIOGitHubAPIException, HacsException) as exception:
            if not self.hacs.status.startup or self.hacs.system.generator:
                self.logger.error("%s %s", self.string, exception)
            if not ignore_issues:
                self.validate.errors.append("Repository does not exist.")
                raise HacsException(exception) from exception

        # Make sure the repository is not archived.
        if self.data.archived and not ignore_issues:
            self.validate.errors.append("Repository is archived.")
            if self.data.full_name not in self.hacs.common.archived_repositories:
                self.hacs.common.archived_repositories.add(self.data.full_name)
            raise HacsRepositoryArchivedException(f"{self} Repository is archived.")

        # Make sure the repository is not in the blacklist.
        if self.hacs.repositories.is_removed(self.data.full_name):
            removed = self.hacs.repositories.removed_repository(self.data.full_name)
            if removed.removal_type != "remove" and not ignore_issues:
                self.validate.errors.append("Repository has been requested to be removed.")
                raise HacsException(f"{self} Repository has been requested to be removed.")

        # Get releases.
        if not skip_releases:
            try:
                releases = await self.get_releases(prerelease=True, returnlimit=30)
                if releases:
                    self.data.prerelease = None
                    for release in releases:
                        if release.draft:
                            continue
                        elif release.prerelease:
                            if self.data.prerelease is None:
                                self.data.prerelease = release.tag_name
                        else:
                            self.data.last_version = release.tag_name
                            break

                    self.data.releases = True

                    filtered_releases = [
                        release
                        for release in releases
                        if not release.draft and (self.data.show_beta or not release.prerelease)
                    ]
                    self.releases.objects = filtered_releases
                    self.data.published_tags = [x.tag_name for x in filtered_releases]

            except HacsException:
                self.data.releases = False

        if not self.force_branch:
            self.ref = self.version_to_download()
        if self.data.releases:
            for release in self.releases.objects or []:
                if release.tag_name == self.ref:
                    if assets := release.assets:
                        downloads = next(iter(assets)).download_count
                        self.data.downloads = downloads
        elif self.hacs.system.generator and self.repository_object:
            await self.repository_object.set_last_commit()
            self.data.last_commit = self.repository_object.last_commit

        self.hacs.log.debug(
            "%s Running checks against %s", self.string, self.ref.replace("tags/", "")
        )

        try:
            self.tree = await self.get_tree(self.ref)
            if not self.tree:
                raise HacsException("No files in tree")
            self.treefiles = []
            for treefile in self.tree:
                self.treefiles.append(treefile.full_path)
        except (AIOGitHubAPIException, HacsException) as exception:
            if (
                not retry
                and self.ref is not None
                and str(exception).startswith("GitHub returned 404")
            ):
                # Handle tags/branches being deleted.
                self.data.selected_tag = None
                self.ref = self.version_to_download()
                self.logger.warning(
                    "%s Selected version/branch %s has been removed, falling back to default",
                    self.string,
                    self.ref,
                )
                return await self.common_update_data(ignore_issues, force, True)
            if not self.hacs.status.startup and not ignore_issues:
                self.logger.error("%s %s", self.string, exception)
            if not ignore_issues:
                raise HacsException(exception) from None

    def gather_files_to_download(self) -> list[FileInformation]:
        """Return a list of file objects to be downloaded."""
        files = []
        tree = self.tree
        ref = f"{self.ref}".replace("tags/", "")
        releaseobjects = self.releases.objects
        category = self.data.category
        remotelocation = self.content.path.remote

        if self.should_try_releases:
            for release in releaseobjects or []:
                if ref == release.tag_name:
                    for asset in release.assets or []:
                        files.append(
                            FileInformation(asset.browser_download_url, asset.name, asset.name)
                        )
            if files:
                return files

        if self.content.single:
            for treefile in tree:
                if treefile.filename == self.data.file_name:
                    files.append(
                        FileInformation(
                            treefile.download_url, treefile.full_path, treefile.filename
                        )
                    )
            return files

        if category == "plugin":
            for treefile in tree:
                if treefile.path in ["", "dist"]:
                    if remotelocation == "dist" and not treefile.filename.startswith("dist"):
                        continue
                    if not remotelocation:
                        if not treefile.filename.endswith(".js"):
                            continue
                        if treefile.path != "":
                            continue
                    if not treefile.is_directory:
                        files.append(
                            FileInformation(
                                treefile.download_url, treefile.full_path, treefile.filename
                            )
                        )
            if files:
                return files

        if self.repository_manifest.content_in_root:
            if not self.repository_manifest.filename:
                if category == "theme":
                    tree = filter_content_return_one_of_type(self.tree, "", "yaml", "full_path")

        for path in tree:
            if path.is_directory:
                continue
            if path.full_path.startswith(self.content.path.remote):
                files.append(FileInformation(path.download_url, path.full_path, path.filename))
        return files

    async def release_contents(self, version: str | None = None) -> list[FileInformation] | None:
        """Gather the contents of a release."""
        release = await self.hacs.async_github_api_method(
            method=self.hacs.githubapi.generic,
            endpoint=f"/repos/{self.data.full_name}/releases/tags/{version}",
            raise_exception=False,
        )
        if release is None:
            return None

        return [
            FileInformation(
                url=asset.get("browser_download_url"),
                path=asset.get("name"),
                name=asset.get("name"),
            )
            for asset in release.data.get("assets", [])
        ]

    @concurrent(concurrenttasks=10)
    async def dowload_repository_content(self, content: FileInformation) -> None:
        """Download content."""
        try:
            self.logger.debug("%s Downloading %s", self.string, content.name)

            filecontent = await self.hacs.async_download_file(content.download_url)

            if filecontent is None:
                self.validate.errors.append(f"[{content.name}] was not downloaded.")
                return

            # Save the content of the file.
            if self.content.single or content.path is None:
                local_directory = self.content.path.local

            else:
                _content_path = content.path
                if not self.repository_manifest.content_in_root:
                    _content_pa
Download .txt
gitextract_ec9nfn8q/

├── .codeclimate.yml
├── .codecov.yml
├── .coveragerc
├── .devcontainer.json
├── .dockerignore
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── a_integration.yml
│   │   ├── b_frontend.yml
│   │   ├── c_bot.yml
│   │   ├── config.yml
│   │   ├── d_documentation.yml
│   │   ├── e_action.yml
│   │   ├── f_addon.yml
│   │   └── removal.yml
│   ├── dependabot.yml
│   ├── pre-commit-config.yaml
│   ├── release.yml
│   └── workflows/
│       ├── action-container.yml
│       ├── generate-hacs-data.yml
│       ├── lint.yaml
│       ├── lock.yml
│       ├── publish.yml
│       ├── pull_requests_labels.yml
│       ├── pytest.yml
│       ├── stale.yml
│       └── validate.yml
├── .gitignore
├── .pylintrc
├── LICENSE
├── README.md
├── action/
│   ├── Dockerfile
│   └── action.py
├── constraints.txt
├── custom_components/
│   └── hacs/
│       ├── __init__.py
│       ├── base.py
│       ├── config_flow.py
│       ├── const.py
│       ├── coordinator.py
│       ├── data_client.py
│       ├── diagnostics.py
│       ├── entity.py
│       ├── enums.py
│       ├── exceptions.py
│       ├── frontend.py
│       ├── icons.json
│       ├── iconset.js
│       ├── manifest.json
│       ├── repairs.py
│       ├── repositories/
│       │   ├── __init__.py
│       │   ├── appdaemon.py
│       │   ├── base.py
│       │   ├── integration.py
│       │   ├── plugin.py
│       │   ├── python_script.py
│       │   ├── template.py
│       │   └── theme.py
│       ├── switch.py
│       ├── system_health.py
│       ├── types.py
│       ├── update.py
│       ├── utils/
│       │   ├── __init__.py
│       │   ├── backup.py
│       │   ├── configuration_schema.py
│       │   ├── data.py
│       │   ├── decode.py
│       │   ├── decorator.py
│       │   ├── file_system.py
│       │   ├── filters.py
│       │   ├── github_graphql_query.py
│       │   ├── json.py
│       │   ├── logger.py
│       │   ├── path.py
│       │   ├── queue_manager.py
│       │   ├── regex.py
│       │   ├── store.py
│       │   ├── url.py
│       │   ├── validate.py
│       │   ├── version.py
│       │   └── workarounds.py
│       ├── validate/
│       │   ├── README.md
│       │   ├── __init__.py
│       │   ├── archived.py
│       │   ├── base.py
│       │   ├── brands.py
│       │   ├── description.py
│       │   ├── hacsjson.py
│       │   ├── images.py
│       │   ├── information.py
│       │   ├── integration_manifest.py
│       │   ├── issues.py
│       │   ├── manager.py
│       │   └── topics.py
│       └── websocket/
│           ├── __init__.py
│           ├── critical.py
│           ├── repositories.py
│           └── repository.py
├── hacs.json
├── info.md
├── pyproject.toml
├── requirements_action.txt
├── requirements_base.txt
├── requirements_core_min.txt
├── requirements_generate_data.txt
├── requirements_lint.txt
├── requirements_test.txt
├── scripts/
│   ├── __init__.py
│   ├── clear_storage
│   ├── coverage
│   ├── data/
│   │   ├── __init__.py
│   │   ├── common.py
│   │   ├── generate_category_data.py
│   │   └── validate_category_data.py
│   ├── develop
│   ├── install/
│   │   ├── core
│   │   ├── core_dev
│   │   ├── frontend
│   │   ├── pip_packages
│   │   └── uv_packages
│   ├── lgtm.js
│   ├── lint
│   ├── setup
│   ├── snapshot-update
│   ├── test
│   └── update/
│       ├── __init__.py
│       ├── default_repositories.py
│       └── manifest.py
└── tests/
    ├── __init__.py
    ├── action/
    │   └── test_hacs_action_integration.py
    ├── common.py
    ├── conftest.py
    ├── fixtures/
    │   ├── proxy/
    │   │   ├── api.github.com/
    │   │   │   ├── rate_limit.json
    │   │   │   └── repos/
    │   │   │       ├── hacs/
    │   │   │       │   ├── default/
    │   │   │       │   │   └── contents/
    │   │   │       │   │       ├── appdaemon.json
    │   │   │       │   │       ├── integration.json
    │   │   │       │   │       ├── plugin.json
    │   │   │       │   │       ├── python_script.json
    │   │   │       │   │       ├── template.json
    │   │   │       │   │       └── theme.json
    │   │   │       │   ├── integration/
    │   │   │       │   │   ├── contents/
    │   │   │       │   │   │   ├── custom_components/
    │   │   │       │   │   │   │   └── hacs/
    │   │   │       │   │   │   │       └── manifest.json
    │   │   │       │   │   │   └── hacs.json
    │   │   │       │   │   └── git/
    │   │   │       │   │       └── trees/
    │   │   │       │   │           └── main.json
    │   │   │       │   └── integration.json
    │   │   │       └── hacs-test-org/
    │   │   │           ├── addon-basic/
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── addon-basic.json
    │   │   │           ├── appdaemon-basic/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   ├── apps/
    │   │   │           │   │   │   └── example.json
    │   │   │           │   │   ├── apps.json
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── appdaemon-basic.json
    │   │   │           ├── integration-basic/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   ├── custom_components/
    │   │   │           │   │   │   └── example/
    │   │   │           │   │   │       └── manifest.json
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   ├── releases/
    │   │   │           │   │   └── latest.json
    │   │   │           │   └── releases.json
    │   │   │           ├── integration-basic-custom/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   ├── custom_components/
    │   │   │           │   │   │   └── example/
    │   │   │           │   │   │       └── manifest.json
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── integration-basic-custom.json
    │   │   │           ├── integration-basic.json
    │   │   │           ├── integration-invalid/
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── integration-invalid.json
    │   │   │           ├── plugin-basic/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── plugin-basic.json
    │   │   │           ├── plugin-custom-dist/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── plugin-custom-dist.json
    │   │   │           ├── python_script-basic/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── python_script-basic.json
    │   │   │           ├── template-basic/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           ├── template-basic.json
    │   │   │           ├── theme-basic/
    │   │   │           │   ├── branches/
    │   │   │           │   │   └── main.json
    │   │   │           │   ├── contents/
    │   │   │           │   │   └── hacs.json
    │   │   │           │   ├── git/
    │   │   │           │   │   └── trees/
    │   │   │           │   │       ├── 1.0.0.json
    │   │   │           │   │       ├── 2.0.0.json
    │   │   │           │   │       └── main.json
    │   │   │           │   └── releases.json
    │   │   │           └── theme-basic.json
    │   │   ├── brands.home-assistant.io/
    │   │   │   └── domains.json
    │   │   ├── data-v2.hacs.xyz/
    │   │   │   ├── appdaemon/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   ├── critical/
    │   │   │   │   └── data.json
    │   │   │   ├── integration/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   ├── netdaemon/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   ├── plugin/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   ├── python_script/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   ├── removed/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   ├── template/
    │   │   │   │   ├── data.json
    │   │   │   │   └── repositories.json
    │   │   │   └── theme/
    │   │   │       ├── data.json
    │   │   │       └── repositories.json
    │   │   ├── github.com/
    │   │   │   └── hacs-test-org/
    │   │   │       ├── appdaemon-basic/
    │   │   │       │   └── _base/
    │   │   │       │       ├── README.md
    │   │   │       │       └── apps/
    │   │   │       │           └── example/
    │   │   │       │               └── __init__.py
    │   │   │       └── integration-basic/
    │   │   │           └── _base/
    │   │   │               ├── README.md
    │   │   │               └── custom_components/
    │   │   │                   └── example/
    │   │   │                       └── manifest.json
    │   │   └── raw.githubusercontent.com/
    │   │       └── hacs-test-org/
    │   │           ├── appdaemon-basic/
    │   │           │   ├── 1.0.0/
    │   │           │   │   ├── README.md
    │   │           │   │   └── hacs.json
    │   │           │   └── 2.0.0/
    │   │           │       ├── README.md
    │   │           │       └── hacs.json
    │   │           ├── integration-basic/
    │   │           │   ├── 1.0.0/
    │   │           │   │   ├── README.md
    │   │           │   │   └── hacs.json
    │   │           │   ├── 2.0.0/
    │   │           │   │   ├── README.md
    │   │           │   │   └── hacs.json
    │   │           │   └── main/
    │   │           │       └── hacs.json
    │   │           ├── plugin-basic/
    │   │           │   ├── 1.0.0/
    │   │           │   │   ├── hacs.json
    │   │           │   │   └── plugin-basic.js
    │   │           │   └── 2.0.0/
    │   │           │       ├── hacs.json
    │   │           │       └── plugin-basic.js
    │   │           ├── python_script-basic/
    │   │           │   ├── 1.0.0/
    │   │           │   │   ├── README.md
    │   │           │   │   ├── hacs.json
    │   │           │   │   └── python_scripts/
    │   │           │   │       └── example.py
    │   │           │   └── 2.0.0/
    │   │           │       ├── README.md
    │   │           │       ├── hacs.json
    │   │           │       └── python_scripts/
    │   │           │           └── example.py
    │   │           ├── template-basic/
    │   │           │   ├── 1.0.0/
    │   │           │   │   ├── example.jinja
    │   │           │   │   └── hacs.json
    │   │           │   └── 2.0.0/
    │   │           │       ├── example.jinja
    │   │           │       └── hacs.json
    │   │           └── theme-basic/
    │   │               ├── 1.0.0/
    │   │               │   ├── hacs.json
    │   │               │   └── themes/
    │   │               │       └── example.yaml
    │   │               └── 2.0.0/
    │   │                   ├── hacs.json
    │   │                   └── themes/
    │   │                       └── example.yaml
    │   ├── repository_data.json
    │   ├── stored_repositories.json
    │   ├── v2-appdaemon-data.json
    │   ├── v2-critical-data.json
    │   ├── v2-integration-data.json
    │   ├── v2-plugin-data.json
    │   ├── v2-python_script-data.json
    │   ├── v2-removed-data.json
    │   ├── v2-template-data.json
    │   └── v2-theme-data.json
    ├── hacsbase/
    │   ├── test_backup.py
    │   ├── test_configuration.py
    │   ├── test_hacs.py
    │   └── test_hacsbase_data.py
    ├── helpers/
    │   ├── classes/
    │   │   ├── test_repository_data.py
    │   │   └── test_validate_class.py
    │   ├── download/
    │   │   ├── test_gather_files_to_download.py
    │   │   └── test_should_try_releases.py
    │   ├── filters/
    │   │   ├── test_filter_content_return_one_of_type.py
    │   │   └── test_get_first_directory_in_directory.py
    │   └── functions/
    │       └── test_extract_repository_from_url.py
    ├── homeassistantfixtures/
    │   ├── __init__.py
    │   ├── common.py
    │   ├── dev.py
    │   └── min.py
    ├── integration/
    │   └── test_integration_setup.py
    ├── patch_time.py
    ├── repositories/
    │   ├── helpers/
    │   │   ├── __init__.py
    │   │   └── test_properties.py
    │   ├── test_can_install.py
    │   ├── test_display_status.py
    │   ├── test_download_repository.py
    │   ├── test_get_documentation.py
    │   ├── test_get_hacs_json.py
    │   ├── test_get_hacs_json_raw.py
    │   ├── test_get_reposiotry_releases.py
    │   ├── test_hacs_manifest.py
    │   ├── test_plugin_repository.py
    │   ├── test_register_repository.py
    │   ├── test_remove_repository.py
    │   ├── test_removed_repository.py
    │   └── test_update_repository.py
    ├── ruff.toml
    ├── scripts/
    │   └── data/
    │       └── test_generate_category_data.py
    ├── snapshots/
    │   ├── action/
    │   │   └── test_hacs_action_integration/
    │   │       ├── bad_documentation.log
    │   │       ├── bad_issue_tracker.log
    │   │       ├── no_releases.log
    │   │       ├── releases_without_assets.log
    │   │       └── valid_manifest.log
    │   ├── api-usage/
    │   │   └── tests/
    │   │       ├── action/
    │   │       │   ├── test_hacs_action_integrationtest-hacs-action-integration-bad-documentation.json
    │   │       │   ├── test_hacs_action_integrationtest-hacs-action-integration-bad-issue-tracker.json
    │   │       │   ├── test_hacs_action_integrationtest-hacs-action-integration-no-releases.json
    │   │       │   ├── test_hacs_action_integrationtest-hacs-action-integration-releases-without-assets.json
    │   │       │   └── test_hacs_action_integrationtest-hacs-action-integration-valid-manifest.json
    │   │       ├── hacsbase/
    │   │       │   ├── test_backuptest-directory.json
    │   │       │   ├── test_backuptest-file.json
    │   │       │   ├── test_backuptest-muilti.json
    │   │       │   ├── test_hacsbase_datatest-hacs-data-async-write1.json
    │   │       │   ├── test_hacsbase_datatest-hacs-data-async-write2.json
    │   │       │   ├── test_hacsbase_datatest-hacs-data-restore-write-not-new.json
    │   │       │   ├── test_hacstest-add-remove-repository.json
    │   │       │   └── test_hacstest-hacs.json
    │   │       ├── helpers/
    │   │       │   └── download/
    │   │       │       ├── test_gather_files_to_downloadtest-gather-appdaemon-files-base.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-appdaemon-files-with-subdir.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-content-in-root-theme.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-files-to-download.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-different-card-name.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-files-from-dist.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-files-from-release-multiple.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-files-from-release.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-files-from-root.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-multiple-files-in-root.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-plugin-multiple-plugin-files-from-dist.json
    │   │       │       ├── test_gather_files_to_downloadtest-gather-zip-release.json
    │   │       │       ├── test_gather_files_to_downloadtest-single-file-repo.json
    │   │       │       ├── test_should_try_releasestest-base.json
    │   │       │       ├── test_should_try_releasestest-category-is-wrong.json
    │   │       │       ├── test_should_try_releasestest-no-releases.json
    │   │       │       ├── test_should_try_releasestest-ref-is-default.json
    │   │       │       └── test_should_try_releasestest-zip-release.json
    │   │       ├── integration/
    │   │       │   └── test_integration_setuptest-integration-setup.json
    │   │       ├── repositories/
    │   │       │   ├── helpers/
    │   │       │   │   ├── test_propertiestest-repository-helpers-properties-can-be-installed.json
    │   │       │   │   └── test_propertiestest-repository-helpers-properties-pending-update.json
    │   │       │   ├── test_can_installtest-hacs-can-install.json
    │   │       │   ├── test_display_statustest-display-status.json
    │   │       │   ├── test_download_repositorytest-download-repository-hacs-test-org-appdaemon-basic.json
    │   │       │   ├── test_download_repositorytest-download-repository-hacs-test-org-integration-basic.json
    │   │       │   ├── test_download_repositorytest-download-repository-hacs-test-org-plugin-basic.json
    │   │       │   ├── test_download_repositorytest-download-repository-hacs-test-org-python-script-basic.json
    │   │       │   ├── test_download_repositorytest-download-repository-hacs-test-org-template-basic.json
    │   │       │   ├── test_download_repositorytest-download-repository-hacs-test-org-theme-basic.json
    │   │       │   ├── test_get_documentationtest-repository-get-documentation-data0.json
    │   │       │   ├── test_get_documentationtest-repository-get-documentation-data1.json
    │   │       │   ├── test_get_documentationtest-repository-get-documentation-data2.json
    │   │       │   ├── test_get_documentationtest-repository-get-documentation-data3.json
    │   │       │   ├── test_get_hacs_json_rawtest-get-hacs-json-raw-1-0-0-expected0.json
    │   │       │   ├── test_get_hacs_json_rawtest-get-hacs-json-raw-99-99-99-none.json
    │   │       │   ├── test_get_hacs_json_rawtest-get-hacs-json-raw-with-exception.json
    │   │       │   ├── test_get_hacs_jsontest-get-hacs-json-with-exception.json
    │   │       │   ├── test_get_hacs_jsontest-validate-repository-1-0-0-integration-basic-1-0-0.json
    │   │       │   ├── test_get_hacs_jsontest-validate-repository-99-99-99-none.json
    │   │       │   ├── test_get_reposiotry_releasestest-get-reposiotry-releases-hacs-test-org-appdaemon-basic.json
    │   │       │   ├── test_get_reposiotry_releasestest-get-reposiotry-releases-hacs-test-org-integration-basic.json
    │   │       │   ├── test_get_reposiotry_releasestest-get-reposiotry-releases-hacs-test-org-plugin-basic.json
    │   │       │   ├── test_get_reposiotry_releasestest-get-reposiotry-releases-hacs-test-org-python-script-basic.json
    │   │       │   ├── test_get_reposiotry_releasestest-get-reposiotry-releases-hacs-test-org-template-basic.json
    │   │       │   ├── test_get_reposiotry_releasestest-get-reposiotry-releases-hacs-test-org-theme-basic.json
    │   │       │   ├── test_plugin_repositorytest-add-dashboard-resource-with-invalid-file-name.json
    │   │       │   ├── test_plugin_repositorytest-add-dashboard-resource.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-hacstag-1-0-0-none-none-100.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-hacstag-1-7-dev09-r2-none-none-17092.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-hacstag-none-2-0-1-none-201.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-hacstag-none-none-3-4-2-342.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-hacstag-none-none-none.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-namespace-hacs-test-org-awesome-plugin-hacsfiles-awesome-plugin.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-namespace-hacs-test-org-plugin-advanced-hacsfiles-plugin-advanced.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-namespace-hacs-test-org-plugin-basic-hacsfiles-plugin-basic.json
    │   │       │   ├── test_plugin_repositorytest-dashboard-url.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-no-hass-data.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-no-lovelace-data.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-no-lovelace-resources.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-no-store.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-none-store.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-wrong-key.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler-wrong-version.json
    │   │       │   ├── test_plugin_repositorytest-get-resource-handler.json
    │   │       │   ├── test_plugin_repositorytest-remove-dashboard-resource.json
    │   │       │   ├── test_plugin_repositorytest-update-dashboard-resource.json
    │   │       │   ├── test_register_repositorytest-register-repository-failures-hacs-test-org-addon-basic-the-repository-does-not-seem-to-be-a-integration-but-an-add-on-repository-hacs-does-not-manage-add-ons.json
    │   │       │   ├── test_register_repositorytest-register-repository-failures-hacs-test-org-integration-invalid-integration-hacs-test-org-integration-invalid-repository-structure-for-main-is-not-compliant.json
    │   │       │   ├── test_register_repositorytest-register-repository-failures-hassio-addons-example-the-repository-does-not-seem-to-be-a-integration-but-an-add-on-repository-hacs-does-not-manage-add-ons.json
    │   │       │   ├── test_register_repositorytest-register-repository-failures-home-assistant-addons-the-repository-does-not-seem-to-be-a-integration-but-an-add-on-repository-hacs-does-not-manage-add-ons.json
    │   │       │   ├── test_register_repositorytest-register-repository-failures-home-assistant-core-you-can-not-add-homeassistant-core-to-use-core-integrations-check-the-home-assistant-documentation-for-how-to-add-them.json
    │   │       │   ├── test_register_repositorytest-register-repository-hacs-test-org-integration-basic-custom-integration.json
    │   │       │   ├── test_register_repositorytest-register-repository-hacs-test-org-plugin-custom-dist-plugin.json
    │   │       │   ├── test_remove_repositorytest-remove-repository-hacs-test-org-appdaemon-basic.json
    │   │       │   ├── test_remove_repositorytest-remove-repository-hacs-test-org-integration-basic.json
    │   │       │   ├── test_remove_repositorytest-remove-repository-hacs-test-org-plugin-basic.json
    │   │       │   ├── test_remove_repositorytest-remove-repository-hacs-test-org-python-script-basic.json
    │   │       │   ├── test_remove_repositorytest-remove-repository-hacs-test-org-template-basic.json
    │   │       │   ├── test_remove_repositorytest-remove-repository-hacs-test-org-theme-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-download-failure.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-hacs-test-org-appdaemon-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-hacs-test-org-integration-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-hacs-test-org-plugin-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-hacs-test-org-python-script-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-hacs-test-org-template-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-hacs-test-org-theme-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-no-manifest.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-no-update.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-old-core-version.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-old-hacs-version.json
    │   │       │   ├── test_update_repositorytest-update-repository-entity-same-provided-version.json
    │   │       │   ├── test_update_repositorytest-update-repository-websocket-hacs-test-org-appdaemon-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-websocket-hacs-test-org-integration-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-websocket-hacs-test-org-plugin-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-websocket-hacs-test-org-python-script-basic.json
    │   │       │   ├── test_update_repositorytest-update-repository-websocket-hacs-test-org-template-basic.json
    │   │       │   └── test_update_repositorytest-update-repository-websocket-hacs-test-org-theme-basic.json
    │   │       ├── scripts/
    │   │       │   └── data/
    │   │       │       ├── test_generate_category_datatest-generate-category-data-error-status-release-304-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-error-status-release-404-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-errors-release-cancellederror-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-errors-release-error2-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-errors-release-timeouterror-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-hacs-test-org-appdaemon-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-hacs-test-org-plugin-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-hacs-test-org-python-script-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-hacs-test-org-template-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-hacs-test-org-theme-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-single-repository-hacs-test-org-appdaemon-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-single-repository-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-single-repository-hacs-test-org-plugin-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-single-repository-hacs-test-org-python-script-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-single-repository-hacs-test-org-template-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-single-repository-hacs-test-org-theme-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-with-30plus-prereleases-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-with-prior-content-hacs-test-org-appdaemon-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-with-prior-content-hacs-test-org-integration-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-with-prior-content-hacs-test-org-plugin-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-with-prior-content-hacs-test-org-python-script-basic.json
    │   │       │       ├── test_generate_category_datatest-generate-category-data-with-prior-content-hacs-test-org-template-basic.json
    │   │       │       └── test_generate_category_datatest-generate-category-data-with-prior-content-hacs-test-org-theme-basic.json
    │   │       ├── test_config_flowtest-flow-with-activation-failure.json
    │   │       ├── test_config_flowtest-flow-with-registration-failure.json
    │   │       ├── test_config_flowtest-flow-with-remove-while-activating.json
    │   │       ├── test_config_flowtest-full-user-flow-implementation.json
    │   │       ├── test_config_flowtest-options-flow.json
    │   │       ├── test_data_clienttest-basic-functionality-data-hacs-test-org-appdaemon-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-data-hacs-test-org-integration-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-data-hacs-test-org-plugin-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-data-hacs-test-org-python-script-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-data-hacs-test-org-template-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-data-hacs-test-org-theme-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-appdaemon-data0.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-critical-data6.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-integration-data1.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-plugin-data2.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-python-script-data3.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-removed-data7.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-template-data4.json
    │   │       ├── test_data_clienttest-basic-functionality-data-validate-theme-data5.json
    │   │       ├── test_data_clienttest-basic-functionality-repositories-hacs-test-org-appdaemon-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-repositories-hacs-test-org-integration-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-repositories-hacs-test-org-plugin-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-repositories-hacs-test-org-python-script-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-repositories-hacs-test-org-template-basic.json
    │   │       ├── test_data_clienttest-basic-functionality-repositories-hacs-test-org-theme-basic.json
    │   │       ├── test_data_clienttest-discard-invalid-repo-data-appdaemon-data0.json
    │   │       ├── test_data_clienttest-discard-invalid-repo-data-integration-data1.json
    │   │       ├── test_data_clienttest-discard-invalid-repo-data-plugin-data2.json
    │   │       ├── test_data_clienttest-discard-invalid-repo-data-python-script-data3.json
    │   │       ├── test_data_clienttest-discard-invalid-repo-data-template-data4.json
    │   │       ├── test_data_clienttest-discard-invalid-repo-data-theme-data5.json
    │   │       ├── test_data_clienttest-exception-handling-exception-error-fetching-data-from-hacs-test.json
    │   │       ├── test_data_clienttest-exception-handling-timeouterror-timeout-of-60s-reached.json
    │   │       ├── test_data_clienttest-status-handling-1009-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-200-does-not-raise.json
    │   │       ├── test_data_clienttest-status-handling-201-does-not-raise.json
    │   │       ├── test_data_clienttest-status-handling-301-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-302-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-304-hacsnotmodifiedexception.json
    │   │       ├── test_data_clienttest-status-handling-400-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-401-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-403-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-418-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-429-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-500-hacsexception.json
    │   │       ├── test_data_clienttest-status-handling-529-hacsexception.json
    │   │       ├── test_diagnosticstest-diagnostics-with-exception.json
    │   │       ├── test_diagnosticstest-diagnostics.json
    │   │       ├── test_sensor_cleanuptest-sensor-cleanup.json
    │   │       ├── test_switchtest-switch-entity-state-hacs-test-org-appdaemon-basic.json
    │   │       ├── test_switchtest-switch-entity-state-hacs-test-org-integration-basic.json
    │   │       ├── test_switchtest-switch-entity-state-hacs-test-org-plugin-basic.json
    │   │       ├── test_switchtest-switch-entity-state-hacs-test-org-python-script-basic.json
    │   │       ├── test_switchtest-switch-entity-state-hacs-test-org-template-basic.json
    │   │       ├── test_switchtest-switch-entity-state-hacs-test-org-theme-basic.json
    │   │       ├── test_system_healthtest-system-health-after-unload.json
    │   │       ├── test_system_healthtest-system-health.json
    │   │       ├── test_updatetest-update-entity-state-hacs-test-org-appdaemon-basic.json
    │   │       ├── test_updatetest-update-entity-state-hacs-test-org-integration-basic.json
    │   │       ├── test_updatetest-update-entity-state-hacs-test-org-plugin-basic.json
    │   │       ├── test_updatetest-update-entity-state-hacs-test-org-python-script-basic.json
    │   │       ├── test_updatetest-update-entity-state-hacs-test-org-template-basic.json
    │   │       ├── test_updatetest-update-entity-state-hacs-test-org-theme-basic.json
    │   │       ├── utils/
    │   │       │   ├── test_pathtest-is-safe.json
    │   │       │   ├── test_queue_managertest-queue-manager.json
    │   │       │   └── test_versiontest-version-to-download.json
    │   │       └── validate/
    │   │           ├── test_async_run_repository_checkstest-async-run-repository-checks.json
    │   │           ├── test_brands_checktest-added-to-brands.json
    │   │           ├── test_brands_checktest-local-brands-asset-content-in-root.json
    │   │           ├── test_brands_checktest-local-brands-asset-missing-falls-back-to-remote.json
    │   │           ├── test_brands_checktest-local-brands-asset-not-in-root.json
    │   │           ├── test_brands_checktest-not-added-to-brands.json
    │   │           ├── test_hacsjson_checktest-hacs-manifest-integration-zip-release-with-filename.json
    │   │           ├── test_hacsjson_checktest-hacs-manifest-no-manifest.json
    │   │           ├── test_hacsjson_checktest-hacs-manifest-with-invalid-manifest.json
    │   │           ├── test_hacsjson_checktest-hacs-manifest-with-missing-filename.json
    │   │           ├── test_hacsjson_checktest-hacs-manifest-with-valid-manifest.json
    │   │           ├── test_images_checktest-repository-has-images.json
    │   │           ├── test_images_checktest-repository-has-not-images.json
    │   │           ├── test_integration_manifest_checktest-hacs-manifest-with-invalid-manifest.json
    │   │           ├── test_integration_manifest_checktest-integration-manifest-with-valid-manifest.json
    │   │           ├── test_integration_manifest_checktest-integration-no-manifest.json
    │   │           ├── test_repository_archived_checktest-repository-archived.json
    │   │           ├── test_repository_archived_checktest-repository-not-archived.json
    │   │           ├── test_repository_description_checktest-repository-hacs-description.json
    │   │           ├── test_repository_description_checktest-repository-no-description.json
    │   │           ├── test_repository_information_file_checktest-has-info-file.json
    │   │           ├── test_repository_information_file_checktest-has-info-md-file.json
    │   │           ├── test_repository_information_file_checktest-has-readme-file.json
    │   │           ├── test_repository_information_file_checktest-has-readme-md-file.json
    │   │           ├── test_repository_information_file_checktest-no-info-file.json
    │   │           ├── test_repository_information_file_checktest-no-readme-file.json
    │   │           ├── test_repository_issues_checktest-repository-issues-enabled.json
    │   │           ├── test_repository_issues_checktest-repository-issues-not-enabled.json
    │   │           ├── test_repository_topics_checktest-repository-hacs-topics.json
    │   │           └── test_repository_topics_checktest-repository-no-topics.json
    │   ├── config_flow/
    │   │   ├── test_already_configured.json
    │   │   ├── test_flow_with_activation_failure.json
    │   │   ├── test_flow_with_registration_failure.json
    │   │   └── test_full_user_flow_implementation.json
    │   ├── data_client/
    │   │   └── base/
    │   │       ├── data/
    │   │       │   ├── appdaemon.json
    │   │       │   ├── integration.json
    │   │       │   ├── plugin.json
    │   │       │   ├── python_script.json
    │   │       │   ├── template.json
    │   │       │   └── theme.json
    │   │       ├── data_validate/
    │   │       │   ├── appdaemon.json
    │   │       │   ├── critical.json
    │   │       │   ├── integration.json
    │   │       │   ├── plugin.json
    │   │       │   ├── python_script.json
    │   │       │   ├── removed.json
    │   │       │   ├── template.json
    │   │       │   └── theme.json
    │   │       └── repositories/
    │   │           ├── appdaemon.json
    │   │           ├── integration.json
    │   │           ├── plugin.json
    │   │           ├── python_script.json
    │   │           ├── template.json
    │   │           └── theme.json
    │   ├── diagnostics/
    │   │   ├── base.json
    │   │   └── exception.json
    │   ├── hacs-test-org/
    │   │   ├── appdaemon-basic/
    │   │   │   ├── test_discard_invalid_repo_data.json
    │   │   │   ├── test_download_repository.json
    │   │   │   ├── test_get_reposiotry_releases.json
    │   │   │   ├── test_remove_repository_post.json
    │   │   │   ├── test_remove_repository_pre.json
    │   │   │   ├── test_switch/
    │   │   │   │   └── entity_states.json
    │   │   │   ├── test_update_entity_state.json
    │   │   │   ├── test_update_repository_entity.json
    │   │   │   └── test_update_repository_websocket.json
    │   │   ├── integration-basic/
    │   │   │   ├── get_documentation/
    │   │   │   │   ├── installed_false_last_version_2_0_0.md
    │   │   │   │   ├── installed_false_last_version_99_99_99.md
    │   │   │   │   ├── installed_true_installed_version_1_0_0.md
    │   │   │   │   └── installed_true_installed_version_1_0_0_last_version_2_0_0.md
    │   │   │   ├── test_discard_invalid_repo_data.json
    │   │   │   ├── test_download_repository.json
    │   │   │   ├── test_get_reposiotry_releases.json
    │   │   │   ├── test_remove_repository_post.json
    │   │   │   ├── test_remove_repository_pre.json
    │   │   │   ├── test_switch/
    │   │   │   │   └── entity_states.json
    │   │   │   ├── test_update_entity_state.json
    │   │   │   ├── test_update_repository_entity.json
    │   │   │   └── test_update_repository_websocket.json
    │   │   ├── integration-basic-custom/
    │   │   │   └── test_register_repository.json
    │   │   ├── plugin-basic/
    │   │   │   ├── test_discard_invalid_repo_data.json
    │   │   │   ├── test_download_repository.json
    │   │   │   ├── test_get_reposiotry_releases.json
    │   │   │   ├── test_remove_repository_post.json
    │   │   │   ├── test_remove_repository_pre.json
    │   │   │   ├── test_switch/
    │   │   │   │   └── entity_states.json
    │   │   │   ├── test_update_entity_state.json
    │   │   │   ├── test_update_repository_entity.json
    │   │   │   └── test_update_repository_websocket.json
    │   │   ├── plugin-custom-dist/
    │   │   │   └── test_register_repository.json
    │   │   ├── python_script-basic/
    │   │   │   ├── test_discard_invalid_repo_data.json
    │   │   │   ├── test_download_repository.json
    │   │   │   ├── test_get_reposiotry_releases.json
    │   │   │   ├── test_remove_repository_post.json
    │   │   │   ├── test_remove_repository_pre.json
    │   │   │   ├── test_switch/
    │   │   │   │   └── entity_states.json
    │   │   │   ├── test_update_entity_state.json
    │   │   │   ├── test_update_repository_entity.json
    │   │   │   └── test_update_repository_websocket.json
    │   │   ├── template-basic/
    │   │   │   ├── test_discard_invalid_repo_data.json
    │   │   │   ├── test_download_repository.json
    │   │   │   ├── test_get_reposiotry_releases.json
    │   │   │   ├── test_remove_repository_post.json
    │   │   │   ├── test_remove_repository_pre.json
    │   │   │   ├── test_switch/
    │   │   │   │   └── entity_states.json
    │   │   │   ├── test_update_entity_state.json
    │   │   │   ├── test_update_repository_entity.json
    │   │   │   └── test_update_repository_websocket.json
    │   │   └── theme-basic/
    │   │       ├── test_discard_invalid_repo_data.json
    │   │       ├── test_download_repository.json
    │   │       ├── test_get_reposiotry_releases.json
    │   │       ├── test_remove_repository_post.json
    │   │       ├── test_remove_repository_pre.json
    │   │       ├── test_switch/
    │   │       │   └── entity_states.json
    │   │       ├── test_update_entity_state.json
    │   │       ├── test_update_repository_entity.json
    │   │       └── test_update_repository_websocket.json
    │   ├── scripts/
    │   │   └── data/
    │   │       ├── generate_category_data/
    │   │       │   ├── appdaemon/
    │   │       │   │   ├── data.json
    │   │       │   │   ├── repositories.json
    │   │       │   │   └── summary.json
    │   │       │   ├── integration/
    │   │       │   │   ├── data.json
    │   │       │   │   ├── repositories.json
    │   │       │   │   └── summary.json
    │   │       │   ├── plugin/
    │   │       │   │   ├── data.json
    │   │       │   │   ├── repositories.json
    │   │       │   │   └── summary.json
    │   │       │   ├── python_script/
    │   │       │   │   ├── data.json
    │   │       │   │   ├── repositories.json
    │   │       │   │   └── summary.json
    │   │       │   ├── single/
    │   │       │   │   ├── appdaemon/
    │   │       │   │   │   └── hacs-test-org/
    │   │       │   │   │       └── appdaemon-basic/
    │   │       │   │   │           ├── data.json
    │   │       │   │   │           ├── repositories.json
    │   │       │   │   │           └── summary.json
    │   │       │   │   ├── integration/
    │   │       │   │   │   └── hacs-test-org/
    │   │       │   │   │       └── integration-basic/
    │   │       │   │   │           ├── data.json
    │   │       │   │   │           ├── repositories.json
    │   │       │   │   │           └── summary.json
    │   │       │   │   ├── plugin/
    │   │       │   │   │   └── hacs-test-org/
    │   │       │   │   │       └── plugin-basic/
    │   │       │   │   │           ├── data.json
    │   │       │   │   │           ├── repositories.json
    │   │       │   │   │           └── summary.json
    │   │       │   │   ├── python_script/
    │   │       │   │   │   └── hacs-test-org/
    │   │       │   │   │       └── python_script-basic/
    │   │       │   │   │           ├── data.json
    │   │       │   │   │           ├── repositories.json
    │   │       │   │   │           └── summary.json
    │   │       │   │   ├── template/
    │   │       │   │   │   └── hacs-test-org/
    │   │       │   │   │       └── template-basic/
    │   │       │   │   │           ├── data.json
    │   │       │   │   │           ├── repositories.json
    │   │       │   │   │           └── summary.json
    │   │       │   │   └── theme/
    │   │       │   │       └── hacs-test-org/
    │   │       │   │           └── theme-basic/
    │   │       │   │               ├── data.json
    │   │       │   │               ├── repositories.json
    │   │       │   │               └── summary.json
    │   │       │   ├── template/
    │   │       │   │   ├── data.json
    │   │       │   │   ├── repositories.json
    │   │       │   │   └── summary.json
    │   │       │   └── theme/
    │   │       │       ├── data.json
    │   │       │       ├── repositories.json
    │   │       │       └── summary.json
    │   │       ├── test_generate_category_data_error_status_release/
    │   │       │   └── integration/
    │   │       │       ├── 304.json
    │   │       │       └── 404.json
    │   │       ├── test_generate_category_data_errors_release/
    │   │       │   └── integration/
    │   │       │       ├── CancelledError.json
    │   │       │       ├── TimeoutError.json
    │   │       │       └── error2.json
    │   │       ├── test_generate_category_data_with_30plus_prereleases/
    │   │       │   └── integration.json
    │   │       └── test_generate_category_data_with_prior_content/
    │   │           ├── appdaemon.json
    │   │           ├── integration.json
    │   │           ├── plugin.json
    │   │           ├── python_script.json
    │   │           ├── template.json
    │   │           └── theme.json
    │   ├── system_health/
    │   │   ├── system_health.json
    │   │   └── system_health_after_unload.json
    │   ├── test_integration_setup.json
    │   └── test_integration_setup_with_custom_updater.json
    ├── test_config_flow.py
    ├── test_data_client.py
    ├── test_diagnostics.py
    ├── test_emuns.py
    ├── test_sensor_cleanup.py
    ├── test_switch.py
    ├── test_system_health.py
    ├── test_update.py
    ├── utils/
    │   ├── test_decorator.py
    │   ├── test_fs_util.py
    │   ├── test_path.py
    │   ├── test_queue_manager.py
    │   ├── test_store.py
    │   ├── test_url.py
    │   ├── test_validate.py
    │   ├── test_version.py
    │   └── test_workarounds.py
    └── validate/
        ├── test_async_run_repository_checks.py
        ├── test_brands_check.py
        ├── test_hacsjson_check.py
        ├── test_images_check.py
        ├── test_integration_manifest_check.py
        ├── test_repository_archived_check.py
        ├── test_repository_description_check.py
        ├── test_repository_information_file_check.py
        ├── test_repository_issues_check.py
        └── test_repository_topics_check.py
Download .txt
SYMBOL INDEX (655 symbols across 119 files)

FILE: action/action.py
  function error (line 50) | def error(error: str):
  function output_in_group (line 55) | def output_in_group(group: str, content: str):
  function get_event_data (line 61) | def get_event_data():
  function choose_repository (line 68) | async def choose_repository(githubapi: GitHubAPI, category: str):
  function choose_category (line 88) | def choose_category():
  function preflight (line 94) | async def preflight():
  function validate_repository (line 163) | async def validate_repository(hacs: HacsBase, repository: str, category:...

FILE: custom_components/hacs/__init__.py
  function _async_initialize_integration (line 36) | async def _async_initialize_integration(
  function async_setup_entry (line 182) | async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEnt...
  function async_unload_entry (line 190) | async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEn...
  function async_reload_entry (line 225) | async def async_reload_entry(hass: HomeAssistant, config_entry: ConfigEn...

FILE: custom_components/hacs/base.py
  class RemovedRepository (line 77) | class RemovedRepository:
    method update_data (line 86) | def update_data(self, data: dict):
    method to_json (line 99) | def to_json(self):
  class HacsConfiguration (line 111) | class HacsConfiguration:
    method to_json (line 133) | def to_json(self) -> str:
    method update_from_dict (line 137) | def update_from_dict(self, data: dict) -> None:
  class HacsCore (line 149) | class HacsCore:
  class HacsCommon (line 158) | class HacsCommon:
  class HacsStatus (line 169) | class HacsStatus:
  class HacsSystem (line 180) | class HacsSystem:
    method disabled (line 190) | def disabled(self) -> bool:
  class HacsRepositories (line 196) | class HacsRepositories:
    method list_all (line 206) | def list_all(self) -> list[HacsRepository]:
    method list_removed (line 211) | def list_removed(self) -> list[RemovedRepository]:
    method list_downloaded (line 216) | def list_downloaded(self) -> list[HacsRepository]:
    method category_downloaded (line 220) | def category_downloaded(self, category: HacsCategory) -> bool:
    method register (line 227) | def register(self, repository: HacsRepository, default: bool = False) ...
    method unregister (line 253) | def unregister(self, repository: HacsRepository) -> None:
    method mark_default (line 272) | def mark_default(self, repository: HacsRepository) -> None:
    method set_repository_id (line 284) | def set_repository_id(self, repository: HacsRepository, repo_id: str):
    method is_default (line 297) | def is_default(self, repository_id: str | None = None) -> bool:
    method is_registered (line 303) | def is_registered(
    method is_downloaded (line 315) | def is_downloaded(
    method get_by_id (line 329) | def get_by_id(self, repository_id: str | None) -> HacsRepository | None:
    method get_by_full_name (line 335) | def get_by_full_name(self, repository_full_name: str | None) -> HacsRe...
    method is_removed (line 341) | def is_removed(self, repository_full_name: str) -> bool:
    method removed_repository (line 345) | def removed_repository(self, repository_full_name: str) -> RemovedRepo...
  class HacsBase (line 355) | class HacsBase:
    method __init__ (line 372) | def __init__(self) -> None:
    method integration_dir (line 385) | def integration_dir(self) -> pathlib.Path:
    method set_stage (line 389) | def set_stage(self, stage: HacsStage | None) -> None:
    method disable_hacs (line 399) | def disable_hacs(self, reason: HacsDisabledReason) -> None:
    method enable_hacs (line 411) | def enable_hacs(self) -> None:
    method enable_hacs_category (line 417) | def enable_hacs_category(self, category: HacsCategory) -> None:
    method disable_hacs_category (line 424) | def disable_hacs_category(self, category: HacsCategory) -> None:
    method async_save_file (line 431) | async def async_save_file(self, file_path: str, content: Any) -> bool:
    method async_can_update (line 470) | async def async_can_update(self) -> int:
    method async_github_api_method (line 491) | async def async_github_api_method(
    method async_register_repository (line 524) | async def async_register_repository(
    method startup_tasks (line 606) | async def startup_tasks(self, _=None) -> None:
    method async_download_file (line 676) | async def async_download_file(
    method async_recreate_entities (line 750) | async def async_recreate_entities(self) -> None:
    method async_dispatch (line 775) | def async_dispatch(self, signal: HacsDispatchEvent, data: dict | None ...
    method set_active_categories (line 779) | def set_active_categories(self) -> None:
    method async_load_hacs_from_github (line 799) | async def async_load_hacs_from_github(self, _=None) -> None:
    method async_get_all_category_repositories (line 840) | async def async_get_all_category_repositories(self, _=None) -> None:
    method async_get_category_repositories_experimental (line 852) | async def async_get_category_repositories_experimental(self, category:...
    method async_check_rate_limit (line 904) | async def async_check_rate_limit(self, _=None) -> None:
    method async_process_queue (line 916) | async def async_process_queue(self, _=None) -> None:
    method async_handle_removed_repositories (line 948) | async def async_handle_removed_repositories(self, _=None) -> None:
    method async_update_downloaded_custom_repositories (line 999) | async def async_update_downloaded_custom_repositories(self, _=None) ->...
    method async_handle_critical_repositories (line 1039) | async def async_handle_critical_repositories(self, _=None) -> None:
    method async_setup_frontend_endpoint_plugin (line 1101) | async def async_setup_frontend_endpoint_plugin(self) -> None:

FILE: custom_components/hacs/config_flow.py
  class HacsFlowHandler (line 39) | class HacsFlowHandler(ConfigFlow, domain=DOMAIN):
    method __init__ (line 52) | def __init__(self) -> None:
    method async_step_user (line 57) | async def async_step_user(self, user_input):
    method async_step_device (line 77) | async def async_step_device(self, _user_input):
    method _show_config_form (line 124) | async def _show_config_form(self, user_input):
    method async_step_device_done (line 150) | async def async_step_device_done(self, user_input: dict[str, bool] | N...
    method async_step_could_not_register (line 170) | async def async_step_could_not_register(self, _user_input=None):
    method async_step_reauth (line 174) | async def async_step_reauth(self, _user_input=None):
    method async_step_reauth_confirm (line 178) | async def async_step_reauth_confirm(self, user_input=None):
    method async_get_options_flow (line 190) | def async_get_options_flow(config_entry):
  class HacsOptionsFlowHandler (line 194) | class HacsOptionsFlowHandler(OptionsFlow):
    method __init__ (line 197) | def __init__(self, config_entry):
    method async_step_init (line 202) | async def async_step_init(self, _user_input=None):
    method async_step_user (line 206) | async def async_step_user(self, user_input=None):

FILE: custom_components/hacs/coordinator.py
  class HacsUpdateCoordinator (line 12) | class HacsUpdateCoordinator(BaseDataUpdateCoordinatorProtocol):
    method __init__ (line 15) | def __init__(self) -> None:
    method async_add_listener (line 20) | def async_add_listener(
    method async_update_listeners (line 35) | def async_update_listeners(self) -> None:

FILE: custom_components/hacs/data_client.py
  class HacsDataClient (line 25) | class HacsDataClient:
    method __init__ (line 28) | def __init__(self, session: ClientSession, client_name: str) -> None:
    method _do_request (line 34) | async def _do_request(
    method get_data (line 64) | async def get_data(self, section: str | None, *, validate: bool) -> di...
    method get_repositories (line 96) | async def get_repositories(self, section: str) -> list[str]:

FILE: custom_components/hacs/diagnostics.py
  function async_get_config_entry_diagnostics (line 16) | async def async_get_config_entry_diagnostics(

FILE: custom_components/hacs/entity.py
  function system_info (line 22) | def system_info(hacs: HacsBase) -> dict:
  class HacsBaseEntity (line 35) | class HacsBaseEntity(Entity):
    method __init__ (line 41) | def __init__(self, hacs: HacsBase) -> None:
  class HacsDispatcherEntity (line 46) | class HacsDispatcherEntity(HacsBaseEntity):
    method async_added_to_hass (line 49) | async def async_added_to_hass(self) -> None:
    method _update (line 60) | def _update(self) -> None:
    method async_update (line 63) | async def async_update(self) -> None:
    method _update_and_write_state (line 68) | def _update_and_write_state(self, _: Any) -> None:
  class HacsSystemEntity (line 74) | class HacsSystemEntity(HacsDispatcherEntity):
    method device_info (line 81) | def device_info(self) -> dict[str, any]:
  class HacsRepositoryEntity (line 86) | class HacsRepositoryEntity(BaseCoordinatorEntity[HacsUpdateCoordinator],...
    method __init__ (line 89) | def __init__(
    method available (line 102) | def available(self) -> bool:
    method device_info (line 107) | def device_info(self) -> dict[str, any]:
    method _handle_coordinator_update (line 127) | def _handle_coordinator_update(self) -> None:
    method async_update (line 139) | async def async_update(self) -> None:

FILE: custom_components/hacs/enums.py
  class HacsGitHubRepo (line 7) | class HacsGitHubRepo(StrEnum):
  class HacsCategory (line 14) | class HacsCategory(StrEnum):
    method __str__ (line 24) | def __str__(self):
  class HacsDispatchEvent (line 28) | class HacsDispatchEvent(StrEnum):
  class RepositoryFile (line 41) | class RepositoryFile(StrEnum):
  class LovelaceMode (line 48) | class LovelaceMode(StrEnum):
  class HacsStage (line 57) | class HacsStage(StrEnum):
  class HacsDisabledReason (line 65) | class HacsDisabledReason(StrEnum):

FILE: custom_components/hacs/exceptions.py
  class HacsException (line 4) | class HacsException(Exception):
  class HacsRepositoryArchivedException (line 8) | class HacsRepositoryArchivedException(HacsException):
  class HacsNotModifiedException (line 12) | class HacsNotModifiedException(HacsException):
  class HacsExpectedException (line 16) | class HacsExpectedException(HacsException):
  class HacsRepositoryExistException (line 20) | class HacsRepositoryExistException(HacsException):
  class HacsExecutionStillInProgress (line 24) | class HacsExecutionStillInProgress(HacsException):
  class AddonRepositoryException (line 28) | class AddonRepositoryException(HacsException):
    method __init__ (line 36) | def __init__(self) -> None:
  class HomeAssistantCoreRepositoryException (line 40) | class HomeAssistantCoreRepositoryException(HacsException):
    method __init__ (line 48) | def __init__(self) -> None:

FILE: custom_components/hacs/frontend.py
  function async_register_frontend (line 23) | async def async_register_frontend(hass: HomeAssistant, hacs: HacsBase) -...

FILE: custom_components/hacs/repairs.py
  class RestartRequiredFixFlow (line 17) | class RestartRequiredFixFlow(RepairsFlow):
    method __init__ (line 20) | def __init__(self, issue_id: str) -> None:
    method async_step_init (line 23) | async def async_step_init(
    method async_step_confirm_restart (line 30) | async def async_step_confirm_restart(
  function async_create_fix_flow (line 48) | async def async_create_fix_flow(

FILE: custom_components/hacs/repositories/appdaemon.py
  class HacsAppdaemonRepository (line 18) | class HacsAppdaemonRepository(HacsRepository):
    method __init__ (line 21) | def __init__(self, hacs: HacsBase, full_name: str):
    method localpath (line 31) | def localpath(self):
    method validate_repository (line 35) | async def validate_repository(self):
    method update_repository (line 63) | async def update_repository(self, ignore_issues=False, force=False):

FILE: custom_components/hacs/repositories/base.py
  class FileInformation (line 130) | class FileInformation:
    method __init__ (line 133) | def __init__(self, url, path, name):
  class RepositoryData (line 140) | class RepositoryData:
    method name (line 178) | def name(self):
    method to_json (line 184) | def to_json(self):
    method create_from_dict (line 189) | def create_from_dict(source: dict, action: bool = False) -> Repository...
    method update_data (line 195) | def update_data(self, data: dict, action: bool = False) -> None:
  class HacsManifest (line 218) | class HacsManifest:
    method to_dict (line 233) | def to_dict(self):
    method from_dict (line 238) | def from_dict(manifest: dict):
    method update_data (line 257) | def update_data(self, data: dict) -> None:
  class RepositoryReleases (line 272) | class RepositoryReleases:
  class RepositoryPath (line 283) | class RepositoryPath:
  class RepositoryContent (line 290) | class RepositoryContent:
  class HacsRepository (line 299) | class HacsRepository:
    method __init__ (line 302) | def __init__(self, hacs: HacsBase) -> None:
    method __str__ (line 323) | def __str__(self) -> str:
    method string (line 328) | def string(self) -> str:
    method display_name (line 333) | def display_name(self) -> str:
    method ignored_by_country_configuration (line 347) | def ignored_by_country_configuration(self) -> bool:
    method display_status (line 361) | def display_status(self) -> str:
    method display_installed_version (line 376) | def display_installed_version(self) -> str:
    method display_available_version (line 388) | def display_available_version(self) -> str:
    method display_version_or_commit (line 402) | def display_version_or_commit(self) -> str:
    method pending_update (line 411) | def pending_update(self) -> bool:
    method can_download (line 433) | def can_download(self) -> bool:
    method localpath (line 445) | def localpath(self) -> str | None:
    method should_try_releases (line 450) | def should_try_releases(self) -> bool:
    method validate_repository (line 464) | async def validate_repository(self) -> None:
    method update_repository (line 468) | async def update_repository(self, ignore_issues=False, force=False) ->...
    method common_validate (line 471) | async def common_validate(self, ignore_issues: bool = False) -> None:
    method common_registration (line 488) | async def common_registration(self) -> None:
    method common_update (line 510) | async def common_update(self, ignore_issues=False, force=False, skip_r...
    method download_zip_files (line 559) | async def download_zip_files(self, validate: Validate) -> None:
    method async_download_zip_file (line 580) | async def async_download_zip_file(
    method download_content (line 620) | async def download_content(self, version: string | None = None) -> None:
    method download_repository_zip (line 657) | async def download_repository_zip(self):
    method async_get_hacs_json (line 714) | async def async_get_hacs_json(self, ref: str = None) -> dict[str, Any]...
    method async_get_info_file_contents (line 730) | async def async_get_info_file_contents(self, *, version: str | None = ...
    method remove (line 751) | def remove(self) -> None:
    method uninstall (line 757) | async def uninstall(self) -> None:
    method remove_local_directory (line 781) | async def remove_local_directory(self) -> None:
    method async_pre_registration (line 835) | async def async_pre_registration(self) -> None:
    method async_registration (line 839) | async def async_registration(self, ref=None) -> None:
    method async_post_registration (line 860) | async def async_post_registration(self) -> None:
    method async_pre_install (line 866) | async def async_pre_install(self) -> None:
    method _async_pre_install (line 869) | async def _async_pre_install(self) -> None:
    method async_install (line 875) | async def async_install(self, *, version: str | None = None, **_) -> N...
    method async_post_installation (line 895) | async def async_post_installation(self) -> None:
    method async_post_uninstall (line 898) | async def async_post_uninstall(self):
    method _async_post_uninstall (line 901) | async def _async_post_uninstall(self):
    method _async_post_install (line 905) | async def _async_post_install(self) -> None:
    method async_install_repository (line 921) | async def async_install_repository(self, *, version: str | None = None...
    method async_get_legacy_repository_object (line 1007) | async def async_get_legacy_repository_object(
    method update_filenames (line 1020) | def update_filenames(self) -> None:
    method get_tree (line 1023) | async def get_tree(self, ref: str):
    method get_releases (line 1033) | async def get_releases(self, prerelease=False, returnlimit=5) -> list[...
    method common_update_data (line 1048) | async def common_update_data(
    method gather_files_to_download (line 1173) | def gather_files_to_download(self) -> list[FileInformation]:
    method release_contents (line 1233) | async def release_contents(self, version: str | None = None) -> list[F...
    method dowload_repository_content (line 1253) | async def dowload_repository_content(self, content: FileInformation) -...
    method async_remove_entity_device (line 1295) | async def async_remove_entity_device(self) -> None:
    method version_to_download (line 1305) | def version_to_download(self) -> str:
    method get_documentation (line 1326) | async def get_documentation(
    method get_hacs_json (line 1367) | async def get_hacs_json(self, *, version: str, **kwargs) -> HacsManife...
    method get_hacs_json_raw (line 1374) | async def get_hacs_json_raw(
    method _ensure_download_capabilities (line 1389) | async def _ensure_download_capabilities(self, ref: str | None, **kwarg...
    method async_download_repository (line 1419) | async def async_download_repository(self, *, ref: str | None = None, *...
    method async_get_releases (line 1453) | async def async_get_releases(self, *, first: int = 30) -> list[GitHubR...

FILE: custom_components/hacs/repositories/integration.py
  class HacsIntegrationRepository (line 23) | class HacsIntegrationRepository(HacsRepository):
    method __init__ (line 26) | def __init__(self, hacs: HacsBase, full_name: str):
    method localpath (line 36) | def localpath(self):
    method async_post_installation (line 40) | async def async_post_installation(self):
    method async_post_uninstall (line 64) | async def async_post_uninstall(self) -> None:
    method validate_repository (line 71) | async def validate_repository(self):
    method update_repository (line 121) | async def update_repository(self, ignore_issues=False, force=False):
    method reload_custom_components (line 165) | async def reload_custom_components(self):
    method async_get_integration_manifest (line 172) | async def async_get_integration_manifest(self, ref: str = None) -> dic...
    method get_integration_manifest (line 195) | async def get_integration_manifest(self, *, version: str, **kwargs) ->...

FILE: custom_components/hacs/repositories/plugin.py
  class HacsPluginRepository (line 22) | class HacsPluginRepository(HacsRepository):
    method __init__ (line 25) | def __init__(self, hacs: HacsBase, full_name: str):
    method localpath (line 35) | def localpath(self):
    method validate_repository (line 39) | async def validate_repository(self):
    method async_post_installation (line 62) | async def async_post_installation(self):
    method async_post_uninstall (line 67) | async def async_post_uninstall(self):
    method update_repository (line 72) | async def update_repository(self, ignore_issues=False, force=False):
    method get_package_content (line 100) | async def get_package_content(self):
    method update_filenames (line 111) | def update_filenames(self) -> None:
    method generate_dashboard_resource_hacstag (line 149) | def generate_dashboard_resource_hacstag(self) -> str:
    method generate_dashboard_resource_namespace (line 158) | def generate_dashboard_resource_namespace(self) -> str:
    method generate_dashboard_resource_url (line 162) | def generate_dashboard_resource_url(self) -> str:
    method _get_resource_handler (line 173) | def _get_resource_handler(self) -> ResourceStorageCollection | None:
    method update_dashboard_resources (line 205) | async def update_dashboard_resources(self) -> None:
    method remove_dashboard_resources (line 232) | async def remove_dashboard_resources(self) -> None:

FILE: custom_components/hacs/repositories/python_script.py
  class HacsPythonScriptRepository (line 16) | class HacsPythonScriptRepository(HacsRepository):
    method __init__ (line 21) | def __init__(self, hacs: HacsBase, full_name: str):
    method localpath (line 32) | def localpath(self):
    method validate_repository (line 36) | async def validate_repository(self):
    method async_post_registration (line 62) | async def async_post_registration(self):
    method update_repository (line 71) | async def update_repository(self, ignore_issues=False, force=False):
    method update_filenames (line 105) | def update_filenames(self) -> None:

FILE: custom_components/hacs/repositories/template.py
  class HacsTemplateRepository (line 18) | class HacsTemplateRepository(HacsRepository):
    method __init__ (line 21) | def __init__(self, hacs: HacsBase, full_name: str):
    method localpath (line 32) | def localpath(self):
    method async_post_installation (line 36) | async def async_post_installation(self):
    method validate_repository (line 40) | async def validate_repository(self):
    method async_post_registration (line 65) | async def async_post_registration(self):
    method async_post_uninstall (line 74) | async def async_post_uninstall(self) -> None:
    method _reload_custom_templates (line 78) | async def _reload_custom_templates(self) -> None:
    method update_repository (line 87) | async def update_repository(self, ignore_issues=False, force=False):

FILE: custom_components/hacs/repositories/theme.py
  class HacsThemeRepository (line 18) | class HacsThemeRepository(HacsRepository):
    method __init__ (line 21) | def __init__(self, hacs: HacsBase, full_name: str):
    method localpath (line 32) | def localpath(self):
    method async_post_installation (line 36) | async def async_post_installation(self):
    method validate_repository (line 40) | async def validate_repository(self):
    method async_post_registration (line 66) | async def async_post_registration(self):
    method _reload_frontend_themes (line 75) | async def _reload_frontend_themes(self) -> None:
    method async_post_uninstall (line 83) | async def async_post_uninstall(self) -> None:
    method update_repository (line 88) | async def update_repository(self, ignore_issues=False, force=False):
    method update_filenames (line 113) | def update_filenames(self) -> None:

FILE: custom_components/hacs/switch.py
  function async_setup_entry (line 19) | async def async_setup_entry(
  class HacsRepositoryPreReleaseSwitchEntity (line 32) | class HacsRepositoryPreReleaseSwitchEntity(HacsRepositoryEntity, SwitchE...
    method __init__ (line 39) | def __init__(self, hacs: HacsBase, repository: HacsRepository) -> None:
    method is_on (line 45) | def is_on(self) -> bool:
    method async_turn_on (line 49) | async def async_turn_on(self, **kwargs: Any) -> None:
    method async_turn_off (line 53) | async def async_turn_off(self, **kwargs: Any) -> None:
    method _handle_change (line 57) | async def _handle_change(self, value: bool) -> None:

FILE: custom_components/hacs/system_health.py
  function async_register (line 17) | def async_register(hass: HomeAssistant, register: system_health.SystemHe...
  function system_health_info (line 23) | async def system_health_info(hass: HomeAssistant) -> dict[str, Any]:

FILE: custom_components/hacs/types.py
  class DownloadableContent (line 6) | class DownloadableContent(TypedDict):

FILE: custom_components/hacs/update.py
  function async_setup_entry (line 20) | async def async_setup_entry(
  class HacsRepositoryUpdateEntity (line 31) | class HacsRepositoryUpdateEntity(HacsRepositoryEntity, UpdateEntity):
    method name (line 42) | def name(self) -> str | None:
    method latest_version (line 47) | def latest_version(self) -> str:
    method release_url (line 52) | def release_url(self) -> str:
    method installed_version (line 59) | def installed_version(self) -> str:
    method release_summary (line 64) | def release_summary(self) -> str | None:
    method entity_picture (line 71) | def entity_picture(self) -> str | None:
    method async_install (line 81) | async def async_install(self, version: str | None, backup: bool, **kwa...
    method async_release_notes (line 93) | async def async_release_notes(self) -> str | None:
    method async_added_to_hass (line 137) | async def async_added_to_hass(self) -> None:
    method _update_download_progress (line 149) | def _update_download_progress(self, data: dict) -> None:
    method _update_in_progress (line 156) | def _update_in_progress(self, progress: int | bool) -> None:

FILE: custom_components/hacs/utils/backup.py
  class Backup (line 21) | class Backup:
    method __init__ (line 24) | def __init__(
    method _init_backup_dir (line 44) | def _init_backup_dir(self) -> bool:
    method create (line 59) | def create(self) -> None:
    method restore (line 83) | def restore(self) -> None:
    method cleanup (line 100) | def cleanup(self) -> None:

FILE: custom_components/hacs/utils/data.py
  class HacsData (line 58) | class HacsData:
    method __init__ (line 61) | def __init__(self, hacs: HacsBase):
    method async_force_write (line 67) | async def async_force_write(self, _=None):
    method async_write (line 71) | async def async_write(self, force: bool = False) -> None:
    method _async_store_content_and_repos (line 91) | async def _async_store_content_and_repos(self, _=None):  # bb: ignore
    method _async_store_experimental_content_and_repos (line 103) | async def _async_store_experimental_content_and_repos(self, _=None):
    method async_store_repository_data (line 114) | def async_store_repository_data(self, repository: HacsRepository) -> d...
    method async_store_experimental_repository_data (line 134) | def async_store_experimental_repository_data(self, repository: HacsRep...
    method restore (line 156) | async def restore(self):
    method register_unknown_repositories (line 235) | async def register_unknown_repositories(
    method async_restore_repository (line 259) | def async_restore_repository(self, entry: str, repository_data: dict[s...

FILE: custom_components/hacs/utils/decode.py
  function decode_content (line 6) | def decode_content(content: str) -> str:

FILE: custom_components/hacs/utils/decorator.py
  function concurrent (line 16) | def concurrent(
  function return_none_on_exception (line 46) | def return_none_on_exception(func):

FILE: custom_components/hacs/utils/file_system.py
  function async_exists (line 16) | async def async_exists(hass: HomeAssistant, path: FileDescriptorOrPath) ...
  function async_remove (line 21) | async def async_remove(
  function async_remove_directory (line 33) | async def async_remove_directory(

FILE: custom_components/hacs/utils/filters.py
  function filter_content_return_one_of_type (line 8) | def filter_content_return_one_of_type(
  function get_first_directory_in_directory (line 39) | def get_first_directory_in_directory(content: list[str | Any], dirname: ...

FILE: custom_components/hacs/utils/path.py
  function _get_safe_paths (line 14) | def _get_safe_paths(
  function is_safe (line 32) | def is_safe(hacs: HacsBase, path: str | Path) -> bool:

FILE: custom_components/hacs/utils/queue_manager.py
  class QueueManager (line 17) | class QueueManager:
    method __init__ (line 20) | def __init__(self, hass: HomeAssistant) -> None:
    method pending_tasks (line 26) | def pending_tasks(self) -> int:
    method has_pending_tasks (line 31) | def has_pending_tasks(self) -> bool:
    method clear (line 35) | def clear(self) -> None:
    method add (line 39) | def add(self, task: Coroutine) -> None:
    method execute (line 43) | async def execute(self, number_of_tasks: int | None = None) -> None:

FILE: custom_components/hacs/utils/regex.py
  function extract_repository_from_url (line 12) | def extract_repository_from_url(url: str) -> str | None:

FILE: custom_components/hacs/utils/store.py
  class HACSStore (line 14) | class HACSStore(Store):
    method load (line 17) | def load(self):
  function get_store_key (line 35) | def get_store_key(key):
  function _get_store_for_key (line 40) | def _get_store_for_key(hass, key, encoder):
  function get_store_for_key (line 45) | def get_store_for_key(hass, key):
  function async_load_from_store (line 50) | async def async_load_from_store(hass, key):
  function async_save_to_store (line 55) | async def async_save_to_store(hass, key, data):
  function async_remove_store (line 75) | async def async_remove_store(hass, key):

FILE: custom_components/hacs/utils/url.py
  function github_release_asset (line 9) | def github_release_asset(
  function github_archive (line 20) | def github_archive(

FILE: custom_components/hacs/utils/validate.py
  class Validate (line 17) | class Validate:
    method success (line 23) | def success(self) -> bool:
  function _country_validator (line 28) | def _country_validator(values) -> list[str]:
  function validate_repo_data (line 75) | def validate_repo_data(schema: dict[str, Any], extra: int) -> Callable[[...
  function validate_version (line 104) | def validate_version(data: Any) -> Any:

FILE: custom_components/hacs/utils/version.py
  function version_left_higher_then_right (line 15) | def version_left_higher_then_right(left: str, right: str) -> bool | None:
  function version_left_higher_or_equal_then_right (line 31) | def version_left_higher_or_equal_then_right(left: str, right: str) -> bool:

FILE: custom_components/hacs/utils/workarounds.py
  function async_register_static_path (line 14) | async def async_register_static_path(
  function async_register_static_path (line 26) | async def async_register_static_path(

FILE: custom_components/hacs/validate/archived.py
  function async_setup_validator (line 11) | async def async_setup_validator(repository: HacsRepository) -> Validator:
  class Validator (line 16) | class Validator(ActionValidationBase):
    method async_validate (line 22) | async def async_validate(self) -> None:

FILE: custom_components/hacs/validate/base.py
  class ValidationException (line 14) | class ValidationException(HacsException):
  class ActionValidationBase (line 18) | class ActionValidationBase:
    method __init__ (line 25) | def __init__(self, repository: HacsRepository) -> None:
    method slug (line 31) | def slug(self) -> str:
    method async_validate (line 35) | async def async_validate(self) -> None:
    method execute_validation (line 38) | async def execute_validation(self, *_: Any, **__: Any) -> None:

FILE: custom_components/hacs/validate/brands.py
  function async_setup_validator (line 16) | async def async_setup_validator(repository: HacsRepository) -> Validator:
  class Validator (line 21) | class Validator(ActionValidationBase):
    method async_validate (line 27) | async def async_validate(self) -> None:

FILE: custom_components/hacs/validate/description.py
  function async_setup_validator (line 11) | async def async_setup_validator(repository: HacsRepository) -> Validator:
  class Validator (line 16) | class Validator(ActionValidationBase):
    method async_validate (line 22) | async def async_validate(self) -> None:

FILE: custom_components/hacs/validate/hacsjson.py
  function async_setup_validator (line 12) | async def async_setup_validator(repository: HacsRepository) -> Validator:
  class Validator (line 17) | class Validator(ActionValidationBase):
    method async_validate (line 22) | async def async_validate(self) -> None:

FILE: custom_components/hacs/validate/images.py
  function async_setup_validator (line 14) | async def async_setup_validator(repository: HacsRepository) -> Validator:
  class Validator (line 19) | class Validator(ActionValidationBase):
    method async_validate (line 25) | async def async_validate(self) -> None:

FILE: custom_components/hacs/validate/information.py
  function async_setup_validator (line 11) | async def async_setup_validator(repository: HacsRepository) -> Validator:
  class Validator (line 16) | class Validator(ActionValidationBase):
    method async_validate (line 21) | async def async_validate(self) -> None:

FILE: custom_components/hacs/validate/integration_manifest.py
  function async_setup_validator (line 17) | async def async_setup_validator(repository: HacsRepository) -> Validator:
  class Validator (line 22) | class Validator(ActionValidationBase):
    method async_validate (line 29) | async def async_validate(self) -> None:

FILE: custom_components/hacs/validate/issues.py
  function async_setup_validator (line 11) | async def async_setup_validator(repository: HacsRepository) -> Validator:
  class Validator (line 16) | class Validator(ActionValidationBase):
    method async_validate (line 22) | async def async_validate(self) -> None:

FILE: custom_components/hacs/validate/manager.py
  class ValidationManager (line 19) | class ValidationManager:
    method __init__ (line 22) | def __init__(self, hacs: HacsBase, hass: HomeAssistant) -> None:
    method validators (line 29) | def validators(self) -> list[ActionValidationBase]:
    method async_load (line 33) | async def async_load(self, repository: HacsRepository) -> None:
    method async_run_repository_checks (line 50) | async def async_run_repository_checks(self, repository: HacsRepository...

FILE: custom_components/hacs/validate/topics.py
  function async_setup_validator (line 11) | async def async_setup_validator(repository: HacsRepository) -> Validator:
  class Validator (line 16) | class Validator(ActionValidationBase):
    method async_validate (line 22) | async def async_validate(self) -> None:

FILE: custom_components/hacs/websocket/__init__.py
  function async_register_websocket_commands (line 39) | def async_register_websocket_commands(hass: HomeAssistant) -> None:
  function hacs_subscribe (line 73) | async def hacs_subscribe(
  function hacs_info (line 100) | async def hacs_info(

FILE: custom_components/hacs/websocket/critical.py
  function hacs_critical_list (line 24) | async def hacs_critical_list(
  function hacs_critical_acknowledge (line 46) | async def hacs_critical_acknowledge(

FILE: custom_components/hacs/websocket/repositories.py
  function hacs_repositories_list (line 31) | async def hacs_repositories_list(
  function hacs_repositories_clear_new (line 88) | async def hacs_repositories_clear_new(
  function hacs_repositories_removed (line 120) | async def hacs_repositories_removed(
  function hacs_repositories_add (line 143) | async def hacs_repositories_add(
  function hacs_repositories_remove (line 204) | async def hacs_repositories_remove(

FILE: custom_components/hacs/websocket/repository.py
  function hacs_repository_info (line 30) | async def hacs_repository_info(
  function hacs_repository_ignore (line 109) | async def hacs_repository_ignore(
  function hacs_repository_state (line 142) | async def hacs_repository_state(
  function hacs_repository_version (line 166) | async def hacs_repository_version(
  function hacs_repository_beta (line 196) | async def hacs_repository_beta(
  function hacs_repository_download (line 223) | async def hacs_repository_download(
  function hacs_repository_remove (line 254) | async def hacs_repository_remove(
  function hacs_repository_refresh (line 282) | async def hacs_repository_refresh(
  function hacs_repository_release_notes (line 307) | async def hacs_repository_release_notes(
  function hacs_repository_releases (line 341) | async def hacs_repository_releases(

FILE: scripts/data/common.py
  function expand_and_humanize_error (line 10) | def expand_and_humanize_error(content: dict[str, Any], error: vol.Invali...
  function print_error_and_exit (line 19) | def print_error_and_exit(err: str, category: str, target_path: str | Non...

FILE: scripts/data/generate_category_data.py
  function jsonprint (line 62) | def jsonprint(data: any):
  function dicts_are_equal (line 73) | def dicts_are_equal(a: dict, b: dict, ignore: set[str]) -> bool:
  function repository_has_missing_keys (line 84) | def repository_has_missing_keys(
  class AdjustedHacsData (line 110) | class AdjustedHacsData(HacsData):
    method register_base_data (line 113) | async def register_base_data(
    method async_store_repository_data (line 130) | def async_store_repository_data(self, repository: HacsRepository) -> d...
  class AdjustedHacs (line 153) | class AdjustedHacs(HacsBase):
    method __init__ (line 158) | def __init__(self, session: ClientSession, *, token: str | None = None):
    method async_can_update (line 184) | async def async_can_update(self) -> int:
    method concurrent_register_repository (line 191) | async def concurrent_register_repository(
    method concurrent_update_repository (line 202) | async def concurrent_update_repository(self, repository: HacsRepositor...
    method generate_data_for_category (line 301) | async def generate_data_for_category(
    method get_category_repositories (line 355) | async def get_category_repositories(
    method summarize_data (line 391) | async def summarize_data(
    method async_github_get_hacs_default_file (line 443) | async def async_github_get_hacs_default_file(self, filename: str) -> l...
  function generate_category_data (line 456) | async def generate_category_data(category: str, repository_name: str = N...

FILE: scripts/data/validate_category_data.py
  function validate_category_data (line 18) | async def validate_category_data(category: str, file_path: str) -> None:

FILE: scripts/update/default_repositories.py
  function update (line 7) | def update():

FILE: scripts/update/manifest.py
  function update_manifest (line 10) | def update_manifest():

FILE: tests/__init__.py
  function async_suggest_report_issue_mock (line 11) | def async_suggest_report_issue_mock(*args, **kwargs):

FILE: tests/action/test_hacs_action_integration.py
  function test_hacs_action_integration (line 62) | async def test_hacs_action_integration(

FILE: tests/common.py
  class CategoryTestData (line 48) | class CategoryTestData(TypedDict):
  function category_test_data_parametrized (line 116) | def category_test_data_parametrized(
  function current_function_name (line 136) | def current_function_name():
  function safe_json_dumps (line 141) | def safe_json_dumps(data: dict | list) -> str:
  function recursive_remove_key (line 150) | def recursive_remove_key(data: dict[str, Any], to_remove: Iterable[str])...
  function fixture (line 193) | def fixture(filename, asjson=True):
  function dummy_repository_base (line 211) | def dummy_repository_base(hacs, repository=None):
  function ensure_auth_manager_loaded (line 237) | def ensure_auth_manager_loaded(auth_mgr):
  function mock_storage (line 245) | def mock_storage(data=None):
  class MockOwner (line 303) | class MockOwner(auth_models.User):
    method __init__ (line 306) | def __init__(self):
    method create (line 320) | def create(hass: ha.HomeAssistant):
  class MockConfigEntry (line 328) | class MockConfigEntry(config_entries.ConfigEntry):
    method add_to_hass (line 331) | def add_to_hass(self, hass: ha.HomeAssistant) -> None:
  class WSClient (line 336) | class WSClient:
    method __init__ (line 341) | def __init__(self, hass: ha.HomeAssistant, token: str) -> None:
    method _create_client (line 346) | async def _create_client(self) -> None:
    method send_json (line 374) | async def send_json(self, type: str, payload: dict[str, Any]) -> dict[...
    method receive_json (line 379) | async def receive_json(self) -> dict[str, Any]:
    method send_and_receive_json (line 382) | async def send_and_receive_json(self, type: str, payload: dict[str, An...
  class MockedResponse (line 387) | class MockedResponse:
    method __init__ (line 388) | def __init__(self, **kwargs) -> None:
    method status (line 394) | def status(self):
    method url (line 398) | def url(self):
    method headers (line 402) | def headers(self):
    method read (line 405) | async def read(self, **kwargs):
    method json (line 410) | async def json(self, **kwargs):
    method text (line 415) | async def text(self, **kwargs):
    method raise_for_status (line 420) | def raise_for_status(self) -> None:
  class ResponseMocker (line 425) | class ResponseMocker:
    method add (line 429) | def add(self, url: str, response: MockedResponse) -> None:
    method get (line 432) | def get(self, url: str, *args, **kwargs) -> MockedResponse:
  class ProxyClientSession (line 447) | class ProxyClientSession(ClientSession):
    method _request (line 450) | async def _request(self, method: str, str_or_url: StrOrURL, *args, **k...
  function client_session_proxy (line 498) | async def client_session_proxy(hass: ha.HomeAssistant) -> ClientSession:
  function create_config_entry (line 553) | def create_config_entry(
  function setup_integration (line 572) | async def setup_integration(hass: ha.HomeAssistant, config_entry: MockCo...
  function get_hacs (line 592) | def get_hacs(hass: ha.HomeAssistant) -> HacsBase:

FILE: tests/conftest.py
  function time_freezer (line 88) | def time_freezer() -> Generator[freezegun.api.FrozenDateTimeFactory, Non...
  function set_request_context (line 94) | def set_request_context(request: pytest.FixtureRequest):
  function connection (line 100) | def connection():
  function hass_storage (line 106) | def hass_storage():
  function mock_zeroconf_resolver (line 113) | def mock_zeroconf_resolver(event_loop) -> Generator[_patch]:
  function hass (line 132) | async def hass(time_freezer, event_loop, tmpdir, check_report_issue: None):
  function hacs (line 193) | def hacs(hass: HomeAssistant, setup_integration: None) -> HacsBase:
  function repository (line 199) | def repository(hacs):
  function repository_integration (line 205) | def repository_integration(hacs):
  function repository_theme (line 212) | def repository_theme(hacs):
  function repository_plugin (line 219) | def repository_plugin(hacs):
  function repository_python_script (line 226) | def repository_python_script(hacs):
  function repository_template (line 233) | def repository_template(hacs):
  function repository_appdaemon (line 240) | def repository_appdaemon(hacs):
  class SnapshotFixture (line 246) | class SnapshotFixture(Snapshot):
    method assert_hacs_data (line 247) | async def assert_hacs_data(
  function snapshots (line 257) | def snapshots(snapshot: Snapshot) -> SnapshotFixture:
  function proxy_session (line 352) | async def proxy_session(hass: HomeAssistant) -> Generator:
  function ws_client (line 364) | async def ws_client(hass: HomeAssistant) -> WSClient:
  function response_mocker (line 388) | def response_mocker() -> ResponseMocker:
  function setup_integration (line 396) | async def setup_integration(hass: HomeAssistant, check_report_issue: Non...
  function check_report_issue (line 421) | async def check_report_issue() -> None:
  function track_api_usage (line 432) | def track_api_usage(snapshots: SnapshotFixture):

FILE: tests/hacsbase/test_backup.py
  function test_file (line 8) | def test_file(hacs, tmpdir):
  function test_directory (line 25) | def test_directory(hacs, tmpdir):
  function test_muilti (line 41) | def test_muilti(hacs, tmpdir):

FILE: tests/hacsbase/test_configuration.py
  function test_configuration_and_option (line 9) | def test_configuration_and_option():
  function test_ignore_experimental (line 40) | def test_ignore_experimental():
  function test_ignore_netdaemon (line 49) | def test_ignore_netdaemon():
  function test_edge_update_with_none (line 58) | def test_edge_update_with_none():

FILE: tests/hacsbase/test_hacs.py
  function test_hacs (line 8) | async def test_hacs(hacs, repository, tmpdir):
  function test_add_remove_repository (line 36) | async def test_add_remove_repository(hacs, repository, tmpdir):

FILE: tests/hacsbase/test_hacsbase_data.py
  function test_hacs_data_async_write1 (line 9) | async def test_hacs_data_async_write1(hacs, repository):
  function test_hacs_data_async_write2 (line 17) | async def test_hacs_data_async_write2(hacs):
  function test_hacs_data_restore_write_not_new (line 24) | async def test_hacs_data_restore_write_not_new(hacs, caplog):

FILE: tests/helpers/classes/test_repository_data.py
  function test_guarded (line 4) | def test_guarded():

FILE: tests/helpers/classes/test_validate_class.py
  function test_validate (line 4) | def test_validate():

FILE: tests/helpers/download/test_gather_files_to_download.py
  function test_gather_files_to_download (line 7) | def test_gather_files_to_download(repository):
  function test_gather_plugin_files_from_root (line 18) | def test_gather_plugin_files_from_root(repository_plugin):
  function test_gather_plugin_files_from_dist (line 36) | def test_gather_plugin_files_from_dist(repository_plugin):
  function test_gather_plugin_multiple_plugin_files_from_dist (line 63) | def test_gather_plugin_multiple_plugin_files_from_dist(repository_plugin):
  function test_gather_plugin_files_from_release (line 82) | def test_gather_plugin_files_from_release(repository_plugin):
  function test_gather_plugin_files_from_release_multiple (line 92) | def test_gather_plugin_files_from_release_multiple(repository_plugin):
  function test_gather_zip_release (line 104) | def test_gather_zip_release(repository_plugin):
  function test_single_file_repo (line 116) | def test_single_file_repo(repository):
  function test_gather_content_in_root_theme (line 137) | def test_gather_content_in_root_theme(repository_theme):
  function test_gather_appdaemon_files_base (line 156) | def test_gather_appdaemon_files_base(repository_appdaemon):
  function test_gather_appdaemon_files_with_subdir (line 173) | def test_gather_appdaemon_files_with_subdir(repository_appdaemon):
  function test_gather_plugin_multiple_files_in_root (line 203) | def test_gather_plugin_multiple_files_in_root(repository_plugin):
  function test_gather_plugin_different_card_name (line 222) | def test_gather_plugin_different_card_name(repository_plugin):

FILE: tests/helpers/download/test_should_try_releases.py
  function test_base (line 5) | def test_base(repository):
  function test_ref_is_default (line 12) | def test_ref_is_default(repository):
  function test_category_is_wrong (line 19) | def test_category_is_wrong(repository):
  function test_no_releases (line 26) | def test_no_releases(repository):
  function test_zip_release (line 33) | def test_zip_release(repository):

FILE: tests/helpers/filters/test_filter_content_return_one_of_type.py
  function test_valid_objects (line 8) | def test_valid_objects():
  function test_valid_list (line 29) | def test_valid_list():

FILE: tests/helpers/filters/test_get_first_directory_in_directory.py
  function test_valid (line 8) | def test_valid():
  function test_not_valid (line 21) | def test_not_valid():

FILE: tests/helpers/functions/test_extract_repository_from_url.py
  function test_extract_repository_from_url (line 5) | def test_extract_repository_from_url():

FILE: tests/homeassistantfixtures/common.py
  function get_test_config_dir (line 12) | def get_test_config_dir(*add_path):
  function ensure_auth_manager_loaded (line 18) | def ensure_auth_manager_loaded(auth_mgr):
  class StoreWithoutWriteLoad (line 25) | class StoreWithoutWriteLoad(storage.Store[_T]):
    method async_save (line 28) | async def async_save(self, *args: Any, **kwargs: Any) -> None:
    method async_save_delay (line 35) | def async_save_delay(self, *args: Any, **kwargs: Any) -> None:

FILE: tests/homeassistantfixtures/dev.py
  function async_test_home_assistant (line 45) | async def async_test_home_assistant(

FILE: tests/homeassistantfixtures/min.py
  function async_test_home_assistant (line 46) | async def async_test_home_assistant(

FILE: tests/integration/test_integration_setup.py
  function test_integration_setup (line 14) | async def test_integration_setup(
  function test_integration_setup_with_custom_updater (line 39) | async def test_integration_setup_with_custom_updater(

FILE: tests/patch_time.py
  function _utcnow (line 14) | def _utcnow() -> datetime.datetime:
  function _monotonic (line 19) | def _monotonic() -> float:

FILE: tests/repositories/helpers/test_properties.py
  function test_repository_helpers_properties_can_be_installed (line 8) | def test_repository_helpers_properties_can_be_installed(hacs):
  function test_repository_helpers_properties_pending_update (line 13) | def test_repository_helpers_properties_pending_update(hacs):

FILE: tests/repositories/test_can_install.py
  function test_hacs_can_install (line 8) | def test_hacs_can_install(hacs):

FILE: tests/repositories/test_display_status.py
  function test_display_status (line 8) | def test_display_status(hacs: HacsBase):

FILE: tests/repositories/test_download_repository.py
  function test_download_repository (line 16) | async def test_download_repository(

FILE: tests/repositories/test_get_documentation.py
  function test_repository_get_documentation (line 27) | async def test_repository_get_documentation(

FILE: tests/repositories/test_get_hacs_json.py
  function test_validate_repository (line 12) | async def test_validate_repository(
  function test_get_hacs_json_with_exception (line 30) | async def test_get_hacs_json_with_exception(hacs: HacsBase):

FILE: tests/repositories/test_get_hacs_json_raw.py
  function test_get_hacs_json_raw (line 15) | async def test_get_hacs_json_raw(
  function test_get_hacs_json_raw_with_exception (line 33) | async def test_get_hacs_json_raw_with_exception(hacs: HacsBase):

FILE: tests/repositories/test_get_reposiotry_releases.py
  function test_get_reposiotry_releases (line 19) | async def test_get_reposiotry_releases(

FILE: tests/repositories/test_hacs_manifest.py
  function test_manifest_structure (line 9) | def test_manifest_structure():
  function test_edge_pass_none (line 42) | def test_edge_pass_none():

FILE: tests/repositories/test_plugin_repository.py
  function downloaded_plugin_repository (line 14) | async def downloaded_plugin_repository(
  function test_dashboard_namespace (line 34) | async def test_dashboard_namespace(
  function test_dashboard_hacstag (line 54) | async def test_dashboard_hacstag(
  function test_dashboard_url (line 73) | async def test_dashboard_url(downloaded_plugin_repository: HacsPluginRep...
  function test_get_resource_handler (line 81) | async def test_get_resource_handler(
  function test_get_resource_handler_wrong_version (line 91) | async def test_get_resource_handler_wrong_version(
  function test_get_resource_handler_wrong_key (line 108) | async def test_get_resource_handler_wrong_key(
  function test_get_resource_handler_none_store (line 126) | async def test_get_resource_handler_none_store(
  function test_get_resource_handler_no_store (line 144) | async def test_get_resource_handler_no_store(
  function test_get_resource_handler_no_lovelace_resources (line 162) | async def test_get_resource_handler_no_lovelace_resources(
  function test_get_resource_handler_no_lovelace_data (line 179) | async def test_get_resource_handler_no_lovelace_data(
  function test_get_resource_handler_no_hass_data (line 191) | async def test_get_resource_handler_no_hass_data(
  function test_remove_dashboard_resource (line 205) | async def test_remove_dashboard_resource(
  function test_add_dashboard_resource (line 230) | async def test_add_dashboard_resource(
  function test_update_dashboard_resource (line 250) | async def test_update_dashboard_resource(
  function test_add_dashboard_resource_with_invalid_file_name (line 284) | async def test_add_dashboard_resource_with_invalid_file_name(

FILE: tests/repositories/test_register_repository.py
  function test_register_repository (line 20) | async def test_register_repository(
  function test_register_repository_failures (line 75) | async def test_register_repository_failures(

FILE: tests/repositories/test_remove_repository.py
  function test_remove_repository (line 20) | async def test_remove_repository(

FILE: tests/repositories/test_removed_repository.py
  function test_removed_repository (line 25) | def test_removed_repository(data: dict[str, any]):

FILE: tests/repositories/test_update_repository.py
  function test_update_repository_entity (line 24) | async def test_update_repository_entity(
  function test_update_repository_websocket (line 66) | async def test_update_repository_websocket(
  function test_update_repository_entity_no_manifest (line 96) | async def test_update_repository_entity_no_manifest(
  function test_update_repository_entity_old_core_version (line 139) | async def test_update_repository_entity_old_core_version(
  function test_update_repository_entity_old_hacs_version (line 182) | async def test_update_repository_entity_old_hacs_version(
  function test_update_repository_entity_download_failure (line 221) | async def test_update_repository_entity_download_failure(
  function test_update_repository_entity_same_provided_version (line 269) | async def test_update_repository_entity_same_provided_version(
  function test_update_repository_entity_no_update (line 305) | async def test_update_repository_entity_no_update(

FILE: tests/scripts/data/test_generate_category_data.py
  function get_generated_category_data (line 33) | def get_generated_category_data(category: str) -> dict[str, Any]:
  function test_generate_category_data_single_repository (line 52) | async def test_generate_category_data_single_repository(
  function test_generate_category_data (line 95) | async def test_generate_category_data(
  function test_generate_category_data_with_prior_content (line 138) | async def test_generate_category_data_with_prior_content(
  function test_generate_category_data_errors_release (line 187) | async def test_generate_category_data_errors_release(
  function test_generate_category_data_error_status_release (line 216) | async def test_generate_category_data_error_status_release(
  function test_generate_category_data_with_30plus_prereleases (line 264) | async def test_generate_category_data_with_30plus_prereleases(

FILE: tests/test_config_flow.py
  function _mock_setup_entry (line 28) | def _mock_setup_entry(hass: HomeAssistant) -> Generator[None, None, None]:
  function test_full_user_flow_implementation (line 35) | async def test_full_user_flow_implementation(
  function test_flow_with_remove_while_activating (line 120) | async def test_flow_with_remove_while_activating(
  function test_flow_with_registration_failure (line 187) | async def test_flow_with_registration_failure(
  function test_flow_with_activation_failure (line 229) | async def test_flow_with_activation_failure(
  function test_already_configured (line 306) | async def test_already_configured(
  function test_options_flow (line 330) | async def test_options_flow(hass: HomeAssistant, setup_integration: Gene...

FILE: tests/test_data_client.py
  function test_basic_functionality_data (line 27) | async def test_basic_functionality_data(
  function test_basic_functionality_repositories (line 42) | async def test_basic_functionality_repositories(
  function test_exception_handling (line 71) | async def test_exception_handling(
  function test_status_handling (line 109) | async def test_status_handling(
  function without (line 148) | def without(d: dict, key: str) -> dict:
  function test_basic_functionality_data_validate (line 168) | async def test_basic_functionality_data_validate(
  function test_discard_invalid_repo_data (line 205) | async def test_discard_invalid_repo_data(

FILE: tests/test_diagnostics.py
  function test_diagnostics (line 19) | async def test_diagnostics(hacs: HacsBase, snapshots: SnapshotFixture):
  function test_diagnostics_with_exception (line 33) | async def test_diagnostics_with_exception(

FILE: tests/test_emuns.py
  function test_enum_value (line 6) | def test_enum_value():

FILE: tests/test_sensor_cleanup.py
  function test_sensor_cleanup (line 11) | async def test_sensor_cleanup(hass: HomeAssistant) -> None:

FILE: tests/test_switch.py
  function test_switch_entity_state (line 27) | async def test_switch_entity_state(

FILE: tests/test_system_health.py
  function get_system_health_info (line 19) | async def get_system_health_info(hass: HomeAssistant, domain: str) -> di...
  function test_system_health (line 24) | async def test_system_health(
  function test_system_health_after_unload (line 65) | async def test_system_health_after_unload(
  function test_system_health_no_hacs (line 81) | async def test_system_health_no_hacs(

FILE: tests/test_update.py
  function test_update_entity_state (line 27) | async def test_update_entity_state(

FILE: tests/utils/test_decorator.py
  function test_sync_function_no_exception (line 7) | def test_sync_function_no_exception():
  function test_sync_function_with_exception (line 16) | def test_sync_function_with_exception():
  function test_sync_method_no_exception (line 25) | def test_sync_method_no_exception():
  function test_sync_method_with_exception (line 36) | def test_sync_method_with_exception():
  function test_async_function_no_exception (line 48) | async def test_async_function_no_exception():
  function test_async_function_with_exception (line 58) | async def test_async_function_with_exception():
  function test_async_method_no_exception (line 68) | async def test_async_method_no_exception():
  function test_async_method_with_exception (line 80) | async def test_async_method_with_exception():
  function test_async_method_with_args (line 92) | async def test_async_method_with_args():
  function test_async_method_with_args_exception (line 107) | async def test_async_method_with_args_exception():

FILE: tests/utils/test_fs_util.py
  function test_async_exists (line 13) | async def test_async_exists(hass, tmpdir):
  function test_async_remove (line 21) | async def test_async_remove(hass, tmpdir):
  function test_async_remove_directory (line 43) | async def test_async_remove_directory(hass, tmpdir):

FILE: tests/utils/test_path.py
  function test_is_safe (line 5) | def test_is_safe(hacs: HacsBase) -> None:

FILE: tests/utils/test_queue_manager.py
  function test_queue_manager (line 13) | async def test_queue_manager(hacs: HacsBase, caplog: pytest.LogCaptureFi...

FILE: tests/utils/test_store.py
  function test_store_load (line 17) | async def test_store_load(hass: HomeAssistant) -> None:
  function test_store_remove (line 38) | async def test_store_remove(hass: HomeAssistant) -> None:
  function test_store_store (line 50) | async def test_store_store(hass: HomeAssistant, caplog: pytest.LogCaptur...

FILE: tests/utils/test_url.py
  function test_github_release_asset (line 16) | def test_github_release_asset(arguments: dict[str, str], url: str) -> None:
  function test_github_archive (line 50) | def test_github_archive(arguments: dict[str, str], url: str) -> None:

FILE: tests/utils/test_validate.py
  function test_hacs_manifest_json_schema (line 22) | def test_hacs_manifest_json_schema():
  function test_integration_json_schema (line 92) | def test_integration_json_schema():
  function test_critical_repo_data_json_schema (line 110) | def test_critical_repo_data_json_schema():
  function test_critical_repo_data_json_schema_bad_data (line 173) | def test_critical_repo_data_json_schema_bad_data(data: dict, expectation...
  function test_repo_data_json_schema (line 192) | def test_repo_data_json_schema(category: str):
  function without (line 223) | def without(d: dict, key: str) -> dict:
  function test_repo_data_json_schema_bad_data (line 591) | def test_repo_data_json_schema_bad_data(
  function test_repo_data_json_schema_multiple_bad_data (line 636) | def test_repo_data_json_schema_multiple_bad_data(categories: list[str], ...
  function test_removed_repo_data_json_schema (line 666) | def test_removed_repo_data_json_schema():
  function test_removed_repo_data_json_schema_bad_data (line 738) | def test_removed_repo_data_json_schema_bad_data(data: dict, expectation_...

FILE: tests/utils/test_version.py
  function test_version_to_download (line 7) | def test_version_to_download(repository):
  function test_version_left_higher_or_equal_then_right (line 85) | def test_version_left_higher_or_equal_then_right(left: str, right: str, ...

FILE: tests/utils/test_workarounds.py
  function test_domain_ovverides (line 4) | def test_domain_ovverides() -> None:

FILE: tests/validate/test_async_run_repository_checks.py
  function test_async_run_repository_checks (line 10) | async def test_async_run_repository_checks(

FILE: tests/validate/test_brands_check.py
  function test_added_to_brands (line 7) | async def test_added_to_brands(repository, response_mocker: ResponseMock...
  function test_not_added_to_brands (line 18) | async def test_not_added_to_brands(repository, response_mocker: Response...
  function test_local_brands_asset_content_in_root (line 29) | async def test_local_brands_asset_content_in_root(repository):
  function test_local_brands_asset_not_in_root (line 39) | async def test_local_brands_asset_not_in_root(repository):
  function test_local_brands_asset_missing_falls_back_to_remote (line 50) | async def test_local_brands_asset_missing_falls_back_to_remote(

FILE: tests/validate/test_hacsjson_check.py
  function test_hacs_manifest_no_manifest (line 17) | async def test_hacs_manifest_no_manifest(repository, caplog):
  function test_hacs_manifest_with_valid_manifest (line 24) | async def test_hacs_manifest_with_valid_manifest(repository):
  function test_hacs_manifest_with_invalid_manifest (line 38) | async def test_hacs_manifest_with_invalid_manifest(repository):
  function test_hacs_manifest_with_missing_filename (line 51) | async def test_hacs_manifest_with_missing_filename(repository, caplog):
  function test_hacs_manifest_integration_zip_release_with_filename (line 69) | async def test_hacs_manifest_integration_zip_release_with_filename(repos...

FILE: tests/validate/test_images_check.py
  function test_repository_has_images (line 4) | async def test_repository_has_images(repository):
  function test_repository_has_not_images (line 19) | async def test_repository_has_not_images(repository):

FILE: tests/validate/test_integration_manifest_check.py
  function test_integration_no_manifest (line 6) | async def test_integration_no_manifest(repository_integration):
  function test_integration_manifest_with_valid_manifest (line 12) | async def test_integration_manifest_with_valid_manifest(repository_integ...
  function test_hacs_manifest_with_invalid_manifest (line 36) | async def test_hacs_manifest_with_invalid_manifest(repository_integration):

FILE: tests/validate/test_repository_archived_check.py
  function test_repository_archived (line 4) | async def test_repository_archived(repository):
  function test_repository_not_archived (line 11) | async def test_repository_not_archived(repository):

FILE: tests/validate/test_repository_description_check.py
  function test_repository_no_description (line 4) | async def test_repository_no_description(repository):
  function test_repository_hacs_description (line 11) | async def test_repository_hacs_description(repository):

FILE: tests/validate/test_repository_information_file_check.py
  function test_no_info_file (line 6) | async def test_no_info_file(repository):
  function test_no_readme_file (line 12) | async def test_no_readme_file(repository):
  function test_has_info_file (line 18) | async def test_has_info_file(repository):
  function test_has_info_md_file (line 27) | async def test_has_info_md_file(repository):
  function test_has_readme_file (line 36) | async def test_has_readme_file(repository):
  function test_has_readme_md_file (line 46) | async def test_has_readme_md_file(repository):

FILE: tests/validate/test_repository_issues_check.py
  function test_repository_issues_enabled (line 4) | async def test_repository_issues_enabled(repository):
  function test_repository_issues_not_enabled (line 11) | async def test_repository_issues_not_enabled(repository):

FILE: tests/validate/test_repository_topics_check.py
  function test_repository_no_topics (line 4) | async def test_repository_no_topics(repository):
  function test_repository_hacs_topics (line 11) | async def test_repository_hacs_topics(repository):
Condensed preview — 701 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,650K chars).
[
  {
    "path": ".codeclimate.yml",
    "chars": 228,
    "preview": "---\nengines:\n  duplication:\n    enabled: true\n    config:\n      languages:\n        - python\n  fixme:\n    enabled: true\n "
  },
  {
    "path": ".codecov.yml",
    "chars": 507,
    "preview": "comment: false\ncodecov:\n  branch: main\n\ncoverage:\n  precision: 2\n  round: down\n  range: \"60...100\"\n\n  status:\n    patch:"
  },
  {
    "path": ".coveragerc",
    "chars": 158,
    "preview": "[run]\nsource = custom_components\n\nomit =\n    # omit tests\n    tests/*\n\n    # omit scripts\n    scripts/update/*\n\n[report]"
  },
  {
    "path": ".devcontainer.json",
    "chars": 1755,
    "preview": "{\n  \"name\": \"hacs/integration\",\n  \"image\": \"mcr.microsoft.com/devcontainers/python:1-3.13\",\n  \"postCreateCommand\": \"scri"
  },
  {
    "path": ".dockerignore",
    "chars": 85,
    "preview": "*\n!custom_components/hacs\n!scripts\n!action\n!constraints.txt\n!requirements_action.txt\n"
  },
  {
    "path": ".gitattributes",
    "chars": 11,
    "preview": "text eol=lf"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/a_integration.yml",
    "chars": 2791,
    "preview": "---\nname: \"Backend/Integration\"\ndescription: You use this when something is not doing what it's supposed to do.\nlabels: "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/b_frontend.yml",
    "chars": 3319,
    "preview": "---\nname: \"Frontend\"\ndescription: You use this when elements in the UI are not working correctly.\nlabels: \"issue:fronten"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/c_bot.yml",
    "chars": 1409,
    "preview": "---\nname: \"hacs-bot\"\ndescription: You use this when hacs-bot did something wrong.\nlabels: \"issue:bot\"\nbody:\n- type: mark"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 787,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: HACS Looks different\n    url: https://experimental.hacs.xyz/docs/us"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/d_documentation.yml",
    "chars": 742,
    "preview": "---\nname: \"Documentation\"\ndescription: You use this when something is wrong with the documentation.\nlabels: \"issue:docum"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/e_action.yml",
    "chars": 1349,
    "preview": "---\nname: \"HACS Action\"\ndescription: You use this when there is an issue with the HACS action.\nlabels: \"issue:action\"\nbo"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/f_addon.yml",
    "chars": 1727,
    "preview": "---\nname: \"HACS Add-ons\"\ndescription: You use this when there is an issue with one of the HACS Add-ons\nlabels: \"issue:ad"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/removal.yml",
    "chars": 1621,
    "preview": "---\nname: Request for repository removal\ndescription: Flagging of repository that should be removed from HACS\nlabels: fl"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 585,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"devcontainers\"\n    directory: \"/\"\n    labels:\n      - \"pr: dependency-update"
  },
  {
    "path": ".github/pre-commit-config.yaml",
    "chars": 2347,
    "preview": "repos:\n  - repo: local\n    hooks:\n      - id: codespell\n        name: Check code for common misspellings\n        languag"
  },
  {
    "path": ".github/release.yml",
    "chars": 531,
    "preview": "changelog:\n  categories:\n    - title: '💥 Breaking changes'\n      labels:\n        - 'Breaking Change'\n\n    - title: '🛎️ E"
  },
  {
    "path": ".github/workflows/action-container.yml",
    "chars": 1690,
    "preview": "name: \"Build the action container\"\n\non:\n  release:\n    types:\n      - published\n  push:\n    branches:\n      - main\n    p"
  },
  {
    "path": ".github/workflows/generate-hacs-data.yml",
    "chars": 10975,
    "preview": "name: Generate HACS Data\n\non:\n  workflow_dispatch:\n    inputs:\n      forceRepositoryUpdate:\n        description: 'Force "
  },
  {
    "path": ".github/workflows/lint.yaml",
    "chars": 1658,
    "preview": "name: Lint\n\non:\n  pull_request:\n    branches:\n      - main\n  push:\n    branches:\n      - main\n\nconcurrency:\n  group: lin"
  },
  {
    "path": ".github/workflows/lock.yml",
    "chars": 602,
    "preview": "name: \"Lock closed issues and PR's\"\n\non:\n  schedule:\n    - cron: \"0 * * * *\"\n\nconcurrency:\n  group: lock-${{ github.ref "
  },
  {
    "path": ".github/workflows/publish.yml",
    "chars": 2131,
    "preview": "name: Publish\n\non:\n  release:\n    types:\n      - published\n  push:\n    branches:\n      - main\n\nconcurrency:\n  group: pub"
  },
  {
    "path": ".github/workflows/pull_requests_labels.yml",
    "chars": 748,
    "preview": "name: \"Check Pull Request labels\"\n\non:\n  pull_request:\n    types:\n      - labeled\n      - opened\n      - synchronize\n   "
  },
  {
    "path": ".github/workflows/pytest.yml",
    "chars": 2685,
    "preview": "name: Test\n\non:\n  pull_request:\n    branches:\n      - main\n  push:\n    branches:\n      - main\n\nconcurrency:\n  group: tes"
  },
  {
    "path": ".github/workflows/stale.yml",
    "chars": 560,
    "preview": "name: 'Close stale issues'\n\non:\n  workflow_dispatch:\n  schedule:\n    - cron: '30 10 * * *'\n\npermissions: {}\n\njobs:\n  iss"
  },
  {
    "path": ".github/workflows/validate.yml",
    "chars": 6212,
    "preview": "name: Validate\n\non:\n  pull_request:\n    branches:\n      - main\n  push:\n    branches:\n      - main\n  schedule:\n    - cron"
  },
  {
    "path": ".gitignore",
    "chars": 388,
    "preview": "# artifacts\n__pycache__\n.pytest*\n*.egg-info\n*/build/*\n*/dist/*\n\n\n# misc\n.claude\n.coverage\n.python-version\n.venv\n.vscode\n"
  },
  {
    "path": ".pylintrc",
    "chars": 124,
    "preview": "[MESSAGES CONTROL]\n# pylint issue with Python 3.9 https://github.com/PyCQA/pylint/issues/3882\ndisable=unsubscriptable-ob"
  },
  {
    "path": "LICENSE",
    "chars": 1090,
    "preview": "MIT License\n\nCopyright (c) 2019 - 2023 Joakim Sørensen (@ludeeus)\n\nPermission is hereby granted, free of charge, to any "
  },
  {
    "path": "README.md",
    "chars": 1044,
    "preview": "# HACS (Home Assistant Community Store)\n\n_Manage (Install, track, upgrade) and discover custom elements for Home Assista"
  },
  {
    "path": "action/Dockerfile",
    "chars": 938,
    "preview": "FROM python:3.13-alpine\nWORKDIR /hacs\n\nCOPY . /hacs\n\nENV \\\n    UV_SYSTEM_PYTHON=true \\\n    UV_EXTRA_INDEX_URL=\"https://w"
  },
  {
    "path": "action/action.py",
    "chars": 6602,
    "preview": "\"\"\"Validate a GitHub repository to be used with HACS.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nimport json"
  },
  {
    "path": "constraints.txt",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "custom_components/hacs/__init__.py",
    "chars": 7689,
    "preview": "\"\"\"HACS gives you a powerful UI to handle downloads of all your custom needs.\n\nFor more details about this integration, "
  },
  {
    "path": "custom_components/hacs/base.py",
    "chars": 41801,
    "preview": "\"\"\"Base HACS class.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nfrom collections.abc import Awaitable, Callab"
  },
  {
    "path": "custom_components/hacs/config_flow.py",
    "chars": 8335,
    "preview": "\"\"\"Adds config flow for HACS.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nfrom contextlib import suppress\nfro"
  },
  {
    "path": "custom_components/hacs/const.py",
    "chars": 3148,
    "preview": "\"\"\"Constants for HACS\"\"\"\n\nfrom typing import TypeVar\n\nfrom aiogithubapi.common.const import ACCEPT_HEADERS\n\nNAME_SHORT ="
  },
  {
    "path": "custom_components/hacs/coordinator.py",
    "chars": 1177,
    "preview": "\"\"\"Coordinator to trigger entity updates.\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Callable\nf"
  },
  {
    "path": "custom_components/hacs/data_client.py",
    "chars": 3335,
    "preview": "\"\"\"HACS Data client.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nfrom typing import Any\n\nfrom aiohttp import "
  },
  {
    "path": "custom_components/hacs/diagnostics.py",
    "chars": 2674,
    "preview": "\"\"\"Diagnostics support for HACS.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import Any\n\nfrom aiogithubapi impor"
  },
  {
    "path": "custom_components/hacs/entity.py",
    "chars": 4525,
    "preview": "\"\"\"HACS Base entities.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Any\n\nfrom homeassistant"
  },
  {
    "path": "custom_components/hacs/enums.py",
    "chars": 1585,
    "preview": "\"\"\"Helper constants.\"\"\"\n\n# pylint: disable=missing-class-docstring\nfrom enum import StrEnum\n\n\nclass HacsGitHubRepo(StrEn"
  },
  {
    "path": "custom_components/hacs/exceptions.py",
    "chars": 1363,
    "preview": "\"\"\"Custom Exceptions for HACS.\"\"\"\n\n\nclass HacsException(Exception):\n    \"\"\"Super basic.\"\"\"\n\n\nclass HacsRepositoryArchive"
  },
  {
    "path": "custom_components/hacs/frontend.py",
    "chars": 2202,
    "preview": "\"\"\"Starting setup task: Frontend.\"\"\"\n\nfrom __future__ import annotations\n\nimport os\nfrom typing import TYPE_CHECKING\n\nfr"
  },
  {
    "path": "custom_components/hacs/icons.json",
    "chars": 173,
    "preview": "{\n  \"entity\": {\n    \"switch\": {\n      \"pre-release\": {\n        \"state\": {\n          \"on\": \"mdi:test-tube\",\n          \"of"
  },
  {
    "path": "custom_components/hacs/iconset.js",
    "chars": 3842,
    "preview": "const hacsIcons = {\n  hacs: {\n    path: \"m 20.064849,22.306912 c -0.0319,0.369835 -0.280561,0.707789 -0.656773,0.918212 "
  },
  {
    "path": "custom_components/hacs/manifest.json",
    "chars": 575,
    "preview": "{\n    \"domain\": \"hacs\",\n    \"name\": \"HACS\",\n    \"after_dependencies\": [\n        \"python_script\"\n    ],\n    \"codeowners\":"
  },
  {
    "path": "custom_components/hacs/repairs.py",
    "chars": 1765,
    "preview": "\"\"\"Repairs platform for HACS.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import Any\n\nfrom homeassistant import "
  },
  {
    "path": "custom_components/hacs/repositories/__init__.py",
    "chars": 786,
    "preview": "\"\"\"Initialize repositories.\"\"\"\n\nfrom __future__ import annotations\n\nfrom ..enums import HacsCategory\nfrom .appdaemon imp"
  },
  {
    "path": "custom_components/hacs/repositories/appdaemon.py",
    "chars": 3196,
    "preview": "\"\"\"Class for appdaemon apps in HACS.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom aiog"
  },
  {
    "path": "custom_components/hacs/repositories/base.py",
    "chars": 55015,
    "preview": "\"\"\"Repository.\"\"\"\n\nfrom __future__ import annotations\n\nfrom asyncio import sleep\nfrom datetime import UTC, datetime\nimpo"
  },
  {
    "path": "custom_components/hacs/repositories/integration.py",
    "chars": 8718,
    "preview": "\"\"\"Class for integrations in HACS.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Any\n\nfrom h"
  },
  {
    "path": "custom_components/hacs/repositories/plugin.py",
    "chars": 9140,
    "preview": "\"\"\"Class for plugins in HACS.\"\"\"\n\nfrom __future__ import annotations\n\nimport re\nfrom typing import TYPE_CHECKING\n\nfrom ."
  },
  {
    "path": "custom_components/hacs/repositories/python_script.py",
    "chars": 3694,
    "preview": "\"\"\"Class for python_scripts in HACS.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom ..en"
  },
  {
    "path": "custom_components/hacs/repositories/template.py",
    "chars": 3628,
    "preview": "\"\"\"Class for themes in HACS.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom homeassistan"
  },
  {
    "path": "custom_components/hacs/repositories/theme.py",
    "chars": 4039,
    "preview": "\"\"\"Class for themes in HACS.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom homeassistan"
  },
  {
    "path": "custom_components/hacs/switch.py",
    "chars": 2780,
    "preview": "\"\"\"Switch entities for HACS.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import Any\n\nfrom homeassistant.componen"
  },
  {
    "path": "custom_components/hacs/system_health.py",
    "chars": 1925,
    "preview": "\"\"\"Provide info to system health.\"\"\"\n\nfrom typing import Any\n\nfrom aiogithubapi.common.const import BASE_API_URL\nfrom ho"
  },
  {
    "path": "custom_components/hacs/types.py",
    "chars": 155,
    "preview": "\"\"\"Custom HACS types.\"\"\"\n\nfrom typing import TypedDict\n\n\nclass DownloadableContent(TypedDict):\n    \"\"\"Downloadable conte"
  },
  {
    "path": "custom_components/hacs/update.py",
    "chars": 6352,
    "preview": "\"\"\"Update entities for HACS.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import Any\n\nfrom homeassistant.componen"
  },
  {
    "path": "custom_components/hacs/utils/__init__.py",
    "chars": 29,
    "preview": "\"\"\"Initialize HACS utils.\"\"\"\n"
  },
  {
    "path": "custom_components/hacs/utils/backup.py",
    "chars": 3568,
    "preview": "\"\"\"Backup.\"\"\"\n\nfrom __future__ import annotations\n\nimport os\nimport shutil\nimport tempfile\nfrom time import sleep\nfrom t"
  },
  {
    "path": "custom_components/hacs/utils/configuration_schema.py",
    "chars": 178,
    "preview": "\"\"\"HACS Configuration Schemas.\"\"\"\n\n# Configuration:\nSIDEPANEL_TITLE = \"sidepanel_title\"\nSIDEPANEL_ICON = \"sidepanel_icon"
  },
  {
    "path": "custom_components/hacs/utils/data.py",
    "chars": 12957,
    "preview": "\"\"\"Data handler for HACS.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nfrom datetime import UTC, datetime\nfrom"
  },
  {
    "path": "custom_components/hacs/utils/decode.py",
    "chars": 208,
    "preview": "\"\"\"Util to decode content from the github API.\"\"\"\n\nfrom base64 import b64decode\n\n\ndef decode_content(content: str) -> st"
  },
  {
    "path": "custom_components/hacs/utils/decorator.py",
    "chars": 1849,
    "preview": "\"\"\"HACS Decorators.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nfrom collections.abc import Coroutine\nfrom fu"
  },
  {
    "path": "custom_components/hacs/utils/file_system.py",
    "chars": 1120,
    "preview": "\"\"\"File system functions.\"\"\"\n\nfrom __future__ import annotations\n\nimport os\nimport shutil\nfrom typing import TypeAlias\n\n"
  },
  {
    "path": "custom_components/hacs/utils/filters.py",
    "chars": 1540,
    "preview": "\"\"\"Filter functions.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import Any\n\n\ndef filter_content_return_one_of_t"
  },
  {
    "path": "custom_components/hacs/utils/github_graphql_query.py",
    "chars": 372,
    "preview": "\"\"\"GitHub GraphQL Queries.\"\"\"\n\nGET_REPOSITORY_RELEASES = \"\"\"\nquery ($owner: String!, $name: String!, $first: Int!) {\n  r"
  },
  {
    "path": "custom_components/hacs/utils/json.py",
    "chars": 92,
    "preview": "\"\"\"JSON utils.\"\"\"\n\nfrom homeassistant.util.json import json_loads\n\n__all__ = [\"json_loads\"]\n"
  },
  {
    "path": "custom_components/hacs/utils/logger.py",
    "chars": 138,
    "preview": "\"\"\"Custom logger for HACS.\"\"\"\n\nimport logging\n\nfrom ..const import PACKAGE_NAME\n\nLOGGER: logging.Logger = logging.getLog"
  },
  {
    "path": "custom_components/hacs/utils/path.py",
    "chars": 1171,
    "preview": "\"\"\"Path utils\"\"\"\n\nfrom __future__ import annotations\n\nfrom functools import lru_cache\nfrom pathlib import Path\nfrom typi"
  },
  {
    "path": "custom_components/hacs/utils/queue_manager.py",
    "chars": 2469,
    "preview": "\"\"\"The QueueManager class.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nfrom collections.abc import Coroutine\n"
  },
  {
    "path": "custom_components/hacs/utils/regex.py",
    "chars": 404,
    "preview": "\"\"\"Regex utils\"\"\"\n\nfrom __future__ import annotations\n\nimport re\n\nRE_REPOSITORY = re.compile(\n    r\"(?:(?:.*github.com.)"
  },
  {
    "path": "custom_components/hacs/utils/store.py",
    "chars": 2585,
    "preview": "\"\"\"Storage handers.\"\"\"\n\nfrom homeassistant.helpers.json import JSONEncoder\nfrom homeassistant.helpers.storage import Sto"
  },
  {
    "path": "custom_components/hacs/utils/url.py",
    "chars": 746,
    "preview": "\"\"\"Various URL utils for HACS.\"\"\"\n\nimport re\nfrom typing import Literal\n\nGIT_SHA = re.compile(r\"^[a-fA-F0-9]{40}$\")\n\n\nde"
  },
  {
    "path": "custom_components/hacs/utils/validate.py",
    "chars": 6369,
    "preview": "\"\"\"Validation utilities.\"\"\"\n\nfrom __future__ import annotations\n\nfrom collections.abc import Callable\nfrom dataclasses i"
  },
  {
    "path": "custom_components/hacs/utils/version.py",
    "chars": 1058,
    "preview": "\"\"\"Version utils.\"\"\"\n\nfrom __future__ import annotations\n\nfrom functools import lru_cache\n\nfrom awesomeversion import (\n"
  },
  {
    "path": "custom_components/hacs/utils/workarounds.py",
    "chars": 1108,
    "preview": "\"\"\"Workarounds.\"\"\"\n\nfrom homeassistant.core import HomeAssistant\n\nDOMAIN_OVERRIDES = {\n    # https://github.com/hacs/int"
  },
  {
    "path": "custom_components/hacs/validate/README.md",
    "chars": 862,
    "preview": "# Repository validation\n\nThis is where the validation rules that run against the various repository categories live.\n\n##"
  },
  {
    "path": "custom_components/hacs/validate/__init__.py",
    "chars": 29,
    "preview": "\"\"\"Initialize validation.\"\"\"\n"
  },
  {
    "path": "custom_components/hacs/validate/archived.py",
    "chars": 718,
    "preview": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import ActionValidationBase, Validation"
  },
  {
    "path": "custom_components/hacs/validate/base.py",
    "chars": 1518,
    "preview": "\"\"\"Base class for validation.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Any\n\nfrom ..exce"
  },
  {
    "path": "custom_components/hacs/validate/brands.py",
    "chars": 1894,
    "preview": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom custom_components.hacs.enums import HacsCateg"
  },
  {
    "path": "custom_components/hacs/validate/description.py",
    "chars": 734,
    "preview": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import ActionValidationBase, Validation"
  },
  {
    "path": "custom_components/hacs/validate/hacsjson.py",
    "chars": 1777,
    "preview": "from __future__ import annotations\n\nfrom voluptuous.error import Invalid\nfrom voluptuous.humanize import humanize_error\n"
  },
  {
    "path": "custom_components/hacs/validate/images.py",
    "chars": 1129,
    "preview": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom ..enums import HacsCategory\nfrom .base import"
  },
  {
    "path": "custom_components/hacs/validate/information.py",
    "chars": 958,
    "preview": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import ActionValidationBase, Validation"
  },
  {
    "path": "custom_components/hacs/validate/integration_manifest.py",
    "chars": 1582,
    "preview": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom voluptuous.error import Invalid\nfrom voluptuo"
  },
  {
    "path": "custom_components/hacs/validate/issues.py",
    "chars": 743,
    "preview": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import ActionValidationBase, Validation"
  },
  {
    "path": "custom_components/hacs/validate/manager.py",
    "chars": 2767,
    "preview": "\"\"\"Hacs validation manager.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nfrom importlib import import_module\ni"
  },
  {
    "path": "custom_components/hacs/validate/topics.py",
    "chars": 730,
    "preview": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING\n\nfrom .base import ActionValidationBase, Validation"
  },
  {
    "path": "custom_components/hacs/websocket/__init__.py",
    "chars": 4187,
    "preview": "\"\"\"Register_commands.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Any\n\nfrom homeassistant."
  },
  {
    "path": "custom_components/hacs/websocket/critical.py",
    "chars": 1643,
    "preview": "\"\"\"Register info websocket commands.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Any\n\nfrom"
  },
  {
    "path": "custom_components/hacs/websocket/repositories.py",
    "chars": 7083,
    "preview": "\"\"\"Register info websocket commands.\"\"\"\n\nfrom __future__ import annotations\n\nimport sys\nfrom typing import TYPE_CHECKING"
  },
  {
    "path": "custom_components/hacs/websocket/repository.py",
    "chars": 12091,
    "preview": "\"\"\"Register info websocket commands.\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Any\n\nfrom"
  },
  {
    "path": "hacs.json",
    "chars": 151,
    "preview": "{\n  \"name\": \"HACS\",\n  \"zip_release\": true,\n  \"hide_default_branch\": true,\n  \"homeassistant\": \"2025.3.0\",\n  \"hacs\": \"0.19"
  },
  {
    "path": "info.md",
    "chars": 365,
    "preview": "## Useful links\n\n- [General documentation](https://hacs.xyz/)\n- [Configuration](https://hacs.xyz/docs/configuration/basi"
  },
  {
    "path": "pyproject.toml",
    "chars": 2043,
    "preview": "[tool.isort]\n# https://github.com/PyCQA/isort/wiki/isort-Settings\nprofile = \"black\"\n# will group `import x` and `from x "
  },
  {
    "path": "requirements_action.txt",
    "chars": 104,
    "preview": "aiogithubapi==26.0.0\nhomeassistant==2025.11.3\n# https://github.com/aio-libs/aiodns/issues/214\npycares<5\n"
  },
  {
    "path": "requirements_base.txt",
    "chars": 137,
    "preview": "aiogithubapi>=21.11.0\naiohttp>=3.8.3,<4.0\naiohttp_cors==0.7.0\nasync-timeout>=4.0.2\nasynctest==0.13.0\ncolorlog==6.10.1\nse"
  },
  {
    "path": "requirements_core_min.txt",
    "chars": 163,
    "preview": "# https://github.com/home-assistant/core/blob/2025.3.0/homeassistant/components/frontend/manifest.json\nhome-assistant-fr"
  },
  {
    "path": "requirements_generate_data.txt",
    "chars": 134,
    "preview": "--requirement requirements_base.txt\nawscli==1.44.58\nhomeassistant==2025.3.0\n# https://github.com/aio-libs/aiodns/issues/"
  },
  {
    "path": "requirements_lint.txt",
    "chars": 153,
    "preview": "--requirement requirements_base.txt\ncodespell==2.4.2\nisort==8.0.1\npre-commit==4.5.1\npre-commit-hooks==6.0.0\npyupgrade==3"
  },
  {
    "path": "requirements_test.txt",
    "chars": 259,
    "preview": "--requirement requirements_base.txt\nasynctest==0.13.0\nfreezegun==1.5.5\njosepy<3\n# https://github.com/aio-libs/aiodns/iss"
  },
  {
    "path": "scripts/__init__.py",
    "chars": 19,
    "preview": "\"\"\"HACS Script.\"\"\"\n"
  },
  {
    "path": "scripts/clear_storage",
    "chars": 111,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\ncd \"$(dirname \"$0\")/..\"\n\nrm -rf config/.storage/hacs\nrm -f config/.storage/hacs*\n\n"
  },
  {
    "path": "scripts/coverage",
    "chars": 143,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\ncd \"$(dirname \"$0\")/..\"\n\nbash scripts/test > /dev/null\npython3 -m \\\n    coverage \\\n    repo"
  },
  {
    "path": "scripts/data/__init__.py",
    "chars": 24,
    "preview": "\"\"\"HACS Data script.\"\"\"\n"
  },
  {
    "path": "scripts/data/common.py",
    "chars": 810,
    "preview": "\"\"\"Common helpers for data.\"\"\"\nfrom __future__ import annotations\n\nimport sys\nfrom typing import Any\n\nimport voluptuous "
  },
  {
    "path": "scripts/data/generate_category_data.py",
    "chars": 20961,
    "preview": "\"\"\"Generate HACS compliant data.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nfrom datetime import datetime\nim"
  },
  {
    "path": "scripts/data/validate_category_data.py",
    "chars": 2368,
    "preview": "\"\"\"Validate HACS V2 data.\"\"\"\nfrom __future__ import annotations\n\nimport asyncio\nimport json\nimport os\nimport sys\nfrom ty"
  },
  {
    "path": "scripts/develop",
    "chars": 941,
    "preview": "#!/usr/bin/env bash\n\ndeclare frontend_dir\n\nset -e\n\ncd \"$(dirname \"$0\")/..\"\n\nif [ ! -f \"${PWD}/config/configuration.yaml\""
  },
  {
    "path": "scripts/install/core",
    "chars": 131,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\ncd \"$(dirname \"$0\")/../..\"\n\nbash scripts/install/pip_packages --requirement requirements_co"
  },
  {
    "path": "scripts/install/core_dev",
    "chars": 180,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\ncd \"$(dirname \"$0\")/../..\"\n\nbash scripts/install/pip_packages \\\n    \"git+https://github.com"
  },
  {
    "path": "scripts/install/frontend",
    "chars": 1515,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\ncd \"$(dirname \"$0\")/../..\"\n\nFRONTEND_VERSION=\"20250128065759\"\n\nfunction installFrontendFrom"
  },
  {
    "path": "scripts/install/pip_packages",
    "chars": 157,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\npython3 -m pip \\\n    install \\\n    --upgrade \\\n    --disable-pip-version-check \\\n    --cons"
  },
  {
    "path": "scripts/install/uv_packages",
    "chars": 98,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\nuv pip \\\n    install \\\n    --constraint constraints.txt \\\n    \"${@}\"\n"
  },
  {
    "path": "scripts/lgtm.js",
    "chars": 47,
    "preview": "console.log(\"Dummy file to make LGTM happy...\")"
  },
  {
    "path": "scripts/lint",
    "chars": 261,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\ncd \"$(dirname \"$0\")/..\"\n\npre-commit install-hooks --config .github/pre-commit-config.yaml;\n"
  },
  {
    "path": "scripts/setup",
    "chars": 394,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\ncd \"$(dirname \"$0\")/..\"\n\nscripts/install/pip_packages \"pip<23.2,>=21.3.1\"\nscripts/install/p"
  },
  {
    "path": "scripts/snapshot-update",
    "chars": 115,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\ncd \"$(dirname \"$0\")/..\"\n\npython3 -m \\\n    pytest \\\n    tests \\\n    --snapshot-update\n\n"
  },
  {
    "path": "scripts/test",
    "chars": 82,
    "preview": "#!/usr/bin/env bash\n\nset -e\n\ncd \"$(dirname \"$0\")/..\"\n\npython3 -m pytest -x tests\n\n"
  },
  {
    "path": "scripts/update/__init__.py",
    "chars": 26,
    "preview": "\"\"\"HACS update script.\"\"\"\n"
  },
  {
    "path": "scripts/update/default_repositories.py",
    "chars": 1362,
    "preview": "\"\"\"Update the shipped default repositories data file.\"\"\"\nimport json\nimport os\nimport sys\n\n\ndef update():\n    \"\"\"Update "
  },
  {
    "path": "scripts/update/manifest.py",
    "chars": 933,
    "preview": "\"\"\"Update the manifest file.\"\"\"\nimport json\nimport os\nfrom pathlib import Path\nimport sys\n\nMANIFEST_FILE = Path(f\"{os.ge"
  },
  {
    "path": "tests/__init__.py",
    "chars": 509,
    "preview": "import json\n\nfrom awesomeversion import AwesomeVersion\nfrom homeassistant import const, core, loader\n\n_async_suggest_rep"
  },
  {
    "path": "tests/action/test_hacs_action_integration.py",
    "chars": 3565,
    "preview": "\"\"\"Tests for the HACS action.\"\"\"\nimport json\nimport os\nfrom unittest import mock\n\nimport pytest\n\nfrom tests.common impor"
  },
  {
    "path": "tests/common.py",
    "chars": 19125,
    "preview": "# pylint: disable=missing-docstring,invalid-name\nfrom __future__ import annotations\n\nfrom collections.abc import Iterabl"
  },
  {
    "path": "tests/conftest.py",
    "chars": 16511,
    "preview": "\"\"\"Set up some common test helper things.\"\"\"\nfrom . import patch_time  # isort:skip\n\nimport asyncio\nfrom collections.abc"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/rate_limit.json",
    "chars": 1491,
    "preview": "{\n    \"resources\": {\n        \"core\": {\n            \"limit\": 5000,\n            \"used\": 1,\n            \"remaining\": 4999,\n"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs/default/contents/appdaemon.json",
    "chars": 860,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"appdaemon\",\n    \"path\": \"appdaemon\",\n    "
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs/default/contents/integration.json",
    "chars": 982,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"integration\",\n    \"path\": \"integration\",\n"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs/default/contents/plugin.json",
    "chars": 835,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"plugin\",\n    \"path\": \"plugin\",\n    \"conte"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs/default/contents/python_script.json",
    "chars": 896,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"python_script\",\n    \"path\": \"python_scrip"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs/default/contents/template.json",
    "chars": 853,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"template\",\n    \"path\": \"template\",\n    \"c"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs/default/contents/theme.json",
    "chars": 828,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"theme\",\n    \"path\": \"theme\",\n    \"content"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs/integration/contents/custom_components/hacs/manifest.json",
    "chars": 1065,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"manifest.json\",\n    \"path\": \"custom_compo"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs/integration/contents/hacs.json",
    "chars": 987,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"hacs.json\",\n    \"path\": \"hacs.json\",\n    "
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs/integration/git/trees/main.json",
    "chars": 2165,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/integrat"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs/integration.json",
    "chars": 7042,
    "preview": "{\n    \"id\": 172733314,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxNzI3MzMzMTQ=\",\n    \"name\": \"integration\",\n    \"full_name\": \"h"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/addon-basic/git/trees/main.json",
    "chars": 842,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org//trees/9"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/addon-basic/releases.json",
    "chars": 2,
    "preview": "[]"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/addon-basic.json",
    "chars": 29414,
    "preview": "{\n    \"id\": 1296269,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"addon\",\n    \"full_name\": \"hacs-test-org"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/appdaemon-basic/branches/main.json",
    "chars": 5403,
    "preview": "{\n    \"name\": \"main\",\n    \"commit\": {\n        \"sha\": \"7fd1a60b01f91b314f59955a4e4d4e80d8edf11d\",\n        \"node_id\": \"MDY"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/appdaemon-basic/contents/apps/example.json",
    "chars": 960,
    "preview": "[\n    {\n        \"type\": \"file\",\n        \"size\": 0,\n        \"name\": \"__init__.py\",\n        \"path\": \"apps/example/__init__"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/appdaemon-basic/contents/apps.json",
    "chars": 895,
    "preview": "[\n    {\n        \"type\": \"dir\",\n        \"size\": 0,\n        \"name\": \"example\",\n        \"path\": \"apps/example\",\n        \"sh"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/appdaemon-basic/contents/hacs.json",
    "chars": 981,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"hacs.json\",\n    \"path\": \"hacs.json\",\n    "
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/appdaemon-basic/git/trees/1.0.0.json",
    "chars": 1532,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/appdaemo"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/appdaemon-basic/git/trees/2.0.0.json",
    "chars": 1532,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/appdaemo"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/appdaemon-basic/git/trees/main.json",
    "chars": 1532,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/appdaemo"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/appdaemon-basic/releases.json",
    "chars": 2183,
    "preview": "[\n    {\n        \"url\": \"https://api.github.com/repos/hacs-test-org/appdaemon-basic/releases/1\",\n        \"html_url\": \"htt"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/appdaemon-basic.json",
    "chars": 30184,
    "preview": "{\n    \"id\": 1296265,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"appdaemon\",\n    \"full_name\": \"hacs-test"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic/branches/main.json",
    "chars": 5403,
    "preview": "{\n    \"name\": \"main\",\n    \"commit\": {\n        \"sha\": \"7fd1a60b01f91b314f59955a4e4d4e80d8edf11d\",\n        \"node_id\": \"MDY"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic/contents/custom_components/example/manifest.json",
    "chars": 1245,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"manifest.json\",\n    \"path\": \"custom_compo"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic/contents/hacs.json",
    "chars": 999,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"hacs.json\",\n    \"path\": \"hacs.json\",\n    "
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic/git/trees/1.0.0.json",
    "chars": 1889,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/integrat"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic/git/trees/2.0.0.json",
    "chars": 2249,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/integrat"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic/git/trees/main.json",
    "chars": 1889,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/integrat"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic/releases/latest.json",
    "chars": 3969,
    "preview": "{\n    \"url\": \"https://api.github.com/repos/hacs-test-org/integration-basic/releases/1\",\n    \"html_url\": \"https://github."
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic/releases.json",
    "chars": 17078,
    "preview": "[\n    {\n        \"url\": \"https://api.github.com/repos/hacs-test-org/integration-basic/releases/4\",\n        \"html_url\": \"h"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic-custom/branches/main.json",
    "chars": 5487,
    "preview": "{\n    \"name\": \"main\",\n    \"commit\": {\n        \"sha\": \"7fd1a60b01f91b314f59955a4e4d4e80d8edf11d\",\n        \"node_id\": \"MDY"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic-custom/contents/custom_components/example/manifest.json",
    "chars": 1258,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"manifest.json\",\n    \"path\": \"custom_compo"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic-custom/contents/hacs.json",
    "chars": 1048,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"hacs.json\",\n    \"path\": \"hacs.json\",\n    "
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic-custom/git/trees/1.0.0.json",
    "chars": 1931,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/integrat"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic-custom/git/trees/2.0.0.json",
    "chars": 2298,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/integrat"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic-custom/git/trees/main.json",
    "chars": 1931,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/integrat"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic-custom/releases.json",
    "chars": 4325,
    "preview": "[\n    {\n        \"url\": \"https://api.github.com/repos/hacs-test-org/integration-basic-custom/releases/1\",\n        \"html_u"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic-custom.json",
    "chars": 31727,
    "preview": "{\n    \"id\": 91296269,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"integration\",\n    \"full_name\": \"hacs-t"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-basic.json",
    "chars": 30494,
    "preview": "{\n    \"id\": 1296269,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"integration\",\n    \"full_name\": \"hacs-te"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-invalid/git/trees/main.json",
    "chars": 1542,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/integrat"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-invalid/releases.json",
    "chars": 2,
    "preview": "[]"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/integration-invalid.json",
    "chars": 30822,
    "preview": "{\n    \"id\": 1296269,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"addon\",\n    \"full_name\": \"hacs-test-org"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-basic/branches/main.json",
    "chars": 5403,
    "preview": "{\n    \"name\": \"main\",\n    \"commit\": {\n        \"sha\": \"7fd1a60b01f91b314f59955a4e4d4e80d8edf11d\",\n        \"node_id\": \"MDY"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-basic/contents/hacs.json",
    "chars": 964,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"hacs.json\",\n    \"path\": \"hacs.json\",\n    "
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-basic/git/trees/1.0.0.json",
    "chars": 1205,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/plugin-b"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-basic/git/trees/2.0.0.json",
    "chars": 1205,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/plugin-b"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-basic/git/trees/main.json",
    "chars": 1205,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/plugin-b"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-basic/releases.json",
    "chars": 2165,
    "preview": "[\n    {\n        \"url\": \"https://api.github.com/repos/hacs-test-org/plugin-basic/releases/1\",\n        \"html_url\": \"https:"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-basic.json",
    "chars": 29504,
    "preview": "{\n    \"id\": 1296267,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"plugin\",\n    \"full_name\": \"hacs-test-or"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-custom-dist/branches/main.json",
    "chars": 5403,
    "preview": "{\n    \"name\": \"main\",\n    \"commit\": {\n        \"sha\": \"7fd1a60b01f91b314f59955a4e4d4e80d8edf11d\",\n        \"node_id\": \"MDY"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-custom-dist/contents/hacs.json",
    "chars": 1006,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"hacs.json\",\n    \"path\": \"hacs.json\",\n    "
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-custom-dist/git/trees/1.0.0.json",
    "chars": 1544,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/plugin-c"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-custom-dist/git/trees/2.0.0.json",
    "chars": 1544,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/plugin-c"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-custom-dist/git/trees/main.json",
    "chars": 1544,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/plugin-c"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-custom-dist/releases.json",
    "chars": 2201,
    "preview": "[\n    {\n        \"url\": \"https://api.github.com/repos/hacs-test-org/plugin-custom-dist/releases/1\",\n        \"html_url\": \""
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/plugin-custom-dist.json",
    "chars": 30561,
    "preview": "{\n    \"id\": 12962674,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"plugin\",\n    \"full_name\": \"hacs-test-o"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/python_script-basic/branches/main.json",
    "chars": 5403,
    "preview": "{\n    \"name\": \"main\",\n    \"commit\": {\n        \"sha\": \"7fd1a60b01f91b314f59955a4e4d4e80d8edf11d\",\n        \"node_id\": \"MDY"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/python_script-basic/contents/hacs.json",
    "chars": 1009,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"hacs.json\",\n    \"path\": \"hacs.json\",\n    "
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/python_script-basic/git/trees/1.0.0.json",
    "chars": 1558,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/python_s"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/python_script-basic/git/trees/2.0.0.json",
    "chars": 1558,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/python_s"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/python_script-basic/git/trees/main.json",
    "chars": 1558,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/python_s"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/python_script-basic/releases.json",
    "chars": 2207,
    "preview": "[\n    {\n        \"url\": \"https://api.github.com/repos/hacs-test-org/python_script-basic/releases/1\",\n        \"html_url\": "
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/python_script-basic.json",
    "chars": 31104,
    "preview": "{\n    \"id\": 1296262,\n    \"node_id\": \"MDEwOlJlcG9zaXRvcnkxMjk2MjY5\",\n    \"name\": \"python_script\",\n    \"full_name\": \"hacs-"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/template-basic/branches/main.json",
    "chars": 5403,
    "preview": "{\n    \"name\": \"main\",\n    \"commit\": {\n        \"sha\": \"7fd1a60b01f91b314f59955a4e4d4e80d8edf11d\",\n        \"node_id\": \"MDY"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/template-basic/contents/hacs.json",
    "chars": 1022,
    "preview": "{\n    \"type\": \"file\",\n    \"encoding\": \"base64\",\n    \"size\": 5362,\n    \"name\": \"hacs.json\",\n    \"path\": \"hacs.json\",\n    "
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/template-basic/git/trees/1.0.0.json",
    "chars": 1211,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/template"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/template-basic/git/trees/2.0.0.json",
    "chars": 1211,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/template"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/template-basic/git/trees/main.json",
    "chars": 1211,
    "preview": "{\n    \"sha\": \"9fb037999f264ba9a7fc6274d15fa3ae2ab98312\",\n    \"url\": \"https://api.github.com/repos/hacs-test-org/template"
  },
  {
    "path": "tests/fixtures/proxy/api.github.com/repos/hacs-test-org/template-basic/releases.json",
    "chars": 4245,
    "preview": "[\n    {\n        \"url\": \"https://api.github.com/repos/hacs-test-org/template-basic/releases/1\",\n        \"html_url\": \"http"
  }
]

// ... and 501 more files (download for full content)

About this extraction

This page contains the full source code of the hacs/integration GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 701 files (2.3 MB), approximately 642.5k tokens, and a symbol index with 655 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!