Skip to content

Commit

Permalink
Deprecate calendar.list_events (#102481)
Browse files Browse the repository at this point in the history
* deprecate calendar.list_events

* rename events to get_events

* raise issue for use of deprecated service

* Make issue fixable

* Add fix_flow

* Add service translation/yaml
  • Loading branch information
eifinger committed Nov 19, 2023
1 parent d3b4dd2 commit 51385dc
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 28 deletions.
43 changes: 39 additions & 4 deletions homeassistant/components/calendar/__init__.py
Expand Up @@ -37,6 +37,7 @@
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.event import async_track_point_in_time
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.template import DATE_STR_FORMAT
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import dt as dt_util
Expand Down Expand Up @@ -261,8 +262,10 @@ def _validate_rrule(value: Any) -> str:
extra=vol.ALLOW_EXTRA,
)

SERVICE_LIST_EVENTS: Final = "list_events"
SERVICE_LIST_EVENTS_SCHEMA: Final = vol.All(
LEGACY_SERVICE_LIST_EVENTS: Final = "list_events"
"""Deprecated: please use SERVICE_LIST_EVENTS."""
SERVICE_GET_EVENTS: Final = "get_events"
SERVICE_GET_EVENTS_SCHEMA: Final = vol.All(
cv.has_at_least_one_key(EVENT_END_DATETIME, EVENT_DURATION),
cv.has_at_most_one_key(EVENT_END_DATETIME, EVENT_DURATION),
cv.make_entity_service_schema(
Expand Down Expand Up @@ -301,11 +304,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
required_features=[CalendarEntityFeature.CREATE_EVENT],
)
component.async_register_legacy_entity_service(
SERVICE_LIST_EVENTS,
SERVICE_LIST_EVENTS_SCHEMA,
LEGACY_SERVICE_LIST_EVENTS,
SERVICE_GET_EVENTS_SCHEMA,
async_list_events_service,
supports_response=SupportsResponse.ONLY,
)
component.async_register_entity_service(
SERVICE_GET_EVENTS,
SERVICE_GET_EVENTS_SCHEMA,
async_get_events_service,
supports_response=SupportsResponse.ONLY,
)
await component.async_setup(config)
return True

Expand Down Expand Up @@ -850,6 +859,32 @@ async def async_create_event(entity: CalendarEntity, call: ServiceCall) -> None:

async def async_list_events_service(
calendar: CalendarEntity, service_call: ServiceCall
) -> ServiceResponse:
"""List events on a calendar during a time range.
Deprecated: please use async_get_events_service.
"""
_LOGGER.warning(
"Detected use of service 'calendar.list_events'. "
"This is deprecated and will stop working in Home Assistant 2024.6. "
"Use 'calendar.get_events' instead which supports multiple entities",
)
async_create_issue(
calendar.hass,
DOMAIN,
"deprecated_service_calendar_list_events",
breaks_in_ha_version="2024.6.0",
is_fixable=True,
is_persistent=False,
issue_domain=calendar.platform.platform_name,
severity=IssueSeverity.WARNING,
translation_key="deprecated_service_calendar_list_events",
)
return await async_get_events_service(calendar, service_call)


async def async_get_events_service(
calendar: CalendarEntity, service_call: ServiceCall
) -> ServiceResponse:
"""List events on a calendar during a time range."""
start = service_call.data.get(EVENT_START_DATETIME, dt_util.now())
Expand Down
16 changes: 16 additions & 0 deletions homeassistant/components/calendar/services.yaml
Expand Up @@ -52,3 +52,19 @@ list_events:
duration:
selector:
duration:
get_events:
target:
entity:
domain: calendar
fields:
start_date_time:
example: "2022-03-22 20:00:00"
selector:
datetime:
end_date_time:
example: "2022-03-22 22:00:00"
selector:
datetime:
duration:
selector:
duration:
37 changes: 34 additions & 3 deletions homeassistant/components/calendar/strings.json
Expand Up @@ -72,9 +72,9 @@
}
}
},
"list_events": {
"name": "List event",
"description": "Lists events on a calendar within a time range.",
"get_events": {
"name": "Get event",
"description": "Get events on a calendar within a time range.",
"fields": {
"start_date_time": {
"name": "Start time",
Expand All @@ -89,6 +89,37 @@
"description": "Returns active events from start_date_time until the specified duration."
}
}
},
"list_events": {
"name": "List event",
"description": "Lists events on a calendar within a time range.",
"fields": {
"start_date_time": {
"name": "[%key:component::calendar::services::get_events::fields::start_date_time::name%]",
"description": "[%key:component::calendar::services::get_events::fields::start_date_time::description%]"
},
"end_date_time": {
"name": "[%key:component::calendar::services::get_events::fields::end_date_time::name%]",
"description": "[%key:component::calendar::services::get_events::fields::end_date_time::description%]"
},
"duration": {
"name": "[%key:component::calendar::services::get_events::fields::duration::name%]",
"description": "[%key:component::calendar::services::get_events::fields::duration::description%]"
}
}
}
},
"issues": {
"deprecated_service_calendar_list_events": {
"title": "Detected use of deprecated service `calendar.list_events`",
"fix_flow": {
"step": {
"confirm": {
"title": "[%key:component::calendar::issues::deprecated_service_calendar_list_events::title%]",
"description": "Use `calendar.get_events` instead which supports multiple entities.\n\nPlease replace this service and adjust your automations and scripts and select **submit** to close this issue."
}
}
}
}
}
}
42 changes: 39 additions & 3 deletions tests/components/calendar/snapshots/test_init.ambr
@@ -1,11 +1,34 @@
# serializer version: 1
# name: test_list_events_service_duration[calendar.calendar_1-00:15:00]
# name: test_list_events_service_duration[calendar.calendar_1-00:15:00-get_events]
dict({
'calendar.calendar_1': dict({
'events': list([
]),
}),
})
# ---
# name: test_list_events_service_duration[calendar.calendar_1-00:15:00-list_events]
dict({
'events': list([
]),
})
# ---
# name: test_list_events_service_duration[calendar.calendar_1-01:00:00]
# name: test_list_events_service_duration[calendar.calendar_1-01:00:00-get_events]
dict({
'calendar.calendar_1': dict({
'events': list([
dict({
'description': 'Future Description',
'end': '2023-10-19T08:20:05-07:00',
'location': 'Future Location',
'start': '2023-10-19T07:20:05-07:00',
'summary': 'Future Event',
}),
]),
}),
})
# ---
# name: test_list_events_service_duration[calendar.calendar_1-01:00:00-list_events]
dict({
'events': list([
dict({
Expand All @@ -18,7 +41,20 @@
]),
})
# ---
# name: test_list_events_service_duration[calendar.calendar_2-00:15:00]
# name: test_list_events_service_duration[calendar.calendar_2-00:15:00-get_events]
dict({
'calendar.calendar_2': dict({
'events': list([
dict({
'end': '2023-10-19T07:20:05-07:00',
'start': '2023-10-19T06:20:05-07:00',
'summary': 'Current Event',
}),
]),
}),
})
# ---
# name: test_list_events_service_duration[calendar.calendar_2-00:15:00-list_events]
dict({
'events': list([
dict({
Expand Down
114 changes: 96 additions & 18 deletions tests/components/calendar/test_init.py
Expand Up @@ -12,9 +12,14 @@
import voluptuous as vol

from homeassistant.bootstrap import async_setup_component
from homeassistant.components.calendar import DOMAIN, SERVICE_LIST_EVENTS
from homeassistant.components.calendar import (
DOMAIN,
LEGACY_SERVICE_LIST_EVENTS,
SERVICE_GET_EVENTS,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.issue_registry import IssueRegistry
import homeassistant.util.dt as dt_util

from tests.typing import ClientSessionGenerator, WebSocketGenerator
Expand Down Expand Up @@ -389,6 +394,41 @@ async def test_create_event_service_invalid_params(


@freeze_time("2023-06-22 10:30:00+00:00")
@pytest.mark.parametrize(
("service", "expected"),
[
(
LEGACY_SERVICE_LIST_EVENTS,
{
"events": [
{
"start": "2023-06-22T05:00:00-06:00",
"end": "2023-06-22T06:00:00-06:00",
"summary": "Future Event",
"description": "Future Description",
"location": "Future Location",
}
]
},
),
(
SERVICE_GET_EVENTS,
{
"calendar.calendar_1": {
"events": [
{
"start": "2023-06-22T05:00:00-06:00",
"end": "2023-06-22T06:00:00-06:00",
"summary": "Future Event",
"description": "Future Description",
"location": "Future Location",
}
]
}
},
),
],
)
@pytest.mark.parametrize(
("start_time", "end_time"),
[
Expand All @@ -402,6 +442,8 @@ async def test_list_events_service(
set_time_zone: None,
start_time: str,
end_time: str,
service: str,
expected: dict[str, Any],
) -> None:
"""Test listing events from the service call using exlplicit start and end time.
Expand All @@ -414,28 +456,26 @@ async def test_list_events_service(

response = await hass.services.async_call(
DOMAIN,
SERVICE_LIST_EVENTS,
{
service,
target={"entity_id": ["calendar.calendar_1"]},
service_data={
"entity_id": "calendar.calendar_1",
"start_date_time": start_time,
"end_date_time": end_time,
},
blocking=True,
return_response=True,
)
assert response == {
"events": [
{
"start": "2023-06-22T05:00:00-06:00",
"end": "2023-06-22T06:00:00-06:00",
"summary": "Future Event",
"description": "Future Description",
"location": "Future Location",
}
]
}
assert response == expected


@pytest.mark.parametrize(
("service"),
[
(LEGACY_SERVICE_LIST_EVENTS),
SERVICE_GET_EVENTS,
],
)
@pytest.mark.parametrize(
("entity", "duration"),
[
Expand All @@ -452,6 +492,7 @@ async def test_list_events_service_duration(
hass: HomeAssistant,
entity: str,
duration: str,
service: str,
snapshot: SnapshotAssertion,
) -> None:
"""Test listing events using a time duration."""
Expand All @@ -460,7 +501,7 @@ async def test_list_events_service_duration(

response = await hass.services.async_call(
DOMAIN,
SERVICE_LIST_EVENTS,
service,
{
"entity_id": entity,
"duration": duration,
Expand All @@ -479,7 +520,7 @@ async def test_list_events_positive_duration(hass: HomeAssistant) -> None:
with pytest.raises(vol.Invalid, match="should be positive"):
await hass.services.async_call(
DOMAIN,
SERVICE_LIST_EVENTS,
SERVICE_GET_EVENTS,
{
"entity_id": "calendar.calendar_1",
"duration": "-01:00:00",
Expand All @@ -499,7 +540,7 @@ async def test_list_events_exclusive_fields(hass: HomeAssistant) -> None:
with pytest.raises(vol.Invalid, match="at most one of"):
await hass.services.async_call(
DOMAIN,
SERVICE_LIST_EVENTS,
SERVICE_GET_EVENTS,
{
"entity_id": "calendar.calendar_1",
"end_date_time": end,
Expand All @@ -518,10 +559,47 @@ async def test_list_events_missing_fields(hass: HomeAssistant) -> None:
with pytest.raises(vol.Invalid, match="at least one of"):
await hass.services.async_call(
DOMAIN,
SERVICE_LIST_EVENTS,
SERVICE_GET_EVENTS,
{
"entity_id": "calendar.calendar_1",
},
blocking=True,
return_response=True,
)


async def test_issue_deprecated_service_calendar_list_events(
hass: HomeAssistant,
issue_registry: IssueRegistry,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test the issue is raised on deprecated service weather.get_forecast."""

await async_setup_component(hass, "calendar", {"calendar": {"platform": "demo"}})
await hass.async_block_till_done()

_ = await hass.services.async_call(
DOMAIN,
LEGACY_SERVICE_LIST_EVENTS,
target={"entity_id": ["calendar.calendar_1"]},
service_data={
"entity_id": "calendar.calendar_1",
"duration": "01:00:00",
},
blocking=True,
return_response=True,
)

issue = issue_registry.async_get_issue(
"calendar", "deprecated_service_calendar_list_events"
)
assert issue
assert issue.issue_domain == "demo"
assert issue.issue_id == "deprecated_service_calendar_list_events"
assert issue.translation_key == "deprecated_service_calendar_list_events"

assert (
"Detected use of service 'calendar.list_events'. "
"This is deprecated and will stop working in Home Assistant 2024.6. "
"Use 'calendar.get_events' instead which supports multiple entities"
) in caplog.text

0 comments on commit 51385dc

Please sign in to comment.