Skip to content

Commit 51385dc

Browse files
authored
Deprecate calendar.list_events (home-assistant#102481)
* 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
1 parent d3b4dd2 commit 51385dc

File tree

5 files changed

+224
-28
lines changed

5 files changed

+224
-28
lines changed

homeassistant/components/calendar/__init__.py

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from homeassistant.helpers.entity import Entity
3838
from homeassistant.helpers.entity_component import EntityComponent
3939
from homeassistant.helpers.event import async_track_point_in_time
40+
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
4041
from homeassistant.helpers.template import DATE_STR_FORMAT
4142
from homeassistant.helpers.typing import ConfigType
4243
from homeassistant.util import dt as dt_util
@@ -261,8 +262,10 @@ def _validate_rrule(value: Any) -> str:
261262
extra=vol.ALLOW_EXTRA,
262263
)
263264

264-
SERVICE_LIST_EVENTS: Final = "list_events"
265-
SERVICE_LIST_EVENTS_SCHEMA: Final = vol.All(
265+
LEGACY_SERVICE_LIST_EVENTS: Final = "list_events"
266+
"""Deprecated: please use SERVICE_LIST_EVENTS."""
267+
SERVICE_GET_EVENTS: Final = "get_events"
268+
SERVICE_GET_EVENTS_SCHEMA: Final = vol.All(
266269
cv.has_at_least_one_key(EVENT_END_DATETIME, EVENT_DURATION),
267270
cv.has_at_most_one_key(EVENT_END_DATETIME, EVENT_DURATION),
268271
cv.make_entity_service_schema(
@@ -301,11 +304,17 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
301304
required_features=[CalendarEntityFeature.CREATE_EVENT],
302305
)
303306
component.async_register_legacy_entity_service(
304-
SERVICE_LIST_EVENTS,
305-
SERVICE_LIST_EVENTS_SCHEMA,
307+
LEGACY_SERVICE_LIST_EVENTS,
308+
SERVICE_GET_EVENTS_SCHEMA,
306309
async_list_events_service,
307310
supports_response=SupportsResponse.ONLY,
308311
)
312+
component.async_register_entity_service(
313+
SERVICE_GET_EVENTS,
314+
SERVICE_GET_EVENTS_SCHEMA,
315+
async_get_events_service,
316+
supports_response=SupportsResponse.ONLY,
317+
)
309318
await component.async_setup(config)
310319
return True
311320

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

851860
async def async_list_events_service(
852861
calendar: CalendarEntity, service_call: ServiceCall
862+
) -> ServiceResponse:
863+
"""List events on a calendar during a time range.
864+
865+
Deprecated: please use async_get_events_service.
866+
"""
867+
_LOGGER.warning(
868+
"Detected use of service 'calendar.list_events'. "
869+
"This is deprecated and will stop working in Home Assistant 2024.6. "
870+
"Use 'calendar.get_events' instead which supports multiple entities",
871+
)
872+
async_create_issue(
873+
calendar.hass,
874+
DOMAIN,
875+
"deprecated_service_calendar_list_events",
876+
breaks_in_ha_version="2024.6.0",
877+
is_fixable=True,
878+
is_persistent=False,
879+
issue_domain=calendar.platform.platform_name,
880+
severity=IssueSeverity.WARNING,
881+
translation_key="deprecated_service_calendar_list_events",
882+
)
883+
return await async_get_events_service(calendar, service_call)
884+
885+
886+
async def async_get_events_service(
887+
calendar: CalendarEntity, service_call: ServiceCall
853888
) -> ServiceResponse:
854889
"""List events on a calendar during a time range."""
855890
start = service_call.data.get(EVENT_START_DATETIME, dt_util.now())

homeassistant/components/calendar/services.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,19 @@ list_events:
5252
duration:
5353
selector:
5454
duration:
55+
get_events:
56+
target:
57+
entity:
58+
domain: calendar
59+
fields:
60+
start_date_time:
61+
example: "2022-03-22 20:00:00"
62+
selector:
63+
datetime:
64+
end_date_time:
65+
example: "2022-03-22 22:00:00"
66+
selector:
67+
datetime:
68+
duration:
69+
selector:
70+
duration:

homeassistant/components/calendar/strings.json

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,9 @@
7272
}
7373
}
7474
},
75-
"list_events": {
76-
"name": "List event",
77-
"description": "Lists events on a calendar within a time range.",
75+
"get_events": {
76+
"name": "Get event",
77+
"description": "Get events on a calendar within a time range.",
7878
"fields": {
7979
"start_date_time": {
8080
"name": "Start time",
@@ -89,6 +89,37 @@
8989
"description": "Returns active events from start_date_time until the specified duration."
9090
}
9191
}
92+
},
93+
"list_events": {
94+
"name": "List event",
95+
"description": "Lists events on a calendar within a time range.",
96+
"fields": {
97+
"start_date_time": {
98+
"name": "[%key:component::calendar::services::get_events::fields::start_date_time::name%]",
99+
"description": "[%key:component::calendar::services::get_events::fields::start_date_time::description%]"
100+
},
101+
"end_date_time": {
102+
"name": "[%key:component::calendar::services::get_events::fields::end_date_time::name%]",
103+
"description": "[%key:component::calendar::services::get_events::fields::end_date_time::description%]"
104+
},
105+
"duration": {
106+
"name": "[%key:component::calendar::services::get_events::fields::duration::name%]",
107+
"description": "[%key:component::calendar::services::get_events::fields::duration::description%]"
108+
}
109+
}
110+
}
111+
},
112+
"issues": {
113+
"deprecated_service_calendar_list_events": {
114+
"title": "Detected use of deprecated service `calendar.list_events`",
115+
"fix_flow": {
116+
"step": {
117+
"confirm": {
118+
"title": "[%key:component::calendar::issues::deprecated_service_calendar_list_events::title%]",
119+
"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."
120+
}
121+
}
122+
}
92123
}
93124
}
94125
}

tests/components/calendar/snapshots/test_init.ambr

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,34 @@
11
# serializer version: 1
2-
# name: test_list_events_service_duration[calendar.calendar_1-00:15:00]
2+
# name: test_list_events_service_duration[calendar.calendar_1-00:15:00-get_events]
3+
dict({
4+
'calendar.calendar_1': dict({
5+
'events': list([
6+
]),
7+
}),
8+
})
9+
# ---
10+
# name: test_list_events_service_duration[calendar.calendar_1-00:15:00-list_events]
311
dict({
412
'events': list([
513
]),
614
})
715
# ---
8-
# name: test_list_events_service_duration[calendar.calendar_1-01:00:00]
16+
# name: test_list_events_service_duration[calendar.calendar_1-01:00:00-get_events]
17+
dict({
18+
'calendar.calendar_1': dict({
19+
'events': list([
20+
dict({
21+
'description': 'Future Description',
22+
'end': '2023-10-19T08:20:05-07:00',
23+
'location': 'Future Location',
24+
'start': '2023-10-19T07:20:05-07:00',
25+
'summary': 'Future Event',
26+
}),
27+
]),
28+
}),
29+
})
30+
# ---
31+
# name: test_list_events_service_duration[calendar.calendar_1-01:00:00-list_events]
932
dict({
1033
'events': list([
1134
dict({
@@ -18,7 +41,20 @@
1841
]),
1942
})
2043
# ---
21-
# name: test_list_events_service_duration[calendar.calendar_2-00:15:00]
44+
# name: test_list_events_service_duration[calendar.calendar_2-00:15:00-get_events]
45+
dict({
46+
'calendar.calendar_2': dict({
47+
'events': list([
48+
dict({
49+
'end': '2023-10-19T07:20:05-07:00',
50+
'start': '2023-10-19T06:20:05-07:00',
51+
'summary': 'Current Event',
52+
}),
53+
]),
54+
}),
55+
})
56+
# ---
57+
# name: test_list_events_service_duration[calendar.calendar_2-00:15:00-list_events]
2258
dict({
2359
'events': list([
2460
dict({

tests/components/calendar/test_init.py

Lines changed: 96 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,14 @@
1212
import voluptuous as vol
1313

1414
from homeassistant.bootstrap import async_setup_component
15-
from homeassistant.components.calendar import DOMAIN, SERVICE_LIST_EVENTS
15+
from homeassistant.components.calendar import (
16+
DOMAIN,
17+
LEGACY_SERVICE_LIST_EVENTS,
18+
SERVICE_GET_EVENTS,
19+
)
1620
from homeassistant.core import HomeAssistant
1721
from homeassistant.exceptions import HomeAssistantError
22+
from homeassistant.helpers.issue_registry import IssueRegistry
1823
import homeassistant.util.dt as dt_util
1924

2025
from tests.typing import ClientSessionGenerator, WebSocketGenerator
@@ -389,6 +394,41 @@ async def test_create_event_service_invalid_params(
389394

390395

391396
@freeze_time("2023-06-22 10:30:00+00:00")
397+
@pytest.mark.parametrize(
398+
("service", "expected"),
399+
[
400+
(
401+
LEGACY_SERVICE_LIST_EVENTS,
402+
{
403+
"events": [
404+
{
405+
"start": "2023-06-22T05:00:00-06:00",
406+
"end": "2023-06-22T06:00:00-06:00",
407+
"summary": "Future Event",
408+
"description": "Future Description",
409+
"location": "Future Location",
410+
}
411+
]
412+
},
413+
),
414+
(
415+
SERVICE_GET_EVENTS,
416+
{
417+
"calendar.calendar_1": {
418+
"events": [
419+
{
420+
"start": "2023-06-22T05:00:00-06:00",
421+
"end": "2023-06-22T06:00:00-06:00",
422+
"summary": "Future Event",
423+
"description": "Future Description",
424+
"location": "Future Location",
425+
}
426+
]
427+
}
428+
},
429+
),
430+
],
431+
)
392432
@pytest.mark.parametrize(
393433
("start_time", "end_time"),
394434
[
@@ -402,6 +442,8 @@ async def test_list_events_service(
402442
set_time_zone: None,
403443
start_time: str,
404444
end_time: str,
445+
service: str,
446+
expected: dict[str, Any],
405447
) -> None:
406448
"""Test listing events from the service call using exlplicit start and end time.
407449
@@ -414,28 +456,26 @@ async def test_list_events_service(
414456

415457
response = await hass.services.async_call(
416458
DOMAIN,
417-
SERVICE_LIST_EVENTS,
418-
{
459+
service,
460+
target={"entity_id": ["calendar.calendar_1"]},
461+
service_data={
419462
"entity_id": "calendar.calendar_1",
420463
"start_date_time": start_time,
421464
"end_date_time": end_time,
422465
},
423466
blocking=True,
424467
return_response=True,
425468
)
426-
assert response == {
427-
"events": [
428-
{
429-
"start": "2023-06-22T05:00:00-06:00",
430-
"end": "2023-06-22T06:00:00-06:00",
431-
"summary": "Future Event",
432-
"description": "Future Description",
433-
"location": "Future Location",
434-
}
435-
]
436-
}
469+
assert response == expected
437470

438471

472+
@pytest.mark.parametrize(
473+
("service"),
474+
[
475+
(LEGACY_SERVICE_LIST_EVENTS),
476+
SERVICE_GET_EVENTS,
477+
],
478+
)
439479
@pytest.mark.parametrize(
440480
("entity", "duration"),
441481
[
@@ -452,6 +492,7 @@ async def test_list_events_service_duration(
452492
hass: HomeAssistant,
453493
entity: str,
454494
duration: str,
495+
service: str,
455496
snapshot: SnapshotAssertion,
456497
) -> None:
457498
"""Test listing events using a time duration."""
@@ -460,7 +501,7 @@ async def test_list_events_service_duration(
460501

461502
response = await hass.services.async_call(
462503
DOMAIN,
463-
SERVICE_LIST_EVENTS,
504+
service,
464505
{
465506
"entity_id": entity,
466507
"duration": duration,
@@ -479,7 +520,7 @@ async def test_list_events_positive_duration(hass: HomeAssistant) -> None:
479520
with pytest.raises(vol.Invalid, match="should be positive"):
480521
await hass.services.async_call(
481522
DOMAIN,
482-
SERVICE_LIST_EVENTS,
523+
SERVICE_GET_EVENTS,
483524
{
484525
"entity_id": "calendar.calendar_1",
485526
"duration": "-01:00:00",
@@ -499,7 +540,7 @@ async def test_list_events_exclusive_fields(hass: HomeAssistant) -> None:
499540
with pytest.raises(vol.Invalid, match="at most one of"):
500541
await hass.services.async_call(
501542
DOMAIN,
502-
SERVICE_LIST_EVENTS,
543+
SERVICE_GET_EVENTS,
503544
{
504545
"entity_id": "calendar.calendar_1",
505546
"end_date_time": end,
@@ -518,10 +559,47 @@ async def test_list_events_missing_fields(hass: HomeAssistant) -> None:
518559
with pytest.raises(vol.Invalid, match="at least one of"):
519560
await hass.services.async_call(
520561
DOMAIN,
521-
SERVICE_LIST_EVENTS,
562+
SERVICE_GET_EVENTS,
522563
{
523564
"entity_id": "calendar.calendar_1",
524565
},
525566
blocking=True,
526567
return_response=True,
527568
)
569+
570+
571+
async def test_issue_deprecated_service_calendar_list_events(
572+
hass: HomeAssistant,
573+
issue_registry: IssueRegistry,
574+
caplog: pytest.LogCaptureFixture,
575+
) -> None:
576+
"""Test the issue is raised on deprecated service weather.get_forecast."""
577+
578+
await async_setup_component(hass, "calendar", {"calendar": {"platform": "demo"}})
579+
await hass.async_block_till_done()
580+
581+
_ = await hass.services.async_call(
582+
DOMAIN,
583+
LEGACY_SERVICE_LIST_EVENTS,
584+
target={"entity_id": ["calendar.calendar_1"]},
585+
service_data={
586+
"entity_id": "calendar.calendar_1",
587+
"duration": "01:00:00",
588+
},
589+
blocking=True,
590+
return_response=True,
591+
)
592+
593+
issue = issue_registry.async_get_issue(
594+
"calendar", "deprecated_service_calendar_list_events"
595+
)
596+
assert issue
597+
assert issue.issue_domain == "demo"
598+
assert issue.issue_id == "deprecated_service_calendar_list_events"
599+
assert issue.translation_key == "deprecated_service_calendar_list_events"
600+
601+
assert (
602+
"Detected use of service 'calendar.list_events'. "
603+
"This is deprecated and will stop working in Home Assistant 2024.6. "
604+
"Use 'calendar.get_events' instead which supports multiple entities"
605+
) in caplog.text

0 commit comments

Comments
 (0)