Skip to content

Commit 66e95d1

Browse files
committed
updated period time trigger to use the date from
the first argument as the default date for the 3rd argument if it is just a time
1 parent 914f6f1 commit 66e95d1

File tree

3 files changed

+70
-19
lines changed

3 files changed

+70
-19
lines changed

custom_components/pyscript/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ def glob_read_files(
572572
for global_ctx_name, global_ctx in ctx_all.items():
573573
if global_ctx_name not in ctx2files:
574574
ctx_delete.add(global_ctx_name)
575-
# delete all global_ctxs that have changeed source or mtime
575+
# delete all global_ctxs that have changed source or mtime
576576
for global_ctx_name, src_info in ctx2files.items():
577577
if global_ctx_name in ctx_all:
578578
ctx = ctx_all[global_ctx_name]

custom_components/pyscript/trigger.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,15 @@ def init(cls, hass):
136136
cls.hass = hass
137137

138138
def wait_until_factory(ast_ctx):
139-
"""Return wapper to call to astFunction with the ast context."""
139+
"""Return wrapper to call to astFunction with the ast context."""
140140

141141
async def wait_until_call(*arg, **kw):
142142
return await cls.wait_until(ast_ctx, *arg, **kw)
143143

144144
return wait_until_call
145145

146146
def user_task_create_factory(ast_ctx):
147-
"""Return wapper to call to astFunction with the ast context."""
147+
"""Return wrapper to call to astFunction with the ast context."""
148148

149149
async def user_task_create(func, *args, **kwargs):
150150
"""Implement task.create()."""
@@ -610,6 +610,7 @@ async def parse_date_time(cls, date_time_str, day_offset, now, startup_time):
610610
year = now.year
611611
month = now.month
612612
day = now.day
613+
fixed_date = False
613614

614615
dt_str_orig = dt_str = date_time_str.strip().lower()
615616
#
@@ -623,6 +624,7 @@ async def parse_date_time(cls, date_time_str, day_offset, now, startup_time):
623624
else:
624625
month, day = int(match0[1]), int(match0[2])
625626
day_offset = 0 # explicit date means no offset
627+
fixed_date = True
626628
dt_str = dt_str[len(match0.group(0)) :]
627629
elif match1:
628630
skip = True
@@ -632,11 +634,17 @@ async def parse_date_time(cls, date_time_str, day_offset, now, startup_time):
632634
day_offset = dow - (now.isoweekday() % 7)
633635
else:
634636
day_offset = 7 + dow - (now.isoweekday() % 7)
637+
fixed_date = True
635638
elif match1[1] == "today":
636639
day_offset = 0
640+
fixed_date = True
637641
elif match1[1] == "tomorrow":
638642
day_offset = 1
643+
fixed_date = True
639644
else:
645+
if match1[1] == "now":
646+
day_offset = 0
647+
fixed_date = True
640648
skip = False
641649
if skip:
642650
dt_str = dt_str[len(match1.group(0)) :]
@@ -649,7 +657,7 @@ async def parse_date_time(cls, date_time_str, day_offset, now, startup_time):
649657
now = dt.datetime(year, month, day)
650658
dt_str = dt_str.strip()
651659
if len(dt_str) == 0:
652-
return now
660+
return now, fixed_date
653661

654662
#
655663
# parse the time
@@ -682,7 +690,7 @@ async def parse_date_time(cls, date_time_str, day_offset, now, startup_time):
682690
except Exception:
683691
_LOGGER.warning("'%s' not defined at this latitude", dt_str)
684692
# return something in the past so it is ignored
685-
return now - dt.timedelta(days=100)
693+
return now - dt.timedelta(days=100), fixed_date
686694
now += time_sun.date() - now.date()
687695
hour, mins, sec = time_sun.hour, time_sun.minute, time_sun.second
688696
elif dt_str.startswith("noon"):
@@ -707,7 +715,7 @@ async def parse_date_time(cls, date_time_str, day_offset, now, startup_time):
707715
dt_str = dt_str.strip()
708716
if len(dt_str) > 0:
709717
now = now + dt.timedelta(seconds=parse_time_offset(dt_str))
710-
return now
718+
return now, fixed_date
711719

712720
@classmethod
713721
async def timer_active_check(cls, time_spec, now, startup_time):
@@ -737,8 +745,8 @@ async def timer_active_check(cls, time_spec, now, startup_time):
737745
_LOGGER.error("Invalid range expression: %s", exc)
738746
return False
739747

740-
start = await cls.parse_date_time(dt_start.strip(), 0, now, startup_time)
741-
end = await cls.parse_date_time(dt_end.strip(), 0, start, startup_time)
748+
start, _ = await cls.parse_date_time(dt_start.strip(), 0, now, startup_time)
749+
end, _ = await cls.parse_date_time(dt_end.strip(), 0, start, startup_time)
742750

743751
if start <= end:
744752
this_match = start <= now <= end
@@ -802,20 +810,20 @@ async def timer_trigger_next(cls, time_spec, now, startup_time):
802810
next_time_adj = now + delta
803811

804812
elif len(match1) == 3:
805-
this_t = await cls.parse_date_time(match1[1].strip(), 0, now, startup_time)
813+
this_t, _ = await cls.parse_date_time(match1[1].strip(), 0, now, startup_time)
806814
day_offset = (now - this_t).days + 1
807815
if day_offset != 0 and this_t != startup_time:
808816
#
809817
# Try a day offset (won't make a difference if spec has full date)
810818
#
811-
this_t = await cls.parse_date_time(match1[1].strip(), day_offset, now, startup_time)
819+
this_t, _ = await cls.parse_date_time(match1[1].strip(), day_offset, now, startup_time)
812820
startup = now == this_t and now == startup_time
813821
if (now < this_t or startup) and (next_time is None or this_t < next_time):
814822
next_time_adj = next_time = this_t
815823

816824
elif len(match2) == 5:
817825
start_str, period_str = match2[1].strip(), match2[2].strip()
818-
start = await cls.parse_date_time(start_str, 0, now, startup_time)
826+
start, fixed_date_start = await cls.parse_date_time(start_str, 0, now, startup_time)
819827
period = parse_time_offset(period_str)
820828
if period <= 0:
821829
_LOGGER.error("Invalid non-positive period %s in period(): %s", period, time_spec)
@@ -832,12 +840,17 @@ async def timer_trigger_next(cls, time_spec, now, startup_time):
832840
next_time_adj = next_time = this_t
833841
continue
834842
end_str = match2[3].strip()
835-
end = await cls.parse_date_time(end_str, 0, now, startup_time)
836-
end_offset = 1 if end < start else 0
837-
for day in [-1, 0, 1]:
838-
start = await cls.parse_date_time(start_str, day, now, startup_time)
839-
end = await cls.parse_date_time(end_str, day + end_offset, now, startup_time)
840-
if now < start or (now == start and now == startup_time):
843+
end, fixed_date_end = await cls.parse_date_time(end_str, 0, now, startup_time)
844+
if not fixed_date_start and not fixed_date_end:
845+
end_offset = 1 if end < start else 0
846+
day_dither = [-1, 0, 1]
847+
else:
848+
end_offset = 0
849+
day_dither = [0]
850+
for day in day_dither:
851+
start, _ = await cls.parse_date_time(start_str, day, now, startup_time)
852+
end, _ = await cls.parse_date_time(end_str, day + end_offset, now, startup_time)
853+
if (now < start or (now == start and now == startup_time)) and start <= end:
841854
if next_time is None or start < next_time:
842855
next_time_adj = next_time = start
843856
break

tests/test_unit_trigger.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ async def test_parse_date_time(hass, caplog):
8989
):
9090
for test_data in parseDateTimeTests:
9191
spec, date_offset, expect = test_data
92-
out = await TrigTime.parse_date_time(spec, date_offset, now, now)
92+
out, _ = await TrigTime.parse_date_time(spec, date_offset, now, now)
9393
assert out == expect
9494
await Function.waiter_sync()
9595
await Function.waiter_stop()
@@ -128,7 +128,7 @@ async def test_parse_date_time_day_names(hass, caplog):
128128
):
129129
for test_data in parseDateTimeTestsDayNames:
130130
spec, date_offset, expect = test_data
131-
out = await TrigTime.parse_date_time(spec, date_offset, now, now)
131+
out, _ = await TrigTime.parse_date_time(spec, date_offset, now, now)
132132
assert out == expect
133133
await Function.waiter_sync()
134134
await Function.waiter_stop()
@@ -358,6 +358,44 @@ async def test_timer_active_check(hass, spec, now, expected):
358358
None,
359359
],
360360
],
361+
# see #714 for these two cases - in 1.6.4 and prior, they keep triggering since the
362+
# end time is considered tomorrow
363+
[
364+
["period(now, 1 hours, 15:00.1)"],
365+
[
366+
dt(2019, 9, 1, 13, 0, 0, 100000),
367+
dt(2019, 9, 1, 14, 0, 0, 100000),
368+
dt(2019, 9, 1, 15, 0, 0, 100000),
369+
None,
370+
],
371+
],
372+
[
373+
["period(now, 1 hours, 10:00)"],
374+
[
375+
None,
376+
],
377+
],
378+
[
379+
["period(now, 1 hours, today 15:01)"],
380+
[
381+
dt(2019, 9, 1, 13, 0, 0, 100000),
382+
dt(2019, 9, 1, 14, 0, 0, 100000),
383+
dt(2019, 9, 1, 15, 0, 0, 100000),
384+
None,
385+
],
386+
],
387+
[
388+
["period(now, 1 hours, 10:00)"],
389+
[
390+
None,
391+
],
392+
],
393+
[
394+
["period(today 11:00, 1 hours, today 10:00)"],
395+
[
396+
None,
397+
],
398+
],
361399
[
362400
["cron(0 14 * * *)"],
363401
[

0 commit comments

Comments
 (0)