Ejemplo n.º 1
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the xbox component."""
    hass.data[DOMAIN] = {}

    if DOMAIN not in config:
        return True

    await application_credentials.async_import_client_credential(
        hass,
        DOMAIN,
        application_credentials.ClientCredential(
            config[DOMAIN][CONF_CLIENT_ID],
            config[DOMAIN][CONF_CLIENT_SECRET]),
    )
    async_create_issue(
        hass,
        DOMAIN,
        "deprecated_yaml",
        breaks_in_ha_version="2022.9.0",
        is_fixable=False,
        severity=IssueSeverity.WARNING,
        translation_key="deprecated_yaml",
    )
    _LOGGER.warning(
        "Configuration of Xbox integration in YAML is deprecated and "
        "will be removed in Home Assistant 2022.9.; Your existing configuration "
        "(including OAuth Application Credentials) has been imported into "
        "the UI automatically and can be safely removed from your "
        "configuration.yaml file")

    return True
Ejemplo n.º 2
0
async def async_setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    async_add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Open Exchange Rates sensor."""
    async_create_issue(
        hass,
        DOMAIN,
        "deprecated_yaml",
        breaks_in_ha_version="2022.11.0",
        is_fixable=False,
        severity=IssueSeverity.WARNING,
        translation_key="deprecated_yaml",
    )
    hass.async_create_task(
        hass.config_entries.flow.async_init(
            DOMAIN,
            context={"source": SOURCE_IMPORT},
            data=config,
        )
    )

    LOGGER.warning(
        "Configuration of Open Exchange Rates integration in YAML is deprecated and "
        "will be removed in Home Assistant 2022.11.; Your existing configuration "
        "has been imported into the UI automatically and can be safely removed from"
        " your configuration.yaml file"
    )
Ejemplo n.º 3
0
async def async_setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    async_add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up our socket to the AVR."""
    async_create_issue(
        hass,
        DOMAIN,
        "deprecated_yaml",
        breaks_in_ha_version="2022.10.0",
        is_fixable=False,
        severity=IssueSeverity.WARNING,
        translation_key="deprecated_yaml",
    )
    _LOGGER.warning(
        "Configuration of the Anthem A/V Receivers integration in YAML is "
        "deprecated and will be removed in Home Assistant 2022.10; Your "
        "existing configuration has been imported into the UI automatically "
        "and can be safely removed from your configuration.yaml file")
    await hass.config_entries.flow.async_init(
        DOMAIN,
        context={"source": SOURCE_IMPORT},
        data=config,
    )
Ejemplo n.º 4
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Migrate from YAML to ConfigEntry."""
    if DOMAIN not in config:
        return True

    hass.data[DOMAIN] = {}

    if not hass.config_entries.async_entries(DOMAIN):
        new_conf = {}
        new_conf[CONF_USERNAME] = config[DOMAIN][CONF_USERNAME]
        new_conf[CONF_PASSWORD] = config[DOMAIN][CONF_PASSWORD]
        new_conf[CONF_REGION] = config[DOMAIN].get(CONF_REGION)
        new_conf[CONF_SCANDINAVIAN_MILES] = config[DOMAIN][
            CONF_SCANDINAVIAN_MILES]
        new_conf[CONF_MUTABLE] = config[DOMAIN][CONF_MUTABLE]

        hass.async_create_task(
            hass.config_entries.flow.async_init(
                DOMAIN, context={"source": SOURCE_IMPORT}, data=new_conf))

        async_create_issue(
            hass,
            DOMAIN,
            "deprecated_yaml",
            breaks_in_ha_version=None,
            is_fixable=False,
            severity=IssueSeverity.WARNING,
            translation_key="deprecated_yaml",
        )

    return True
Ejemplo n.º 5
0
    def validator(config: ConfigType) -> ConfigType:
        """Return a validator."""
        nonlocal warned

        if domain in warned:
            return config

        _LOGGER.warning(
            "Manually configured MQTT %s(s) found under platform key '%s', "
            "please move to the mqtt integration key, see "
            "https://www.home-assistant.io/integrations/%s.mqtt/#new_format",
            domain,
            domain,
            domain,
        )
        warned.add(domain)
        # Register a repair
        async_create_issue(
            async_get_hass(),
            DOMAIN,
            f"deprecated_yaml_{domain}",
            breaks_in_ha_version="2022.12.0",  # Warning first added in 2022.6.0
            is_fixable=False,
            severity=IssueSeverity.WARNING,
            translation_key="deprecated_yaml",
            translation_placeholders={
                "more_info_url":
                f"https://www.home-assistant.io/integrations/{domain}.mqtt/#new_format",
                "platform": domain,
            },
        )
        return config
Ejemplo n.º 6
0
async def async_get_service(
    hass: HomeAssistant,
    config: ConfigType,
    discovery_info: DiscoveryInfoType | None = None,
) -> PushoverNotificationService | None:
    """Get the Pushover notification service."""
    if discovery_info is None:

        async_create_issue(
            hass,
            DOMAIN,
            "deprecated_yaml",
            breaks_in_ha_version="2022.11.0",
            is_fixable=False,
            severity=IssueSeverity.WARNING,
            translation_key="deprecated_yaml",
        )
        hass.async_create_task(
            hass.config_entries.flow.async_init(
                DOMAIN,
                context={"source": SOURCE_IMPORT},
                data=config,
            ))
        return None

    pushover_api: PushoverAPI = hass.data[DOMAIN][discovery_info["entry_id"]]
    return PushoverNotificationService(hass, pushover_api,
                                       discovery_info[CONF_USER_KEY])
Ejemplo n.º 7
0
def async_log_deprecated_service_call(
    hass: HomeAssistant,
    call: ServiceCall,
    alternate_service: str,
    alternate_target: str,
    breaks_in_ha_version: str,
) -> None:
    """Log a warning about a deprecated service call."""
    deprecated_service = f"{call.domain}.{call.service}"

    async_create_issue(
        hass,
        DOMAIN,
        f"deprecated_service_{deprecated_service}",
        breaks_in_ha_version=breaks_in_ha_version,
        is_fixable=True,
        is_persistent=True,
        severity=IssueSeverity.WARNING,
        translation_key="deprecated_service",
        translation_placeholders={
            "alternate_service": alternate_service,
            "alternate_target": alternate_target,
            "deprecated_service": deprecated_service,
        },
    )

    LOGGER.warning(
        ('The "%s" service is deprecated and will be removed in %s; use the "%s" '
         'service and pass it a target entity ID of "%s"'),
        deprecated_service,
        breaks_in_ha_version,
        alternate_service,
        alternate_target,
    )
Ejemplo n.º 8
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Google component."""
    if DOMAIN not in config:
        return True

    conf = config.get(DOMAIN, {})
    hass.data[DOMAIN] = {DATA_CONFIG: conf}

    if CONF_CLIENT_ID in conf and CONF_CLIENT_SECRET in conf:
        await async_import_client_credential(
            hass,
            DOMAIN,
            ClientCredential(
                conf[CONF_CLIENT_ID],
                conf[CONF_CLIENT_SECRET],
            ),
            DEVICE_AUTH_IMPL,
        )

    # Import credentials from the old token file into the new way as
    # a ConfigEntry managed by home assistant.
    storage = Storage(hass.config.path(TOKEN_FILE))
    creds = await hass.async_add_executor_job(storage.get)
    if creds and get_feature_access(hass).scope in creds.scopes:
        _LOGGER.debug("Importing configuration entry with credentials")
        hass.async_create_task(
            hass.config_entries.flow.async_init(
                DOMAIN,
                context={"source": config_entries.SOURCE_IMPORT},
                data={
                    "creds": creds,
                },
            ))
    async_create_issue(
        hass,
        DOMAIN,
        "deprecated_yaml",
        breaks_in_ha_version="2022.9.0",  # Warning first added in 2022.6.0
        is_fixable=False,
        severity=IssueSeverity.WARNING,
        translation_key="deprecated_yaml",
    )
    if conf.get(CONF_TRACK_NEW) is False:
        # The track_new as False would previously result in new entries
        # in google_calendars.yaml with track set to False which is
        # handled at calendar entity creation time.
        async_create_issue(
            hass,
            DOMAIN,
            "removed_track_new_yaml",
            breaks_in_ha_version="2022.6.0",
            is_fixable=False,
            severity=IssueSeverity.WARNING,
            translation_key="removed_track_new_yaml",
        )
    return True
Ejemplo n.º 9
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Ambee integration."""
    async_create_issue(
        hass,
        DOMAIN,
        "pending_removal",
        breaks_in_ha_version="2022.10.0",
        is_fixable=False,
        severity=IssueSeverity.WARNING,
        translation_key="pending_removal",
    )
    return True
Ejemplo n.º 10
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Flu Near You as config entry."""
    async_create_issue(
        hass,
        DOMAIN,
        "integration_removal",
        is_fixable=True,
        severity=IssueSeverity.ERROR,
        translation_key="integration_removal",
    )

    websession = aiohttp_client.async_get_clientsession(hass)
    client = Client(session=websession)

    latitude = entry.data.get(CONF_LATITUDE, hass.config.latitude)
    longitude = entry.data.get(CONF_LONGITUDE, hass.config.longitude)

    async def async_update(api_category: str) -> dict[str, Any]:
        """Get updated date from the API based on category."""
        try:
            if api_category == CATEGORY_CDC_REPORT:
                data = await client.cdc_reports.status_by_coordinates(
                    latitude, longitude)
            else:
                data = await client.user_reports.status_by_coordinates(
                    latitude, longitude)
        except FluNearYouError as err:
            raise UpdateFailed(err) from err

        return data

    coordinators = {}
    data_init_tasks = []

    for api_category in (CATEGORY_CDC_REPORT, CATEGORY_USER_REPORT):
        coordinator = coordinators[api_category] = DataUpdateCoordinator(
            hass,
            LOGGER,
            name=f"{api_category} ({latitude}, {longitude})",
            update_interval=DEFAULT_UPDATE_INTERVAL,
            update_method=partial(async_update, api_category),
        )
        data_init_tasks.append(coordinator.async_config_entry_first_refresh())

    await asyncio.gather(*data_init_tasks)
    hass.data.setdefault(DOMAIN, {})
    hass.data[DOMAIN][entry.entry_id] = coordinators

    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

    return True
Ejemplo n.º 11
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the Spotify integration."""
    if DOMAIN in config:
        async_create_issue(
            hass,
            DOMAIN,
            "removed_yaml",
            breaks_in_ha_version="2022.8.0",
            is_fixable=False,
            severity=IssueSeverity.WARNING,
            translation_key="removed_yaml",
        )

    return True
Ejemplo n.º 12
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the LaMetric integration."""
    hass.data[DOMAIN] = {"hass_config": config}
    if DOMAIN in config:
        async_create_issue(
            hass,
            DOMAIN,
            "manual_migration",
            breaks_in_ha_version="2022.9.0",
            is_fixable=False,
            severity=IssueSeverity.ERROR,
            translation_key="manual_migration",
        )

    return True
Ejemplo n.º 13
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the SkyBell component."""
    hass.data.setdefault(DOMAIN, {})

    if DOMAIN in config:
        async_create_issue(
            hass,
            DOMAIN,
            "removed_yaml",
            breaks_in_ha_version="2022.9.0",
            is_fixable=False,
            severity=IssueSeverity.WARNING,
            translation_key="removed_yaml",
        )

    return True
Ejemplo n.º 14
0
async def async_setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    async_add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the MiFlora sensor."""
    async_create_issue(
        hass,
        "miflora",
        "replaced",
        breaks_in_ha_version="2022.8.0",
        is_fixable=False,
        severity=IssueSeverity.ERROR,
        translation_key="replaced",
        learn_more_url="https://www.home-assistant.io/integrations/xiaomi_ble/",
    )
Ejemplo n.º 15
0
async def async_get_service(
    hass: HomeAssistant,
    config: ConfigType,
    discovery_info: DiscoveryInfoType | None = None,
) -> SimplePushNotificationService | None:
    """Get the Simplepush notification service."""
    if discovery_info is None:
        async_create_issue(
            hass,
            DOMAIN,
            "removed_yaml",
            breaks_in_ha_version="2022.9.0",
            is_fixable=False,
            severity=IssueSeverity.WARNING,
            translation_key="removed_yaml",
        )
        return None

    return SimplePushNotificationService(discovery_info)
Ejemplo n.º 16
0
        async def request_update(call: ServiceCall) -> None:
            """Request update."""
            async_create_issue(
                self.hass,
                DOMAIN,
                "deprecated_service",
                breaks_in_ha_version="2022.11.0",
                is_fixable=True,
                is_persistent=True,
                severity=IssueSeverity.WARNING,
                translation_key="deprecated_service",
            )

            _LOGGER.warning(
                (
                    'The "%s" service is deprecated and will be removed in "2022.11.0"; '
                    'use the "homeassistant.update_entity" service and pass it a target Speedtest entity_id'
                ),
                SPEED_TEST_SERVICE,
            )
            await self.async_request_refresh()
Ejemplo n.º 17
0
async def test_create_issue_invalid_version(hass: HomeAssistant,
                                            hass_ws_client,
                                            ha_version) -> None:
    """Test creating an issue with invalid breaks in version."""
    assert await async_setup_component(hass, DOMAIN, {})

    client = await hass_ws_client(hass)

    issue = {
        "breaks_in_ha_version": ha_version,
        "domain": "test",
        "issue_id": "issue_1",
        "is_fixable": True,
        "learn_more_url": "https://theuselessweb.com",
        "severity": "error",
        "translation_key": "abc_123",
        "translation_placeholders": {
            "abc": "123"
        },
    }

    with pytest.raises(Exception):
        async_create_issue(
            hass,
            issue["domain"],
            issue["issue_id"],
            breaks_in_ha_version=issue["breaks_in_ha_version"],
            is_fixable=issue["is_fixable"],
            is_persistent=False,
            learn_more_url=issue["learn_more_url"],
            severity=issue["severity"],
            translation_key=issue["translation_key"],
            translation_placeholders=issue["translation_placeholders"],
        )

    await client.send_json({"id": 1, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {"issues": []}
Ejemplo n.º 18
0
async def async_setup_platform(
    hass: HomeAssistant,
    config: ConfigType,
    async_add_entities: AddEntitiesCallback,
    discovery_info: DiscoveryInfoType | None = None,
) -> None:
    """Set up the Radio Thermostat."""
    async_create_issue(
        hass,
        DOMAIN,
        "deprecated_yaml",
        breaks_in_ha_version="2022.9.0",
        is_fixable=False,
        severity=IssueSeverity.WARNING,
        translation_key="deprecated_yaml",
    )
    _LOGGER.warning(
        "Configuration of the Radio Thermostat climate platform in YAML is deprecated and "
        "will be removed in Home Assistant 2022.9; Your existing configuration "
        "has been imported into the UI automatically and can be safely removed "
        "from your configuration.yaml file")

    hosts: list[str] = []
    if CONF_HOST in config:
        hosts = config[CONF_HOST]
    else:
        hosts.append(await hass.async_add_executor_job(
            radiotherm.discover.discover_address))

    if not hosts:
        _LOGGER.error("No Radiotherm Thermostats detected")
        return

    for host in hosts:
        hass.async_create_task(
            hass.config_entries.flow.async_init(
                DOMAIN,
                context={"source": SOURCE_IMPORT},
                data={CONF_HOST: host},
            ))
Ejemplo n.º 19
0
async def create_issues(hass, ws_client, issues=None):
    """Create issues."""

    def api_issue(issue):
        excluded_keys = ("data",)
        return dict(
            {key: issue[key] for key in issue if key not in excluded_keys},
            created=ANY,
            dismissed_version=None,
            ignored=False,
            issue_domain=None,
        )

    if issues is None:
        issues = DEFAULT_ISSUES

    for issue in issues:
        issue_registry.async_create_issue(
            hass,
            issue["domain"],
            issue["issue_id"],
            breaks_in_ha_version=issue["breaks_in_ha_version"],
            data=issue.get("data"),
            is_fixable=issue["is_fixable"],
            is_persistent=False,
            learn_more_url=issue["learn_more_url"],
            severity=issue["severity"],
            translation_key=issue["translation_key"],
            translation_placeholders=issue["translation_placeholders"],
        )

    await ws_client.send_json({"id": 1, "type": "repairs/list_issues"})
    msg = await ws_client.receive_json()

    assert msg["success"]
    assert msg["result"] == {"issues": [api_issue(issue) for issue in issues]}

    return issues
Ejemplo n.º 20
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the IP Webcam component."""

    if DOMAIN not in config:
        return True

    async_create_issue(
        hass,
        DOMAIN,
        "deprecated_yaml",
        breaks_in_ha_version="2022.11.0",
        is_fixable=False,
        severity=IssueSeverity.WARNING,
        translation_key="deprecated_yaml",
    )
    for entry in config[DOMAIN]:
        hass.async_create_task(
            hass.config_entries.flow.async_init(
                DOMAIN, context={"source": SOURCE_IMPORT}, data=entry
            )
        )

    return True
Ejemplo n.º 21
0
    async def async_update_alerts() -> None:
        nonlocal last_alerts

        active_alerts: dict[str, str | None] = {}

        for issue_id, alert in coordinator.data.items():
            # Skip creation if already created and not updated since then
            if issue_id in last_alerts and alert.date_updated == last_alerts[
                    issue_id]:
                active_alerts[issue_id] = alert.date_updated
                continue

            # Fetch alert to get title + description
            try:
                response = await async_get_clientsession(hass).get(
                    f"https://alerts.home-assistant.io/alerts/{alert.filename}",
                    timeout=aiohttp.ClientTimeout(total=10),
                )
            except asyncio.TimeoutError:
                _LOGGER.warning("Error fetching %s: timeout", alert.filename)
                continue

            alert_content = await response.text()
            alert_parts = alert_content.split("---")

            if len(alert_parts) != 3:
                _LOGGER.warning("Error parsing %s: unexpected metadata format",
                                alert.filename)
                continue

            try:
                alert_info = parse_yaml(alert_parts[1])
            except ValueError as err:
                _LOGGER.warning("Error parsing %s metadata: %s",
                                alert.filename, err)
                continue

            if not isinstance(alert_info, dict) or "title" not in alert_info:
                _LOGGER.warning("Error in %s metadata: title not found",
                                alert.filename)
                continue

            alert_title = alert_info["title"]
            alert_content = alert_parts[2].strip()

            async_create_issue(
                hass,
                DOMAIN,
                issue_id,
                is_fixable=False,
                issue_domain=alert.integration,
                severity=IssueSeverity.WARNING,
                translation_key="alert",
                translation_placeholders={
                    "title": alert_title,
                    "description": alert_content,
                },
            )
            active_alerts[issue_id] = alert.date_updated

        inactive_alerts = last_alerts.keys() - active_alerts.keys()
        for issue_id in inactive_alerts:
            async_delete_issue(hass, DOMAIN, issue_id)

        last_alerts = active_alerts
Ejemplo n.º 22
0
async def async_import_config(hass: HomeAssistant, entry: ConfigEntry) -> None:
    """Attempt to import configuration.yaml settings."""
    config = hass.data[DOMAIN][DATA_NEST_CONFIG]
    new_data = {
        CONF_PROJECT_ID: config[CONF_PROJECT_ID],
        **entry.data,
    }
    if CONF_SUBSCRIBER_ID not in entry.data:
        if CONF_SUBSCRIBER_ID not in config:
            raise ValueError("Configuration option 'subscriber_id' missing")
        new_data.update({
            CONF_SUBSCRIBER_ID: config[CONF_SUBSCRIBER_ID],
            CONF_SUBSCRIBER_ID_IMPORTED:
            True,  # Don't delete user managed subscriber
        })
    hass.config_entries.async_update_entry(entry,
                                           data=new_data,
                                           unique_id=new_data[CONF_PROJECT_ID])

    if entry.data["auth_implementation"] == INSTALLED_AUTH_DOMAIN:
        # App Auth credentials have been deprecated and must be re-created
        # by the user in the config flow
        async_create_issue(
            hass,
            DOMAIN,
            "removed_app_auth",
            is_fixable=False,
            severity=IssueSeverity.ERROR,
            translation_key="removed_app_auth",
            translation_placeholders={
                "more_info_url":
                "https://www.home-assistant.io/more-info/nest-auth-deprecation",
                "documentation_url":
                "https://www.home-assistant.io/integrations/nest/",
            },
        )
        raise ConfigEntryAuthFailed(
            "Google has deprecated App Auth credentials, and the integration "
            "must be reconfigured in the UI to restore access to Nest Devices."
        )

    if entry.data["auth_implementation"] == WEB_AUTH_DOMAIN:
        await async_import_client_credential(
            hass,
            DOMAIN,
            ClientCredential(
                config[CONF_CLIENT_ID],
                config[CONF_CLIENT_SECRET],
            ),
            WEB_AUTH_DOMAIN,
        )

    async_create_issue(
        hass,
        DOMAIN,
        "deprecated_yaml",
        breaks_in_ha_version="2022.10.0",
        is_fixable=False,
        severity=IssueSeverity.WARNING,
        translation_key="deprecated_yaml",
    )
Ejemplo n.º 23
0
async def test_load_issues(hass: HomeAssistant) -> None:
    """Make sure that we can load/save data correctly."""
    issues = [
        {
            "breaks_in_ha_version": "2022.9",
            "domain": "test",
            "issue_id": "issue_1",
            "is_fixable": True,
            "is_persistent": False,
            "learn_more_url": "https://theuselessweb.com",
            "severity": "error",
            "translation_key": "abc_123",
            "translation_placeholders": {
                "abc": "123"
            },
        },
        {
            "breaks_in_ha_version": "2022.8",
            "domain": "test",
            "issue_id": "issue_2",
            "is_fixable": True,
            "is_persistent": False,
            "learn_more_url": "https://theuselessweb.com/abc",
            "severity": "other",
            "translation_key": "even_worse",
            "translation_placeholders": {
                "def": "456"
            },
        },
        {
            "breaks_in_ha_version": "2022.7",
            "domain": "test",
            "issue_id": "issue_3",
            "is_fixable": True,
            "is_persistent": False,
            "learn_more_url": "https://checkboxrace.com",
            "severity": "other",
            "translation_key": "even_worse",
            "translation_placeholders": {
                "def": "789"
            },
        },
        {
            "breaks_in_ha_version": "2022.6",
            "data": {
                "entry_id": "123"
            },
            "domain": "test",
            "issue_id": "issue_4",
            "is_fixable": True,
            "is_persistent": True,
            "learn_more_url": "https://checkboxrace.com/blah",
            "severity": "other",
            "translation_key": "even_worse",
            "translation_placeholders": {
                "xyz": "abc"
            },
        },
    ]

    events = async_capture_events(
        hass, issue_registry.EVENT_REPAIRS_ISSUE_REGISTRY_UPDATED)

    for issue in issues:
        issue_registry.async_create_issue(
            hass,
            issue["domain"],
            issue["issue_id"],
            breaks_in_ha_version=issue["breaks_in_ha_version"],
            is_fixable=issue["is_fixable"],
            is_persistent=issue["is_persistent"],
            learn_more_url=issue["learn_more_url"],
            severity=issue["severity"],
            translation_key=issue["translation_key"],
            translation_placeholders=issue["translation_placeholders"],
        )

    await hass.async_block_till_done()

    assert len(events) == 4
    assert events[0].data == {
        "action": "create",
        "domain": "test",
        "issue_id": "issue_1",
    }
    assert events[1].data == {
        "action": "create",
        "domain": "test",
        "issue_id": "issue_2",
    }
    assert events[2].data == {
        "action": "create",
        "domain": "test",
        "issue_id": "issue_3",
    }
    assert events[3].data == {
        "action": "create",
        "domain": "test",
        "issue_id": "issue_4",
    }

    issue_registry.async_ignore_issue(hass, issues[0]["domain"],
                                      issues[0]["issue_id"], True)
    await hass.async_block_till_done()

    assert len(events) == 5
    assert events[4].data == {
        "action": "update",
        "domain": "test",
        "issue_id": "issue_1",
    }

    issue_registry.async_delete_issue(hass, issues[2]["domain"],
                                      issues[2]["issue_id"])
    await hass.async_block_till_done()

    assert len(events) == 6
    assert events[5].data == {
        "action": "remove",
        "domain": "test",
        "issue_id": "issue_3",
    }

    registry: issue_registry.IssueRegistry = hass.data[
        issue_registry.DATA_REGISTRY]
    assert len(registry.issues) == 3
    issue1 = registry.async_get_issue("test", "issue_1")
    issue2 = registry.async_get_issue("test", "issue_2")
    issue4 = registry.async_get_issue("test", "issue_4")

    registry2 = issue_registry.IssueRegistry(hass)
    await flush_store(registry._store)
    await registry2.async_load()

    assert list(registry.issues) == list(registry2.issues)

    issue1_registry2 = registry2.async_get_issue("test", "issue_1")
    assert issue1_registry2 == issue_registry.IssueEntry(
        active=False,
        breaks_in_ha_version=None,
        created=issue1.created,
        data=None,
        dismissed_version=issue1.dismissed_version,
        domain=issue1.domain,
        is_fixable=None,
        is_persistent=issue1.is_persistent,
        issue_domain=None,
        issue_id=issue1.issue_id,
        learn_more_url=None,
        severity=None,
        translation_key=None,
        translation_placeholders=None,
    )
    issue2_registry2 = registry2.async_get_issue("test", "issue_2")
    assert issue2_registry2 == issue_registry.IssueEntry(
        active=False,
        breaks_in_ha_version=None,
        created=issue2.created,
        data=None,
        dismissed_version=issue2.dismissed_version,
        domain=issue2.domain,
        is_fixable=None,
        is_persistent=issue2.is_persistent,
        issue_domain=None,
        issue_id=issue2.issue_id,
        learn_more_url=None,
        severity=None,
        translation_key=None,
        translation_placeholders=None,
    )
    issue4_registry2 = registry2.async_get_issue("test", "issue_4")
    assert issue4_registry2 == issue4
Ejemplo n.º 24
0
async def test_delete_issue(hass: HomeAssistant, hass_ws_client,
                            freezer) -> None:
    """Test we can delete an issue."""
    freezer.move_to("2022-07-19 07:53:05")
    assert await async_setup_component(hass, DOMAIN, {})

    client = await hass_ws_client(hass)

    issues = [
        {
            "breaks_in_ha_version": "2022.9",
            "domain": "fake_integration",
            "issue_id": "issue_1",
            "is_fixable": True,
            "learn_more_url": "https://theuselessweb.com",
            "severity": "error",
            "translation_key": "abc_123",
            "translation_placeholders": {
                "abc": "123"
            },
        },
    ]

    for issue in issues:
        async_create_issue(
            hass,
            issue["domain"],
            issue["issue_id"],
            breaks_in_ha_version=issue["breaks_in_ha_version"],
            is_fixable=issue["is_fixable"],
            is_persistent=False,
            learn_more_url=issue["learn_more_url"],
            severity=issue["severity"],
            translation_key=issue["translation_key"],
            translation_placeholders=issue["translation_placeholders"],
        )

    await client.send_json({"id": 1, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {
        "issues": [
            dict(
                issue,
                created="2022-07-19T07:53:05+00:00",
                dismissed_version=None,
                ignored=False,
                issue_domain=None,
            ) for issue in issues
        ]
    }

    # Delete a non-existing issue
    async_delete_issue(hass, issues[0]["domain"], "no_such_issue")

    await client.send_json({"id": 2, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {
        "issues": [
            dict(
                issue,
                created="2022-07-19T07:53:05+00:00",
                dismissed_version=None,
                ignored=False,
                issue_domain=None,
            ) for issue in issues
        ]
    }

    # Delete an existing issue
    async_delete_issue(hass, issues[0]["domain"], issues[0]["issue_id"])

    await client.send_json({"id": 3, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {"issues": []}

    # Delete the same issue again
    async_delete_issue(hass, issues[0]["domain"], issues[0]["issue_id"])

    await client.send_json({"id": 4, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {"issues": []}

    # Create the same issues again created timestamp should change
    freezer.move_to("2022-07-19 08:53:05")

    for issue in issues:
        async_create_issue(
            hass,
            issue["domain"],
            issue["issue_id"],
            breaks_in_ha_version=issue["breaks_in_ha_version"],
            is_fixable=issue["is_fixable"],
            is_persistent=False,
            learn_more_url=issue["learn_more_url"],
            severity=issue["severity"],
            translation_key=issue["translation_key"],
            translation_placeholders=issue["translation_placeholders"],
        )

    await client.send_json({"id": 5, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {
        "issues": [
            dict(
                issue,
                created="2022-07-19T08:53:05+00:00",
                dismissed_version=None,
                ignored=False,
                issue_domain=None,
            ) for issue in issues
        ]
    }
Ejemplo n.º 25
0
async def test_ignore_issue(hass: HomeAssistant, hass_ws_client) -> None:
    """Test ignoring issues."""
    assert await async_setup_component(hass, DOMAIN, {})

    client = await hass_ws_client(hass)

    await client.send_json({"id": 1, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {"issues": []}

    issues = [
        {
            "breaks_in_ha_version": "2022.9",
            "domain": "test",
            "is_fixable": True,
            "issue_id": "issue_1",
            "learn_more_url": "https://theuselessweb.com",
            "severity": "error",
            "translation_key": "abc_123",
            "translation_placeholders": {
                "abc": "123"
            },
        },
    ]

    for issue in issues:
        async_create_issue(
            hass,
            issue["domain"],
            issue["issue_id"],
            breaks_in_ha_version=issue["breaks_in_ha_version"],
            is_fixable=issue["is_fixable"],
            is_persistent=False,
            learn_more_url=issue["learn_more_url"],
            severity=issue["severity"],
            translation_key=issue["translation_key"],
            translation_placeholders=issue["translation_placeholders"],
        )

    await client.send_json({"id": 2, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {
        "issues": [
            dict(
                issue,
                created="2022-07-19T07:53:05+00:00",
                dismissed_version=None,
                ignored=False,
                issue_domain=None,
            ) for issue in issues
        ]
    }

    # Ignore a non-existing issue
    with pytest.raises(KeyError):
        async_ignore_issue(hass, issues[0]["domain"], "no_such_issue", True)

    await client.send_json({"id": 3, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {
        "issues": [
            dict(
                issue,
                created="2022-07-19T07:53:05+00:00",
                dismissed_version=None,
                ignored=False,
                issue_domain=None,
            ) for issue in issues
        ]
    }

    # Ignore an existing issue
    async_ignore_issue(hass, issues[0]["domain"], issues[0]["issue_id"], True)

    await client.send_json({"id": 4, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {
        "issues": [
            dict(
                issue,
                created="2022-07-19T07:53:05+00:00",
                dismissed_version=ha_version,
                ignored=True,
                issue_domain=None,
            ) for issue in issues
        ]
    }

    # Ignore the same issue again
    async_ignore_issue(hass, issues[0]["domain"], issues[0]["issue_id"], True)

    await client.send_json({"id": 5, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {
        "issues": [
            dict(
                issue,
                created="2022-07-19T07:53:05+00:00",
                dismissed_version=ha_version,
                ignored=True,
                issue_domain=None,
            ) for issue in issues
        ]
    }

    # Update an ignored issue
    async_create_issue(
        hass,
        issues[0]["domain"],
        issues[0]["issue_id"],
        breaks_in_ha_version=issues[0]["breaks_in_ha_version"],
        is_fixable=issues[0]["is_fixable"],
        is_persistent=False,
        learn_more_url="blablabla",
        severity=issues[0]["severity"],
        translation_key=issues[0]["translation_key"],
        translation_placeholders=issues[0]["translation_placeholders"],
    )

    await client.send_json({"id": 6, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"]["issues"][0] == dict(
        issues[0],
        created="2022-07-19T07:53:05+00:00",
        dismissed_version=ha_version,
        ignored=True,
        learn_more_url="blablabla",
        issue_domain=None,
    )

    # Unignore the same issue
    async_ignore_issue(hass, issues[0]["domain"], issues[0]["issue_id"], False)

    await client.send_json({"id": 7, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {
        "issues": [
            dict(
                issue,
                created="2022-07-19T07:53:05+00:00",
                dismissed_version=None,
                ignored=False,
                learn_more_url="blablabla",
                issue_domain=None,
            ) for issue in issues
        ]
    }
Ejemplo n.º 26
0
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the demo environment."""
    if DOMAIN not in config:
        return True

    if not hass.config_entries.async_entries(DOMAIN):
        hass.async_create_task(
            hass.config_entries.flow.async_init(
                DOMAIN,
                context={"source": config_entries.SOURCE_IMPORT},
                data={}))

    # Set up demo platforms
    for platform in COMPONENTS_WITH_DEMO_PLATFORM:
        hass.async_create_task(
            async_load_platform(hass, platform, DOMAIN, {}, config))

    config.setdefault(ha.DOMAIN, {})
    config.setdefault(DOMAIN, {})

    # Set up sun
    if not hass.config.latitude:
        hass.config.latitude = 32.87336

    if not hass.config.longitude:
        hass.config.longitude = 117.22743

    tasks = [setup.async_setup_component(hass, "sun", config)]

    # Set up input select
    tasks.append(
        setup.async_setup_component(
            hass,
            "input_select",
            {
                "input_select": {
                    "living_room_preset": {
                        "options":
                        ["Visitors", "Visitors with kids", "Home Alone"]
                    },
                    "who_cooks": {
                        "icon": "mdi:panda",
                        "initial": "Anne Therese",
                        "name": "Cook today",
                        "options": ["Paulus", "Anne Therese"],
                    },
                }
            },
        ))

    # Set up input boolean
    tasks.append(
        setup.async_setup_component(
            hass,
            "input_boolean",
            {
                "input_boolean": {
                    "notify": {
                        "icon": "mdi:car",
                        "initial": False,
                        "name": "Notify Anne Therese is home",
                    }
                }
            },
        ))

    # Set up input button
    tasks.append(
        setup.async_setup_component(
            hass,
            "input_button",
            {
                "input_button": {
                    "bell": {
                        "icon": "mdi:bell-ring-outline",
                        "name": "Ring bell",
                    }
                }
            },
        ))

    # Set up input number
    tasks.append(
        setup.async_setup_component(
            hass,
            "input_number",
            {
                "input_number": {
                    "noise_allowance": {
                        "icon": "mdi:bell-ring",
                        "min": 0,
                        "max": 10,
                        "name": "Allowed Noise",
                        "unit_of_measurement": SOUND_PRESSURE_DB,
                    }
                }
            },
        ))

    results = await asyncio.gather(*tasks)

    if any(not result for result in results):
        return False

    # Set up example persistent notification
    persistent_notification.async_create(
        hass,
        "This is an example of a persistent notification.",
        title="Example Notification",
    )

    async def demo_start_listener(_event: Event) -> None:
        """Finish set up."""
        await finish_setup(hass, config)

    hass.bus.async_listen(EVENT_HOMEASSISTANT_START, demo_start_listener)

    # Create issues
    async_create_issue(
        hass,
        DOMAIN,
        "transmogrifier_deprecated",
        breaks_in_ha_version="2023.1.1",
        is_fixable=False,
        learn_more_url="https://en.wiktionary.org/wiki/transmogrifier",
        severity=IssueSeverity.WARNING,
        translation_key="transmogrifier_deprecated",
    )

    async_create_issue(
        hass,
        DOMAIN,
        "out_of_blinker_fluid",
        breaks_in_ha_version="2023.1.1",
        is_fixable=True,
        learn_more_url="https://www.youtube.com/watch?v=b9rntRxLlbU",
        severity=IssueSeverity.CRITICAL,
        translation_key="out_of_blinker_fluid",
    )

    async_create_issue(
        hass,
        DOMAIN,
        "unfixable_problem",
        is_fixable=False,
        learn_more_url="https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        severity=IssueSeverity.WARNING,
        translation_key="unfixable_problem",
    )

    async_create_issue(
        hass,
        DOMAIN,
        "bad_psu",
        is_fixable=True,
        learn_more_url="https://www.youtube.com/watch?v=b9rntRxLlbU",
        severity=IssueSeverity.CRITICAL,
        translation_key="bad_psu",
    )

    return True
Ejemplo n.º 27
0
async def test_list_issues(hass: HomeAssistant, hass_storage, hass_ws_client) -> None:
    """Test we can list issues."""

    # Add an inactive issue, this should not be exposed in the list
    hass_storage[issue_registry.STORAGE_KEY] = {
        "version": issue_registry.STORAGE_VERSION_MAJOR,
        "data": {
            "issues": [
                {
                    "created": "2022-07-19T09:41:13.746514+00:00",
                    "dismissed_version": None,
                    "domain": "test",
                    "is_persistent": False,
                    "issue_id": "issue_3_inactive",
                    "issue_domain": None,
                },
            ]
        },
    }

    assert await async_setup_component(hass, DOMAIN, {})

    client = await hass_ws_client(hass)

    await client.send_json({"id": 1, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {"issues": []}

    issues = [
        {
            "breaks_in_ha_version": "2022.9",
            "domain": "test",
            "is_fixable": True,
            "issue_id": "issue_1",
            "issue_domain": None,
            "learn_more_url": "https://theuselessweb.com",
            "severity": "error",
            "translation_key": "abc_123",
            "translation_placeholders": {"abc": "123"},
        },
        {
            "breaks_in_ha_version": "2022.8",
            "domain": "test",
            "is_fixable": False,
            "issue_id": "issue_2",
            "issue_domain": None,
            "learn_more_url": "https://theuselessweb.com/abc",
            "severity": "other",
            "translation_key": "even_worse",
            "translation_placeholders": {"def": "456"},
        },
    ]

    for issue in issues:
        issue_registry.async_create_issue(
            hass,
            issue["domain"],
            issue["issue_id"],
            breaks_in_ha_version=issue["breaks_in_ha_version"],
            is_fixable=issue["is_fixable"],
            is_persistent=False,
            learn_more_url=issue["learn_more_url"],
            severity=issue["severity"],
            translation_key=issue["translation_key"],
            translation_placeholders=issue["translation_placeholders"],
        )

    await client.send_json({"id": 2, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {
        "issues": [
            dict(
                issue,
                created="2022-07-19T07:53:05+00:00",
                dismissed_version=None,
                ignored=False,
            )
            for issue in issues
        ]
    }
Ejemplo n.º 28
0
    async def async_trigger(
        self,
        run_variables: dict[str, Any],
        context: Context | None = None,
        skip_condition: bool = False,
    ) -> None:
        """Trigger automation.

        This method is a coroutine.
        """
        reason = ""
        alias = ""
        if "trigger" in run_variables:
            if "description" in run_variables["trigger"]:
                reason = f' by {run_variables["trigger"]["description"]}'
            if "alias" in run_variables["trigger"]:
                alias = f' trigger \'{run_variables["trigger"]["alias"]}\''
        self._logger.debug("Automation%s triggered%s", alias, reason)

        # Create a new context referring to the old context.
        parent_id = None if context is None else context.id
        trigger_context = Context(parent_id=parent_id)

        with trace_automation(
                self.hass,
                self.unique_id,
                self._raw_config,
                self._blueprint_inputs,
                trigger_context,
                self._trace_config,
        ) as automation_trace:
            this = None
            if state := self.hass.states.get(self.entity_id):
                this = state.as_dict()
            variables: dict[str, Any] = {"this": this, **(run_variables or {})}
            if self._variables:
                try:
                    variables = self._variables.async_render(
                        self.hass, variables)
                except TemplateError as err:
                    self._logger.error("Error rendering variables: %s", err)
                    automation_trace.set_error(err)
                    return

            # Prepare tracing the automation
            automation_trace.set_trace(trace_get())

            # Set trigger reason
            trigger_description = variables.get("trigger",
                                                {}).get("description")
            automation_trace.set_trigger_description(trigger_description)

            # Add initial variables as the trigger step
            if "trigger" in variables and "idx" in variables["trigger"]:
                trigger_path = f"trigger/{variables['trigger']['idx']}"
            else:
                trigger_path = "trigger"
            trace_element = TraceElement(variables, trigger_path)
            trace_append_element(trace_element)

            if (not skip_condition and self._cond_func is not None
                    and not self._cond_func(variables)):
                self._logger.debug(
                    "Conditions not met, aborting automation. Condition summary: %s",
                    trace_get(clear=False),
                )
                script_execution_set("failed_conditions")
                return

            self.async_set_context(trigger_context)
            event_data = {
                ATTR_NAME: self.name,
                ATTR_ENTITY_ID: self.entity_id,
            }
            if "trigger" in variables and "description" in variables["trigger"]:
                event_data[ATTR_SOURCE] = variables["trigger"]["description"]

            @callback
            def started_action():
                self.hass.bus.async_fire(EVENT_AUTOMATION_TRIGGERED,
                                         event_data,
                                         context=trigger_context)

            # Make a new empty script stack; automations are allowed
            # to recursively trigger themselves
            script_stack_cv.set([])

            try:
                with trace_path("action"):
                    await self.action_script.async_run(variables,
                                                       trigger_context,
                                                       started_action)
            except ServiceNotFound as err:
                async_create_issue(
                    self.hass,
                    DOMAIN,
                    f"{self.entity_id}_service_not_found_{err.domain}.{err.service}",
                    is_fixable=True,
                    is_persistent=True,
                    severity=IssueSeverity.ERROR,
                    translation_key="service_not_found",
                    translation_placeholders={
                        "service": f"{err.domain}.{err.service}",
                        "entity_id": self.entity_id,
                        "name": self.name or self.entity_id,
                        "edit": f"/config/automation/edit/{self.unique_id}",
                    },
                )
                automation_trace.set_error(err)
            except (vol.Invalid, HomeAssistantError) as err:
                self._logger.error(
                    "Error while executing automation %s: %s",
                    self.entity_id,
                    err,
                )
                automation_trace.set_error(err)
            except Exception as err:  # pylint: disable=broad-except
                self._logger.exception("While executing automation %s",
                                       self.entity_id)
                automation_trace.set_error(err)
Ejemplo n.º 29
0
async def test_create_update_issue(hass: HomeAssistant,
                                   hass_ws_client) -> None:
    """Test creating and updating issues."""
    assert await async_setup_component(hass, DOMAIN, {})

    client = await hass_ws_client(hass)

    await client.send_json({"id": 1, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {"issues": []}

    issues = [
        {
            "breaks_in_ha_version": "2022.9.0dev0",
            "domain": "test",
            "issue_id": "issue_1",
            "is_fixable": True,
            "learn_more_url": "https://theuselessweb.com",
            "severity": "error",
            "translation_key": "abc_123",
            "translation_placeholders": {
                "abc": "123"
            },
        },
        {
            "breaks_in_ha_version": "2022.8",
            "domain": "test",
            "issue_id": "issue_2",
            "is_fixable": False,
            "learn_more_url": "https://theuselessweb.com/abc",
            "severity": "other",
            "translation_key": "even_worse",
            "translation_placeholders": {
                "def": "456"
            },
        },
    ]

    for issue in issues:
        async_create_issue(
            hass,
            issue["domain"],
            issue["issue_id"],
            breaks_in_ha_version=issue["breaks_in_ha_version"],
            is_fixable=issue["is_fixable"],
            is_persistent=False,
            learn_more_url=issue["learn_more_url"],
            severity=issue["severity"],
            translation_key=issue["translation_key"],
            translation_placeholders=issue["translation_placeholders"],
        )

    await client.send_json({"id": 2, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"] == {
        "issues": [
            dict(
                issue,
                created="2022-07-19T07:53:05+00:00",
                dismissed_version=None,
                ignored=False,
                issue_domain=None,
            ) for issue in issues
        ]
    }

    # Update an issue
    async_create_issue(
        hass,
        issues[0]["domain"],
        issues[0]["issue_id"],
        breaks_in_ha_version=issues[0]["breaks_in_ha_version"],
        is_fixable=issues[0]["is_fixable"],
        is_persistent=False,
        issue_domain="my_issue_domain",
        learn_more_url="blablabla",
        severity=issues[0]["severity"],
        translation_key=issues[0]["translation_key"],
        translation_placeholders=issues[0]["translation_placeholders"],
    )

    await client.send_json({"id": 3, "type": "repairs/list_issues"})
    msg = await client.receive_json()

    assert msg["success"]
    assert msg["result"]["issues"][0] == dict(
        issues[0],
        created="2022-07-19T07:53:05+00:00",
        dismissed_version=None,
        ignored=False,
        learn_more_url="blablabla",
        issue_domain="my_issue_domain",
    )