HACS gives you a powerful UI to handle downloads of all your custom needs.
This report summarizes scanner evidence for hacs/integration, a Python project with 7,783 GitHub stars. SynthScan examined 29,306 lines of code across 593 source files, recording 362 pattern matches distributed across 9 syntactic categories. The legacy adjusted pattern signal is 13.7 (Moderate pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 261 finding(s).
Are imports declared and package/API names verified? 66 finding(s).
Are wrappers, guards, and repeated structures necessary? 260 finding(s).
Which changes need focused human review? 362 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 362 distinct pattern matches across 9 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_diagnostics.py | 33 | async def test_diagnostics_with_exception( | CODE |
| LOW | tests/test_init_function.py | 21 | async def test_async_remove_config_entry_device_success(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init_function.py | 39 | async def test_async_remove_config_entry_device_no_identifier(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init_function.py | 55 | async def test_async_remove_config_entry_device_hacs_system_id(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init_function.py | 70 | async def test_async_remove_config_entry_device_still_downloaded(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init_function.py | 95 | async def test_async_remove_config_entry_device_empty_identifiers(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init_function.py | 110 | async def test_async_remove_config_entry_device_invalid_identifier_format(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init_function.py | 129 | async def test_async_remove_config_entry_device_multiple_identifiers(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init_function.py | 151 | async def test_async_remove_config_entry_device_with_integer_id(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init_function.py | 169 | async def test_async_remove_config_entry_device_system_id_with_multiple_identifiers(hass: HomeAssistant, hacs: HacsBase) | CODE |
| LOW | tests/__init__.py | 11 | def async_suggest_report_issue_mock(*args, **kwargs): | CODE |
| LOW | tests/common.py | 117 | def category_test_data_parametrized( | CODE |
| LOW | tests/common.py | 151 | def normalize_voluptuous_paths(text: str) -> str: | CODE |
| LOW | tests/common.py | 257 | def ensure_auth_manager_loaded(auth_mgr): | CODE |
| LOW | tests/test_config_flow.py | 35 | async def test_full_user_flow_implementation( | CODE |
| LOW | tests/test_config_flow.py | 120 | async def test_flow_with_remove_while_activating( | CODE |
| LOW | tests/test_config_flow.py | 187 | async def test_flow_with_registration_failure( | CODE |
| LOW | tests/test_config_flow.py | 229 | async def test_flow_with_activation_failure( | CODE |
| LOW | tests/test_data_client.py | 27 | async def test_basic_functionality_data( | CODE |
| LOW | tests/test_data_client.py | 42 | async def test_basic_functionality_repositories( | CODE |
| LOW | tests/test_data_client.py | 168 | async def test_basic_functionality_data_validate( | CODE |
| LOW | tests/test_data_client.py | 205 | async def test_discard_invalid_repo_data( | CODE |
| LOW | tests/test_init.py | 16 | async def test_async_remove_config_entry_device_no_identifier(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init.py | 32 | async def test_async_remove_config_entry_device_hacs_system_id(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init.py | 47 | async def test_async_remove_config_entry_device_success(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init.py | 64 | async def test_async_remove_config_entry_device_still_downloaded(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init.py | 89 | async def test_async_remove_config_entry_device_empty_identifiers(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init.py | 104 | async def test_async_remove_config_entry_device_invalid_identifier_format(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init.py | 123 | async def test_async_remove_config_entry_device_multiple_identifiers(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init.py | 144 | async def test_async_remove_config_entry_device_with_integer_id(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_init.py | 161 | async def test_async_remove_config_entry_device_system_id_with_multiple_identifiers(hass: HomeAssistant, hacs: HacsBase) | CODE |
| LOW | tests/test_init.py | 180 | async def test_async_remove_config_entry_device_finds_first_valid_identifier(hass: HomeAssistant, hacs: HacsBase): | CODE |
| LOW | tests/test_system_health.py | 74 | async def test_system_health_after_unload( | CODE |
| LOW | tests/test_system_health.py | 90 | async def test_system_health_no_hacs( | CODE |
| LOW | tests/validate/test_repository_topics_check.py | 4 | async def test_repository_no_topics(repository): | CODE |
| LOW | tests/validate/test_repository_topics_check.py | 11 | async def test_repository_hacs_topics(repository): | CODE |
| LOW | tests/validate/test_repository_issues_check.py | 4 | async def test_repository_issues_enabled(repository): | CODE |
| LOW | tests/validate/test_repository_issues_check.py | 11 | async def test_repository_issues_not_enabled(repository): | CODE |
| LOW⚡ | tests/validate/test_integration_manifest_check.py | 6 | async def test_integration_no_manifest(repository_integration): | CODE |
| LOW⚡ | tests/validate/test_integration_manifest_check.py | 12 | async def test_integration_manifest_with_valid_manifest(repository_integration): | CODE |
| LOW⚡ | tests/validate/test_integration_manifest_check.py | 19 | async def _async_get_integration_manifest(**__): | CODE |
| LOW | tests/validate/test_integration_manifest_check.py | 36 | async def test_hacs_manifest_with_invalid_manifest(repository_integration): | CODE |
| LOW | tests/validate/test_integration_manifest_check.py | 43 | async def _async_get_integration_manifest(**__): | CODE |
| LOW | tests/validate/test_repository_archived_check.py | 11 | async def test_repository_not_archived(repository): | CODE |
| LOW⚡ | tests/validate/test_license.py | 11 | async def test_repository_no_license(repository): | CODE |
| LOW⚡ | tests/validate/test_license.py | 19 | async def test_repository_unrecognized_license(repository): | CODE |
| LOW⚡ | tests/validate/test_license.py | 29 | async def test_repository_missing_spdx_id(repository): | CODE |
| LOW⚡ | tests/validate/test_license.py | 39 | async def test_repository_non_osi_license(repository): | CODE |
| LOW | tests/validate/test_license.py | 53 | async def test_spdx_license_list_fetch_failure(repository, response_mocker: ResponseMocker): | CODE |
| LOW | tests/validate/test_license.py | 66 | async def test_repository_popular_license_skips_fetch( | CODE |
| LOW | tests/validate/test_license.py | 85 | async def test_repository_uncommon_osi_license(repository, response_mocker: ResponseMocker): | CODE |
| LOW | tests/validate/test_images_check.py | 4 | async def test_repository_has_images(repository): | CODE |
| LOW | tests/validate/test_images_check.py | 8 | async def _async_get_info_file_contents(*_, **__): | CODE |
| LOW | tests/validate/test_images_check.py | 19 | async def test_repository_has_not_images(repository): | STRING |
| LOW | tests/validate/test_images_check.py | 23 | async def _async_get_info_file_contents(*_, **__): | STRING |
| LOW | tests/validate/test_repository_description_check.py | 4 | async def test_repository_no_description(repository): | CODE |
| LOW | tests/validate/test_repository_description_check.py | 11 | async def test_repository_hacs_description(repository): | CODE |
| LOW | tests/validate/test_hacsjson_check.py | 17 | async def test_hacs_manifest_no_manifest(repository, caplog): | CODE |
| LOW | tests/validate/test_hacsjson_check.py | 24 | async def test_hacs_manifest_with_valid_manifest(repository): | CODE |
| LOW | tests/validate/test_hacsjson_check.py | 38 | async def test_hacs_manifest_with_invalid_manifest(repository): | CODE |
| 165 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/patch_time.py | 5 | CODE | |
| LOW | tests/conftest.py | 2 | CODE | |
| LOW | tests/conftest.py | 8 | CODE | |
| LOW | tests/__init__.py | 1 | CODE | |
| LOW | tests/__init__.py | 3 | CODE | |
| LOW | tests/__init__.py | 4 | CODE | |
| LOW | tests/common.py | 2 | CODE | |
| LOW | tests/common.py | 17 | CODE | |
| LOW | tests/common.py | 20 | CODE | |
| LOW | tests/homeassistantfixtures/min.py | 8 | CODE | |
| LOW | tests/homeassistantfixtures/dev.py | 8 | CODE | |
| LOW | tests/repositories/test_update_repository.py | 4 | CODE | |
| LOW | tests/repositories/test_get_documentation.py | 3 | CODE | |
| LOW | custom_components/hacs/update.py | 3 | CODE | |
| LOW | custom_components/hacs/coordinator.py | 3 | CODE | |
| LOW | custom_components/hacs/diagnostics.py | 3 | CODE | |
| LOW | custom_components/hacs/frontend.py | 3 | CODE | |
| LOW | custom_components/hacs/__init__.py | 7 | CODE | |
| LOW | custom_components/hacs/__init__.py | 115 | CODE | |
| LOW | custom_components/hacs/switch.py | 3 | CODE | |
| LOW | custom_components/hacs/repairs.py | 3 | CODE | |
| LOW | custom_components/hacs/data_client.py | 3 | CODE | |
| LOW | custom_components/hacs/data_client.py | 5 | CODE | |
| LOW | custom_components/hacs/entity.py | 3 | CODE | |
| LOW | custom_components/hacs/base.py | 3 | CODE | |
| LOW | custom_components/hacs/base.py | 64 | CODE | |
| LOW | custom_components/hacs/config_flow.py | 3 | CODE | |
| LOW | custom_components/hacs/validate/integration_manifest.py | 1 | CODE | |
| LOW | custom_components/hacs/validate/issues.py | 1 | CODE | |
| LOW | custom_components/hacs/validate/archived.py | 1 | CODE | |
| LOW | custom_components/hacs/validate/description.py | 1 | CODE | |
| LOW | custom_components/hacs/validate/information.py | 1 | CODE | |
| LOW | custom_components/hacs/validate/hacsjson.py | 1 | CODE | |
| LOW | custom_components/hacs/validate/topics.py | 1 | CODE | |
| LOW | custom_components/hacs/validate/brands.py | 1 | CODE | |
| LOW | custom_components/hacs/validate/images.py | 1 | CODE | |
| LOW | custom_components/hacs/validate/license.py | 1 | CODE | |
| LOW | custom_components/hacs/validate/manager.py | 3 | CODE | |
| LOW | custom_components/hacs/validate/base.py | 3 | CODE | |
| LOW | custom_components/hacs/websocket/critical.py | 3 | CODE | |
| LOW | custom_components/hacs/websocket/__init__.py | 3 | CODE | |
| LOW | custom_components/hacs/websocket/repositories.py | 3 | CODE | |
| LOW | custom_components/hacs/websocket/repository.py | 3 | CODE | |
| LOW | custom_components/hacs/utils/backup.py | 3 | CODE | |
| LOW | custom_components/hacs/utils/version.py | 3 | CODE | |
| LOW | custom_components/hacs/utils/decorator.py | 3 | CODE | |
| LOW | custom_components/hacs/utils/validate.py | 3 | CODE | |
| LOW | custom_components/hacs/utils/file_system.py | 3 | CODE | |
| LOW | custom_components/hacs/utils/path.py | 3 | CODE | |
| LOW | custom_components/hacs/utils/json.py | 3 | CODE | |
| LOW | custom_components/hacs/utils/regex.py | 3 | CODE | |
| LOW | custom_components/hacs/utils/filters.py | 3 | CODE | |
| LOW | custom_components/hacs/utils/data.py | 3 | CODE | |
| LOW | custom_components/hacs/utils/queue_manager.py | 3 | CODE | |
| LOW | custom_components/hacs/repositories/theme.py | 3 | CODE | |
| LOW | custom_components/hacs/repositories/__init__.py | 3 | CODE | |
| LOW | custom_components/hacs/repositories/integration.py | 3 | CODE | |
| LOW | custom_components/hacs/repositories/plugin.py | 3 | CODE | |
| LOW | custom_components/hacs/repositories/template.py | 3 | CODE | |
| LOW | custom_components/hacs/repositories/appdaemon.py | 3 | CODE | |
| 6 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | custom_components/hacs/__init__.py | 85 | except BaseException: # lgtm [py/catch-base-exception] pylint: disable=broad-except | CODE |
| HIGH | custom_components/hacs/__init__.py | 215 | except AttributeError: | CODE |
| HIGH | custom_components/hacs/__init__.py | 116 | except ImportError: | CODE |
| HIGH | custom_components/hacs/base.py | 1046 | except HacsException: | CODE |
| HIGH | custom_components/hacs/utils/version.py | 25 | except (AwesomeVersionException, AttributeError, KeyError): | CODE |
| HIGH | custom_components/hacs/utils/data.py | 164 | except HomeAssistantError: | CODE |
| HIGH | custom_components/hacs/repositories/base.py | 723 | except BaseException: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/common.py | 170 | CODE | |
| LOW | tests/repositories/test_download_counting.py | 157 | CODE | |
| LOW | tests/repositories/test_download_counting.py | 179 | CODE | |
| LOW | tests/repositories/test_download_counting.py | 205 | CODE | |
| LOW | tests/repositories/test_download_counting.py | 233 | CODE | |
| LOW | tests/repositories/test_download_counting.py | 258 | CODE | |
| LOW | tests/repositories/test_download_counting.py | 281 | CODE | |
| LOW | tests/repositories/test_download_counting.py | 304 | CODE | |
| LOW | tests/repositories/test_download_counting.py | 329 | CODE | |
| LOW | tests/repositories/test_download_counting.py | 356 | CODE | |
| LOW | tests/hacsbase/test_hacsbase_data.py | 24 | CODE | |
| LOW | tests/hacsbase/test_hacsbase_data.py | 27 | CODE | |
| LOW | custom_components/hacs/base.py | 424 | CODE | |
| LOW | custom_components/hacs/base.py | 517 | CODE | |
| LOW | custom_components/hacs/base.py | 669 | CODE | |
| LOW | custom_components/hacs/base.py | 848 | CODE | |
| LOW | custom_components/hacs/base.py | 427 | CODE | |
| LOW | custom_components/hacs/validate/information.py | 21 | CODE | |
| LOW | custom_components/hacs/utils/filters.py | 8 | CODE | |
| LOW | custom_components/hacs/utils/data.py | 156 | CODE | |
| LOW | custom_components/hacs/repositories/plugin.py | 99 | CODE | |
| LOW | custom_components/hacs/repositories/base.py | 197 | CODE | |
| LOW | custom_components/hacs/repositories/base.py | 358 | CODE | |
| LOW | custom_components/hacs/repositories/base.py | 408 | CODE | |
| LOW | custom_components/hacs/repositories/base.py | 653 | CODE | |
| LOW | custom_components/hacs/repositories/base.py | 777 | CODE | |
| LOW | custom_components/hacs/repositories/base.py | 1047 | CODE | |
| LOW | custom_components/hacs/repositories/base.py | 1176 | CODE | |
| LOW | custom_components/hacs/repositories/base.py | 680 | CODE | |
| LOW | action/action.py | 94 | CODE | |
| LOW | scripts/data/validate_category_data.py | 19 | CODE | |
| LOW | scripts/data/generate_category_data.py | 199 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | custom_components/hacs/update.py | 149 | def _update_download_progress(self, data: dict) -> None: | CODE |
| LOW⚡ | custom_components/hacs/update.py | 156 | def _update_in_progress(self, progress: int | bool) -> None: | CODE |
| LOW | custom_components/hacs/entity.py | 68 | def _update_and_write_state(self, _: Any) -> None: | CODE |
| LOW | custom_components/hacs/base.py | 137 | def update_from_dict(self, data: dict) -> None: | CODE |
| LOW | custom_components/hacs/base.py | 389 | def set_stage(self, stage: HacsStage | None) -> None: | CODE |
| LOW | custom_components/hacs/base.py | 775 | def set_active_categories(self) -> None: | CODE |
| LOW | custom_components/hacs/base.py | 1004 | async def update_repository(repository: HacsRepository) -> None: | CODE |
| LOW | custom_components/hacs/base.py | 1020 | async def update_coordinators() -> None: | CODE |
| LOW | custom_components/hacs/utils/json.py | 5 | __all__ = ["json_loads"] | CODE |
| LOW | custom_components/hacs/repositories/theme.py | 113 | def update_filenames(self) -> None: | CODE |
| LOW | custom_components/hacs/repositories/plugin.py | 99 | def update_filenames(self) -> None: | CODE |
| LOW | custom_components/hacs/repositories/plugin.py | 193 | async def update_dashboard_resources(self) -> None: | CODE |
| LOW | custom_components/hacs/repositories/appdaemon.py | 50 | async def update_repository(self, ignore_issues: bool = False, force: bool = False) -> None: | CODE |
| LOW | custom_components/hacs/repositories/python_script.py | 105 | def update_filenames(self) -> None: | CODE |
| LOW | custom_components/hacs/repositories/base.py | 197 | def update_data(self, data: dict, action: bool = False) -> None: | CODE |
| LOW | custom_components/hacs/repositories/base.py | 254 | def update_data(self, data: dict) -> None: | CODE |
| LOW | custom_components/hacs/repositories/base.py | 465 | async def update_repository(self, ignore_issues=False, force=False) -> None: | CODE |
| LOW | custom_components/hacs/repositories/base.py | 1016 | def update_filenames(self) -> None: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | custom_components/hacs/data_client.py | 57 | except Exception as exception: | CODE |
| LOW | custom_components/hacs/validate/license.py | 78 | except Exception as err: | CODE |
| LOW | custom_components/hacs/websocket/repository.py | 50 | except Exception as exception: # pylint: disable=broad-except | CODE |
| LOW | custom_components/hacs/websocket/repository.py | 266 | except Exception as exception: # pylint: disable=broad-except | CODE |
| LOW | custom_components/hacs/websocket/repository.py | 351 | except Exception as exception: | CODE |
| LOW | custom_components/hacs/utils/decorator.py | 53 | except Exception: # pylint: disable=broad-except | CODE |
| LOW | custom_components/hacs/utils/decorator.py | 60 | except Exception: # pylint: disable=broad-except | CODE |
| MEDIUM | custom_components/hacs/utils/decorator.py | 50 | def sync_wrapper(*args, **kwargs): | CODE |
| MEDIUM | custom_components/hacs/utils/decorator.py | 57 | def async_wrapper(*args, **kwargs): | CODE |
| LOW | custom_components/hacs/repositories/integration.py | 215 | except Exception: # pylint: disable=broad-except | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/test_init_function.py | 72 | # Create a mock repository | COMMENT |
| MEDIUM | tests/test_init.py | 66 | # Create a mock repository | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | custom_components/hacs/validate/brands.py | 37 | # Check if the integraiton provides local brand assets | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | …et_documentation/installed_false_last_version_2_0_0.md | 1 | ## Example readme file (2.0.0) | COMMENT |