diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index 4ae8c8cce03..a62d2bf1d6b 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -89,6 +89,7 @@ OPAQUE = "opaque" RRULE_PREFIX = "RRULE:" SERVICE_CREATE_EVENT = "create_event" +FILTERED_EVENT_TYPES = [EventTypeEnum.BIRTHDAY, EventTypeEnum.WORKING_LOCATION] @dataclasses.dataclass(frozen=True, kw_only=True) @@ -103,7 +104,7 @@ class GoogleCalendarEntityDescription(CalendarEntityDescription): search: str | None local_sync: bool device_id: str - working_location: bool = False + event_type: EventTypeEnum | None = None def _get_entity_descriptions( @@ -173,14 +174,24 @@ def _get_entity_descriptions( local_sync, ) if calendar_item.primary and local_sync: - _LOGGER.debug("work location entity") + # Create a separate calendar for birthdays + entity_descriptions.append( + dataclasses.replace( + entity_description, + key=f"{key}-birthdays", + translation_key="birthdays", + event_type=EventTypeEnum.BIRTHDAY, + name=None, + entity_id=None, + ) + ) # Create an optional disabled by default entity for Work Location entity_descriptions.append( dataclasses.replace( entity_description, key=f"{key}-work-location", translation_key="working_location", - working_location=True, + event_type=EventTypeEnum.WORKING_LOCATION, name=None, entity_id=None, entity_registry_enabled_default=False, @@ -383,8 +394,17 @@ class GoogleCalendarEntity( for attendee in event.attendees ): return False - is_working_location_event = event.event_type == EventTypeEnum.WORKING_LOCATION - if self.entity_description.working_location != is_working_location_event: + # Calendar enttiy may be limited to a specific event type + if ( + self.entity_description.event_type is not None + and self.entity_description.event_type != event.event_type + ): + return False + # Default calendar entity omits the special types but includes all the others + if ( + self.entity_description.event_type is None + and event.event_type in FILTERED_EVENT_TYPES + ): return False if self._ignore_availability: return True diff --git a/homeassistant/components/google/strings.json b/homeassistant/components/google/strings.json index 5ee0cdd9c14..5776fd0480b 100644 --- a/homeassistant/components/google/strings.json +++ b/homeassistant/components/google/strings.json @@ -131,6 +131,9 @@ "calendar": { "working_location": { "name": "Working location" + }, + "birthdays": { + "name": "Birthdays" } } } diff --git a/tests/components/google/test_calendar.py b/tests/components/google/test_calendar.py index 274e310fbce..720c0176850 100644 --- a/tests/components/google/test_calendar.py +++ b/tests/components/google/test_calendar.py @@ -1455,6 +1455,7 @@ async def test_working_location_ignored( ("event_type", "expected_event_message"), [ ("workingLocation", "Test All Day Event"), + ("birthday", None), ("default", None), ], ) @@ -1515,3 +1516,49 @@ async def test_no_working_location_entity( entity_entry = entity_registry.async_get("calendar.working_location") assert not entity_entry + + +@pytest.mark.parametrize( + ("event_type", "expected_event_message"), + [ + ("workingLocation", None), + ("birthday", "Test All Day Event"), + ("default", None), + ], +) +@pytest.mark.parametrize("calendar_is_primary", [True]) +async def test_birthday_entity( + hass: HomeAssistant, + hass_client: ClientSessionGenerator, + entity_registry: er.EntityRegistry, + mock_events_list_items: Callable[[list[dict[str, Any]]], None], + component_setup: ComponentSetup, + event_type: str, + expected_event_message: str | None, +) -> None: + """Test that birthday events appear only on the birthdays calendar.""" + event = { + **TEST_EVENT, + **upcoming(), + "eventType": event_type, + } + mock_events_list_items([event]) + assert await component_setup() + + entity_entry = entity_registry.async_get("calendar.birthdays") + assert entity_entry + assert entity_entry.disabled_by is None # Enabled by default + + entity_registry.async_update_entity( + entity_id="calendar.birthdays", disabled_by=None + ) + async_fire_time_changed( + hass, + dt_util.utcnow() + datetime.timedelta(seconds=RELOAD_AFTER_UPDATE_DELAY + 1), + ) + await hass.async_block_till_done() + + state = hass.states.get("calendar.birthdays") + assert state + assert state.name == "Birthdays" + assert state.attributes.get("message") == expected_event_message