1

I have 2 parameters in DB: start and stop. value for them can be eg 07:00-23:00 or 23:00-07:00 (start after 07, stop after 23 or start after 23, stop after 07)

In that time, a status must be 0 or 1, let's say it's LED

How to create unified logic controller that won't mess up after/before midnight?

My poor implementation (wont work) is below. Actually, I've tried many-many variations and still ended up where I currently am..

            if curtime >= vv_time_trig1 and curtime <= vv_time_trig2:
                logger.info("turning socket on")
                logger.debug("#1")

                #check current status
                #if current is 0
                #turn socket on
                if vvstatus == 0:
                    logger.debug("current status off, turning socket on")
                    GPIO.output(25, GPIO.HIGH)

                #check current status
                #if current is already 1
                #do nothing
                elif vvstatus == 1:
                    logger.info("skiping. already on")

                #unhandeled current status
                else:
                    logger.critical("unhandeled vvstatus!")
                    logger.critical("turning socket off")
                    GPIO.output(25, GPIO.LOW)

            #if current time is before start
            #turn off
            elif curtime <= vv_time_trig1 and curtime >= vv_time_trig2:
                logger.info("turning socket off")
                logger.debug("#2")

                #check current status
                #if current is 1
                #turn socket off
                if vvstatus == 1:
                    logger.debug("current status on, turning socket off")
                    GPIO.output(25, GPIO.LOW)

                #check current status
                #if current is already 0
                #do nothing
                elif vvstatus == 0:
                    logger.info("skiping. already off")

                #unhandeled current status
                else:
                    logger.critical("unhandeled vvstatus!")
                    logger.critical("turning socket off")
                    GPIO.output(25, GPIO.LOW)

            #if current time is after stop
            #turn off
            elif curtime >= vv_time_trig2:
                logger.info("turning socket off")
                logger.debug("#3")

                #check current status
                #if current is 1
                #turn socket off
                if vvstatus == 1:
                    logger.debug("current status: %s, turning socket off", vvstatus)
                    GPIO.output(25, GPIO.LOW)

                #check current status
                #if current is already 0
                #do nothing
                elif vvstatus == 0:
                    logger.info("skiping. already on")

                #unhandeled current status
                else:
                    logger.critical("unhandeled vvstatus!")
                    logger.critical("turning socket off")
                    GPIO.output(25, GPIO.LOW)

            #if current time is before stop
            #turn off
            elif curtime <= vv_time_trig2 and curtime <= vv_time_trig1:
                logger.info("turning socket on")
                logger.debug("#4")

                #check current status
                #if current is 0
                #turn socket on
                if vvstatus == 0:
                    logger.debug("current status off, turning socket on")
                    GPIO.output(25, GPIO.HIGH)

                #check current status
                #if current is already 1
                #do nothing
                elif vvstatus == 1:
                    logger.info("skiping. already on")

                #unhandeled current status
                else:
                    logger.critical("unhandeled vvstatus!")
                    logger.critical("turning socket off")
                    GPIO.output(25, GPIO.LOW)

Updated version. Determine current position in time relative to the end. end is set to tomorrow if midnight passes

            n1 = datetime.now()
            startTrig = datetime(n1.year, n1.month, n1.day, 23, 00, 0)
            logger.debug("start: %s",startTrig)

            n = datetime.now()
            endTrig = datetime(n.year, n.month, n.day, 07, 00, 0)
            logger.debug("end: %s",endTrig)

            if startTrig > endTrig:
                logger.debug("start > stop")
                endTrig += timedelta(days=1)
                logger.debug("new stop trig: %s",endTrig)


            if datetime.now() < endTrig:

                if curStatus == 1:
                    logger.debug("socket %s already on. doing nothing.")

                elif curStatus == 0:
                    logger.debug("socket %s sould be on. flipping switch")
                    flipSocketStatus(bcmNo,bcmDir)

                else:
                    logger.critical("unhandeled socket %s current status %s",socName,curStatus)

                    if curStatus == 1:
                        logger.critical("shutting socket %s down",socName)
                        GPIO.output(bcmNo, GPIO.LOW)

                    elif curStatus == 0:
                        logger.warn("socket %s already off",socName)

                    else:
                        logger.critical("unhandeled current status for pin: %s",bcmNo)
                        logger.critical("forcing socket %s down",socName)
                        GPIO.output(bcmNo, GPIO.LOW)
            else:
                logger.critical("unhandeled start-stop rules")
user1492810
  • 87
  • 1
  • 7
  • unrelated: do not use the local time internally, convert it to UTC as soon as possible, [here's an example of why](http://stackoverflow.com/a/26313848/4279). – jfs Feb 17 '15 at 00:48
  • thank you for the tip, i see what's going on. – user1492810 Feb 18 '15 at 12:12

2 Answers2

2

There are two cases: the current time is between given times (clock-wise) or outside (imagine the clock circle):

#!/usr/bin/env python
from datetime import datetime

def in_between(now, start, end):
    if start < end: # e.g., "07:00-23:00"
        return start <= now < end
    elif end < start: # e.g., "23:00-07:00"
        return start <= now or now < end
    else: # start == end
        return True # consider it 24 hour interval

now = datetime.now().time()
for date_range in ["07:00-23:00", "23:00-07:00"]:
    start, end = [datetime.strptime(s, "%H:%M").time()
                  for s in date_range.split("-")]
    not_ = '' if in_between(now, start, end) else 'not '
    print("{now:%H:%M} is {not_}in between {date_range}".format(**vars()))

Output

02:26 is not in between 07:00-23:00
02:26 is in between 23:00-07:00
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • That's excactly what I was looking for: def in_between(now, start, end): if start < end: # e.g., "07:00-23:00" return start <= now < end elif end < start: # e.g., "23:00-07:00" return start <= now or now < end else: # start == end return True # consider it 24 hour interval – user1492810 Feb 17 '15 at 21:58
1

UPDATE: Based on your update

This looks better, the only thing I'm confused about is your if datetime.now() < endTrig conditional.

Correct me if I'm still misunderstanding you, but your current conditional looks like it reads:

if now() is before endTrigger:
  - if status is 1, we are good, stay on
  - if status is 0, turn on
  - if status is anything else (but cant it only be 1 or 0?):
    * if status is 1 ... (but wait, we already checked for this above!)
    * if status is 0 ... (ditto, these two checks will never trigger)
    * otherwise, force off
if now() is after endTime:
  - some logging (shouldnt this be where we turn off?)

You mentioned status must be 0 or 1. Based on that I would expect your conditional to look something like:

.
. [start/end setup stuff]
.
# If we haven't reached end time yet
if datetime.now() < endTrig:
    # If status is 1, we're already on
    if curStatus == 1:
        log('Already on')
    # If status isn't 1, it must be 0 and we should turn on
    else:
        log('Turn on')
        flipSwitchOn()

# If we have passed the end time
else:
    # If we're on, time to turn off
    if curStatus == 1:
        log('Turning off')
        flipSwitchOff()
    # If its not 1 it must be 0, we're off, stay off
    else:
        log('Already off')
dbernard
  • 451
  • 3
  • 14
  • the problem with this solution is that I may change start and end values at any given time. added to that, my program (or raspberry) can reboot while working and for correcting it's status it needs to know exact point in time. – user1492810 Feb 15 '15 at 13:44
  • I anticipated that might be the issue. Check my updated answer and let me know if that looks better! – dbernard Feb 15 '15 at 14:40
  • Took me a while, but I think i understand what you want to say. I updated my original post, please correct me if i'm wrong. With modified code, i always end up in logger.debug("socket %s already on. doing nothing.") No matter if start is before or after midnight, end as same I don't need sleep loops as this is a tiny bit from much larger process that handeles that problem :) – user1492810 Feb 16 '15 at 22:55
  • my script runs periodically every n seconds, between 1 and 5 – user1492810 Feb 16 '15 at 23:31
  • I see what you're saying. Updated my answer for you! Hopefully this keeps you moving forward! Let me know if theres more, im happy to help – dbernard Feb 17 '15 at 01:04
  • Thank you so much for your effort, your solution is valid too – user1492810 Feb 17 '15 at 22:02