Remove deprecated yaml config from dlna_dmr (#62344)
parent
b5de2c38b3
commit
e689afc0b3
|
@ -14,13 +14,7 @@ import voluptuous as vol
|
|||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import ssdp
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE_ID,
|
||||
CONF_HOST,
|
||||
CONF_NAME,
|
||||
CONF_TYPE,
|
||||
CONF_URL,
|
||||
)
|
||||
from homeassistant.const import CONF_DEVICE_ID, CONF_HOST, CONF_TYPE, CONF_URL
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.exceptions import IntegrationError
|
||||
|
@ -125,85 +119,6 @@ class DlnaDmrFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
step_id="manual", data_schema=data_schema, errors=errors
|
||||
)
|
||||
|
||||
async def async_step_import(self, import_data: FlowInput = None) -> FlowResult:
|
||||
"""Import a new DLNA DMR device from a config entry.
|
||||
|
||||
This flow is triggered by `async_setup_platform`. If the device has not
|
||||
been migrated, and can be connected to, automatically import it. If it
|
||||
cannot be connected to, prompt the user to turn it on. If it has already
|
||||
been migrated, do nothing.
|
||||
"""
|
||||
LOGGER.debug("async_step_import: import_data: %s", import_data)
|
||||
|
||||
if not import_data or CONF_URL not in import_data:
|
||||
LOGGER.debug("Entry not imported: incomplete_config")
|
||||
return self.async_abort(reason="incomplete_config")
|
||||
|
||||
self._location = import_data[CONF_URL]
|
||||
self._async_abort_entries_match({CONF_URL: self._location})
|
||||
|
||||
# Use the location as this config flow's unique ID until UDN is known
|
||||
await self.async_set_unique_id(self._location)
|
||||
|
||||
# Set options from the import_data, except listen_ip which is no longer used
|
||||
self._options[CONF_LISTEN_PORT] = import_data.get(CONF_LISTEN_PORT)
|
||||
self._options[CONF_CALLBACK_URL_OVERRIDE] = import_data.get(
|
||||
CONF_CALLBACK_URL_OVERRIDE
|
||||
)
|
||||
|
||||
# Override device name if it's set in the YAML
|
||||
self._name = import_data.get(CONF_NAME)
|
||||
|
||||
discoveries = await self._async_get_discoveries()
|
||||
|
||||
# Find the device in the list of unconfigured devices
|
||||
for discovery in discoveries:
|
||||
if discovery.ssdp_location == self._location:
|
||||
# Device found via SSDP, it shouldn't need polling
|
||||
self._options[CONF_POLL_AVAILABILITY] = False
|
||||
# Discovery info has everything required to create config entry
|
||||
await self._async_set_info_from_discovery(discovery)
|
||||
LOGGER.debug(
|
||||
"Entry %s found via SSDP, with UDN %s",
|
||||
self._location,
|
||||
self._udn,
|
||||
)
|
||||
return self._create_entry()
|
||||
|
||||
# This device will need to be polled
|
||||
self._options[CONF_POLL_AVAILABILITY] = True
|
||||
|
||||
# Device was not found via SSDP, connect directly for configuration
|
||||
try:
|
||||
await self._async_connect()
|
||||
except ConnectError as err:
|
||||
# This will require user action
|
||||
LOGGER.debug("Entry %s not imported yet: %s", self._location, err.args[0])
|
||||
return await self.async_step_import_turn_on()
|
||||
|
||||
LOGGER.debug("Entry %s ready for import", self._location)
|
||||
return self._create_entry()
|
||||
|
||||
async def async_step_import_turn_on(
|
||||
self, user_input: FlowInput = None
|
||||
) -> FlowResult:
|
||||
"""Request the user to turn on the device so that import can finish."""
|
||||
LOGGER.debug("async_step_import_turn_on: %s", user_input)
|
||||
|
||||
self.context["title_placeholders"] = {"name": self._name or self._location}
|
||||
|
||||
errors = {}
|
||||
if user_input is not None:
|
||||
try:
|
||||
await self._async_connect()
|
||||
except ConnectError as err:
|
||||
errors["base"] = err.args[0]
|
||||
else:
|
||||
return self._create_entry()
|
||||
|
||||
self._set_confirm_only()
|
||||
return self.async_show_form(step_id="import_turn_on", errors=errors)
|
||||
|
||||
async def async_step_ssdp(self, discovery_info: ssdp.SsdpServiceInfo) -> FlowResult:
|
||||
"""Handle a flow initialized by SSDP discovery."""
|
||||
LOGGER.debug("async_step_ssdp: discovery_info %s", pformat(discovery_info))
|
||||
|
|
|
@ -13,11 +13,10 @@ from async_upnp_client.const import NotificationSubType
|
|||
from async_upnp_client.exceptions import UpnpError, UpnpResponseError
|
||||
from async_upnp_client.profiles.dlna import DmrDevice, PlayMode, TransportState
|
||||
from async_upnp_client.utils import async_get_local_ip
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import ssdp
|
||||
from homeassistant.components.media_player import PLATFORM_SCHEMA, MediaPlayerEntity
|
||||
from homeassistant.components.media_player import MediaPlayerEntity
|
||||
from homeassistant.components.media_player.const import (
|
||||
ATTR_MEDIA_EXTRA,
|
||||
REPEAT_MODE_ALL,
|
||||
|
@ -38,7 +37,6 @@ from homeassistant.components.media_player.const import (
|
|||
)
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE_ID,
|
||||
CONF_NAME,
|
||||
CONF_TYPE,
|
||||
CONF_URL,
|
||||
STATE_IDLE,
|
||||
|
@ -49,9 +47,7 @@ from homeassistant.const import (
|
|||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry, entity_registry
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
|
||||
from .const import (
|
||||
CONF_CALLBACK_URL_OVERRIDE,
|
||||
|
@ -69,25 +65,6 @@ from .data import EventListenAddr, get_domain_data
|
|||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
# Configuration via YAML is deprecated in favour of config flow
|
||||
CONF_LISTEN_IP = "listen_ip"
|
||||
PLATFORM_SCHEMA = vol.All(
|
||||
cv.deprecated(CONF_URL),
|
||||
cv.deprecated(CONF_LISTEN_IP),
|
||||
cv.deprecated(CONF_LISTEN_PORT),
|
||||
cv.deprecated(CONF_NAME),
|
||||
cv.deprecated(CONF_CALLBACK_URL_OVERRIDE),
|
||||
PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_URL): cv.string,
|
||||
vol.Optional(CONF_LISTEN_IP): cv.string,
|
||||
vol.Optional(CONF_LISTEN_PORT): cv.port,
|
||||
vol.Optional(CONF_NAME): cv.string,
|
||||
vol.Optional(CONF_CALLBACK_URL_OVERRIDE): cv.url,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
Func = TypeVar("Func", bound=Callable[..., Any])
|
||||
|
||||
|
||||
|
@ -111,29 +88,6 @@ def catch_request_errors(func: Func) -> Func:
|
|||
return cast(Func, wrapper)
|
||||
|
||||
|
||||
async def async_setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up DLNA media_player platform."""
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data=config,
|
||||
)
|
||||
)
|
||||
|
||||
_LOGGER.warning(
|
||||
"Configuring dlna_dmr via yaml is deprecated; the configuration for"
|
||||
" %s will be migrated to a config entry and can be safely removed when"
|
||||
"migration is complete",
|
||||
config.get(CONF_URL),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: config_entries.ConfigEntry,
|
||||
|
|
|
@ -15,14 +15,7 @@ from homeassistant.components.dlna_dmr.const import (
|
|||
CONF_POLL_AVAILABILITY,
|
||||
DOMAIN as DLNA_DOMAIN,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE_ID,
|
||||
CONF_HOST,
|
||||
CONF_NAME,
|
||||
CONF_PLATFORM,
|
||||
CONF_TYPE,
|
||||
CONF_URL,
|
||||
)
|
||||
from homeassistant.const import CONF_DEVICE_ID, CONF_HOST, CONF_TYPE, CONF_URL
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .conftest import (
|
||||
|
@ -43,13 +36,6 @@ pytestmark = [
|
|||
|
||||
WRONG_DEVICE_TYPE = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"
|
||||
|
||||
IMPORTED_DEVICE_NAME = "Imported DMR device"
|
||||
|
||||
MOCK_CONFIG_IMPORT_DATA = {
|
||||
CONF_PLATFORM: DLNA_DOMAIN,
|
||||
CONF_URL: MOCK_DEVICE_LOCATION,
|
||||
}
|
||||
|
||||
MOCK_ROOT_DEVICE_UDN = "ROOT_DEVICE"
|
||||
|
||||
MOCK_DISCOVERY = ssdp.SsdpServiceInfo(
|
||||
|
@ -276,212 +262,6 @@ async def test_user_flow_wrong_st(hass: HomeAssistant, domain_data_mock: Mock) -
|
|||
assert result["step_id"] == "manual"
|
||||
|
||||
|
||||
async def test_import_flow_invalid(hass: HomeAssistant, domain_data_mock: Mock) -> None:
|
||||
"""Test import flow of invalid YAML config."""
|
||||
# Missing CONF_URL
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DLNA_DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={CONF_PLATFORM: DLNA_DOMAIN},
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "incomplete_config"
|
||||
|
||||
|
||||
async def test_import_flow_ssdp_discovered(
|
||||
hass: HomeAssistant, ssdp_scanner_mock: Mock
|
||||
) -> None:
|
||||
"""Test import of YAML config with a device also found via SSDP."""
|
||||
ssdp_scanner_mock.async_get_discovery_info_by_st.side_effect = [
|
||||
[MOCK_DISCOVERY],
|
||||
[],
|
||||
[],
|
||||
]
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DLNA_DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data=MOCK_CONFIG_IMPORT_DATA,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert ssdp_scanner_mock.async_get_discovery_info_by_st.call_count >= 1
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == MOCK_DEVICE_NAME
|
||||
assert result["data"] == {
|
||||
CONF_URL: MOCK_DEVICE_LOCATION,
|
||||
CONF_DEVICE_ID: MOCK_DEVICE_UDN,
|
||||
CONF_TYPE: MOCK_DEVICE_TYPE,
|
||||
}
|
||||
assert result["options"] == {
|
||||
CONF_LISTEN_PORT: None,
|
||||
CONF_CALLBACK_URL_OVERRIDE: None,
|
||||
CONF_POLL_AVAILABILITY: False,
|
||||
}
|
||||
|
||||
# The config entry should not be duplicated when dlna_dmr is restarted
|
||||
ssdp_scanner_mock.async_get_discovery_info_by_st.side_effect = [
|
||||
[MOCK_DISCOVERY],
|
||||
[],
|
||||
[],
|
||||
]
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DLNA_DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data=MOCK_CONFIG_IMPORT_DATA,
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
# Wait for platform to be fully setup
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_import_flow_direct_connect(
|
||||
hass: HomeAssistant, ssdp_scanner_mock: Mock
|
||||
) -> None:
|
||||
"""Test import of YAML config with a device *not found* via SSDP."""
|
||||
ssdp_scanner_mock.async_get_discovery_info_by_st.return_value = []
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DLNA_DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data=MOCK_CONFIG_IMPORT_DATA,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert ssdp_scanner_mock.async_get_discovery_info_by_st.call_count >= 1
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == MOCK_DEVICE_NAME
|
||||
assert result["data"] == {
|
||||
CONF_URL: MOCK_DEVICE_LOCATION,
|
||||
CONF_DEVICE_ID: MOCK_DEVICE_UDN,
|
||||
CONF_TYPE: MOCK_DEVICE_TYPE,
|
||||
}
|
||||
assert result["options"] == {
|
||||
CONF_LISTEN_PORT: None,
|
||||
CONF_CALLBACK_URL_OVERRIDE: None,
|
||||
CONF_POLL_AVAILABILITY: True,
|
||||
}
|
||||
|
||||
# The config entry should not be duplicated when dlna_dmr is restarted
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DLNA_DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data=MOCK_CONFIG_IMPORT_DATA,
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_import_flow_offline(
|
||||
hass: HomeAssistant, domain_data_mock: Mock, ssdp_scanner_mock: Mock
|
||||
) -> None:
|
||||
"""Test import flow of offline device."""
|
||||
# Device is not yet contactable
|
||||
domain_data_mock.upnp_factory.async_create_device.side_effect = UpnpError
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DLNA_DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_PLATFORM: DLNA_DOMAIN,
|
||||
CONF_URL: MOCK_DEVICE_LOCATION,
|
||||
CONF_NAME: IMPORTED_DEVICE_NAME,
|
||||
CONF_CALLBACK_URL_OVERRIDE: "http://override/callback",
|
||||
CONF_LISTEN_PORT: 2222,
|
||||
},
|
||||
)
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["errors"] == {}
|
||||
assert result["step_id"] == "import_turn_on"
|
||||
|
||||
import_flow_id = result["flow_id"]
|
||||
|
||||
# User clicks submit, same form is displayed with an error
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
import_flow_id, user_input={}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||
assert result["errors"] == {"base": "cannot_connect"}
|
||||
assert result["step_id"] == "import_turn_on"
|
||||
|
||||
# Device is discovered via SSDP, new flow should not be initialized
|
||||
ssdp_scanner_mock.async_get_discovery_info_by_st.side_effect = [
|
||||
[MOCK_DISCOVERY],
|
||||
[],
|
||||
[],
|
||||
]
|
||||
domain_data_mock.upnp_factory.async_create_device.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DLNA_DOMAIN,
|
||||
context={"source": config_entries.SOURCE_SSDP},
|
||||
data=MOCK_DISCOVERY,
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||
assert result["reason"] == "already_in_progress"
|
||||
|
||||
# User clicks submit, config entry should be created
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
import_flow_id, user_input={}
|
||||
)
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == IMPORTED_DEVICE_NAME
|
||||
assert result["data"] == {
|
||||
CONF_URL: MOCK_DEVICE_LOCATION,
|
||||
CONF_DEVICE_ID: MOCK_DEVICE_UDN,
|
||||
CONF_TYPE: MOCK_DEVICE_TYPE,
|
||||
}
|
||||
# Options should be retained
|
||||
assert result["options"] == {
|
||||
CONF_LISTEN_PORT: 2222,
|
||||
CONF_CALLBACK_URL_OVERRIDE: "http://override/callback",
|
||||
CONF_POLL_AVAILABILITY: True,
|
||||
}
|
||||
|
||||
# Wait for platform to be fully setup
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_import_flow_options(
|
||||
hass: HomeAssistant, ssdp_scanner_mock: Mock
|
||||
) -> None:
|
||||
"""Test import of YAML config with options set."""
|
||||
ssdp_scanner_mock.async_get_discovery_info_by_st.return_value = []
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DLNA_DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_PLATFORM: DLNA_DOMAIN,
|
||||
CONF_URL: MOCK_DEVICE_LOCATION,
|
||||
CONF_NAME: IMPORTED_DEVICE_NAME,
|
||||
CONF_LISTEN_PORT: 2222,
|
||||
CONF_CALLBACK_URL_OVERRIDE: "http://override/callback",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
|
||||
assert result["title"] == IMPORTED_DEVICE_NAME
|
||||
assert result["data"] == {
|
||||
CONF_URL: MOCK_DEVICE_LOCATION,
|
||||
CONF_DEVICE_ID: MOCK_DEVICE_UDN,
|
||||
CONF_TYPE: MOCK_DEVICE_TYPE,
|
||||
}
|
||||
assert result["options"] == {
|
||||
CONF_LISTEN_PORT: 2222,
|
||||
CONF_CALLBACK_URL_OVERRIDE: "http://override/callback",
|
||||
CONF_POLL_AVAILABILITY: True,
|
||||
}
|
||||
|
||||
# Wait for platform to be fully setup
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_ssdp_flow_success(hass: HomeAssistant) -> None:
|
||||
"""Test that SSDP discovery with an available device works."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
|
|
|
@ -29,7 +29,7 @@ from homeassistant.components.dlna_dmr.const import (
|
|||
from homeassistant.components.dlna_dmr.data import EventListenAddr
|
||||
from homeassistant.components.media_player import ATTR_TO_PROPERTY, const as mp_const
|
||||
from homeassistant.components.media_player.const import DOMAIN as MP_DOMAIN
|
||||
from homeassistant.const import ATTR_ENTITY_ID, CONF_PLATFORM, CONF_URL
|
||||
from homeassistant.const import ATTR_ENTITY_ID
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import async_get as async_get_dr
|
||||
from homeassistant.helpers.entity_component import async_update_entity
|
||||
|
@ -37,7 +37,6 @@ from homeassistant.helpers.entity_registry import (
|
|||
async_entries_for_config_entry,
|
||||
async_get as async_get_er,
|
||||
)
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .conftest import (
|
||||
|
@ -183,38 +182,6 @@ async def mock_disconnected_entity_id(
|
|||
assert dmr_device_mock.on_event is None
|
||||
|
||||
|
||||
async def test_setup_platform_import_flow_started(
|
||||
hass: HomeAssistant, domain_data_mock: Mock
|
||||
) -> None:
|
||||
"""Test import flow of YAML config is started if there's config data."""
|
||||
# Cause connection attempts to fail
|
||||
domain_data_mock.upnp_factory.async_create_device.side_effect = UpnpConnectionError
|
||||
|
||||
# Run the setup
|
||||
mock_config: ConfigType = {
|
||||
MP_DOMAIN: [
|
||||
{
|
||||
CONF_PLATFORM: DLNA_DOMAIN,
|
||||
CONF_URL: MOCK_DEVICE_LOCATION,
|
||||
CONF_LISTEN_PORT: 1234,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
await async_setup_component(hass, MP_DOMAIN, mock_config)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Check config_flow has started
|
||||
flows = hass.config_entries.flow.async_progress(include_uninitialized=True)
|
||||
assert len(flows) == 1
|
||||
|
||||
# It should be paused, waiting for the user to turn on the device
|
||||
flow = flows[0]
|
||||
assert flow["handler"] == "dlna_dmr"
|
||||
assert flow["step_id"] == "import_turn_on"
|
||||
assert flow["context"].get("unique_id") == MOCK_DEVICE_LOCATION
|
||||
|
||||
|
||||
async def test_setup_entry_no_options(
|
||||
hass: HomeAssistant,
|
||||
domain_data_mock: Mock,
|
||||
|
|
Loading…
Reference in New Issue