Skip to content

Commit 557f712

Browse files
WIP
1 parent b661a45 commit 557f712

File tree

1 file changed

+82
-47
lines changed

1 file changed

+82
-47
lines changed

custom_components/calendar_event/binary_sensor.py

Lines changed: 82 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@
99
from homeassistant.const import EVENT_STATE_CHANGED, EVENT_STATE_REPORTED
1010
from homeassistant.core import Event, HomeAssistant, callback
1111
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
12-
13-
from .const import (
14-
ATTR_DESCRIPTION,
15-
CONF_CALENDAR_ENTITY_ID,
16-
CONF_SUMMARY,
12+
from homeassistant.helpers.event import (
13+
EventStateChangedData,
14+
EventStateReportedData,
15+
async_track_entity_registry_updated_event,
16+
async_track_state_change_event,
17+
async_track_state_report_event,
1718
)
1819

20+
from .const import ATTR_DESCRIPTION, CONF_CALENDAR_ENTITY_ID, CONF_SUMMARY, LOGGER
21+
1922

2023
async def config_entry_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
2124
"""Update listener, called when the config entry options are changed."""
@@ -87,20 +90,70 @@ async def async_added_to_hass(self) -> None:
8790

8891
# Add state listener for the calendar entity
8992
self.async_on_remove(
90-
self._hass.bus.async_listen(
91-
EVENT_STATE_CHANGED, self._calendar_state_changed
92-
)
93+
self._hass.bus.async_listen(EVENT_STATE_CHANGED, self._state_changed)
9394
)
9495

95-
# Check initial state
96-
await self._update_state()
96+
# self.async_on_remove(
97+
# async_track_state_report_event(
98+
# self.hass,
99+
# [self._calendar_entity_id],
100+
# self.async_state_reported,
101+
# )
102+
# )
103+
104+
# await self._update_state()
97105

98106
@callback
99-
async def _calendar_state_changed(self, event: Event) -> None:
107+
async def _state_changed(self, event: Event) -> None:
100108
"""Handle calendar entity state changes."""
101109
if event.data.get("entity_id") == self._calendar_entity_id:
102110
await self._update_state()
103111

112+
@callback
113+
async def async_state_reported(
114+
self, event: Event[EventStateReportedData] | None = None
115+
) -> None:
116+
"""Handle calendar entity state updates."""
117+
118+
# This gets fired every minute, but not at startup
119+
print(event)
120+
121+
if event is None or event.data is None:
122+
return
123+
124+
state = event.data.get("new_state")
125+
126+
if state and state.entity_id != self._calendar_entity_id:
127+
return
128+
129+
if state.state == "off":
130+
return
131+
132+
summary = state.attributes.get("message")
133+
if not summary:
134+
return
135+
136+
if self._summary.lower() in str(summary).lower():
137+
self._attr_is_on = True
138+
self._attr_extra_state_attributes.update(
139+
{
140+
ATTR_DESCRIPTION: state.attributes.get("description", ""),
141+
}
142+
)
143+
self.async_write_ha_state()
144+
return
145+
146+
event = await self._get_event_matching_summary()
147+
if event:
148+
self._attr_is_on = True
149+
self._attr_extra_state_attributes.update(
150+
{
151+
ATTR_DESCRIPTION: event.get("description", ""),
152+
}
153+
)
154+
155+
self.async_write_ha_state()
156+
104157
async def _update_state(self) -> None:
105158
"""Update the binary sensor state based on calendar events."""
106159

@@ -116,6 +169,22 @@ async def _update_state(self) -> None:
116169
self.async_write_ha_state()
117170
return
118171

172+
# TODO: If state is on, then we need to check every minute in case our event is not the one that turned it on
173+
174+
event = await self._get_event_matching_summary()
175+
if event:
176+
self._attr_is_on = True
177+
self._attr_extra_state_attributes.update(
178+
{
179+
ATTR_DESCRIPTION: event.get("description", ""),
180+
}
181+
)
182+
183+
self.async_write_ha_state()
184+
185+
async def _get_event_matching_summary(self) -> Event | None:
186+
"""Check if the summary is in the calendar events."""
187+
119188
# Fetch all events for the calendar entity using the get_events service
120189
now = datetime.now()
121190
end_date_time = (now + timedelta(hours=1)).isoformat()
@@ -131,45 +200,11 @@ async def _update_state(self) -> None:
131200
return_response=True,
132201
)
133202

134-
print(events)
135-
136-
# {
137-
# "calendar.system": {
138-
# "events": [
139-
# {
140-
# "start": "2025-08-06T17:00:00+01:00",
141-
# "end": "2025-08-06T23:00:00+01:00",
142-
# "summary": "Evening",
143-
# "description": "It's evening",
144-
# }
145-
# ]
146-
# }
147-
# }
148-
149-
# TODO: This will be on if there's an existing event, so we don't get a new event
150-
# need to create a polling mechanism to check for new events, or look at state_reported
151-
# https://developers.home-assistant.io/blog/2024/03/20/state_reported_timestamp/
152-
# also https://github.com/andrew-codechimp/HA-Battery-Notes/pull/1337/files#diff-1bb5e0ea81eb98dae60348901d92fb42f456eccfb7c47623deca78d325338935
153-
154203
calendar_events = events.get(self._calendar_entity_id, {}).get("events", [])
155204
for event in calendar_events:
156-
# Check if the event summary matches the configured summary (case-insensitive, partial match)
157205
if not isinstance(event, dict):
158206
continue
159207
if self._summary.lower() in event.get("summary", "").lower():
160-
self._attr_is_on = True
161-
self._attr_extra_state_attributes.update(
162-
{
163-
ATTR_DESCRIPTION: event.get("description", ""),
164-
}
165-
)
166-
break
167-
else:
168-
self._attr_is_on = False
169-
self._attr_extra_state_attributes.update(
170-
{
171-
ATTR_DESCRIPTION: None,
172-
}
173-
)
208+
return event
174209

175-
self.async_write_ha_state()
210+
return None

0 commit comments

Comments
 (0)