Add Options flow to YouTube (#93667)

* Add Options flow to YouTube

* Add strings for options flow

* Add strings for options flow

* Add strings for options flow
This commit is contained in:
Joost Lekkerkerker 2023-05-29 03:44:45 +02:00 committed by GitHub
parent 795ef075da
commit 20d1a0fc77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 112 additions and 12 deletions

View file

@ -11,8 +11,9 @@ from googleapiclient.errors import HttpError
from googleapiclient.http import HttpRequest
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry
from homeassistant.config_entries import ConfigEntry, OptionsFlowWithConfigEntry
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_TOKEN
from homeassistant.core import HomeAssistant, callback
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import config_entry_oauth2_flow
from homeassistant.helpers.selector import (
@ -24,6 +25,19 @@ from homeassistant.helpers.selector import (
from .const import CONF_CHANNELS, DEFAULT_ACCESS, DOMAIN, LOGGER
async def get_resource(hass: HomeAssistant, token: str) -> Resource:
"""Get Youtube resource async."""
def _build_resource() -> Resource:
return build(
"youtube",
"v3",
credentials=Credentials(token),
)
return await hass.async_add_executor_job(_build_resource)
class OAuth2FlowHandler(
config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN
):
@ -36,6 +50,14 @@ class OAuth2FlowHandler(
reauth_entry: ConfigEntry | None = None
@staticmethod
@callback
def async_get_options_flow(
config_entry: ConfigEntry,
) -> YouTubeOptionsFlowHandler:
"""Get the options flow for this handler."""
return YouTubeOptionsFlowHandler(config_entry)
@property
def logger(self) -> logging.Logger:
"""Return logger."""
@ -69,7 +91,7 @@ class OAuth2FlowHandler(
async def async_oauth_create_entry(self, data: dict[str, Any]) -> FlowResult:
"""Create an entry for the flow, or update existing entry."""
try:
service = await self._get_resource(data[CONF_TOKEN][CONF_ACCESS_TOKEN])
service = await get_resource(self.hass, data[CONF_TOKEN][CONF_ACCESS_TOKEN])
# pylint: disable=no-member
own_channel_request: HttpRequest = service.channels().list(
part="snippet", mine=True
@ -116,7 +138,9 @@ class OAuth2FlowHandler(
data=self._data,
options=user_input,
)
service = await self._get_resource(self._data[CONF_TOKEN][CONF_ACCESS_TOKEN])
service = await get_resource(
self.hass, self._data[CONF_TOKEN][CONF_ACCESS_TOKEN]
)
# pylint: disable=no-member
subscription_request: HttpRequest = service.subscriptions().list(
part="snippet", mine=True, maxResults=50
@ -140,12 +164,46 @@ class OAuth2FlowHandler(
),
)
async def _get_resource(self, token: str) -> Resource:
def _build_resource() -> Resource:
return build(
"youtube",
"v3",
credentials=Credentials(token),
)
return await self.hass.async_add_executor_job(_build_resource)
class YouTubeOptionsFlowHandler(OptionsFlowWithConfigEntry):
"""YouTube Options flow handler."""
async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Initialize form."""
if user_input is not None:
return self.async_create_entry(
title=self.config_entry.title,
data=user_input,
)
service = await get_resource(
self.hass, self.config_entry.data[CONF_TOKEN][CONF_ACCESS_TOKEN]
)
# pylint: disable=no-member
subscription_request: HttpRequest = service.subscriptions().list(
part="snippet", mine=True, maxResults=50
)
response = await self.hass.async_add_executor_job(subscription_request.execute)
selectable_channels = [
SelectOptionDict(
value=subscription["snippet"]["resourceId"]["channelId"],
label=subscription["snippet"]["title"],
)
for subscription in response["items"]
]
return self.async_show_form(
step_id="init",
data_schema=self.add_suggested_values_to_schema(
vol.Schema(
{
vol.Required(CONF_CHANNELS): SelectSelector(
SelectSelectorConfig(
options=selectable_channels, multiple=True
)
),
}
),
self.options,
),
)

View file

@ -19,6 +19,16 @@
}
}
},
"options": {
"step": {
"init": {
"description": "Select the channels you want to add.",
"data": {
"channels": "YouTube channels"
}
}
}
},
"entity": {
"sensor": {
"latest_upload": {

View file

@ -12,7 +12,14 @@ from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import config_entry_oauth2_flow
from . import MockService
from .conftest import CLIENT_ID, GOOGLE_AUTH_URI, GOOGLE_TOKEN_URI, SCOPES, TITLE
from .conftest import (
CLIENT_ID,
GOOGLE_AUTH_URI,
GOOGLE_TOKEN_URI,
SCOPES,
TITLE,
ComponentSetup,
)
from tests.common import MockConfigEntry, load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
@ -267,3 +274,28 @@ async def test_flow_exception(
result = await hass.config_entries.flow.async_configure(result["flow_id"])
assert result["type"] == FlowResultType.ABORT
assert result["reason"] == "unknown"
async def test_options_flow(
hass: HomeAssistant, setup_integration: ComponentSetup
) -> None:
"""Test the full options flow."""
await setup_integration()
with patch(
"homeassistant.components.youtube.config_flow.build", return_value=MockService()
):
entry = hass.config_entries.async_entries(DOMAIN)[0]
result = await hass.config_entries.options.async_init(entry.entry_id)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "init"
result = await hass.config_entries.options.async_configure(
result["flow_id"],
user_input={CONF_CHANNELS: ["UC_x5XG1OV2P6uZZ5FSM9Ttw"]},
)
await hass.async_block_till_done()
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["data"] == {CONF_CHANNELS: ["UC_x5XG1OV2P6uZZ5FSM9Ttw"]}