Track time spent on an activity

If you hate filling up your time sheet every week or need to log time for any activity really, then you have come to right place. This blog post explains how you can automate that process with push of a button and a Raspberry Pi.

When the user presses the button, the program notes down the start time and turns the LED on. When the button is pressed again, the program notes down the finish time for that activity and saves the time spent in the CSV file, turns off the LED and beeps the buzzer for two seconds to indicate successful save. Functionality is explained below in more detail.

CSV file generated by script
CSV file created by the python script

Hardware Requirements

  • Raspberry Pi
  • Jumper wires
  • Breadboard
  • Button
  • LED
  • Resistor
  • Buzzer

Wiring

Wiring Diagram
Wiring LED, Buzzer and Push Button with Raspberry Pi

Software

The following example creates a new CSV file every time the program starts and then while the program is running, each record is saved in that file. I have also provided link to weekly.py which creates a new file for every week. With little modification, this program can be used for other time combinations as well.

Importing the dependencies

    import RPi.GPIO as GPIO
    import time
    import csv
  

Raspberry Pi board

    GPIO.setmode(GPIO.BCM)
  

Pin Configuration

    # Pin config for Button
    btn = 21
    GPIO.setup(btn, GPIO.IN, pull_up_down=GPIO.PUD_UP)

    # Pin config for LED
    led = 20
    GPIO.setup(led, GPIO.OUT)

    # Pin config for Buzzer
    buzzer = 16
    GPIO.setup(buzzer, GPIO.OUT)
  

Global Variables

We will use these variables later in the main() function. These will update with every new record.
    is_working = False
    start_time_UTC = None
    start_date_local = None
    start_time_local = None
  

The main() function

The main() function is the first function that gets called when program starts. It creates a new comma separated values (CSV) file and then waits for the user to press the button and when that happens, it checks whether the user is already working or not. If the user is not working, then it sets the is_working variable to True, turns the LED on and note down the start time.

When the user presses the button again, the function checks the value of is_working variable again and because user was already working, so it changes the value to False and note down the finish time. It then calculates the time spent working on an activity and also saves the date, time, start and finish times in the file. After saving, LED is turned off and buzzer beeps for 2 seconds to indicate successful time logged.

    def main():
      global is_working
      global start_time_UTC
      global start_date_local
      global start_time_local

      # name of the file contains date and time of creation e.g. 08 May 2018 2102 
      file_name = time.strftime('%d %b %Y %H%M', time.localtime()) + '.csv' 
      createCSV(file_name) # create the file when program starts

      while True:
        if GPIO.input(btn) == False: 
          if(is_working): 
            is_working = False
            finish_time_UTC = time.time() # only used to calcuate the worked hours
            finish_date_local = time.strftime('%d %b %Y', time.localtime())
            finish_time_local = time.strftime('%I:%M %P', time.localtime())
            # save the record
            updateCSV(file_name, start_date_local, start_time_local, finish_date_local, finish_time_local, calculateWorkTime(finish_time_UTC))
            GPIO.output(led, GPIO.LOW) # turn the LED off to indicate not working
            GPIO.output(buzzer, GPIO.HIGH) # beep the buzzer for 2 seconds to indicate that record has been saved.
            time.sleep(2)
            GPIO.output(buzzer, GPIO.LOW)
          else: # Executed when push button is pressed for 1st time. Saves the start time and turn the led on.
            is_working = True
            start_time_UTC = time.time()
            start_date_local = time.strftime('%d %b %Y', time.localtime())
            start_time_local = time.strftime('%I:%M %P', time.localtime())
            GPIO.output(led, GPIO.HIGH)
          time.sleep(0.5)
  

Since, the code is wrapped in a While loop, so the program keeps running and does not stop unless stopped manually and time recorded is saved in the same CSV file as a new record. The other functions used in the main() function are explained below.

The createCSV() function

create_CSV() function takes file_name as an argument and takes advantage of the Python's built in CSV module to create a comma separated value file in working directory. This function, however, does not save any data in the file except the headings. If for any reason, it failed to create the file, then the reason will be printed on the console and buzzer will start to beep.

    def createCSV(file_name):
      try:
        with open(file_name, 'w', newline='') as f:
          writer = csv.writer(f) 
          GPIO.output(buzzer, GPIO.LOW) # turn off buzzer to clear last exception if any
          writer.writerow(['Start Date', 'Start Time', 'Finish Date', 'Finish Time', 'Hours Worked (hh:mm)'])
      except Exception as e:
        print(e)
        GPIO.output(buzzer, GPIO.HIGH)
  

The updateCSV() function

As the name suggests, this function saves the date, time and hours worked on an activity to the file earlier function created. This function is called from main() function when an activity is ended by pushing the button. Same as before, any exception caused by this function will be logged to console and if that happens program won't stop running but the user will be notified with beep sound untill user presses the button again.

    def updateCSV(file_name, start_date_local, start_time_local, finish_date_local, finish_time_local, hours_worked):
      try:
        with open(file_name, 'a', newline='') as f:
          writer = csv.writer(f)
          GPIO.output(buzzer, GPIO.LOW) # turn off buzzer to clear last exception if any
          writer.writerow([start_date_local, start_time_local, finish_date_local, finish_time_local, hours_worked])
      except Exception as e:
        print(e)
        GPIO.output(buzzer, GPIO.HIGH) # make constant sound with buzzer to indicate exception in program
  

The calculateWorkTime() function

This piece of code calculates the difference between start and finish time and then returns it in user friendly hours and minutes format.

    def calculateWorkTime(finish_time_UTC):
      work_time = finish_time_UTC - start_time_UTC
      work_time = time.strftime('%H:%M', time.gmtime(work_time))
      return work_time
  

Starting point of our application

This is where we call the main() function. Try block will run if everything is okay and if program is not able to run for any reason then that reason will be printed. Finally, we cleanup on exit.

    try:
      GPIO.output(buzzer, GPIO.LOW) # turn off buzzer to clear last exception if any
      main()
    except Exception as e:
      print(str(e))
      GPIO.output(led, GPIO.LOW) # turn the led off
      GPIO.output(buzzer, GPIO.HIGH) # make constant sound with buzzer to indicate exception in program
    finally:
      GPIO.output(led, GPIO.LOW)
      GPIO.output(buzzer, GPIO.LOW)
      GPIO.cleanup()
  

Running the program

There are two versions of the program namely app.py and weekly.py. The code above is in app.py file and weekly.py contains code for program which creates one CSV file per week and all the week's time tracked should be in that file. The file can be modified to create the file monthly depending on your requirements.

Download the desired file from github, open up your terminal and cd into the folder where you downloaded the file and run the following command.

    python3 (name of the file you downloaded)
  

That's it. Thanks for reading. Please do share if you find this post useful.