Advertisement
whitehusky

WeeWxUpdateFromWFAPI.py

Jun 24th, 2020
392
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.03 KB | None | 0 0
  1. #
  2. # Python 3 program to:
  3. #   1. Download a days worth of data from the WeatherFlow API in CSV format (or use a provided CSV file)
  4. #   2. Upadte the weewx.sdb file for the data in the CSV file:
  5. #       2a. lightning_strike_count = Total number of lightning strikes during the 5 minute time period
  6. #       2b. lightning_distance = AverageLightning Distance during the 5 minute time period
  7. #       2c. rain = Amount of rain in millimeters during the 5 minute time period
  8. #       2d. rainrate = Amount of rain if received over an hour period (rain * 12)
  9. #       2e. rainBatteryStatus = Precipitation Type (0=None; 1=Rain; 2=Hail)
  10. #       2f. signal7 = Closest (Min) Lightning Distance during the 5 minute time period
  11. #       2g. signal8 = Furthest (Max) Lightning Distance during the 5 minute time period
  12. #
  13. # By James Bellanca
  14. #
  15.  
  16. #
  17. # Can take one of two command line arguments.
  18. # Both cannot be used at the same time. The last one entered wins.
  19. # If no argument is specified, it assumes -d 1 to retrieve and process yesterday's data.
  20. #
  21. # -d, -DaysAgo x Retrieves data from the WeatherFlow API for x days ago and processes it.
  22. #                The file is saved in the folder that this program is in.
  23. # -f, -File x    Processes the file x. It must be a properly formatted WeatherFlow API csv file.
  24.  
  25. from datetime import datetime, timedelta
  26. import sys, getopt
  27. import time
  28. import subprocess
  29. import csv
  30. import sqlite3
  31. import urllib.request
  32.  
  33. # Set to the path and file of your WeeWx SDB database file
  34. DEFAULT_PATH = "/Users/FamilyMini/weewx/current/archive/weewx.sdb"
  35.  
  36. # Set the Tempest Device ID to your value
  37. stationID = "68706" # IMPORTANT -- Put you Device ID here!!!
  38.  
  39. # This is the demo API key which works for now for individual use
  40. APIkey = "20c70eae-e62f-4d3b-b3a4-8586e90f3ac8"
  41.  
  42. # Function to update the values in the WeeWx database
  43. def updateRow(nextTimeInterval, loopIndex, writeRainData, timestamp, averagingStrikeIndex, strike_distance, precip_final, local_daily_precip_final, strike_count, precipTypeMax, strikeDistanceMin, strikeDistanceMax):
  44.     nextTimeInterval = str(int(timestamp)+300)
  45.     if strikeDistanceMin == 999:
  46.         strikeDistanceMin = 0
  47.     if averagingStrikeIndex > 0:
  48.         strike_distance = round(strike_distance / averagingStrikeIndex, 2)
  49.     strikeDistanceMin = round(strikeDistanceMin / 1.609, 2) # Convert from KM to Miles
  50.     strikeDistanceMax = round(strikeDistanceMax / 1.609, 2)
  51.     strike_distance = round(strike_distance / 1.609, 2)
  52.     precip_final = round(precip_final/25.4, 6) #convert from mm to inches and round
  53.     rainRate = round(precip_final*12, 6) #convert from mm to inches and round
  54.     if strike_distance == 0:
  55.         strike_distance_write = "null"
  56.     else:
  57.         strike_distance_write = str(strike_distance)
  58.     if strikeDistanceMin == 0:
  59.         strikeDistanceMinWrite = "null"
  60.     else:
  61.         strikeDistanceMinWrite = str(strikeDistanceMin)
  62.     if strikeDistanceMax == 0:
  63.         strikeDistanceMaxWrite = "null"
  64.     else:
  65.         strikeDistanceMaxWrite = str(strikeDistanceMax)
  66.     local_daily_precip_final = round(local_daily_precip_final/25.4, 6) #convert mm to inches and round
  67.     sqltoexecute = "update archive set lightning_strike_count = " + str(strike_count) + ", lightning_distance = " + str(strike_distance_write) + ", signal7 = " + str(strikeDistanceMinWrite)+ ", signal8 = " + str(strikeDistanceMaxWrite) + " where dateTime = " + timestamp
  68.     cursorObj.execute(sqltoexecute)
  69.     con.commit()
  70.     print (sqltoexecute)
  71.     if writeRainData == 1:
  72.         sqltoexecute = "update archive set rain = " + str(precip_final) + ", rainrate = " + str(rainRate) + ", rainBatteryStatus = " + str(precipTypeMax)+ " where dateTime = " + timestamp
  73.         print (sqltoexecute)
  74.     cursorObj.execute(sqltoexecute)
  75.     con.commit()
  76.     return
  77.  
  78. argumentList = sys.argv[1:]
  79.  
  80. # Options
  81. options = "hd:f:"
  82.  
  83. # Long options
  84. long_options = ["Help", "Days Ago", "File ="]
  85.  
  86. try:
  87.     # Parsing argument
  88.     arguments, values = getopt.getopt(argumentList, options, long_options)
  89.    
  90.     daysAgo = 1
  91.     processingMode = 0 # 0 = not set, assume 1 day ago; 1 = retrieve file from x days ago; 2 = Process filen from command line arguments
  92.    
  93.     # checking each argument
  94.     for currentArgument, currentValue in arguments:
  95.    
  96.         if currentArgument in ("-h", "--Help"):
  97.             print ("usage: -d | -f | -h")
  98.             print ("")
  99.             print ("Options:")
  100.             print ("  -d x, --Days Ago x   Retrieves data from the WeatherFlow API for x days ago")
  101.             print ("  -f x, --File x       Processes the file x which must be a properly formatted WeatherFlow API csv file")
  102.             print ("  -h, --Help           Displays help")
  103.             print ("  No arguments         Same as running -d 1")
  104.             sys.exit()
  105.              
  106.         elif currentArgument in ("-d", "--DaysAgo"):
  107.             daysAgo = int(currentValue)
  108.             if daysAgo < 1 or daysAgo > 7:
  109.                 print("Days Ago argument must be between 1 and 7.")
  110.                 sys.exit()
  111.             else:
  112.                 processingMode = 1
  113.        
  114.         elif currentArgument in ("-f", "--File"):
  115.             processingMode = 2
  116.             print (("File to process (% s)") % (currentValue))
  117.             filename = str(currentValue)
  118.             print(filename)
  119.            
  120. except getopt.error as err:
  121.     # output error, and return with an error code
  122.     print (str(err))
  123.  
  124. if processingMode == 0 or processingMode == 1:
  125.     if daysAgo == 1:
  126.         print("Retrieving data from yesterday.")
  127.     else:
  128.         print ("Retrieving data from %s day(s) ago." % (daysAgo))
  129.     # URL uses the demo API key from WeatherFlow
  130.     url = 'https://swd.weatherflow.com/swd/rest/observations/device/' + stationID + '?api_key=' + APIkey + '&day_offset=' + str(daysAgo) + '&format=csv'
  131.     fileDate = datetime.now() - timedelta(hours=(daysAgo*24))
  132.     filename = "WF_"+str(fileDate.strftime("%Y-%m-%d"))+".csv"
  133.     print(url)
  134.     print(filename)
  135.     urllib.request.urlretrieve(url, filename)
  136.  
  137. # Open WeeWx SQLite Database
  138.            
  139. con = sqlite3.connect(DEFAULT_PATH)
  140. cursorObj = con.cursor()
  141.  
  142. # Main processing loop for the CSV file
  143.  
  144. with open(filename) as csvfile:
  145.    
  146.     # Set default values
  147.     rowIndex = -1
  148.     loopIndex = 0
  149.     averagingStrikeIndex = 0
  150.     precip_type = 0
  151.     nextTimeInterval = 0
  152.     timestamp = ""
  153.     strike_distance = float(0)
  154.     strike_count = int(0)
  155.     precip_final = float(0)
  156.     strikeDistanceMin = float(999)
  157.     strikeDistanceMax = float(0)
  158.     precipTypeMax = float(0)
  159.     writeRainData = 1 # Sometimes the daily downloaded data doesn't populate the final rain check data. If that happens, ignore he rain data in the file.
  160.    
  161.     # Read and process each row in the CSV file
  162.     readCSV = csv.reader(csvfile, delimiter=',')
  163.     for row in readCSV:
  164.         #print(row)
  165.         rowIndex+=1
  166.         if rowIndex > 0:
  167.                 timestamp = row[3]
  168.                 # Check to see if the current row has a time later than the next row to update in WeeWx.
  169.                 # This could happen if the csv file doesn't have an entry for the exact 5 minute entry that will be in WeeWx.
  170.                 if (int(timestamp) > int(nextTimeInterval)) and (int(nextTimeInterval) > 0):
  171.                     print("Timestamp missing! ", nextTimeInterval)
  172.                     # Call function to do the database update
  173.                     updateRow(nextTimeInterval, loopIndex, writeRainData, nextTimeInterval, averagingStrikeIndex, strike_distance, precip_final, local_daily_precip_final, strike_count, precipTypeMax, strikeDistanceMin, strikeDistanceMax)
  174.                     # Determine what the next WeeWx row should be to make sure we compute values at that row even if it's not present in the file
  175.                     nextTimeInterval = str(int(timestamp)+300)
  176.                     # Reset all values
  177.                     strike_distance = 0
  178.                     strike_count = 0
  179.                     precip_final = 0
  180.                     loopIndex = 0
  181.                     averagingStrikeIndex = 0
  182.                     strikeDistanceMin = 999
  183.                     strikeDistanceMax = 0
  184.                     precip_type = 0
  185.                     precipTypeMax = 0
  186.                 # Sum or set values as appropriate
  187.                 strike_distance += float(row[17])
  188.                 strike_count += int(row[18])
  189.                 if row[22] == "" or row[23] == "":
  190.                     writeRainData = 0 # Fina;l rain check data is blank, so stop writing rain data from this point on, only write lightning data
  191.                 else:
  192.                     precip_final += float(row[22])
  193.                     local_daily_precip_final = float(row[23])
  194.                     precip_type = int(row[16])
  195.                     if int(row[16]) > precipTypeMax:
  196.                         precipTypeMax = int(row[16])
  197.                 loopIndex+=1
  198.                 # Compute number of rows with lightning to compute the average, and record min/max distance over the period, and precip type (0=none; 1=rain; 2=hail)
  199.                 if int(row[18]) != 0:
  200.                     averagingStrikeIndex +=1
  201.                 if (int(row[17]) < strikeDistanceMin) and (int(row[18]) > 0):
  202.                     strikeDistanceMin = float(row[17])
  203.                 if int(row[17]) > strikeDistanceMax:
  204.                     strikeDistanceMax = float(row[17])
  205.                 if int(row[3])%300 == 0:
  206.                      # Call function to do the database update
  207.                      updateRow(nextTimeInterval, loopIndex, writeRainData, timestamp, averagingStrikeIndex, strike_distance, precip_final, local_daily_precip_final, strike_count, precipTypeMax, strikeDistanceMin, strikeDistanceMax)
  208.                      # Determine what the next WeeWx row should be to make sure we compute values at that row even if it's not present in the file
  209.                      nextTimeInterval = str(int(timestamp)+300)
  210.                      # Reset all values
  211.                      strike_distance = 0
  212.                      strike_count = 0
  213.                      precip_final = 0
  214.                      loopIndex = 0
  215.                      averagingStrikeIndex = 0
  216.                      strikeDistanceMin = 999
  217.                      strikeDistanceMax = 0
  218.                      precip_type = 0
  219.                      precipTypeMax = 0
  220.  
  221. if writeRainData == 0:
  222.     print ("Rain Check Data missing from file!")
  223.    
  224. # To auto-run the daily rebuild, make sure to edit wee_database to comment out the Proceed lines starting at line 253
  225. # Corrected so that the Proceed lines in wee_database don't need to be commented out by using redirection
  226. #wee_database --rebuild-daily --date=YYYY-mm-dd
  227. time.sleep(5)
  228. #lineargs = "--rebuild-daily --date=" + str(fileDate.strftime("%Y-%m-%d"))
  229. #subprocess.call(["wee_database", lineargs])
  230. lineargs = 'echo "y" | wee_database --rebuild-daily --date=' + str(fileDate.strftime("%Y-%m-%d"))
  231. p=subprocess.Popen(lineargs,stdout=subprocess.PIPE,shell=True)
  232. (output,err)=p.communicate()
  233. p_status=p.wait()
  234. print (output)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement