peterhinch / micropython-samples

Assorted code ideas, unofficial MP FAQ, plus index to my other repositories.
MIT License
442 stars 91 forks source link

Astronomy Integration With Scheduling #39

Closed Beormund closed 4 months ago

Beormund commented 4 months ago

Hi Peter,

With respect to:

import uasyncio as asyncio
from sched.sched import schedule
from sched.sun_moon import RiSet

async def turn_off_lights(rs):  # Runs every day at 00:01:00
    rs.set_day()  # Re-calculate for new daylight
    await asyncio.sleep(rs.sunrise() - 60)
    # Actually turn them off

async def main():
    rs = RiSet()  # May need args for your location
    await schedule(turn_off_lights, rs, hrs=0, mins=1)  # Never terminates

try:
    asyncio.run(main())
finally:
    _ = asyncio.new_event_loop()

Using this approach, when main() is run before today's sunrise/sunset the cron fires tne next day at 00:01:00 so today's sunset never triggers. The integration with cron is beneficial as it is useful to be able to run events at sunrise/sunset only for certain days of the week etc. The issue is similar to schedule initialisation where wait_for is used to pause execution until a few seconds before the first cron run.

peterhinch commented 4 months ago

I'm not sure what the problem is. The code does behave as you describe, and the benefit of using schedule is also as you describe.

If it is important that the trigger also occurs on the day the code starts running you could do something like:

async def main():
    rs = RiSet()  # May need args for your location
    rs.set_day()
    if (tw := rs.sunrise(1) - time.time()) > 0:  # Today's sunrise is in the future
        await asyncio.sleep(tw)
        #actually turn them off
    await schedule(turn_off_lights, rs, hrs=0, mins=1)  # Never terminates

Clearly "actually turn them off" would now be best implemented as a function as it is now called from two places.