mirror of
https://github.com/home-assistant/core.git
synced 2026-04-02 00:20:30 +01:00
Add skeleton with repair issue to bmw integration (#166983)
Co-authored-by: Franck Nijhof <git@frenck.dev> Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
committed by
Franck Nijhof
parent
9650aea6a1
commit
12dc33eabc
41
homeassistant/components/bmw_connected_drive/__init__.py
Normal file
41
homeassistant/components/bmw_connected_drive/__init__.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""The BMW Connected Drive integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
|
||||
DOMAIN = "bmw_connected_drive"
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up BMW Connected Drive from a config entry."""
|
||||
ir.async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
DOMAIN,
|
||||
is_fixable=False,
|
||||
severity=ir.IssueSeverity.ERROR,
|
||||
translation_key="integration_removed",
|
||||
translation_placeholders={
|
||||
"entries": "/config/integrations/integration/bmw_connected_drive",
|
||||
"custom_component_url": "https://github.com/kvanbiesen/bmw-cardata-ha",
|
||||
},
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return True
|
||||
|
||||
|
||||
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Remove a config entry."""
|
||||
if not hass.config_entries.async_loaded_entries(DOMAIN):
|
||||
ir.async_delete_issue(hass, DOMAIN, DOMAIN)
|
||||
# Remove any remaining disabled or ignored entries
|
||||
for _entry in hass.config_entries.async_entries(DOMAIN):
|
||||
hass.async_create_task(hass.config_entries.async_remove(_entry.entry_id))
|
||||
@@ -0,0 +1,9 @@
|
||||
"""The BMW Connected Drive integration config flow."""
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow
|
||||
|
||||
from . import DOMAIN
|
||||
|
||||
|
||||
class BMWConnectedDriveConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for BMW Connected Drive."""
|
||||
10
homeassistant/components/bmw_connected_drive/manifest.json
Normal file
10
homeassistant/components/bmw_connected_drive/manifest.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"domain": "bmw_connected_drive",
|
||||
"name": "BMW Connected Drive",
|
||||
"codeowners": [],
|
||||
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
|
||||
"integration_type": "system",
|
||||
"iot_class": "cloud_polling",
|
||||
"quality_scale": "legacy",
|
||||
"requirements": []
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"issues": {
|
||||
"integration_removed": {
|
||||
"description": "The BMW Connected Drive integration has been removed from Home Assistant.\n\nIn September 2025, BMW blocked third-party access to their servers by adding additional security measures. For EU-registered cars, a community-developed [custom component]({custom_component_url}) using BMW's CarData API is available as an alternative.\n\nTo resolve this issue, please remove the (now defunct) integration entries from your Home Assistant setup. [Click here to see your existing BMW Connected Drive integration entries]({entries}).",
|
||||
"title": "The BMW Connected Drive integration has been removed"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,6 +212,7 @@ INTEGRATIONS_WITHOUT_QUALITY_SCALE_FILE = [
|
||||
"bluetooth",
|
||||
"bluetooth_adapters",
|
||||
"bluetooth_le_tracker",
|
||||
"bmw_connected_drive",
|
||||
"bond",
|
||||
"bosch_shc",
|
||||
"braviatv",
|
||||
@@ -1183,6 +1184,7 @@ INTEGRATIONS_WITHOUT_SCALE = [
|
||||
"bluetooth",
|
||||
"bluetooth_adapters",
|
||||
"bluetooth_le_tracker",
|
||||
"bmw_connected_drive",
|
||||
"bond",
|
||||
"bosch_shc",
|
||||
"braviatv",
|
||||
|
||||
1
tests/components/bmw_connected_drive/__init__.py
Normal file
1
tests/components/bmw_connected_drive/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Tests for the BMW Connected Drive integration."""
|
||||
79
tests/components/bmw_connected_drive/test_init.py
Normal file
79
tests/components/bmw_connected_drive/test_init.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""Tests for the BMW Connected Drive integration."""
|
||||
|
||||
from homeassistant.components.bmw_connected_drive import DOMAIN
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_IGNORE,
|
||||
ConfigEntryDisabler,
|
||||
ConfigEntryState,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import issue_registry as ir
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_bmw_connected_drive_repair_issue(
|
||||
hass: HomeAssistant, issue_registry: ir.IssueRegistry
|
||||
) -> None:
|
||||
"""Test the BMW Connected Drive configuration entry loading/unloading handles the repair."""
|
||||
config_entry_1 = MockConfigEntry(
|
||||
title="Example 1",
|
||||
domain=DOMAIN,
|
||||
)
|
||||
config_entry_1.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry_1.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert config_entry_1.state is ConfigEntryState.LOADED
|
||||
|
||||
# Add a second one
|
||||
config_entry_2 = MockConfigEntry(
|
||||
title="Example 2",
|
||||
domain=DOMAIN,
|
||||
)
|
||||
config_entry_2.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry_2.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry_2.state is ConfigEntryState.LOADED
|
||||
assert issue_registry.async_get_issue(DOMAIN, DOMAIN)
|
||||
|
||||
# Add an ignored entry
|
||||
config_entry_3 = MockConfigEntry(
|
||||
source=SOURCE_IGNORE,
|
||||
domain=DOMAIN,
|
||||
)
|
||||
config_entry_3.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry_3.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry_3.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
# Add a disabled entry
|
||||
config_entry_4 = MockConfigEntry(
|
||||
disabled_by=ConfigEntryDisabler.USER,
|
||||
domain=DOMAIN,
|
||||
)
|
||||
config_entry_4.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry_4.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry_4.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
# Remove the first one
|
||||
await hass.config_entries.async_remove(config_entry_1.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry_1.state is ConfigEntryState.NOT_LOADED
|
||||
assert config_entry_2.state is ConfigEntryState.LOADED
|
||||
assert issue_registry.async_get_issue(DOMAIN, DOMAIN)
|
||||
|
||||
# Remove the second one
|
||||
await hass.config_entries.async_remove(config_entry_2.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry_1.state is ConfigEntryState.NOT_LOADED
|
||||
assert config_entry_2.state is ConfigEntryState.NOT_LOADED
|
||||
assert issue_registry.async_get_issue(DOMAIN, DOMAIN) is None
|
||||
|
||||
# Check the ignored and disabled entries are removed
|
||||
assert not hass.config_entries.async_entries(DOMAIN)
|
||||
Reference in New Issue
Block a user