Fix bug in google calendar offset calculation (#70024)

Move the offset reached computation outside of the update method so that it is
computed when state updates occur rather than when data refreshes happen (which
are throttled and happen at most every 15 minutes).

Issue #69892
This commit is contained in:
Allen Porter 2022-04-13 19:04:59 -07:00 committed by GitHub
parent c00e226b2a
commit 36bb947cdf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 86 additions and 5 deletions

View File

@ -120,13 +120,22 @@ class GoogleCalendarEntity(CalendarEntity):
self._event: CalendarEvent | None = None self._event: CalendarEvent | None = None
self._name: str = data[CONF_NAME] self._name: str = data[CONF_NAME]
self._offset = data.get(CONF_OFFSET, DEFAULT_CONF_OFFSET) self._offset = data.get(CONF_OFFSET, DEFAULT_CONF_OFFSET)
self._offset_reached = False self._offset_value: timedelta | None = None
self.entity_id = entity_id self.entity_id = entity_id
@property @property
def extra_state_attributes(self) -> dict[str, bool]: def extra_state_attributes(self) -> dict[str, bool]:
"""Return the device state attributes.""" """Return the device state attributes."""
return {"offset_reached": self._offset_reached} return {"offset_reached": self.offset_reached}
@property
def offset_reached(self) -> bool:
"""Return whether or not the event offset was reached."""
if self._event and self._offset_value:
return is_offset_reached(
self._event.start_datetime_local, self._offset_value
)
return False
@property @property
def event(self) -> CalendarEvent | None: def event(self) -> CalendarEvent | None:
@ -187,9 +196,7 @@ class GoogleCalendarEntity(CalendarEntity):
(summary, offset) = extract_offset(event.get("summary", ""), self._offset) (summary, offset) = extract_offset(event.get("summary", ""), self._offset)
event["summary"] = summary event["summary"] = summary
self._event = _get_calendar_event(event) self._event = _get_calendar_event(event)
self._offset_reached = is_offset_reached( self._offset_value = offset
self._event.start_datetime_local, offset
)
else: else:
self._event = None self._event = None

View File

@ -505,3 +505,77 @@ async def test_scan_calendar_error(
assert await component_setup() assert await component_setup()
assert not hass.states.get(TEST_ENTITY) assert not hass.states.get(TEST_ENTITY)
async def test_future_event_update_behavior(
hass, mock_events_list_items, component_setup
):
"""Test an future event that becomes active."""
now = dt_util.now()
now_utc = dt_util.utcnow()
one_hour_from_now = now + datetime.timedelta(minutes=60)
end_event = one_hour_from_now + datetime.timedelta(minutes=90)
event = {
**TEST_EVENT,
"start": {"dateTime": one_hour_from_now.isoformat()},
"end": {"dateTime": end_event.isoformat()},
}
mock_events_list_items([event])
assert await component_setup()
# Event has not started yet
state = hass.states.get(TEST_ENTITY)
assert state.name == TEST_ENTITY_NAME
assert state.state == STATE_OFF
# Advance time until event has started
now += datetime.timedelta(minutes=60)
now_utc += datetime.timedelta(minutes=30)
with patch("homeassistant.util.dt.utcnow", return_value=now_utc), patch(
"homeassistant.util.dt.now", return_value=now
):
async_fire_time_changed(hass, now)
await hass.async_block_till_done()
# Event has started
state = hass.states.get(TEST_ENTITY)
assert state.state == STATE_ON
async def test_future_event_offset_update_behavior(
hass, mock_events_list_items, component_setup
):
"""Test an future event that becomes active."""
now = dt_util.now()
now_utc = dt_util.utcnow()
one_hour_from_now = now + datetime.timedelta(minutes=60)
end_event = one_hour_from_now + datetime.timedelta(minutes=90)
event_summary = "Test Event in Progress"
event = {
**TEST_EVENT,
"start": {"dateTime": one_hour_from_now.isoformat()},
"end": {"dateTime": end_event.isoformat()},
"summary": f"{event_summary} !!-15",
}
mock_events_list_items([event])
assert await component_setup()
# Event has not started yet
state = hass.states.get(TEST_ENTITY)
assert state.name == TEST_ENTITY_NAME
assert state.state == STATE_OFF
assert not state.attributes["offset_reached"]
# Advance time until event has started
now += datetime.timedelta(minutes=45)
now_utc += datetime.timedelta(minutes=45)
with patch("homeassistant.util.dt.utcnow", return_value=now_utc), patch(
"homeassistant.util.dt.now", return_value=now
):
async_fire_time_changed(hass, now)
await hass.async_block_till_done()
# Event has not started, but the offset was reached
state = hass.states.get(TEST_ENTITY)
assert state.state == STATE_OFF
assert state.attributes["offset_reached"]