W. Churchill
W. Churchill

Reputation: 356

Programming a thermostat in Python with “if statement” logic for time scheduling

I am working on a thermostat that can be programmed to be off according to the time of day.

This is my thermostat function:

def thermostat(ac_on, acPower, temp_inside, Temp_desired):
   if temp_inside > Temp_desired:
        ac_on = True
   if temp_inside < Temp_desired:
        ac_on = False
   ac_heatflow = acPower if ac_on == True else 0
   return ac_heatflow, ac_on

Temp_desired is a set integer value, and temp_inside is the changing indoor temperature value that gets compared to Temp_desired (right now every couple of seconds) to see if the air conditioner (A/C) will be “on,” or “off.” ac_heatflow is the power usage when the A/C is on, and acPower is the A/C power value assigned outside of the function.

My issue now is that I want to now add a time component to my thermostat, where the the A/C can be programmed to be off. For example, the A/C will work normally throughout the day, but from 8:00am to 10:00am it must be off, and from 3:00pm to 5:45pm it must be off, so during these intervals ac_on = False, but outside these intervals the thermostat will revert back to the original method where it determines if the air conditioner is on/off based on the room temperature.

The aforementioned time intervals will be in seconds when used in the function, so 8:00am would be 28800, 10:00am --> 36000, 3:00pm --> 54000, 5:45pm --> 63900.

This is what I have been trying:

def thermostat(ac_on, acPower, temp_inside, Temp_desired, start, end, t):
    while t >= start or t <= end:
        ac_on = False 
    else:
        if temp_inside > Temp_desired + 2:
            ac_on = True
        if temp_inside < Temp_desired:
            ac_on = False
    ac_heatflow = 400 if ac_on == True else 0
    return ac_heatflow, ac_on

start is a start time of the interval, end is the interval end time and t right now is in seconds. There is a calculation of temperature every couple of seconds over a 24 hour period, a full day is 86400 seconds, so start and end can be any value between 0 and 86400, for example, start = 1000 and end = 3000. The issue with the above code is that there is no output, it seems as though Python is “hung” up on that initial while statement, so I have to stop the script. I also tried:

if t >= start or t <= end:
        ac_on = False 
else:
    if temp_inside > Temp_desired + 2:
        ac_on = True
    if temp_inside < Temp_desired:
        ac_on = False
ac_heatflow = 400 if ac_on == True else 0
return ac_heatflow, ac_on

But this sets all the values for ac_heatflow to 0.

I am unsure how to code the function so that it can be programmed to also take into account the time of day. Maybe this is a nested loop issue, or perhaps it requires a separate function that focuses on defining the time intervals and feeds those assignments into the thermostat function.

Upvotes: 0

Views: 2568

Answers (2)

Xero Smith
Xero Smith

Reputation: 2076

The key is to use pythons datetime library. Some pointers:

  1. You do not use the prior value of ac_on in you function so there is no need to pass it in.
  2. The current time can be found in the function body using the datetime lib
  3. Since you are accustomed to pythons conditional assignment, we can be moe concise and pythonic with a little refactoring.

    import datetime
    
    def ithermostat(temp, ac_power, desired_temp,\
       off_times={'morning':(datetime.time(8, 0), datetime.time(10, 0)),\
                  'afternoon':(datetime.time(15, 0), datetime.time(17, 45))}):
    
        """(num, num, num, dict) -> (num, bool)
    
    Return the tuple (ac_power_usage per unit time, ac_on status) of the thermostat
    The ac is turned on only when the current time is not in off times AND
    the tempreture is higher than the desired tempreture
    """
    
    off_period = False # this flag will be set when the system is in the off period
    current_time = datetime.datetime.now().time()
    for k, v in off_times.items():
        print(v)
        if v[0] <= current_time <= v[1]:
            print("system in mandatory off period")
            off_period = True
            break
    
    ac_on = False if off_period else temp > desired_temp   
    ac_power_usage = ac_power if ac_on else 0
    return ac_power_usage, ac_on
    

A more concise more readable and more pythonic version of the same function is as follows:

def ithermostat2(temp, ac_power, desired_temp,\
           off_times = {'morning':(datetime.time(8, 0), datetime.time(10, 0)),\
                        'afternoon':(datetime.time(15, 0), datetime.time(17, 45))}):
    """(num, num, num, dict) -> (num, bool)

    Return the tuple (ac_power_usage per unit time, ac_on status) of the thermostat
    The ac is turned on only when the current time is not in off times AND
    the tempreture is higher than the desired tempreture
    """

    current_time = datetime.datetime.now().time()
    off_period = any(v[0] <= current_time <= v[1] for k, v in off_times.items()) #check if the current time is in off_times
    ac_on = False if off_period else temp > desired_temp   
    ac_power_usage = ac_power if ac_on else 0
    return ac_power_usage, ac_on

Both functions have been tested and work well.

Upvotes: 0

tim-mccurrach
tim-mccurrach

Reputation: 6815

The problem is with the expression t >= start or t <= end. Firstly what are start an end? You describe in the situation two time periods, but you are only passing into the function arguments for one potential time period?

Lets assume start=100 and end =700. If t is before the start, say t=5. Well t is still less than 700, so this statement will be true. Or if t is after the end, say t=705, then t is still larger than 100, so again this expression will evaluate to true (which is why ac_on is always False). Basically no matter what the value of t this statement will always be true. I think what you want is t >= start and t <= end.

Although I am still slightly confused by the two time periods you have described, maybe pass in 4 arguments, start1, end1, start2 and end2, then use:

if (t >= start1 and t <= end1) or (t >= start2 and t <= end2):

Upvotes: 1

Related Questions