1
0
mirror of https://github.com/home-assistant/core.git synced 2026-04-02 00:20:30 +01:00

Create IntegrationType enum (#166598)

This commit is contained in:
Robert Resch
2026-03-26 15:53:57 +01:00
committed by GitHub
parent cb7f9b5f49
commit 3c67c6087a
9 changed files with 62 additions and 42 deletions

View File

@@ -2,7 +2,7 @@
from __future__ import annotations
from .model import Config, Integration
from .model import Config, Integration, IntegrationType
BASE = """
# This file is generated by script/hassfest/codeowners.py
@@ -65,7 +65,7 @@ def generate_and_validate(integrations: dict[str, Integration], config: Config)
for domain in sorted(integrations):
integration = integrations[domain]
if integration.integration_type == "virtual":
if integration.integration_type == IntegrationType.VIRTUAL:
continue
codeowners = integration.manifest["codeowners"]

View File

@@ -6,7 +6,7 @@ import json
from typing import Any
from .brand import validate as validate_brands
from .model import Brand, Config, Integration
from .model import Brand, Config, Integration, IntegrationType
from .serializer import format_python_namespace
UNIQUE_ID_IGNORE = {"huawei_lte", "mqtt", "adguard"}
@@ -75,7 +75,7 @@ def _generate_and_validate(integrations: dict[str, Integration], config: Config)
_validate_integration(config, integration)
if integration.integration_type == "helper":
if integration.integration_type == IntegrationType.HELPER:
domains["helper"].append(domain)
else:
domains["integration"].append(domain)
@@ -94,8 +94,8 @@ def _populate_brand_integrations(
for domain in sub_integrations:
integration = integrations.get(domain)
if not integration or integration.integration_type in (
"entity",
"system",
IntegrationType.ENTITY,
IntegrationType.SYSTEM,
):
continue
metadata: dict[str, Any] = {
@@ -170,7 +170,10 @@ def _generate_integrations(
result["integration"][domain] = metadata
else: # integration
integration = integrations[domain]
if integration.integration_type in ("entity", "system"):
if integration.integration_type in (
IntegrationType.ENTITY,
IntegrationType.SYSTEM,
):
continue
if integration.translated_name:
@@ -180,7 +183,7 @@ def _generate_integrations(
metadata["integration_type"] = integration.integration_type
if integration.integration_type == "virtual":
if integration.integration_type == IntegrationType.VIRTUAL:
if integration.supported_by:
metadata["supported_by"] = integration.supported_by
if integration.iot_standards:
@@ -195,7 +198,7 @@ def _generate_integrations(
):
metadata["single_config_entry"] = single_config_entry
if integration.integration_type == "helper":
if integration.integration_type == IntegrationType.HELPER:
result["helper"][domain] = metadata
else:
result["integration"][domain] = metadata

View File

@@ -4,7 +4,7 @@ import re
from homeassistant.util.yaml import load_yaml_dict
from .model import Config, Integration
from .model import Config, Integration, IntegrationType
# Non-entity-platform components that belong in base_platforms
EXTRA_BASE_PLATFORMS = {"diagnostics"}
@@ -29,7 +29,7 @@ def validate(integrations: dict[str, Integration], config: Config) -> None:
entity_platforms = {
integration.domain
for integration in integrations.values()
if integration.manifest.get("integration_type") == "entity"
if integration.integration_type == IntegrationType.ENTITY
and integration.domain != "tag"
}

View File

@@ -11,7 +11,7 @@ from voluptuous.humanize import humanize_error
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.icon import convert_shorthand_service_icon
from .model import Config, Integration
from .model import Config, Integration, IntegrationType
from .translations import translation_key_validator
@@ -141,7 +141,7 @@ TRIGGER_ICONS_SCHEMA = cv.schema_with_slug_keys(
def icon_schema(
core_integration: bool, integration_type: str, no_entity_platform: bool
core_integration: bool, integration_type: IntegrationType, no_entity_platform: bool
) -> vol.Schema:
"""Create an icon schema."""
@@ -189,8 +189,12 @@ def icon_schema(
}
)
if integration_type in ("entity", "helper", "system"):
if integration_type != "entity" or no_entity_platform:
if integration_type in (
IntegrationType.ENTITY,
IntegrationType.HELPER,
IntegrationType.SYSTEM,
):
if integration_type != IntegrationType.ENTITY or no_entity_platform:
field = vol.Optional("entity_component")
else:
field = vol.Required("entity_component")
@@ -207,7 +211,7 @@ def icon_schema(
)
}
)
if integration_type not in ("entity", "system"):
if integration_type not in (IntegrationType.ENTITY, IntegrationType.SYSTEM):
schema = schema.extend(
{
vol.Optional("entity"): vol.All(

View File

@@ -2,7 +2,7 @@
from __future__ import annotations
from .model import Config, Integration
from .model import Config, Integration, IntegrationType
from .serializer import format_python
@@ -12,12 +12,12 @@ def validate(integrations: dict[str, Integration], config: Config) -> None:
if config.specific_integrations:
return
int_type = "entity"
int_type = IntegrationType.ENTITY
domains = [
integration.domain
for integration in integrations.values()
if integration.manifest.get("integration_type") == int_type
if integration.integration_type == int_type
# Tag is type "entity" but has no entity platform
and integration.domain != "tag"
]
@@ -36,7 +36,7 @@ def validate(integrations: dict[str, Integration], config: Config) -> None:
def generate(integrations: dict[str, Integration], config: Config) -> None:
"""Generate integration file."""
int_type = "entity"
int_type = IntegrationType.ENTITY
filename = "entity_platforms"
platform_path = config.root / f"homeassistant/generated/{filename}.py"
platform_path.write_text(config.cache[f"integrations_{int_type}"])

View File

@@ -21,7 +21,7 @@ from homeassistant.const import Platform
from homeassistant.helpers import config_validation as cv
from script.util import sort_manifest as util_sort_manifest
from .model import Config, Integration, ScaledQualityScaleTiers
from .model import Config, Integration, IntegrationType, ScaledQualityScaleTiers
DOCUMENTATION_URL_SCHEMA = "https"
DOCUMENTATION_URL_HOST = "www.home-assistant.io"
@@ -206,15 +206,7 @@ INTEGRATION_MANIFEST_SCHEMA = vol.Schema(
vol.Required("domain"): str,
vol.Required("name"): str,
vol.Optional("integration_type", default="hub"): vol.In(
[
"device",
"entity",
"hardware",
"helper",
"hub",
"service",
"system",
]
[t.value for t in IntegrationType if t != IntegrationType.VIRTUAL]
),
vol.Optional("config_flow"): bool,
vol.Optional("mqtt"): [str],
@@ -311,7 +303,7 @@ VIRTUAL_INTEGRATION_MANIFEST_SCHEMA = vol.Schema(
{
vol.Required("domain"): str,
vol.Required("name"): str,
vol.Required("integration_type"): "virtual",
vol.Required("integration_type"): IntegrationType.VIRTUAL.value,
vol.Exclusive("iot_standards", "virtual_integration"): [
vol.Any("homekit", "zigbee", "zwave")
],
@@ -322,7 +314,7 @@ VIRTUAL_INTEGRATION_MANIFEST_SCHEMA = vol.Schema(
def manifest_schema(value: dict[str, Any]) -> vol.Schema:
"""Validate integration manifest."""
if value.get("integration_type") == "virtual":
if value.get("integration_type") == IntegrationType.VIRTUAL:
return VIRTUAL_INTEGRATION_MANIFEST_SCHEMA(value)
return INTEGRATION_MANIFEST_SCHEMA(value)
@@ -373,12 +365,12 @@ def validate_manifest(integration: Integration, core_components_dir: Path) -> No
if (
domain not in NO_IOT_CLASS
and "iot_class" not in integration.manifest
and integration.manifest.get("integration_type") != "virtual"
and integration.integration_type != IntegrationType.VIRTUAL
):
integration.add_error("manifest", "Domain is missing an IoT Class")
if (
integration.manifest.get("integration_type") == "virtual"
integration.integration_type == IntegrationType.VIRTUAL
and (supported_by := integration.manifest.get("supported_by"))
and not (core_components_dir / supported_by).exists()
):

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
from dataclasses import dataclass, field
from enum import IntEnum
from enum import IntEnum, StrEnum
import json
import pathlib
from typing import Any, Literal
@@ -200,9 +200,15 @@ class Integration:
return self.manifest.get("supported_by", {})
@property
def integration_type(self) -> str:
def integration_type(self) -> IntegrationType:
"""Get integration_type."""
return self.manifest.get("integration_type", "hub")
integration_type = self.manifest.get("integration_type", "hub")
try:
return IntegrationType(integration_type)
except ValueError:
# The manifest validation will catch this as an error, so we can default to
# a valid value here to avoid ValueErrors in other plugins
return IntegrationType.HUB
@property
def iot_class(self) -> str | None:
@@ -248,6 +254,19 @@ class Integration:
self.manifest_path = manifest_path
class IntegrationType(StrEnum):
"""Supported integration types."""
DEVICE = "device"
ENTITY = "entity"
HARDWARE = "hardware"
HELPER = "helper"
HUB = "hub"
SERVICE = "service"
SYSTEM = "system"
VIRTUAL = "virtual"
class ScaledQualityScaleTiers(IntEnum):
"""Supported manifest quality scales."""

View File

@@ -11,7 +11,7 @@ from homeassistant.const import Platform
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util.yaml import load_yaml_dict
from .model import Config, Integration, ScaledQualityScaleTiers
from .model import Config, Integration, IntegrationType, ScaledQualityScaleTiers
from .quality_scale_validation import (
RuleValidationProtocol,
action_setup,
@@ -2200,7 +2200,7 @@ def validate_iqs_file(config: Config, integration: Integration) -> None:
if (
integration.domain not in INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE
and integration.domain not in NO_QUALITY_SCALE
and integration.integration_type != "virtual"
and integration.integration_type != IntegrationType.VIRTUAL
):
integration.add_error(
"quality_scale",
@@ -2218,7 +2218,7 @@ def validate_iqs_file(config: Config, integration: Integration) -> None:
)
return
return
if integration.integration_type == "virtual":
if integration.integration_type == IntegrationType.VIRTUAL:
integration.add_error(
"quality_scale",
"Virtual integrations are not allowed to have a quality scale file.",

View File

@@ -14,7 +14,7 @@ from voluptuous.humanize import humanize_error
import homeassistant.helpers.config_validation as cv
from script.translations import upload
from .model import Config, Integration
from .model import Config, Integration, IntegrationType
UNDEFINED = 0
REQUIRED = 1
@@ -345,7 +345,9 @@ def gen_strings_schema(config: Config, integration: Integration) -> vol.Schema:
flow_title=REMOVED,
require_step_title=False,
mandatory_description=(
"user" if integration.integration_type == "helper" else None
"user"
if integration.integration_type == IntegrationType.HELPER
else None
),
),
vol.Optional("config_subentries"): cv.schema_with_slug_keys(