mirror of
https://github.com/home-assistant/core.git
synced 2025-12-25 05:26:47 +00:00
Fix bug in group notify entities when title is missing (#157171)
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
@@ -18,10 +18,12 @@ from homeassistant.components.notify import (
|
||||
SERVICE_SEND_MESSAGE,
|
||||
BaseNotificationService,
|
||||
NotifyEntity,
|
||||
NotifyEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
CONF_ACTION,
|
||||
CONF_ENTITIES,
|
||||
CONF_SERVICE,
|
||||
@@ -173,14 +175,23 @@ class NotifyGroup(GroupEntity, NotifyEntity):
|
||||
|
||||
async def async_send_message(self, message: str, title: str | None = None) -> None:
|
||||
"""Send a message to all members of the group."""
|
||||
|
||||
data = {
|
||||
ATTR_MESSAGE: message,
|
||||
ATTR_ENTITY_ID: self._entity_ids,
|
||||
}
|
||||
|
||||
# add title only if supported and provided
|
||||
if (
|
||||
title is not None
|
||||
and self._attr_supported_features & NotifyEntityFeature.TITLE
|
||||
):
|
||||
data[ATTR_TITLE] = title
|
||||
|
||||
await self.hass.services.async_call(
|
||||
NOTIFY_DOMAIN,
|
||||
SERVICE_SEND_MESSAGE,
|
||||
{
|
||||
ATTR_MESSAGE: message,
|
||||
ATTR_TITLE: title,
|
||||
ATTR_ENTITY_ID: self._entity_ids,
|
||||
},
|
||||
data,
|
||||
blocking=True,
|
||||
context=self._context,
|
||||
)
|
||||
@@ -194,3 +205,15 @@ class NotifyGroup(GroupEntity, NotifyEntity):
|
||||
for entity_id in self._entity_ids
|
||||
if (state := self.hass.states.get(entity_id)) is not None
|
||||
)
|
||||
|
||||
# Support title if all members support it
|
||||
self._attr_supported_features |= NotifyEntityFeature.TITLE
|
||||
for entity_id in self._entity_ids:
|
||||
state = self.hass.states.get(entity_id)
|
||||
if (
|
||||
state is None
|
||||
or not state.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
& NotifyEntityFeature.TITLE
|
||||
):
|
||||
self._attr_supported_features &= ~NotifyEntityFeature.TITLE
|
||||
break
|
||||
|
||||
@@ -16,6 +16,7 @@ from homeassistant.components.notify import (
|
||||
DOMAIN as NOTIFY_DOMAIN,
|
||||
SERVICE_SEND_MESSAGE,
|
||||
NotifyEntity,
|
||||
NotifyEntityFeature,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry, ConfigFlow
|
||||
from homeassistant.const import (
|
||||
@@ -298,9 +299,11 @@ def config_flow_fixture(hass: HomeAssistant) -> Generator[None]:
|
||||
class MockNotifyEntity(MockEntity, NotifyEntity):
|
||||
"""Mock Email notifier entity to use in tests."""
|
||||
|
||||
def __init__(self, **values: Any) -> None:
|
||||
def __init__(self, *, is_title_supported: bool, **values: Any) -> None:
|
||||
"""Initialize the mock entity."""
|
||||
super().__init__(**values)
|
||||
if is_title_supported:
|
||||
self._attr_supported_features = NotifyEntityFeature.TITLE
|
||||
self.send_message_mock_calls = MagicMock()
|
||||
|
||||
async def async_send_message(self, message: str, title: str | None = None) -> None:
|
||||
@@ -330,11 +333,21 @@ async def help_async_unload_entry(
|
||||
@pytest.fixture
|
||||
async def mock_notifiers(
|
||||
hass: HomeAssistant, config_flow_fixture: None
|
||||
) -> list[NotifyEntity]:
|
||||
) -> list[MockNotifyEntity]:
|
||||
"""Set up the notify entities."""
|
||||
entity = MockNotifyEntity(name="test", entity_id="notify.test")
|
||||
entity2 = MockNotifyEntity(name="test2", entity_id="notify.test2")
|
||||
entities = [entity, entity2]
|
||||
entity_title_1 = MockNotifyEntity(
|
||||
is_title_supported=True, name="has_title_1", entity_id="notify.has_title_1"
|
||||
)
|
||||
entity_title_2 = MockNotifyEntity(
|
||||
is_title_supported=True, name="has_title_2", entity_id="notify.has_title_2"
|
||||
)
|
||||
entity_no_title_1 = MockNotifyEntity(
|
||||
is_title_supported=False, name="no_title_1", entity_id="notify.no_title_1"
|
||||
)
|
||||
entity_no_title_2 = MockNotifyEntity(
|
||||
is_title_supported=False, name="no_title_2", entity_id="notify.no_title_2"
|
||||
)
|
||||
entities = [entity_title_1, entity_title_2, entity_no_title_1, entity_no_title_2]
|
||||
test_entry = MockConfigEntry(domain="test")
|
||||
test_entry.add_to_hass(hass)
|
||||
mock_integration(
|
||||
@@ -352,19 +365,23 @@ async def mock_notifiers(
|
||||
|
||||
|
||||
async def test_notify_entity_group(
|
||||
hass: HomeAssistant, mock_notifiers: list[NotifyEntity]
|
||||
hass: HomeAssistant, mock_notifiers: list[MockNotifyEntity]
|
||||
) -> None:
|
||||
"""Test sending a message to a notify group."""
|
||||
entity, entity2 = mock_notifiers
|
||||
assert entity.send_message_mock_calls.call_count == 0
|
||||
assert entity2.send_message_mock_calls.call_count == 0
|
||||
entity_title_1, entity_title_2, entity_no_title_1, entity_no_title_2 = (
|
||||
mock_notifiers
|
||||
)
|
||||
for mock_notifier in mock_notifiers:
|
||||
assert mock_notifier.send_message_mock_calls.call_count == 0
|
||||
|
||||
# test group containing 1 member with title supported
|
||||
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
options={
|
||||
"group_type": "notify",
|
||||
"name": "Test Group",
|
||||
"entities": ["notify.test", "notify.test2"],
|
||||
"entities": ["notify.has_title_1"],
|
||||
"hide_members": True,
|
||||
},
|
||||
title="Test Group",
|
||||
@@ -384,14 +401,144 @@ async def test_notify_entity_group(
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert entity.send_message_mock_calls.call_count == 1
|
||||
assert entity.send_message_mock_calls.call_args == call(
|
||||
assert entity_title_1.send_message_mock_calls.call_count == 1
|
||||
assert entity_title_1.send_message_mock_calls.call_args == call(
|
||||
"Hello", title="Test notification"
|
||||
)
|
||||
assert entity2.send_message_mock_calls.call_count == 1
|
||||
assert entity2.send_message_mock_calls.call_args == call(
|
||||
|
||||
for mock_notifier in mock_notifiers:
|
||||
mock_notifier.send_message_mock_calls.reset_mock()
|
||||
|
||||
# test group containing 1 member with title supported but no title provided
|
||||
|
||||
await hass.services.async_call(
|
||||
NOTIFY_DOMAIN,
|
||||
SERVICE_SEND_MESSAGE,
|
||||
{
|
||||
ATTR_MESSAGE: "Hello",
|
||||
ATTR_ENTITY_ID: "notify.test_group",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert entity_title_1.send_message_mock_calls.call_count == 1
|
||||
assert entity_title_1.send_message_mock_calls.call_args == call("Hello", title=None)
|
||||
|
||||
for mock_notifier in mock_notifiers:
|
||||
mock_notifier.send_message_mock_calls.reset_mock()
|
||||
|
||||
# test group containing 2 members with title supported
|
||||
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
options={
|
||||
"group_type": "notify",
|
||||
"name": "Test Group 2",
|
||||
"entities": ["notify.has_title_1", "notify.has_title_2"],
|
||||
"hide_members": True,
|
||||
},
|
||||
title="Test Group 2",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
NOTIFY_DOMAIN,
|
||||
SERVICE_SEND_MESSAGE,
|
||||
{
|
||||
ATTR_MESSAGE: "Hello",
|
||||
ATTR_TITLE: "Test notification",
|
||||
ATTR_ENTITY_ID: "notify.test_group_2",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert entity_title_1.send_message_mock_calls.call_count == 1
|
||||
assert entity_title_1.send_message_mock_calls.call_args == call(
|
||||
"Hello", title="Test notification"
|
||||
)
|
||||
assert entity_title_2.send_message_mock_calls.call_count == 1
|
||||
assert entity_title_2.send_message_mock_calls.call_args == call(
|
||||
"Hello", title="Test notification"
|
||||
)
|
||||
|
||||
for mock_notifier in mock_notifiers:
|
||||
mock_notifier.send_message_mock_calls.reset_mock()
|
||||
|
||||
# test group containing 2 members: 1 title supported and 1 not supported
|
||||
# title is not supported since not all members support it
|
||||
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
options={
|
||||
"group_type": "notify",
|
||||
"name": "Test Group",
|
||||
"entities": ["notify.has_title_1", "notify.no_title_1"],
|
||||
"hide_members": True,
|
||||
},
|
||||
title="Test Group 3",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
NOTIFY_DOMAIN,
|
||||
SERVICE_SEND_MESSAGE,
|
||||
{
|
||||
ATTR_MESSAGE: "Hello",
|
||||
ATTR_TITLE: "Test notification",
|
||||
ATTR_ENTITY_ID: "notify.test_group_3",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert entity_title_1.send_message_mock_calls.call_count == 1
|
||||
assert entity_title_1.send_message_mock_calls.call_args == call("Hello", title=None)
|
||||
assert entity_no_title_1.send_message_mock_calls.call_count == 1
|
||||
assert entity_no_title_1.send_message_mock_calls.call_args == call(
|
||||
"Hello", title=None
|
||||
)
|
||||
|
||||
for mock_notifier in mock_notifiers:
|
||||
mock_notifier.send_message_mock_calls.reset_mock()
|
||||
|
||||
# test group containing 2 members: both not supporting title
|
||||
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
options={
|
||||
"group_type": "notify",
|
||||
"name": "Test Group",
|
||||
"entities": ["notify.no_title_1", "notify.no_title_2"],
|
||||
"hide_members": True,
|
||||
},
|
||||
title="Test Group 4",
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
NOTIFY_DOMAIN,
|
||||
SERVICE_SEND_MESSAGE,
|
||||
{
|
||||
ATTR_MESSAGE: "Hello",
|
||||
ATTR_TITLE: "Test notification",
|
||||
ATTR_ENTITY_ID: "notify.test_group_4",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert entity_no_title_1.send_message_mock_calls.call_count == 1
|
||||
assert entity_no_title_1.send_message_mock_calls.call_args == call(
|
||||
"Hello", title=None
|
||||
)
|
||||
assert entity_no_title_2.send_message_mock_calls.call_count == 1
|
||||
assert entity_no_title_2.send_message_mock_calls.call_args == call(
|
||||
"Hello", title=None
|
||||
)
|
||||
|
||||
|
||||
async def test_state_reporting(hass: HomeAssistant) -> None:
|
||||
|
||||
Reference in New Issue
Block a user