diff --git a/docs/apps/schedy/CHANGELOG.md b/docs/apps/schedy/CHANGELOG.md index 55c56d52..116a7c5d 100644 --- a/docs/apps/schedy/CHANGELOG.md +++ b/docs/apps/schedy/CHANGELOG.md @@ -20,9 +20,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Changed * The `Skip` expression result type has been renamed to `Next`, which better describes its purpose. +* The behaviour of the `OVERLAY_REVERT_ON_NO_RESULT` result marker now is the + default with `OVERLAY`. The marker will be removed. ### Deprecated * 0.7: The previous name `Skip` for the `Next` expression result type will be removed. +* 0.7: The `OVERLAY_REVERT_ON_NO_RESULT` marker will be removed, it's the default now. ### Removed diff --git a/docs/apps/schedy/schedules/expressions/result-markers.rst b/docs/apps/schedy/schedules/expressions/result-markers.rst index 233a696b..55539777 100644 --- a/docs/apps/schedy/schedules/expressions/result-markers.rst +++ b/docs/apps/schedy/schedules/expressions/result-markers.rst @@ -37,9 +37,9 @@ The following markers are available: An occasion for using this marker is :doc:`../../tips-and-tricks/open-window-detection`. -* ``OVERLAY_REVERT_ON_NO_RESULT``: When applied in conjunction with the - ``OVERLAY`` marker, the overlay is cancelled as soon as a schedule - evaluation produces no result (e.g. because ``Abort()`` was used). When - an overlay is created without this additional marker, the value marked - with ``OVERLAY`` stays active until the schedule really results in - another value. +* ``OVERLAY_REVERT_ON_NO_RESULT``: When applied in conjunction with the ``OVERLAY`` + marker, the overlay is cancelled as soon as a schedule evaluation produces no result + (e.g. because ``Abort()`` was used or all rules evaluated to ``Next()``). When an + overlay is created without this additional marker, the value marked with ``OVERLAY`` + stays active until the schedule really results in another value. + **DEPRECATED:** This is the default behaviour of `OVERLAY` now. diff --git a/hass_apps/schedy/room.py b/hass_apps/schedy/room.py index 122805bf..520d1ce5 100644 --- a/hass_apps/schedy/room.py +++ b/hass_apps/schedy/room.py @@ -78,7 +78,6 @@ def __init__(self, name: str, cfg: dict, app: "SchedyApp") -> None: self._overlaid_wanted_value = None # type: T.Any self._overlaid_scheduled_value = None # type: T.Any self._overlaid_rescheduling_time = None # type: T.Optional[datetime.datetime] - self._overlay_revert_on_no_result = None # type: T.Optional[bool] self._last_state = None # type: T.Optional[T.Tuple[str, T.Dict[str, T.Any]]] @@ -100,7 +99,6 @@ def _clear_overlay(self) -> None: self._overlaid_wanted_value = None self._overlaid_scheduled_value = None self._overlaid_rescheduling_time = None - self._overlay_revert_on_no_result = None @sync_proxy def _initialize_actor_cb(self, kwargs: dict) -> None: @@ -173,7 +171,6 @@ def _deserialize_dt(value: T.Any) -> T.Optional[datetime.datetime]: self._overlaid_rescheduling_time = _deserialize_dt( attrs.get("overlaid_rescheduling_time") ) - self._overlay_revert_on_no_result = attrs.get("overlay_revert_on_no_result") if self._rescheduling_time: if self._rescheduling_time > self.app.datetime(): @@ -210,26 +207,25 @@ def _state_entity_id(self) -> str: return "schedy_room.{}_{}".format(self.app.name, self.name) - def _store_for_overlaying(self, scheduled_value: T.Any) -> bool: + def _store_for_overlaying(self) -> bool: """This method is called before a value overlay is put into place. When a re-scheduling timer is running or the scheduled and wanted values differ, this method stores the scheduled and wanted value together with the re-scheduling time to later be able to re-set it. - Everything except scheduled_value is fetched from self._*. A running re-scheduling timer is cancelled. If there already is an overlaid value stored, this does nothing. Returns whether values have been stored.""" if self._overlaid_wanted_value is None and ( - scheduled_value != self._wanted_value or self._rescheduling_timer + self._scheduled_value != self._wanted_value or self._rescheduling_timer ): self.log( "Storing currently wanted value {} before an overlay " "is applied.".format(repr(self._wanted_value)) ) self._overlaid_wanted_value = self._wanted_value - self._overlaid_scheduled_value = scheduled_value + self._overlaid_scheduled_value = self._scheduled_value self._overlaid_rescheduling_time = self._rescheduling_time self.cancel_rescheduling_timer() return True @@ -274,7 +270,6 @@ def _maybe_add(key: str, value: T.Any) -> None: "overlaid_rescheduling_time", _serialize_dt(self._overlaid_rescheduling_time), ) - _maybe_add("overlay_revert_on_no_result", self._overlay_revert_on_no_result) _maybe_add("friendly_name", self.cfg.get("friendly_name")) unchanged = (state, attrs) == self._last_state @@ -328,12 +323,19 @@ def _restore_overlaid_value() -> bool: Returns whether a value has actually been set or not.""" overlaid_wanted_value = self._overlaid_wanted_value - delay = None # type: T.Union[None, datetime.datetime] - if ( - self._overlaid_rescheduling_time - and self._overlaid_rescheduling_time > self.app.datetime() - ): + if overlaid_wanted_value is None: + return False + + delay = None # type: T.Union[None, int, datetime.datetime] + if not self._overlaid_rescheduling_time: + if new_scheduled_value == self._overlaid_scheduled_value: + # scheduled value hasn't changed compared to before overlay, + # hence revert to overlaid value without timer + delay = 0 + elif self._overlaid_rescheduling_time > self.app.datetime(): + # resume overlaid re-scheduling timer delay = self._overlaid_rescheduling_time + self._clear_overlay() if delay is None: return False @@ -355,30 +357,25 @@ def _restore_overlaid_value() -> bool: result = self.schedule.evaluate(self, self.app.datetime()) if result is None: self.log("No suitable value found in schedule.", level="DEBUG") - if self._overlay_revert_on_no_result: - self._scheduled_value = self._overlaid_scheduled_value - _restore_overlaid_value() + # revert an eventual overlay + new_scheduled_value = self._overlaid_scheduled_value + _restore_overlaid_value() return - value, markers = result[:2] - if value == self._scheduled_value and not reset and not force_resend: + new_scheduled_value, markers = result[:2] + if not (new_scheduled_value != self._scheduled_value or reset or force_resend): self.log("Result didn't change, not setting it again.", level="DEBUG") return - previous_scheduled_value = self._scheduled_value - self._scheduled_value = value - if reset: self.cancel_rescheduling_timer() self._clear_overlay() elif expression.types.Mark.OVERLAY in markers: - self._store_for_overlaying(previous_scheduled_value) - self._overlay_revert_on_no_result = ( - expression.types.Mark.OVERLAY_REVERT_ON_NO_RESULT in markers - ) - elif self._overlaid_wanted_value is not None: - if _restore_overlaid_value(): - return + # create new or replace existing overlay + self._store_for_overlaying() + # no overlay should be set, hence try to revert an existing one + elif _restore_overlaid_value(): + return elif self._rescheduling_timer: self.log( "Not applying the schedule now due to a running " @@ -387,7 +384,8 @@ def _restore_overlaid_value() -> bool: ) return - self.set_value(value, force_resend=force_resend) + self._scheduled_value = new_scheduled_value + self.set_value(new_scheduled_value, force_resend=force_resend) def cancel_rescheduling_timer(self) -> bool: """Cancels the re-scheduling timer for this room, if one @@ -640,7 +638,7 @@ def set_value_manually( return if expression.types.Mark.OVERLAY in markers: - self._store_for_overlaying(self._scheduled_value) + self._store_for_overlaying() self.set_value(value, force_resend=force_resend) if rescheduling_delay != 0: