99from homeassistant .const import EVENT_STATE_CHANGED , EVENT_STATE_REPORTED
1010from homeassistant .core import Event , HomeAssistant , callback
1111from 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
2023async 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