mirror of
https://github.com/home-assistant/core.git
synced 2026-04-18 07:56:03 +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
GitHub
parent
02bcae00cf
commit
ef6718c242
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",
|
||||||
"bluetooth_adapters",
|
"bluetooth_adapters",
|
||||||
"bluetooth_le_tracker",
|
"bluetooth_le_tracker",
|
||||||
|
"bmw_connected_drive",
|
||||||
"bond",
|
"bond",
|
||||||
"bosch_shc",
|
"bosch_shc",
|
||||||
"braviatv",
|
"braviatv",
|
||||||
@@ -1183,6 +1184,7 @@ INTEGRATIONS_WITHOUT_SCALE = [
|
|||||||
"bluetooth",
|
"bluetooth",
|
||||||
"bluetooth_adapters",
|
"bluetooth_adapters",
|
||||||
"bluetooth_le_tracker",
|
"bluetooth_le_tracker",
|
||||||
|
"bmw_connected_drive",
|
||||||
"bond",
|
"bond",
|
||||||
"bosch_shc",
|
"bosch_shc",
|
||||||
"braviatv",
|
"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