Add options flow to File (#120269)
* Add options flow to File * Review comments
This commit is contained in:
parent
e39bfeac08
commit
24a20c75eb
9 changed files with 307 additions and 38 deletions
|
@ -1,5 +1,8 @@
|
|||
"""The file component."""
|
||||
|
||||
from copy import deepcopy
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.notify import migrate_notify_issue
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import (
|
||||
|
@ -84,7 +87,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up a file component entry."""
|
||||
config = dict(entry.data)
|
||||
config = {**entry.data, **entry.options}
|
||||
filepath: str = config[CONF_FILE_PATH]
|
||||
if filepath and not await hass.async_add_executor_job(
|
||||
hass.config.is_allowed_path, filepath
|
||||
|
@ -98,6 +101,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
await hass.config_entries.async_forward_entry_setups(
|
||||
entry, [Platform(entry.data[CONF_PLATFORM])]
|
||||
)
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
if entry.data[CONF_PLATFORM] == Platform.NOTIFY and CONF_NAME in entry.data:
|
||||
# New notify entities are being setup through the config entry,
|
||||
# but during the deprecation period we want to keep the legacy notify platform,
|
||||
|
@ -121,3 +125,29 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
return await hass.config_entries.async_unload_platforms(
|
||||
entry, [entry.data[CONF_PLATFORM]]
|
||||
)
|
||||
|
||||
|
||||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Handle options update."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
"""Migrate config entry."""
|
||||
if config_entry.version > 2:
|
||||
# Downgraded from future
|
||||
return False
|
||||
|
||||
if config_entry.version < 2:
|
||||
# Move optional fields from data to options in config entry
|
||||
data: dict[str, Any] = deepcopy(dict(config_entry.data))
|
||||
options = {}
|
||||
for key, value in config_entry.data.items():
|
||||
if key not in (CONF_FILE_PATH, CONF_PLATFORM, CONF_NAME):
|
||||
data.pop(key)
|
||||
options[key] = value
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry, version=2, data=data, options=options
|
||||
)
|
||||
return True
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
"""Config flow for file integration."""
|
||||
|
||||
from copy import deepcopy
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.config_entries import (
|
||||
ConfigEntry,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
OptionsFlow,
|
||||
OptionsFlowWithConfigEntry,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_FILE_PATH,
|
||||
CONF_FILENAME,
|
||||
|
@ -15,6 +22,7 @@ from homeassistant.const import (
|
|||
CONF_VALUE_TEMPLATE,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.selector import (
|
||||
BooleanSelector,
|
||||
BooleanSelectorConfig,
|
||||
|
@ -31,27 +39,44 @@ BOOLEAN_SELECTOR = BooleanSelector(BooleanSelectorConfig())
|
|||
TEMPLATE_SELECTOR = TemplateSelector(TemplateSelectorConfig())
|
||||
TEXT_SELECTOR = TextSelector(TextSelectorConfig(type=TextSelectorType.TEXT))
|
||||
|
||||
FILE_FLOW_SCHEMAS = {
|
||||
FILE_OPTIONS_SCHEMAS = {
|
||||
Platform.SENSOR.value: vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_FILE_PATH): TEXT_SELECTOR,
|
||||
vol.Optional(CONF_VALUE_TEMPLATE): TEMPLATE_SELECTOR,
|
||||
vol.Optional(CONF_UNIT_OF_MEASUREMENT): TEXT_SELECTOR,
|
||||
}
|
||||
),
|
||||
Platform.NOTIFY.value: vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_FILE_PATH): TEXT_SELECTOR,
|
||||
vol.Optional(CONF_TIMESTAMP, default=False): BOOLEAN_SELECTOR,
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
FILE_FLOW_SCHEMAS = {
|
||||
Platform.SENSOR.value: vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_FILE_PATH): TEXT_SELECTOR,
|
||||
}
|
||||
).extend(FILE_OPTIONS_SCHEMAS[Platform.SENSOR.value].schema),
|
||||
Platform.NOTIFY.value: vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_FILE_PATH): TEXT_SELECTOR,
|
||||
}
|
||||
).extend(FILE_OPTIONS_SCHEMAS[Platform.NOTIFY.value].schema),
|
||||
}
|
||||
|
||||
|
||||
class FileConfigFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a file config flow."""
|
||||
|
||||
VERSION = 1
|
||||
VERSION = 2
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlow:
|
||||
"""Get the options flow for this handler."""
|
||||
return FileOptionsFlowHandler(config_entry)
|
||||
|
||||
async def validate_file_path(self, file_path: str) -> bool:
|
||||
"""Ensure the file path is valid."""
|
||||
|
@ -80,7 +105,13 @@ class FileConfigFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
errors[CONF_FILE_PATH] = "not_allowed"
|
||||
else:
|
||||
title = f"{DEFAULT_NAME} [{user_input[CONF_FILE_PATH]}]"
|
||||
return self.async_create_entry(data=user_input, title=title)
|
||||
data = deepcopy(user_input)
|
||||
options = {}
|
||||
for key, value in user_input.items():
|
||||
if key not in (CONF_FILE_PATH, CONF_PLATFORM, CONF_NAME):
|
||||
data.pop(key)
|
||||
options[key] = value
|
||||
return self.async_create_entry(data=data, title=title, options=options)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id=platform, data_schema=FILE_FLOW_SCHEMAS[platform], errors=errors
|
||||
|
@ -114,4 +145,29 @@ class FileConfigFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||
else:
|
||||
file_path = import_data[CONF_FILE_PATH]
|
||||
title = f"{name} [{file_path}]"
|
||||
return self.async_create_entry(title=title, data=import_data)
|
||||
data = deepcopy(import_data)
|
||||
options = {}
|
||||
for key, value in import_data.items():
|
||||
if key not in (CONF_FILE_PATH, CONF_PLATFORM, CONF_NAME):
|
||||
data.pop(key)
|
||||
options[key] = value
|
||||
return self.async_create_entry(title=title, data=data, options=options)
|
||||
|
||||
|
||||
class FileOptionsFlowHandler(OptionsFlowWithConfigEntry):
|
||||
"""Handle File options."""
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Manage File options."""
|
||||
if user_input:
|
||||
return self.async_create_entry(data=user_input)
|
||||
|
||||
platform = self.config_entry.data[CONF_PLATFORM]
|
||||
return self.async_show_form(
|
||||
step_id="init",
|
||||
data_schema=self.add_suggested_values_to_schema(
|
||||
FILE_OPTIONS_SCHEMAS[platform], self.config_entry.options or {}
|
||||
),
|
||||
)
|
||||
|
|
|
@ -5,7 +5,6 @@ from __future__ import annotations
|
|||
from functools import partial
|
||||
import logging
|
||||
import os
|
||||
from types import MappingProxyType
|
||||
from typing import Any, TextIO
|
||||
|
||||
import voluptuous as vol
|
||||
|
@ -109,7 +108,7 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Set up notify entity."""
|
||||
unique_id = entry.entry_id
|
||||
async_add_entities([FileNotifyEntity(unique_id, entry.data)])
|
||||
async_add_entities([FileNotifyEntity(unique_id, {**entry.data, **entry.options})])
|
||||
|
||||
|
||||
class FileNotifyEntity(NotifyEntity):
|
||||
|
@ -118,7 +117,7 @@ class FileNotifyEntity(NotifyEntity):
|
|||
_attr_icon = FILE_ICON
|
||||
_attr_supported_features = NotifyEntityFeature.TITLE
|
||||
|
||||
def __init__(self, unique_id: str, config: MappingProxyType[str, Any]) -> None:
|
||||
def __init__(self, unique_id: str, config: dict[str, Any]) -> None:
|
||||
"""Initialize the service."""
|
||||
self._file_path: str = config[CONF_FILE_PATH]
|
||||
self._add_timestamp: bool = config.get(CONF_TIMESTAMP, False)
|
||||
|
|
|
@ -60,14 +60,15 @@ async def async_setup_entry(
|
|||
) -> None:
|
||||
"""Set up the file sensor."""
|
||||
config = dict(entry.data)
|
||||
options = dict(entry.options)
|
||||
file_path: str = config[CONF_FILE_PATH]
|
||||
unique_id: str = entry.entry_id
|
||||
name: str = config.get(CONF_NAME, DEFAULT_NAME)
|
||||
unit: str | None = config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||
unit: str | None = options.get(CONF_UNIT_OF_MEASUREMENT)
|
||||
value_template: Template | None = None
|
||||
|
||||
if CONF_VALUE_TEMPLATE in config:
|
||||
value_template = Template(config[CONF_VALUE_TEMPLATE], hass)
|
||||
if CONF_VALUE_TEMPLATE in options:
|
||||
value_template = Template(options[CONF_VALUE_TEMPLATE], hass)
|
||||
|
||||
async_add_entities(
|
||||
[FileSensor(unique_id, name, file_path, unit, value_template)], True
|
||||
|
|
|
@ -42,6 +42,22 @@
|
|||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"value_template": "[%key:component::file::config::step::sensor::data::value_template%]",
|
||||
"unit_of_measurement": "[%key:component::file::config::step::sensor::data::unit_of_measurement%]",
|
||||
"timestamp": "[%key:component::file::config::step::notify::data::timestamp%]"
|
||||
},
|
||||
"data_description": {
|
||||
"value_template": "[%key:component::file::config::step::sensor::data_description::value_template%]",
|
||||
"unit_of_measurement": "[%key:component::file::config::step::sensor::data_description::unit_of_measurement%]",
|
||||
"timestamp": "[%key:component::file::config::step::notify::data_description::timestamp%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"dir_not_allowed": {
|
||||
"message": "Access to {filename} is not allowed."
|
||||
|
|
|
@ -7,6 +7,7 @@ import pytest
|
|||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.file import DOMAIN
|
||||
from homeassistant.const import CONF_UNIT_OF_MEASUREMENT
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
|
@ -15,20 +16,22 @@ from tests.common import MockConfigEntry
|
|||
MOCK_CONFIG_NOTIFY = {
|
||||
"platform": "notify",
|
||||
"file_path": "some_file",
|
||||
"timestamp": True,
|
||||
}
|
||||
MOCK_OPTIONS_NOTIFY = {"timestamp": True}
|
||||
MOCK_CONFIG_SENSOR = {
|
||||
"platform": "sensor",
|
||||
"file_path": "some/path",
|
||||
"value_template": "{{ value | round(1) }}",
|
||||
}
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||
MOCK_OPTIONS_SENSOR = {"value_template": "{{ value | round(1) }}"}
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
@pytest.mark.parametrize(
|
||||
("platform", "data"),
|
||||
[("sensor", MOCK_CONFIG_SENSOR), ("notify", MOCK_CONFIG_NOTIFY)],
|
||||
("platform", "data", "options"),
|
||||
[
|
||||
("sensor", MOCK_CONFIG_SENSOR, MOCK_OPTIONS_SENSOR),
|
||||
("notify", MOCK_CONFIG_NOTIFY, MOCK_OPTIONS_NOTIFY),
|
||||
],
|
||||
)
|
||||
async def test_form(
|
||||
hass: HomeAssistant,
|
||||
|
@ -36,6 +39,7 @@ async def test_form(
|
|||
mock_is_allowed_path: bool,
|
||||
platform: str,
|
||||
data: dict[str, Any],
|
||||
options: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
|
@ -50,7 +54,7 @@ async def test_form(
|
|||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
user_input = dict(data)
|
||||
user_input = {**data, **options}
|
||||
user_input.pop("platform")
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], user_input=user_input
|
||||
|
@ -59,12 +63,17 @@ async def test_form(
|
|||
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["data"] == data
|
||||
assert result2["options"] == options
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
@pytest.mark.parametrize(
|
||||
("platform", "data"),
|
||||
[("sensor", MOCK_CONFIG_SENSOR), ("notify", MOCK_CONFIG_NOTIFY)],
|
||||
("platform", "data", "options"),
|
||||
[
|
||||
("sensor", MOCK_CONFIG_SENSOR, MOCK_OPTIONS_SENSOR),
|
||||
("notify", MOCK_CONFIG_NOTIFY, MOCK_OPTIONS_NOTIFY),
|
||||
],
|
||||
)
|
||||
async def test_already_configured(
|
||||
hass: HomeAssistant,
|
||||
|
@ -72,9 +81,10 @@ async def test_already_configured(
|
|||
mock_is_allowed_path: bool,
|
||||
platform: str,
|
||||
data: dict[str, Any],
|
||||
options: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test aborting if the entry is already configured."""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=data)
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=data, options=options)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
|
@ -91,7 +101,7 @@ async def test_already_configured(
|
|||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == platform
|
||||
|
||||
user_input = dict(data)
|
||||
user_input = {**data, **options}
|
||||
user_input.pop("platform")
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
|
@ -103,10 +113,14 @@ async def test_already_configured(
|
|||
assert result2["reason"] == "already_configured"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("mock_setup_entry")
|
||||
@pytest.mark.parametrize("is_allowed", [False], ids=["not_allowed"])
|
||||
@pytest.mark.parametrize(
|
||||
("platform", "data"),
|
||||
[("sensor", MOCK_CONFIG_SENSOR), ("notify", MOCK_CONFIG_NOTIFY)],
|
||||
("platform", "data", "options"),
|
||||
[
|
||||
("sensor", MOCK_CONFIG_SENSOR, MOCK_OPTIONS_SENSOR),
|
||||
("notify", MOCK_CONFIG_NOTIFY, MOCK_OPTIONS_NOTIFY),
|
||||
],
|
||||
)
|
||||
async def test_not_allowed(
|
||||
hass: HomeAssistant,
|
||||
|
@ -114,6 +128,7 @@ async def test_not_allowed(
|
|||
mock_is_allowed_path: bool,
|
||||
platform: str,
|
||||
data: dict[str, Any],
|
||||
options: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test aborting if the file path is not allowed."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
|
@ -130,7 +145,7 @@ async def test_not_allowed(
|
|||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == platform
|
||||
|
||||
user_input = dict(data)
|
||||
user_input = {**data, **options}
|
||||
user_input.pop("platform")
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
|
@ -140,3 +155,49 @@ async def test_not_allowed(
|
|||
|
||||
assert result2["type"] is FlowResultType.FORM
|
||||
assert result2["errors"] == {"file_path": "not_allowed"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("platform", "data", "options", "new_options"),
|
||||
[
|
||||
(
|
||||
"sensor",
|
||||
MOCK_CONFIG_SENSOR,
|
||||
MOCK_OPTIONS_SENSOR,
|
||||
{CONF_UNIT_OF_MEASUREMENT: "mm"},
|
||||
),
|
||||
("notify", MOCK_CONFIG_NOTIFY, MOCK_OPTIONS_NOTIFY, {"timestamp": False}),
|
||||
],
|
||||
)
|
||||
async def test_options_flow(
|
||||
hass: HomeAssistant,
|
||||
mock_is_allowed_path: bool,
|
||||
platform: str,
|
||||
data: dict[str, Any],
|
||||
options: dict[str, Any],
|
||||
new_options: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test options config flow."""
|
||||
entry = MockConfigEntry(domain=DOMAIN, data=data, options=options, version=2)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["step_id"] == "init"
|
||||
|
||||
result = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input=new_options,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["data"] == new_options
|
||||
|
||||
entry = hass.config_entries.async_get_entry(entry.entry_id)
|
||||
assert entry.state is config_entries.ConfigEntryState.LOADED
|
||||
assert entry.options == new_options
|
||||
|
|
65
tests/components/file/test_init.py
Normal file
65
tests/components/file/test_init.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
"""The tests for local file init."""
|
||||
|
||||
from unittest.mock import MagicMock, Mock, patch
|
||||
|
||||
from homeassistant.components.file import DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry, get_fixture_path
|
||||
|
||||
|
||||
@patch("os.path.isfile", Mock(return_value=True))
|
||||
@patch("os.access", Mock(return_value=True))
|
||||
async def test_migration_to_version_2(
|
||||
hass: HomeAssistant, mock_is_allowed_path: MagicMock
|
||||
) -> None:
|
||||
"""Test the File sensor with JSON entries."""
|
||||
data = {
|
||||
"platform": "sensor",
|
||||
"name": "file2",
|
||||
"file_path": get_fixture_path("file_value_template.txt", "file"),
|
||||
"value_template": "{{ value_json.temperature }}",
|
||||
}
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
version=1,
|
||||
data=data,
|
||||
title=f"test [{data['file_path']}]",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
||||
assert entry.state is ConfigEntryState.LOADED
|
||||
assert entry.version == 2
|
||||
assert entry.data == {
|
||||
"platform": "sensor",
|
||||
"name": "file2",
|
||||
"file_path": get_fixture_path("file_value_template.txt", "file"),
|
||||
}
|
||||
assert entry.options == {
|
||||
"value_template": "{{ value_json.temperature }}",
|
||||
}
|
||||
|
||||
|
||||
@patch("os.path.isfile", Mock(return_value=True))
|
||||
@patch("os.access", Mock(return_value=True))
|
||||
async def test_migration_from_future_version(
|
||||
hass: HomeAssistant, mock_is_allowed_path: MagicMock
|
||||
) -> None:
|
||||
"""Test the File sensor with JSON entries."""
|
||||
data = {
|
||||
"platform": "sensor",
|
||||
"name": "file2",
|
||||
"file_path": get_fixture_path("file_value_template.txt", "file"),
|
||||
"value_template": "{{ value_json.temperature }}",
|
||||
}
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, version=3, data=data, title=f"test [{data['file_path']}]"
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
||||
assert entry.state is ConfigEntryState.MIGRATION_ERROR
|
|
@ -174,7 +174,7 @@ async def test_legacy_notify_file_exception(
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("timestamp", "data"),
|
||||
("timestamp", "data", "options"),
|
||||
[
|
||||
(
|
||||
False,
|
||||
|
@ -182,6 +182,8 @@ async def test_legacy_notify_file_exception(
|
|||
"name": "test",
|
||||
"platform": "notify",
|
||||
"file_path": "mock_file",
|
||||
},
|
||||
{
|
||||
"timestamp": False,
|
||||
},
|
||||
),
|
||||
|
@ -191,6 +193,8 @@ async def test_legacy_notify_file_exception(
|
|||
"name": "test",
|
||||
"platform": "notify",
|
||||
"file_path": "mock_file",
|
||||
},
|
||||
{
|
||||
"timestamp": True,
|
||||
},
|
||||
),
|
||||
|
@ -203,6 +207,7 @@ async def test_legacy_notify_file_entry_only_setup(
|
|||
timestamp: bool,
|
||||
mock_is_allowed_path: MagicMock,
|
||||
data: dict[str, Any],
|
||||
options: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test the legacy notify file output in entry only setup."""
|
||||
filename = "mock_file"
|
||||
|
@ -213,7 +218,11 @@ async def test_legacy_notify_file_entry_only_setup(
|
|||
message = params["message"]
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=data, title=f"test [{data['file_path']}]"
|
||||
domain=DOMAIN,
|
||||
data=data,
|
||||
version=2,
|
||||
options=options,
|
||||
title=f"test [{data['file_path']}]",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
@ -252,7 +261,7 @@ async def test_legacy_notify_file_entry_only_setup(
|
|||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("is_allowed", "config"),
|
||||
("is_allowed", "config", "options"),
|
||||
[
|
||||
(
|
||||
False,
|
||||
|
@ -260,6 +269,8 @@ async def test_legacy_notify_file_entry_only_setup(
|
|||
"name": "test",
|
||||
"platform": "notify",
|
||||
"file_path": "mock_file",
|
||||
},
|
||||
{
|
||||
"timestamp": False,
|
||||
},
|
||||
),
|
||||
|
@ -271,10 +282,15 @@ async def test_legacy_notify_file_not_allowed(
|
|||
caplog: pytest.LogCaptureFixture,
|
||||
mock_is_allowed_path: MagicMock,
|
||||
config: dict[str, Any],
|
||||
options: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test legacy notify file output not allowed."""
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=config, title=f"test [{config['file_path']}]"
|
||||
domain=DOMAIN,
|
||||
data=config,
|
||||
version=2,
|
||||
options=options,
|
||||
title=f"test [{config['file_path']}]",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
assert not await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
@ -293,13 +309,15 @@ async def test_legacy_notify_file_not_allowed(
|
|||
],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("data", "is_allowed"),
|
||||
("data", "options", "is_allowed"),
|
||||
[
|
||||
(
|
||||
{
|
||||
"name": "test",
|
||||
"platform": "notify",
|
||||
"file_path": "mock_file",
|
||||
},
|
||||
{
|
||||
"timestamp": False,
|
||||
},
|
||||
True,
|
||||
|
@ -314,12 +332,17 @@ async def test_notify_file_write_access_failed(
|
|||
service: str,
|
||||
params: dict[str, Any],
|
||||
data: dict[str, Any],
|
||||
options: dict[str, Any],
|
||||
) -> None:
|
||||
"""Test the notify file fails."""
|
||||
domain = notify.DOMAIN
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=data, title=f"test [{data['file_path']}]"
|
||||
domain=DOMAIN,
|
||||
data=data,
|
||||
version=2,
|
||||
options=options,
|
||||
title=f"test [{data['file_path']}]",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
|
|
@ -47,7 +47,11 @@ async def test_file_value_entry_setup(
|
|||
}
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=data, title=f"test [{data['file_path']}]"
|
||||
domain=DOMAIN,
|
||||
data=data,
|
||||
version=2,
|
||||
options={},
|
||||
title=f"test [{data['file_path']}]",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
@ -66,11 +70,17 @@ async def test_file_value_template(
|
|||
"platform": "sensor",
|
||||
"name": "file2",
|
||||
"file_path": get_fixture_path("file_value_template.txt", "file"),
|
||||
}
|
||||
options = {
|
||||
"value_template": "{{ value_json.temperature }}",
|
||||
}
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=data, title=f"test [{data['file_path']}]"
|
||||
domain=DOMAIN,
|
||||
data=data,
|
||||
version=2,
|
||||
options=options,
|
||||
title=f"test [{data['file_path']}]",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
@ -90,7 +100,11 @@ async def test_file_empty(hass: HomeAssistant, mock_is_allowed_path: MagicMock)
|
|||
}
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=data, title=f"test [{data['file_path']}]"
|
||||
domain=DOMAIN,
|
||||
data=data,
|
||||
version=2,
|
||||
options={},
|
||||
title=f"test [{data['file_path']}]",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
@ -113,7 +127,11 @@ async def test_file_path_invalid(
|
|||
}
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN, data=data, title=f"test [{data['file_path']}]"
|
||||
domain=DOMAIN,
|
||||
data=data,
|
||||
version=2,
|
||||
options={},
|
||||
title=f"test [{data['file_path']}]",
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue