Add config message items selector to imap option flow (#115108)
* Update const.py * Update config_flow.py * Update coordinator.py * Update coordinator.py * Update strings.json * Update config_flow.py * Update const.py * Update coordinator.py * Update config_flow.py * Update config_flow.py * Update test_diagnostics.py * Update const.py * Update test_init.py * Update test_diagnostics.py * Update test_diagnostics.py * Update test_diagnostics.py * Update test_init.py * Update test_diagnostics.py * Update test_init.py * Update test_diagnostics.py * Update test_diagnostics.py * Update test_diagnostics.py * Update test_config_flow.py * Update config_flow.py * Update test_config_flow.py * Update test_init.py * Update const.py * Only make text and headers optional * Add message data tests * Add message data test * Update test_config_flow.py * Update test message data * Fix ruff * Fix ruff * Update test_init.py * Update strings.json --------- Co-authored-by: jbouwh <jan@jbsoft.nl> Co-authored-by: Jan Bouwhuis <jbouwh@users.noreply.github.com>
This commit is contained in:
parent
f23e48f373
commit
fc1ebdaaa3
7 changed files with 112 additions and 10 deletions
|
@ -33,6 +33,7 @@ from .const import (
|
|||
CONF_CHARSET,
|
||||
CONF_CUSTOM_EVENT_DATA_TEMPLATE,
|
||||
CONF_ENABLE_PUSH,
|
||||
CONF_EVENT_MESSAGE_DATA,
|
||||
CONF_FOLDER,
|
||||
CONF_MAX_MESSAGE_SIZE,
|
||||
CONF_SEARCH,
|
||||
|
@ -42,6 +43,7 @@ from .const import (
|
|||
DEFAULT_PORT,
|
||||
DOMAIN,
|
||||
MAX_MESSAGE_SIZE_LIMIT,
|
||||
MESSAGE_DATA_OPTIONS,
|
||||
)
|
||||
from .coordinator import connect_to_server
|
||||
from .errors import InvalidAuth, InvalidFolder
|
||||
|
@ -55,6 +57,13 @@ CIPHER_SELECTOR = SelectSelector(
|
|||
)
|
||||
)
|
||||
TEMPLATE_SELECTOR = TemplateSelector(TemplateSelectorConfig())
|
||||
EVENT_MESSAGE_DATA_SELECTOR = SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=MESSAGE_DATA_OPTIONS,
|
||||
translation_key=CONF_EVENT_MESSAGE_DATA,
|
||||
multiple=True,
|
||||
)
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
|
@ -65,6 +74,8 @@ CONFIG_SCHEMA = vol.Schema(
|
|||
vol.Optional(CONF_CHARSET, default="utf-8"): str,
|
||||
vol.Optional(CONF_FOLDER, default="INBOX"): str,
|
||||
vol.Optional(CONF_SEARCH, default="UnSeen UnDeleted"): str,
|
||||
# The default for new entries is to not include text and headers
|
||||
vol.Optional(CONF_EVENT_MESSAGE_DATA, default=[]): cv.ensure_list,
|
||||
}
|
||||
)
|
||||
CONFIG_SCHEMA_ADVANCED = {
|
||||
|
@ -78,6 +89,10 @@ OPTIONS_SCHEMA = vol.Schema(
|
|||
{
|
||||
vol.Optional(CONF_FOLDER, default="INBOX"): str,
|
||||
vol.Optional(CONF_SEARCH, default="UnSeen UnDeleted"): str,
|
||||
# The default for older entries is to include text and headers
|
||||
vol.Optional(
|
||||
CONF_EVENT_MESSAGE_DATA, default=MESSAGE_DATA_OPTIONS
|
||||
): EVENT_MESSAGE_DATA_SELECTOR,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@ CONF_SERVER: Final = "server"
|
|||
CONF_FOLDER: Final = "folder"
|
||||
CONF_SEARCH: Final = "search"
|
||||
CONF_CHARSET: Final = "charset"
|
||||
CONF_MAX_MESSAGE_SIZE = "max_message_size"
|
||||
CONF_EVENT_MESSAGE_DATA: Final = "event_message_data"
|
||||
CONF_MAX_MESSAGE_SIZE: Final = "max_message_size"
|
||||
CONF_CUSTOM_EVENT_DATA_TEMPLATE: Final = "custom_event_data_template"
|
||||
CONF_SSL_CIPHER_LIST: Final = "ssl_cipher_list"
|
||||
CONF_ENABLE_PUSH: Final = "enable_push"
|
||||
|
@ -17,4 +18,6 @@ DEFAULT_PORT: Final = 993
|
|||
|
||||
DEFAULT_MAX_MESSAGE_SIZE = 2048
|
||||
|
||||
MAX_MESSAGE_SIZE_LIMIT = 30000
|
||||
MESSAGE_DATA_OPTIONS: Final = ["text", "headers"]
|
||||
|
||||
MAX_MESSAGE_SIZE_LIMIT: Final = 30000
|
||||
|
|
|
@ -41,6 +41,7 @@ from homeassistant.util.ssl import (
|
|||
from .const import (
|
||||
CONF_CHARSET,
|
||||
CONF_CUSTOM_EVENT_DATA_TEMPLATE,
|
||||
CONF_EVENT_MESSAGE_DATA,
|
||||
CONF_FOLDER,
|
||||
CONF_MAX_MESSAGE_SIZE,
|
||||
CONF_SEARCH,
|
||||
|
@ -48,6 +49,7 @@ from .const import (
|
|||
CONF_SSL_CIPHER_LIST,
|
||||
DEFAULT_MAX_MESSAGE_SIZE,
|
||||
DOMAIN,
|
||||
MESSAGE_DATA_OPTIONS,
|
||||
)
|
||||
from .errors import InvalidAuth, InvalidFolder
|
||||
|
||||
|
@ -225,6 +227,12 @@ class ImapDataUpdateCoordinator(DataUpdateCoordinator[int | None]):
|
|||
self._last_message_id: str | None = None
|
||||
self.custom_event_template = None
|
||||
self._diagnostics_data: dict[str, Any] = {}
|
||||
self._event_data_keys: list[str] = entry.data.get(
|
||||
CONF_EVENT_MESSAGE_DATA, MESSAGE_DATA_OPTIONS
|
||||
)
|
||||
self._max_event_size: int = entry.data.get(
|
||||
CONF_MAX_MESSAGE_SIZE, DEFAULT_MAX_MESSAGE_SIZE
|
||||
)
|
||||
_custom_event_template = entry.data.get(CONF_CUSTOM_EVENT_DATA_TEMPLATE)
|
||||
if _custom_event_template is not None:
|
||||
self.custom_event_template = Template(_custom_event_template, hass=hass)
|
||||
|
@ -261,12 +269,11 @@ class ImapDataUpdateCoordinator(DataUpdateCoordinator[int | None]):
|
|||
"folder": self.config_entry.data[CONF_FOLDER],
|
||||
"initial": initial,
|
||||
"date": message.date,
|
||||
"text": message.text,
|
||||
"sender": message.sender,
|
||||
"subject": message.subject,
|
||||
"headers": message.headers,
|
||||
"uid": last_message_uid,
|
||||
}
|
||||
data.update({key: getattr(message, key) for key in self._event_data_keys})
|
||||
if self.custom_event_template is not None:
|
||||
try:
|
||||
data["custom"] = self.custom_event_template.async_render(
|
||||
|
@ -289,11 +296,8 @@ class ImapDataUpdateCoordinator(DataUpdateCoordinator[int | None]):
|
|||
last_message_uid,
|
||||
err,
|
||||
)
|
||||
data["text"] = message.text[
|
||||
: self.config_entry.data.get(
|
||||
CONF_MAX_MESSAGE_SIZE, DEFAULT_MAX_MESSAGE_SIZE
|
||||
)
|
||||
]
|
||||
if "text" in data:
|
||||
data["text"] = message.text[: self._max_event_size]
|
||||
self._update_diagnostics(data)
|
||||
if (size := len(json_bytes(data))) > MAX_EVENT_DATA_BYTES:
|
||||
_LOGGER.warning(
|
||||
|
|
|
@ -72,7 +72,8 @@
|
|||
"search": "[%key:component::imap::config::step::user::data::search%]",
|
||||
"custom_event_data_template": "Template to create custom event data",
|
||||
"max_message_size": "Max message size (2048 < size < 30000)",
|
||||
"enable_push": "Enable Push-IMAP if the server supports it. Turn off if Push-IMAP updates are unreliable."
|
||||
"enable_push": "Enable Push-IMAP if the server supports it. Turn off if Push-IMAP updates are unreliable.",
|
||||
"event_message_data": "Message data to be included in the `imap_content` event data:"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -92,6 +93,12 @@
|
|||
"modern": "Modern ciphers",
|
||||
"intermediate": "Intermediate ciphers"
|
||||
}
|
||||
},
|
||||
"event_message_data": {
|
||||
"options": {
|
||||
"text": "Body text",
|
||||
"headers": "Message headers"
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
|
|
|
@ -29,11 +29,13 @@ MOCK_CONFIG = {
|
|||
"charset": "utf-8",
|
||||
"folder": "INBOX",
|
||||
"search": "UnSeen UnDeleted",
|
||||
"event_message_data": ["text", "headers"],
|
||||
}
|
||||
|
||||
MOCK_OPTIONS = {
|
||||
"folder": "INBOX",
|
||||
"search": "UnSeen UnDeleted",
|
||||
"event_message_data": ["text", "headers"],
|
||||
}
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||
|
@ -504,6 +506,38 @@ async def test_config_flow_with_cipherlist_and_ssl_verify(
|
|||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("event_message_data", [[], ["headers"], ["text", "headers"]])
|
||||
async def test_config_flow_with_event_message_data(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock, event_message_data: list
|
||||
) -> None:
|
||||
"""Test with different message data."""
|
||||
config = MOCK_CONFIG.copy()
|
||||
config["event_message_data"] = event_message_data
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_USER, "show_advanced_options": False},
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] is None
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.imap.config_flow.connect_to_server"
|
||||
) as mock_client:
|
||||
mock_client.return_value.search.return_value = (
|
||||
"OK",
|
||||
[b""],
|
||||
)
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"], config
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "email@email.com"
|
||||
assert result2["data"] == config
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_config_flow_from_with_advanced_settings(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock
|
||||
) -> None:
|
||||
|
|
|
@ -66,6 +66,10 @@ async def test_entry_diagnostics(
|
|||
"port": 993,
|
||||
"charset": "utf-8",
|
||||
"folder": "INBOX",
|
||||
"event_message_data": [
|
||||
"text",
|
||||
"headers",
|
||||
],
|
||||
"search": "UnSeen UnDeleted",
|
||||
"custom_event_data_template": "{{ 4 * 4 }}",
|
||||
}
|
||||
|
|
|
@ -674,6 +674,41 @@ async def test_message_is_truncated(
|
|||
assert len(event_data["text"]) == 3
|
||||
|
||||
|
||||
@pytest.mark.parametrize("imap_search", [TEST_SEARCH_RESPONSE])
|
||||
@pytest.mark.parametrize(
|
||||
"imap_fetch", [(TEST_FETCH_RESPONSE_TEXT_PLAIN)], ids=["plain"]
|
||||
)
|
||||
@pytest.mark.parametrize("imap_has_capability", [True, False], ids=["push", "poll"])
|
||||
@pytest.mark.parametrize("event_message_data", [[], ["text"], ["text", "headers"]])
|
||||
async def test_message_data(
|
||||
hass: HomeAssistant,
|
||||
mock_imap_protocol: MagicMock,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
event_message_data: list,
|
||||
) -> None:
|
||||
"""Test with different message data."""
|
||||
event_called = async_capture_events(hass, "imap_content")
|
||||
|
||||
config = MOCK_CONFIG.copy()
|
||||
# Mock different message data
|
||||
config["event_message_data"] = event_message_data
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data=config)
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
# Make sure we have had one update (when polling)
|
||||
async_fire_time_changed(hass, utcnow() + timedelta(seconds=5))
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get("sensor.imap_email_email_com")
|
||||
# We should have received one message
|
||||
assert state is not None
|
||||
assert state.state == "1"
|
||||
assert len(event_called) == 1
|
||||
|
||||
event_data = event_called[0].data
|
||||
assert set(event_message_data).issubset(set(event_data))
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("imap_search", "imap_fetch"),
|
||||
[(TEST_SEARCH_RESPONSE, TEST_FETCH_RESPONSE_TEXT_PLAIN)],
|
||||
|
|
Loading…
Add table
Reference in a new issue