@@ -75,10 +75,10 @@ def __init__(self, name: str, cfg: dict, app: "SchedyApp") -> None:
7575 self ._scheduled_value = None # type: T.Any
7676 self ._rescheduling_time = None # type: T.Optional[datetime.datetime]
7777 self ._rescheduling_timer = None # type: T.Optional[uuid.UUID]
78+ self ._overlay_active = False
7879 self ._overlaid_wanted_value = None # type: T.Any
7980 self ._overlaid_scheduled_value = None # type: T.Any
8081 self ._overlaid_rescheduling_time = None # type: T.Optional[datetime.datetime]
81- self ._overlay_revert_on_no_result = None # type: T.Optional[bool]
8282
8383 self ._last_state = None # type: T.Optional[T.Tuple[str, T.Dict[str, T.Any]]]
8484
@@ -97,10 +97,10 @@ def __str__(self) -> str:
9797 def _clear_overlay (self ) -> None :
9898 """Removes all stored overlay state."""
9999
100+ self ._overlay_active = False
100101 self ._overlaid_wanted_value = None
101102 self ._overlaid_scheduled_value = None
102103 self ._overlaid_rescheduling_time = None
103- self ._overlay_revert_on_no_result = None
104104
105105 @sync_proxy
106106 def _initialize_actor_cb (self , kwargs : dict ) -> None :
@@ -164,6 +164,7 @@ def _deserialize_dt(value: T.Any) -> T.Optional[datetime.datetime]:
164164 self ._wanted_value = _deserialize (state .get ("state" ) or None )
165165 self ._scheduled_value = _deserialize (attrs .get ("scheduled_value" ))
166166 self ._rescheduling_time = _deserialize_dt (attrs .get ("rescheduling_time" ))
167+ self ._overlay_active = attrs .get ("overlay_active" ) or False
167168 self ._overlaid_wanted_value = _deserialize (
168169 attrs .get ("overlaid_wanted_value" )
169170 )
@@ -173,7 +174,6 @@ def _deserialize_dt(value: T.Any) -> T.Optional[datetime.datetime]:
173174 self ._overlaid_rescheduling_time = _deserialize_dt (
174175 attrs .get ("overlaid_rescheduling_time" )
175176 )
176- self ._overlay_revert_on_no_result = attrs .get ("overlay_revert_on_no_result" )
177177
178178 if self ._rescheduling_time :
179179 if self ._rescheduling_time > self .app .datetime ():
@@ -210,30 +210,22 @@ def _state_entity_id(self) -> str:
210210
211211 return "schedy_room.{}_{}" .format (self .app .name , self .name )
212212
213- def _store_for_overlaying (self , scheduled_value : T . Any ) -> bool :
213+ def _store_for_overlaying (self ) -> None :
214214 """This method is called before a value overlay is put into place.
215- When a re-scheduling timer is running or the scheduled and
216- wanted values differ, this method stores the scheduled and wanted
217- value together with the re-scheduling time to later be able to
218- re-set it.
219- Everything except scheduled_value is fetched from self._*.
220- A running re-scheduling timer is cancelled.
221- If there already is an overlaid value stored, this does nothing.
222- Returns whether values have been stored."""
223-
224- if self ._overlaid_wanted_value is None and (
225- scheduled_value != self ._wanted_value or self ._rescheduling_timer
226- ):
227- self .log (
228- "Storing currently wanted value {} before an overlay "
229- "is applied." .format (repr (self ._wanted_value ))
230- )
231- self ._overlaid_wanted_value = self ._wanted_value
232- self ._overlaid_scheduled_value = scheduled_value
233- self ._overlaid_rescheduling_time = self ._rescheduling_time
234- self .cancel_rescheduling_timer ()
235- return True
236- return False
215+ It stores the scheduled and wanted value together with the re-scheduling
216+ time for later restoration. A running re-scheduling timer is cancelled.
217+ If an overlay is active already, this does nothing."""
218+
219+ if self ._overlay_active :
220+ # Don't overwrite existing restoration records
221+ return
222+
223+ self .log ("Storing value {!r} before overlaying." .format (self ._wanted_value ))
224+ self ._overlay_active = True
225+ self ._overlaid_wanted_value = self ._wanted_value
226+ self ._overlaid_scheduled_value = self ._scheduled_value
227+ self ._overlaid_rescheduling_time = self ._rescheduling_time
228+ self .cancel_rescheduling_timer ()
237229
238230 def _update_state (self ) -> None :
239231 """Update the room's state in Home Assistant."""
@@ -263,6 +255,7 @@ def _maybe_add(key: str, value: T.Any) -> None:
263255 },
264256 "scheduled_value" : _serialize (self ._scheduled_value , None ),
265257 "rescheduling_time" : _serialize_dt (self ._rescheduling_time ),
258+ "overlay_active" : self ._overlay_active ,
266259 } # type: T.Dict[str, T.Any]
267260 _maybe_add (
268261 "overlaid_wanted_value" , _serialize (self ._overlaid_wanted_value , None )
@@ -274,7 +267,6 @@ def _maybe_add(key: str, value: T.Any) -> None:
274267 "overlaid_rescheduling_time" ,
275268 _serialize_dt (self ._overlaid_rescheduling_time ),
276269 )
277- _maybe_add ("overlay_revert_on_no_result" , self ._overlay_revert_on_no_result )
278270 _maybe_add ("friendly_name" , self .cfg .get ("friendly_name" ))
279271
280272 unchanged = (state , attrs ) == self ._last_state
@@ -325,15 +317,32 @@ def apply_schedule(self, reset: bool = False, force_resend: bool = False) -> Non
325317
326318 def _restore_overlaid_value () -> bool :
327319 """Restores and clears an overlaid value.
328- Returns whether a value has actually been set or not."""
320+ Returns whether a value has actually been restored or not."""
329321
330322 overlaid_wanted_value = self ._overlaid_wanted_value
331- delay = None # type: T.Union[None, datetime.datetime]
332- if (
333- self ._overlaid_rescheduling_time
334- and self ._overlaid_rescheduling_time > self .app .datetime ()
335- ):
323+ if overlaid_wanted_value is None :
324+ self .log (
325+ "Overlay ended but knowing no value to restore." , level = "WARNING"
326+ )
327+ self ._clear_overlay ()
328+ return False
329+
330+ delay = None # type: T.Union[None, int, datetime.datetime]
331+ if not self ._overlaid_rescheduling_time :
332+ if new_scheduled_value == self ._overlaid_scheduled_value :
333+ # Scheduled value hasn't changed compared to before overlay,
334+ # hence revert to overlaid wanted value without timer
335+ delay = 0
336+ elif self ._overlaid_rescheduling_time > self .app .datetime ():
337+ # Resume overlaid re-scheduling timer
336338 delay = self ._overlaid_rescheduling_time
339+ else :
340+ self .log (
341+ "Overlaid value {!r} has expired, not restoring it." .format (
342+ overlaid_wanted_value
343+ )
344+ )
345+
337346 self ._clear_overlay ()
338347 if delay is None :
339348 return False
@@ -355,30 +364,28 @@ def _restore_overlaid_value() -> bool:
355364 result = self .schedule .evaluate (self , self .app .datetime ())
356365 if result is None :
357366 self .log ("No suitable value found in schedule." , level = "DEBUG" )
358- if self ._overlay_revert_on_no_result :
359- self ._scheduled_value = self ._overlaid_scheduled_value
367+ # revert an eventual overlay
368+ if self ._overlay_active :
369+ new_scheduled_value = self ._overlaid_scheduled_value
360370 _restore_overlaid_value ()
371+ self ._scheduled_value = new_scheduled_value
361372 return
362373
363- value , markers = result [:2 ]
364- if value == self ._scheduled_value and not reset and not force_resend :
374+ new_scheduled_value , markers = result [:2 ]
375+ if not ( new_scheduled_value != self ._scheduled_value or reset or force_resend ) :
365376 self .log ("Result didn't change, not setting it again." , level = "DEBUG" )
366377 return
367378
368- previous_scheduled_value = self ._scheduled_value
369- self ._scheduled_value = value
370-
371379 if reset :
372380 self .cancel_rescheduling_timer ()
373381 self ._clear_overlay ()
374382 elif expression .types .Mark .OVERLAY in markers :
375- self ._store_for_overlaying (previous_scheduled_value )
376- self ._overlay_revert_on_no_result = (
377- expression .types .Mark .OVERLAY_REVERT_ON_NO_RESULT in markers
378- )
379- elif self ._overlaid_wanted_value is not None :
380- if _restore_overlaid_value ():
381- return
383+ # Create restoration records if overlay not active already
384+ self ._store_for_overlaying ()
385+ # No overlay should be set, hence try to revert an existing one
386+ elif self ._overlay_active and _restore_overlaid_value ():
387+ self ._scheduled_value = new_scheduled_value
388+ return
382389 elif self ._rescheduling_timer :
383390 self .log (
384391 "Not applying the schedule now due to a running "
@@ -387,7 +394,8 @@ def _restore_overlaid_value() -> bool:
387394 )
388395 return
389396
390- self .set_value (value , force_resend = force_resend )
397+ self ._scheduled_value = new_scheduled_value
398+ self .set_value (new_scheduled_value , force_resend = force_resend )
391399
392400 def cancel_rescheduling_timer (self ) -> bool :
393401 """Cancels the re-scheduling timer for this room, if one
@@ -640,7 +648,7 @@ def set_value_manually(
640648 return
641649
642650 if expression .types .Mark .OVERLAY in markers :
643- self ._store_for_overlaying (self . _scheduled_value )
651+ self ._store_for_overlaying ()
644652
645653 self .set_value (value , force_resend = force_resend )
646654 if rescheduling_delay != 0 :
0 commit comments