Add notify entity component (#110950)
* Add notify entity component * Device classes, restore state, icons * Add icons file * Add tests for kitchen_sink * Remove notify from no_entity_platforms in hassfest icons, translation link * ruff * Remove `data` feature * Only message support * Complete initial device classes * mypy pylint * Remove device_class implementation * format * Follow up comments * Remove _attr_supported_features * Use setup_test_component_platform * User helper at other places * last comment * Add entry unload test and non async test * Avoid default mutable object in constructor
This commit is contained in:
parent
df5d818c08
commit
10076e6523
11 changed files with 493 additions and 25 deletions
|
@ -32,6 +32,7 @@ COMPONENTS_WITH_DEMO_PLATFORM = [
|
|||
Platform.IMAGE,
|
||||
Platform.LAWN_MOWER,
|
||||
Platform.LOCK,
|
||||
Platform.NOTIFY,
|
||||
Platform.SENSOR,
|
||||
Platform.SWITCH,
|
||||
Platform.WEATHER,
|
||||
|
@ -70,7 +71,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
|||
return True
|
||||
|
||||
|
||||
def _create_issues(hass):
|
||||
def _create_issues(hass: HomeAssistant) -> None:
|
||||
"""Create some issue registry issues."""
|
||||
async_create_issue(
|
||||
hass,
|
||||
|
|
54
homeassistant/components/kitchen_sink/notify.py
Normal file
54
homeassistant/components/kitchen_sink/notify.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
"""Demo platform that offers a fake notify entity."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components import persistent_notification
|
||||
from homeassistant.components.notify import NotifyEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the demo notify entity platform."""
|
||||
async_add_entities(
|
||||
[
|
||||
DemoNotify(
|
||||
unique_id="just_notify_me",
|
||||
device_name="MyBox",
|
||||
entity_name="Personal notifier",
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class DemoNotify(NotifyEntity):
|
||||
"""Representation of a demo notify entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_should_poll = False
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
unique_id: str,
|
||||
device_name: str,
|
||||
entity_name: str | None,
|
||||
) -> None:
|
||||
"""Initialize the Demo button entity."""
|
||||
self._attr_unique_id = unique_id
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, unique_id)},
|
||||
name=device_name,
|
||||
)
|
||||
self._attr_name = entity_name
|
||||
|
||||
async def async_send_message(self, message: str) -> None:
|
||||
"""Send out a persistent notification."""
|
||||
persistent_notification.async_create(self.hass, message, "Demo notification")
|
|
@ -2,24 +2,36 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from functools import cached_property, partial
|
||||
import logging
|
||||
from typing import Any, final, override
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components.persistent_notification as pn
|
||||
from homeassistant.const import CONF_NAME, CONF_PLATFORM
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_NAME, CONF_PLATFORM, STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import EntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.restore_state import RestoreEntity
|
||||
from homeassistant.helpers.template import Template
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import ( # noqa: F401
|
||||
ATTR_DATA,
|
||||
ATTR_MESSAGE,
|
||||
ATTR_RECIPIENTS,
|
||||
ATTR_TARGET,
|
||||
ATTR_TITLE,
|
||||
DOMAIN,
|
||||
NOTIFY_SERVICE_SCHEMA,
|
||||
SERVICE_NOTIFY,
|
||||
SERVICE_PERSISTENT_NOTIFICATION,
|
||||
SERVICE_SEND_MESSAGE,
|
||||
)
|
||||
from .legacy import ( # noqa: F401
|
||||
BaseNotificationService,
|
||||
|
@ -29,9 +41,17 @@ from .legacy import ( # noqa: F401
|
|||
check_templates_warn,
|
||||
)
|
||||
|
||||
# mypy: disallow-any-generics
|
||||
|
||||
# Platform specific data
|
||||
ATTR_TITLE_DEFAULT = "Home Assistant"
|
||||
|
||||
ENTITY_ID_FORMAT = DOMAIN + ".{}"
|
||||
|
||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
PLATFORM_SCHEMA = vol.Schema(
|
||||
{vol.Required(CONF_PLATFORM): cv.string, vol.Optional(CONF_NAME): cv.string},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
|
@ -50,6 +70,13 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
# legacy platforms to finish setting up.
|
||||
hass.async_create_task(setup, eager_start=True)
|
||||
|
||||
component = hass.data[DOMAIN] = EntityComponent[NotifyEntity](_LOGGER, DOMAIN, hass)
|
||||
component.async_register_entity_service(
|
||||
SERVICE_SEND_MESSAGE,
|
||||
{vol.Required(ATTR_MESSAGE): cv.string},
|
||||
"_async_send_message",
|
||||
)
|
||||
|
||||
async def persistent_notification(service: ServiceCall) -> None:
|
||||
"""Send notification via the built-in persistent_notify integration."""
|
||||
message: Template = service.data[ATTR_MESSAGE]
|
||||
|
@ -79,3 +106,66 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class NotifyEntityDescription(EntityDescription, frozen_or_thawed=True):
|
||||
"""A class that describes button entities."""
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up a config entry."""
|
||||
component: EntityComponent[NotifyEntity] = hass.data[DOMAIN]
|
||||
return await component.async_setup_entry(entry)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
component: EntityComponent[NotifyEntity] = hass.data[DOMAIN]
|
||||
return await component.async_unload_entry(entry)
|
||||
|
||||
|
||||
class NotifyEntity(RestoreEntity):
|
||||
"""Representation of a notify entity."""
|
||||
|
||||
entity_description: NotifyEntityDescription
|
||||
_attr_should_poll = False
|
||||
_attr_device_class: None
|
||||
_attr_state: None = None
|
||||
__last_notified_isoformat: str | None = None
|
||||
|
||||
@cached_property
|
||||
@final
|
||||
@override
|
||||
def state(self) -> str | None:
|
||||
"""Return the entity state."""
|
||||
return self.__last_notified_isoformat
|
||||
|
||||
def __set_state(self, state: str | None) -> None:
|
||||
"""Invalidate the cache of the cached property."""
|
||||
self.__dict__.pop("state", None)
|
||||
self.__last_notified_isoformat = state
|
||||
|
||||
async def async_internal_added_to_hass(self) -> None:
|
||||
"""Call when the notify entity is added to hass."""
|
||||
await super().async_internal_added_to_hass()
|
||||
state = await self.async_get_last_state()
|
||||
if state is not None and state.state not in (STATE_UNAVAILABLE, None):
|
||||
self.__set_state(state.state)
|
||||
|
||||
@final
|
||||
async def _async_send_message(self, **kwargs: Any) -> None:
|
||||
"""Send a notification message (from e.g., service call).
|
||||
|
||||
Should not be overridden, handle setting last notification timestamp.
|
||||
"""
|
||||
self.__set_state(dt_util.utcnow().isoformat())
|
||||
self.async_write_ha_state()
|
||||
await self.async_send_message(**kwargs)
|
||||
|
||||
def send_message(self, message: str) -> None:
|
||||
"""Send a message."""
|
||||
raise NotImplementedError
|
||||
|
||||
async def async_send_message(self, message: str) -> None:
|
||||
"""Send a message."""
|
||||
await self.hass.async_add_executor_job(partial(self.send_message, message))
|
||||
|
|
|
@ -11,9 +11,12 @@ ATTR_DATA = "data"
|
|||
# Text to notify user of
|
||||
ATTR_MESSAGE = "message"
|
||||
|
||||
# Target of the notification (user, device, etc)
|
||||
# Target of the (legacy) notification (user, device, etc)
|
||||
ATTR_TARGET = "target"
|
||||
|
||||
# Recipients for a notification
|
||||
ATTR_RECIPIENTS = "recipients"
|
||||
|
||||
# Title of notification
|
||||
ATTR_TITLE = "title"
|
||||
|
||||
|
@ -22,6 +25,7 @@ DOMAIN = "notify"
|
|||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
SERVICE_NOTIFY = "notify"
|
||||
SERVICE_SEND_MESSAGE = "send_message"
|
||||
SERVICE_PERSISTENT_NOTIFICATION = "persistent_notification"
|
||||
|
||||
NOTIFY_SERVICE_SCHEMA = vol.Schema(
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
{
|
||||
"entity_component": {
|
||||
"_": {
|
||||
"default": "mdi:message"
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"notify": "mdi:bell-ring",
|
||||
"persistent_notification": "mdi:bell-badge"
|
||||
"persistent_notification": "mdi:bell-badge",
|
||||
"send_message": "mdi:message-arrow-right"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,16 @@ notify:
|
|||
selector:
|
||||
object:
|
||||
|
||||
send_message:
|
||||
target:
|
||||
entity:
|
||||
domain: notify
|
||||
fields:
|
||||
message:
|
||||
required: true
|
||||
selector:
|
||||
text:
|
||||
|
||||
persistent_notification:
|
||||
fields:
|
||||
message:
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
{
|
||||
"title": "Notifications",
|
||||
"entity_component": {
|
||||
"_": {
|
||||
"name": "[%key:component::notify::title%]"
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"notify": {
|
||||
"name": "Send a notification",
|
||||
|
@ -23,6 +28,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"send_message": {
|
||||
"name": "Send a notification message",
|
||||
"description": "Sends a notification message.",
|
||||
"fields": {
|
||||
"message": {
|
||||
"name": "Message",
|
||||
"description": "Your notification message."
|
||||
}
|
||||
}
|
||||
},
|
||||
"persistent_notification": {
|
||||
"name": "Send a persistent notification",
|
||||
"description": "Sends a notification that is visible in the **Notifications** panel.",
|
||||
|
|
|
@ -93,6 +93,7 @@ def _base_components() -> dict[str, ModuleType]:
|
|||
light,
|
||||
lock,
|
||||
media_player,
|
||||
notify,
|
||||
remote,
|
||||
siren,
|
||||
todo,
|
||||
|
@ -112,6 +113,7 @@ def _base_components() -> dict[str, ModuleType]:
|
|||
"light": light,
|
||||
"lock": lock,
|
||||
"media_player": media_player,
|
||||
"notify": notify,
|
||||
"remote": remote,
|
||||
"siren": siren,
|
||||
"todo": todo,
|
||||
|
|
66
tests/components/kitchen_sink/test_notify.py
Normal file
66
tests/components/kitchen_sink/test_notify.py
Normal file
|
@ -0,0 +1,66 @@
|
|||
"""The tests for the demo button component."""
|
||||
|
||||
from collections.abc import AsyncGenerator
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.kitchen_sink import DOMAIN
|
||||
from homeassistant.components.notify import (
|
||||
DOMAIN as NOTIFY_DOMAIN,
|
||||
SERVICE_SEND_MESSAGE,
|
||||
)
|
||||
from homeassistant.components.notify.const import ATTR_MESSAGE
|
||||
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
ENTITY_DIRECT_MESSAGE = "notify.mybox_personal_notifier"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def notify_only() -> AsyncGenerator[None, None]:
|
||||
"""Enable only the button platform."""
|
||||
with patch(
|
||||
"homeassistant.components.kitchen_sink.COMPONENTS_WITH_DEMO_PLATFORM",
|
||||
[Platform.NOTIFY],
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def setup_comp(hass: HomeAssistant, notify_only: None):
|
||||
"""Set up demo component."""
|
||||
assert await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
def test_setup_params(hass: HomeAssistant) -> None:
|
||||
"""Test the initial parameters."""
|
||||
state = hass.states.get(ENTITY_DIRECT_MESSAGE)
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_send_message(
|
||||
hass: HomeAssistant, freezer: FrozenDateTimeFactory
|
||||
) -> None:
|
||||
"""Test pressing the button."""
|
||||
state = hass.states.get(ENTITY_DIRECT_MESSAGE)
|
||||
assert state
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
now = dt_util.parse_datetime("2021-01-09 12:00:00+00:00")
|
||||
freezer.move_to(now)
|
||||
await hass.services.async_call(
|
||||
NOTIFY_DOMAIN,
|
||||
SERVICE_SEND_MESSAGE,
|
||||
{ATTR_ENTITY_ID: ENTITY_DIRECT_MESSAGE, ATTR_MESSAGE: "You have an update!"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
state = hass.states.get(ENTITY_DIRECT_MESSAGE)
|
||||
assert state
|
||||
assert state.state == now.isoformat()
|
23
tests/components/notify/conftest.py
Normal file
23
tests/components/notify/conftest.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
"""Fixtures for Notify platform tests."""
|
||||
|
||||
from collections.abc import Generator
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import mock_config_flow, mock_platform
|
||||
|
||||
|
||||
class MockFlow(ConfigFlow):
|
||||
"""Test flow."""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config_flow_fixture(hass: HomeAssistant) -> Generator[None, None, None]:
|
||||
"""Mock config flow."""
|
||||
mock_platform(hass, "test.config_flow")
|
||||
|
||||
with mock_config_flow("test", MockFlow):
|
||||
yield
|
|
@ -1,28 +1,216 @@
|
|||
"""The tests for notify services that change targets."""
|
||||
|
||||
import asyncio
|
||||
import copy
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, patch
|
||||
from typing import Any
|
||||
from unittest.mock import MagicMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from homeassistant import config as hass_config
|
||||
from homeassistant.components import notify
|
||||
from homeassistant.const import SERVICE_RELOAD, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.components.notify import (
|
||||
DOMAIN,
|
||||
SERVICE_SEND_MESSAGE,
|
||||
NotifyEntity,
|
||||
NotifyEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
SERVICE_RELOAD,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, State
|
||||
from homeassistant.helpers.discovery import async_load_platform
|
||||
from homeassistant.helpers.reload import async_setup_reload_service
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockPlatform, async_get_persistent_notifications, mock_platform
|
||||
from tests.common import (
|
||||
MockConfigEntry,
|
||||
MockEntity,
|
||||
MockModule,
|
||||
MockPlatform,
|
||||
async_get_persistent_notifications,
|
||||
mock_integration,
|
||||
mock_platform,
|
||||
mock_restore_cache,
|
||||
setup_test_component_platform,
|
||||
)
|
||||
|
||||
TEST_KWARGS = {"message": "Test message"}
|
||||
|
||||
|
||||
class MockNotifyEntity(MockEntity, NotifyEntity):
|
||||
"""Mock Email notitier entity to use in tests."""
|
||||
|
||||
send_message_mock_calls = MagicMock()
|
||||
|
||||
async def async_send_message(self, message: str) -> None:
|
||||
"""Send a notification message."""
|
||||
self.send_message_mock_calls(message=message)
|
||||
|
||||
|
||||
class MockNotifyEntityNonAsync(MockEntity, NotifyEntity):
|
||||
"""Mock Email notitier entity to use in tests."""
|
||||
|
||||
send_message_mock_calls = MagicMock()
|
||||
|
||||
def send_message(self, message: str) -> None:
|
||||
"""Send a notification message."""
|
||||
self.send_message_mock_calls(message=message)
|
||||
|
||||
|
||||
async def help_async_setup_entry_init(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> bool:
|
||||
"""Set up test config entry."""
|
||||
await hass.config_entries.async_forward_entry_setup(config_entry, DOMAIN)
|
||||
return True
|
||||
|
||||
|
||||
async def help_async_unload_entry(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> bool:
|
||||
"""Unload test config emntry."""
|
||||
return await hass.config_entries.async_unload_platforms(
|
||||
config_entry, [Platform.NOTIFY]
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"entity",
|
||||
[
|
||||
MockNotifyEntityNonAsync(name="test", entity_id="notify.test"),
|
||||
MockNotifyEntity(name="test", entity_id="notify.test"),
|
||||
],
|
||||
ids=["non_async", "async"],
|
||||
)
|
||||
async def test_send_message_service(
|
||||
hass: HomeAssistant, config_flow_fixture: None, entity: NotifyEntity
|
||||
) -> None:
|
||||
"""Test send_message service."""
|
||||
|
||||
config_entry = MockConfigEntry(domain="test")
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
mock_integration(
|
||||
hass,
|
||||
MockModule(
|
||||
"test",
|
||||
async_setup_entry=help_async_setup_entry_init,
|
||||
async_unload_entry=help_async_unload_entry,
|
||||
),
|
||||
)
|
||||
setup_test_component_platform(hass, DOMAIN, [entity], from_config_entry=True)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
|
||||
state = hass.states.get("notify.test")
|
||||
assert state.state is STATE_UNKNOWN
|
||||
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SEND_MESSAGE,
|
||||
copy.deepcopy(TEST_KWARGS) | {"entity_id": "notify.test"},
|
||||
blocking=True,
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity.send_message_mock_calls.assert_called_once()
|
||||
|
||||
# Test unloading the entry succeeds
|
||||
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("state", "init_state"),
|
||||
[
|
||||
("2021-01-01T23:59:59+00:00", "2021-01-01T23:59:59+00:00"),
|
||||
(STATE_UNAVAILABLE, STATE_UNKNOWN),
|
||||
],
|
||||
)
|
||||
async def test_restore_state(
|
||||
hass: HomeAssistant, config_flow_fixture: None, state: str, init_state: str
|
||||
) -> None:
|
||||
"""Test we restore state integration."""
|
||||
mock_restore_cache(hass, (State("notify.test", state),))
|
||||
|
||||
mock_integration(
|
||||
hass,
|
||||
MockModule(
|
||||
"test",
|
||||
async_setup_entry=help_async_setup_entry_init,
|
||||
),
|
||||
)
|
||||
|
||||
entity = MockNotifyEntity(name="test", entity_id="notify.test")
|
||||
setup_test_component_platform(hass, DOMAIN, [entity], from_config_entry=True)
|
||||
|
||||
config_entry = MockConfigEntry(domain="test")
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
|
||||
state = hass.states.get("notify.test")
|
||||
assert state is not None
|
||||
assert state.state is init_state
|
||||
|
||||
|
||||
async def test_name(hass: HomeAssistant, config_flow_fixture: None) -> None:
|
||||
"""Test notify name."""
|
||||
|
||||
mock_platform(hass, "test.config_flow")
|
||||
mock_integration(
|
||||
hass,
|
||||
MockModule(
|
||||
"test",
|
||||
async_setup_entry=help_async_setup_entry_init,
|
||||
),
|
||||
)
|
||||
|
||||
# Unnamed notify entity -> no name
|
||||
entity1 = NotifyEntity()
|
||||
entity1.entity_id = "notify.test1"
|
||||
|
||||
# Unnamed notify entity and has_entity_name True -> unnamed
|
||||
entity2 = NotifyEntity()
|
||||
entity2.entity_id = "notify.test3"
|
||||
entity2._attr_has_entity_name = True
|
||||
|
||||
# Named notify entity and has_entity_name True -> named
|
||||
entity3 = NotifyEntity()
|
||||
entity3.entity_id = "notify.test4"
|
||||
entity3.entity_description = NotifyEntityDescription("test", has_entity_name=True)
|
||||
|
||||
setup_test_component_platform(
|
||||
hass, DOMAIN, [entity1, entity2, entity3], from_config_entry=True
|
||||
)
|
||||
|
||||
config_entry = MockConfigEntry(domain="test")
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity1.entity_id)
|
||||
assert state
|
||||
assert state.attributes == {}
|
||||
|
||||
state = hass.states.get(entity2.entity_id)
|
||||
assert state
|
||||
assert state.attributes == {}
|
||||
|
||||
state = hass.states.get(entity3.entity_id)
|
||||
assert state
|
||||
assert state.attributes == {}
|
||||
|
||||
|
||||
class MockNotifyPlatform(MockPlatform):
|
||||
"""Help to set up test notify service."""
|
||||
"""Help to set up a legacy test notify service."""
|
||||
|
||||
def __init__(self, async_get_service=None, get_service=None):
|
||||
"""Return the notify service."""
|
||||
def __init__(self, async_get_service: Any = None, get_service: Any = None) -> None:
|
||||
"""Return a legacy notify service."""
|
||||
super().__init__()
|
||||
if get_service:
|
||||
self.get_service = get_service
|
||||
|
@ -31,9 +219,13 @@ class MockNotifyPlatform(MockPlatform):
|
|||
|
||||
|
||||
def mock_notify_platform(
|
||||
hass, tmp_path, integration="notify", async_get_service=None, get_service=None
|
||||
hass: HomeAssistant,
|
||||
tmp_path: Path,
|
||||
integration: str = "notify",
|
||||
async_get_service: Any = None,
|
||||
get_service: Any = None,
|
||||
):
|
||||
"""Specialize the mock platform for notify."""
|
||||
"""Specialize the mock platform for legacy notify service."""
|
||||
loaded_platform = MockNotifyPlatform(async_get_service, get_service)
|
||||
mock_platform(hass, f"{integration}.notify", loaded_platform)
|
||||
|
||||
|
@ -41,7 +233,7 @@ def mock_notify_platform(
|
|||
|
||||
|
||||
async def test_same_targets(hass: HomeAssistant) -> None:
|
||||
"""Test not changing the targets in a notify service."""
|
||||
"""Test not changing the targets in a legacy notify service."""
|
||||
test = NotificationService(hass)
|
||||
await test.async_setup(hass, "notify", "test")
|
||||
await test.async_register_services()
|
||||
|
@ -56,7 +248,7 @@ async def test_same_targets(hass: HomeAssistant) -> None:
|
|||
|
||||
|
||||
async def test_change_targets(hass: HomeAssistant) -> None:
|
||||
"""Test changing the targets in a notify service."""
|
||||
"""Test changing the targets in a legacy notify service."""
|
||||
test = NotificationService(hass)
|
||||
await test.async_setup(hass, "notify", "test")
|
||||
await test.async_register_services()
|
||||
|
@ -73,7 +265,7 @@ async def test_change_targets(hass: HomeAssistant) -> None:
|
|||
|
||||
|
||||
async def test_add_targets(hass: HomeAssistant) -> None:
|
||||
"""Test adding the targets in a notify service."""
|
||||
"""Test adding the targets in a legacy notify service."""
|
||||
test = NotificationService(hass)
|
||||
await test.async_setup(hass, "notify", "test")
|
||||
await test.async_register_services()
|
||||
|
@ -90,7 +282,7 @@ async def test_add_targets(hass: HomeAssistant) -> None:
|
|||
|
||||
|
||||
async def test_remove_targets(hass: HomeAssistant) -> None:
|
||||
"""Test removing targets from the targets in a notify service."""
|
||||
"""Test removing targets from the targets in a legacy notify service."""
|
||||
test = NotificationService(hass)
|
||||
await test.async_setup(hass, "notify", "test")
|
||||
await test.async_register_services()
|
||||
|
@ -107,17 +299,22 @@ async def test_remove_targets(hass: HomeAssistant) -> None:
|
|||
|
||||
|
||||
class NotificationService(notify.BaseNotificationService):
|
||||
"""A test class for notification services."""
|
||||
"""A test class for legacy notification services."""
|
||||
|
||||
def __init__(self, hass, target_list={"a": 1, "b": 2}, name="notify"):
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
target_list: dict[str, Any] | None = None,
|
||||
name="notify",
|
||||
) -> None:
|
||||
"""Initialize the service."""
|
||||
|
||||
async def _async_make_reloadable(hass):
|
||||
async def _async_make_reloadable(hass: HomeAssistant) -> None:
|
||||
"""Initialize the reload service."""
|
||||
await async_setup_reload_service(hass, name, [notify.DOMAIN])
|
||||
|
||||
self.hass = hass
|
||||
self.target_list = target_list
|
||||
self.target_list = target_list or {"a": 1, "b": 2}
|
||||
hass.async_create_task(_async_make_reloadable(hass))
|
||||
|
||||
@property
|
||||
|
@ -229,7 +426,7 @@ async def test_platform_setup_with_error(
|
|||
async def test_reload_with_notify_builtin_platform_reload(
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, tmp_path: Path
|
||||
) -> None:
|
||||
"""Test reload using the notify platform reload method."""
|
||||
"""Test reload using the legacy notify platform reload method."""
|
||||
|
||||
async def async_get_service(hass, config, discovery_info=None):
|
||||
"""Get notify service for mocked platform."""
|
||||
|
@ -271,7 +468,7 @@ async def test_setup_platform_and_reload(
|
|||
return NotificationService(hass, targetlist, "testnotify")
|
||||
|
||||
async def async_get_service2(hass, config, discovery_info=None):
|
||||
"""Get notify service for mocked platform."""
|
||||
"""Get legacy notify service for mocked platform."""
|
||||
get_service_called(config, discovery_info)
|
||||
targetlist = {"c": 3, "d": 4}
|
||||
return NotificationService(hass, targetlist, "testnotify2")
|
||||
|
@ -351,7 +548,7 @@ async def test_setup_platform_and_reload(
|
|||
async def test_setup_platform_before_notify_setup(
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, tmp_path: Path
|
||||
) -> None:
|
||||
"""Test trying to setup a platform before notify is setup."""
|
||||
"""Test trying to setup a platform before legacy notify service is setup."""
|
||||
get_service_called = Mock()
|
||||
|
||||
async def async_get_service(hass, config, discovery_info=None):
|
||||
|
@ -401,7 +598,7 @@ async def test_setup_platform_before_notify_setup(
|
|||
async def test_setup_platform_after_notify_setup(
|
||||
hass: HomeAssistant, caplog: pytest.LogCaptureFixture, tmp_path: Path
|
||||
) -> None:
|
||||
"""Test trying to setup a platform after notify is setup."""
|
||||
"""Test trying to setup a platform after legacy notify service is set up."""
|
||||
get_service_called = Mock()
|
||||
|
||||
async def async_get_service(hass, config, discovery_info=None):
|
||||
|
|
Loading…
Add table
Reference in a new issue