10/18/2023 Map Stopped Working?
Visit this post for the fix
My map didn't turn off tonight. I looked at the code, and as near as I could tell, the problem is that you set the time to turn the map off and it includes the date when the script is started, so there's some complicated footwork in the script to work with the datetime objects. I think I was able to simplify it using the technique in this post.
The biggest problem I ran into is that the datetime and time functions get overloaded. Fortunately, Python lets us pull in functions with different names. So, I make one import change, to import a different time function.
@@ -72,6 +72,7 @@
import time
from datetime import datetime
from datetime import timedelta
+from datetime import time as time_
from rpi_ws281x import * #works with python 3.7. sudo pip3 install rpi_ws281x
import sys
import os
By importing the time functions as "time_" (with the underscore), I can use them without changing any of the other code in the script. This is important in my next chunk, where I define the time of day as a time_ type. (See https://topherpedersen.blog/2019/04/18/attributeerror-type-object-datetime-time-has-no-attribute-time/ for why I needed to do this. This took a lot of time to find!)
@@ -302,6 +303,8 @@
timeoff = now.replace(hour=offhour, minute=offminutes, second=0, microsecond=0) #When to turn map off
timeon = now.replace(hour=onhour, minute=onminutes, second=0, microsecond=0) #When to turn map back on
diff = timeon - timeoff
+lights_out = time_(offhour, offminutes, 0)
+lights_on = time_(onhour, onminutes, 0)
delay_time = 10 #Number of seconds to delay before retrying to connect to the internet.
temp_time_flag = 0 #Set flag for next round if sleep timer is interrupted by button push.
Next, I define a new function that compares the current clock time to the start and end times. This naturally takes care of the problem where you have times that might span over the day boundary. You're either in between the two numbers (because it's a range like 10:00-14:00), or you're outside the range (because if you're trying to tell if you're between 23:00 and 08:00, you look to be either past the start time or earlier than the stop time).
def time_in_range(start, end, x):
if start <= end:
return start <= x <= end
else:
return start <= x or x <= end
Then, I can replace all the time delta stuff in the main loop, and the difference, and checking whether it was a day earlier or not with a call to my new function:
@@ -1219,13 +1233,17 @@
end_time = temp_end_time
temp_time_flag = 0 #reset flag for next round- if timeoff <= datetime.now() <= end_time:
+
+# if timeoff <= datetime.now() <= end_time:
+# Alternate clock check using defined function
+ if time_in_range(lights_out, lights_on, datetime.now().time()):
sys.stdout.write ("\n\033[1;34;40m Sleeping- ") #Escape codes to render Blue text on screen
sys.stdout.flush ()
turnoff(strip)
logger.info("Map Going to Sleep")- while timeoff <= datetime.now() <= end_time:
+# while timeoff <= datetime.now() <= end_time:
+ while time_in_range(lights_out, lights_on, datetime.now().time()):
temp_timeoff = timeoff #store original timeoff time and restore later.
sys.stdout.write ("z")
sys.stdout.flush ()
So, same logic, but because I'm able to do it all as clock time, the code is a bit more readable, and it's more robust. I think I was seeing problems with scripts not turning the lights off at night, because if you start the script after midnight, what will happen is that the timeoff variable gets set to the time later. The time_in_range function will work no matter what kind of range you have, and it doesn't need to pull in new timestamps to keep the day in sync when all you want is the clock time.
Wow, that's awesome. I wouldn't have known about the script startup issue, so thanks for the research. Please run this for a bit and verify that its working as expected, then let me know and if you don't mind, I'll incorporate it into the next update and be sure to give you credit in the source code and 'thanks' on the web site as well. - Mark
@markyharris Yeah, time comparisons are always fiddly. It took a bit of work to figure out what was going on - I think what's happening is that the script gets the current date (MM/DD/YYYY) and then tries to have an offset for that to convert it to the current day if the script runs a while. My first attempt at a fix was to use the time() function, but you can only do that if you import one of them into a slightly different namespace. As is typical for a bugfix, it's not many lines, but the understanding of how to write the lines was 90% of the work.
You're certainly welcome to the patch. The whole reason for sharing is to put it into the project!
What are you doing for version control right now? I'd be happy to dig into github to lend a hand - it would make both dev & contribution easier, and I'm quarantined and could do so as another contribution. I use git to keep my keyboard firmware in sync between three machines I make changes on, while keeping up with the release schedule, so I know it will work for a simpler project like this!
Small update on this: patch seems to be working reliably. The map has gone off every night at the desired time, and has come on at the scheduled time as well. I'm going to give it a week before making a final report - the previous version of the script worked most of the time but would usually fail to toggle the lights once every few days. I'd either see the lights on before the time they turn on, or they wouldn't go off at night. After a week, I'll feel pretty good this is an improvement.
Great news. Once you are satisfied with it, please let me know so I can get the patch from you and incorporate it. - Mark