Plex discovery on demand (#35303)

* Allow Plex discovery on demand

* Add new discovery source

* Add tests
This commit is contained in:
jjlawren 2020-05-13 08:11:00 -05:00 committed by GitHub
parent ee96ff2846
commit c12f8bed43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 81 additions and 2 deletions

View file

@ -4,6 +4,7 @@ import logging
from aiohttp import web_response from aiohttp import web_response
import plexapi.exceptions import plexapi.exceptions
from plexapi.gdm import GDM
from plexauth import PlexAuth from plexauth import PlexAuth
import requests.exceptions import requests.exceptions
import voluptuous as vol import voluptuous as vol
@ -62,6 +63,18 @@ def configured_servers(hass):
} }
async def async_discover(hass):
"""Scan for available Plex servers."""
gdm = GDM()
await hass.async_add_executor_job(gdm.scan)
for server_data in gdm.entries:
await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_INTEGRATION_DISCOVERY},
data=server_data,
)
class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a Plex config flow.""" """Handle a Plex config flow."""
@ -266,6 +279,19 @@ class PlexFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
_LOGGER.debug("Imported Plex configuration") _LOGGER.debug("Imported Plex configuration")
return await self.async_step_server_validate(import_config) return await self.async_step_server_validate(import_config)
async def async_step_integration_discovery(self, discovery_info):
"""Handle GDM discovery."""
machine_identifier = discovery_info["data"]["Resource-Identifier"]
await self.async_set_unique_id(machine_identifier)
self._abort_if_unique_id_configured()
host = f"{discovery_info['from'][0]}:{discovery_info['data']['Port']}"
name = discovery_info["data"]["Name"]
self.context["title_placeholders"] = { # pylint: disable=no-member
"host": host,
"name": name,
}
return await self.async_step_user()
async def async_step_plex_website_auth(self): async def async_step_plex_website_auth(self):
"""Begin external auth flow on Plex website.""" """Begin external auth flow on Plex website."""
self.hass.http.register_view(PlexAuthorizationCallbackView) self.hass.http.register_view(PlexAuthorizationCallbackView)

View file

@ -1,5 +1,6 @@
{ {
"config": { "config": {
"flow_title": "{name} ({host})",
"step": { "step": {
"user": { "user": {
"title": "Plex Media Server", "title": "Plex Media Server",

View file

@ -22,6 +22,7 @@ _UNDEF: dict = {}
SOURCE_DISCOVERY = "discovery" SOURCE_DISCOVERY = "discovery"
SOURCE_IMPORT = "import" SOURCE_IMPORT = "import"
SOURCE_INTEGRATION_DISCOVERY = "integration_discovery"
SOURCE_SSDP = "ssdp" SOURCE_SSDP = "ssdp"
SOURCE_USER = "user" SOURCE_USER = "user"
SOURCE_ZEROCONF = "zeroconf" SOURCE_ZEROCONF = "zeroconf"

View file

@ -8,6 +8,32 @@ from homeassistant.const import CONF_URL
from .const import DEFAULT_DATA, MOCK_SERVERS, MOCK_USERS from .const import DEFAULT_DATA, MOCK_SERVERS, MOCK_USERS
GDM_PAYLOAD = [
{
"data": {
"Content-Type": "plex/media-server",
"Name": "plextest",
"Port": "32400",
"Resource-Identifier": "1234567890123456789012345678901234567890",
"Updated-At": "157762684800",
"Version": "1.0",
},
"from": ("1.2.3.4", 32414),
}
]
class MockGDM:
"""Mock a GDM instance."""
def __init__(self):
"""Initialize the object."""
self.entries = GDM_PAYLOAD
def scan(self):
"""Mock the scan call."""
pass
class MockResource: class MockResource:
"""Mock a PlexAccount resource.""" """Mock a PlexAccount resource."""

View file

@ -22,7 +22,10 @@ from homeassistant.components.plex.const import (
SERVERS, SERVERS,
) )
from homeassistant.config import async_process_ha_core_config from homeassistant.config import async_process_ha_core_config
from homeassistant.config_entries import ENTRY_STATE_LOADED from homeassistant.config_entries import (
ENTRY_STATE_LOADED,
SOURCE_INTEGRATION_DISCOVERY,
)
from homeassistant.const import ( from homeassistant.const import (
CONF_HOST, CONF_HOST,
CONF_PORT, CONF_PORT,
@ -34,7 +37,7 @@ from homeassistant.const import (
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.dispatcher import async_dispatcher_send
from .const import DEFAULT_DATA, DEFAULT_OPTIONS, MOCK_SERVERS, MOCK_TOKEN from .const import DEFAULT_DATA, DEFAULT_OPTIONS, MOCK_SERVERS, MOCK_TOKEN
from .mock_classes import MockPlexAccount, MockPlexServer from .mock_classes import MockGDM, MockPlexAccount, MockPlexServer
from tests.async_mock import patch from tests.async_mock import patch
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -746,3 +749,25 @@ async def test_setup_with_limited_credentials(hass):
assert len(hass.config_entries.async_entries(DOMAIN)) == 1 assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert entry.state == ENTRY_STATE_LOADED assert entry.state == ENTRY_STATE_LOADED
async def test_integration_discovery(hass):
"""Test integration self-discovery."""
mock_gdm = MockGDM()
with patch("homeassistant.components.plex.config_flow.GDM", return_value=mock_gdm):
await config_flow.async_discover(hass)
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 1
flow = flows[0]
assert flow["handler"] == DOMAIN
assert flow["context"]["source"] == SOURCE_INTEGRATION_DISCOVERY
assert (
flow["context"]["unique_id"]
== mock_gdm.entries[0]["data"]["Resource-Identifier"]
)
assert flow["step_id"] == "user"