Change duration for timer.start service to only change running duration (#99628)

* Get back duration for timer

* running duration

* Mods

* Finish

* Fix start call

* remove restore idle

* running duration not None

* fix tests
This commit is contained in:
G Johansson 2023-09-25 08:57:02 +02:00 committed by GitHub
parent 6b19602322
commit c414e52b55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 19 additions and 50 deletions

View File

@ -205,7 +205,8 @@ class Timer(collection.CollectionEntity, RestoreEntity):
"""Initialize a timer."""
self._config: dict = config
self._state: str = STATUS_IDLE
self._duration = cv.time_period_str(config[CONF_DURATION])
self._configured_duration = cv.time_period_str(config[CONF_DURATION])
self._running_duration: timedelta = self._configured_duration
self._remaining: timedelta | None = None
self._end: datetime | None = None
self._listener: Callable[[], None] | None = None
@ -248,7 +249,7 @@ class Timer(collection.CollectionEntity, RestoreEntity):
def extra_state_attributes(self):
"""Return the state attributes."""
attrs = {
ATTR_DURATION: _format_timedelta(self._duration),
ATTR_DURATION: _format_timedelta(self._running_duration),
ATTR_EDITABLE: self.editable,
}
if self._end is not None:
@ -275,12 +276,12 @@ class Timer(collection.CollectionEntity, RestoreEntity):
# Begin restoring state
self._state = state.state
self._duration = cv.time_period(state.attributes[ATTR_DURATION])
# Nothing more to do if the timer is idle
if self._state == STATUS_IDLE:
return
self._running_duration = cv.time_period(state.attributes[ATTR_DURATION])
# If the timer was paused, we restore the remaining time
if self._state == STATUS_PAUSED:
self._remaining = cv.time_period(state.attributes[ATTR_REMAINING])
@ -314,11 +315,11 @@ class Timer(collection.CollectionEntity, RestoreEntity):
self._state = STATUS_ACTIVE
start = dt_util.utcnow().replace(microsecond=0)
# Set remaining to new value if needed
# Set remaining and running duration unless resuming or restarting
if duration:
self._remaining = self._duration = duration
self._remaining = self._running_duration = duration
elif not self._remaining:
self._remaining = self._duration
self._remaining = self._running_duration
self._end = start + self._remaining
@ -336,9 +337,9 @@ class Timer(collection.CollectionEntity, RestoreEntity):
raise HomeAssistantError(
f"Timer {self.entity_id} is not running, only active timers can be changed"
)
if self._remaining and (self._remaining + duration) > self._duration:
if self._remaining and (self._remaining + duration) > self._running_duration:
raise HomeAssistantError(
f"Not possible to change timer {self.entity_id} beyond configured duration"
f"Not possible to change timer {self.entity_id} beyond duration"
)
if self._remaining and (self._remaining + duration) < timedelta():
raise HomeAssistantError(
@ -377,6 +378,7 @@ class Timer(collection.CollectionEntity, RestoreEntity):
self._state = STATUS_IDLE
self._end = None
self._remaining = None
self._running_duration = self._configured_duration
self.hass.bus.async_fire(
EVENT_TIMER_CANCELLED, {ATTR_ENTITY_ID: self.entity_id}
)
@ -395,6 +397,7 @@ class Timer(collection.CollectionEntity, RestoreEntity):
self._state = STATUS_IDLE
self._end = None
self._remaining = None
self._running_duration = self._configured_duration
self.hass.bus.async_fire(
EVENT_TIMER_FINISHED,
{ATTR_ENTITY_ID: self.entity_id, ATTR_FINISHED_AT: end.isoformat()},
@ -412,6 +415,7 @@ class Timer(collection.CollectionEntity, RestoreEntity):
end = self._end
self._end = None
self._remaining = None
self._running_duration = self._configured_duration
self.hass.bus.async_fire(
EVENT_TIMER_FINISHED,
{ATTR_ENTITY_ID: self.entity_id, ATTR_FINISHED_AT: end.isoformat()},
@ -421,6 +425,8 @@ class Timer(collection.CollectionEntity, RestoreEntity):
async def async_update_config(self, config: ConfigType) -> None:
"""Handle when the config is updated."""
self._config = config
self._duration = cv.time_period_str(config[CONF_DURATION])
self._configured_duration = cv.time_period_str(config[CONF_DURATION])
if self._state == STATUS_IDLE:
self._running_duration = self._configured_duration
self._restore = config.get(CONF_RESTORE, DEFAULT_RESTORE)
self.async_write_ha_state()

View File

@ -319,7 +319,7 @@ async def test_start_service(hass: HomeAssistant) -> None:
with pytest.raises(
HomeAssistantError,
match="Not possible to change timer timer.test1 beyond configured duration",
match="Not possible to change timer timer.test1 beyond duration",
):
await hass.services.async_call(
DOMAIN,
@ -370,7 +370,7 @@ async def test_start_service(hass: HomeAssistant) -> None:
state = hass.states.get("timer.test1")
assert state
assert state.state == STATUS_IDLE
assert state.attributes[ATTR_DURATION] == "0:00:15"
assert state.attributes[ATTR_DURATION] == "0:00:10"
assert ATTR_REMAINING not in state.attributes
with pytest.raises(
@ -387,7 +387,7 @@ async def test_start_service(hass: HomeAssistant) -> None:
state = hass.states.get("timer.test1")
assert state
assert state.state == STATUS_IDLE
assert state.attributes[ATTR_DURATION] == "0:00:15"
assert state.attributes[ATTR_DURATION] == "0:00:10"
assert ATTR_REMAINING not in state.attributes
@ -844,43 +844,6 @@ async def test_setup_no_config(hass: HomeAssistant, hass_admin_user: MockUser) -
assert count_start == len(hass.states.async_entity_ids())
async def test_restore_idle(hass: HomeAssistant) -> None:
"""Test entity restore logic when timer is idle."""
utc_now = utcnow()
stored_state = StoredState(
State(
"timer.test",
STATUS_IDLE,
{ATTR_DURATION: "0:00:30"},
),
None,
utc_now,
)
data = async_get(hass)
await data.store.async_save([stored_state.as_dict()])
await data.async_load()
entity = Timer.from_storage(
{
CONF_ID: "test",
CONF_NAME: "test",
CONF_DURATION: "0:01:00",
CONF_RESTORE: True,
}
)
entity.hass = hass
entity.entity_id = "timer.test"
await entity.async_added_to_hass()
await hass.async_block_till_done()
assert entity.state == STATUS_IDLE
assert entity.extra_state_attributes[ATTR_DURATION] == "0:00:30"
assert ATTR_REMAINING not in entity.extra_state_attributes
assert ATTR_FINISHES_AT not in entity.extra_state_attributes
assert entity.extra_state_attributes[ATTR_RESTORE]
@pytest.mark.freeze_time("2023-06-05 17:47:50")
async def test_restore_paused(hass: HomeAssistant) -> None:
"""Test entity restore logic when timer is paused."""
@ -1007,7 +970,7 @@ async def test_restore_active_finished_outside_grace(hass: HomeAssistant) -> Non
await hass.async_block_till_done()
assert entity.state == STATUS_IDLE
assert entity.extra_state_attributes[ATTR_DURATION] == "0:00:30"
assert entity.extra_state_attributes[ATTR_DURATION] == "0:01:00"
assert ATTR_REMAINING not in entity.extra_state_attributes
assert ATTR_FINISHES_AT not in entity.extra_state_attributes
assert entity.extra_state_attributes[ATTR_RESTORE]