Some work on units
[infoex-autowx.git] / infoex-autowx.py
index 5f35d0e658b5e6ab83c0c13d857c01f750b0f545..78c4d49be685bd86719fa0ccbc7651b681c8bbf3 100755 (executable)
@@ -2,14 +2,15 @@
 # -*- coding: utf-8 -*-
 
 """
 # -*- coding: utf-8 -*-
 
 """
-InfoEx <-> NRCS Auto Wx implementation
+InfoEx <-> NRCS/MesoWest Auto Wx implementation
 Alexander Vasarab
 Wylark Mountaineering LLC
 
 Alexander Vasarab
 Wylark Mountaineering LLC
 
-Version 1.0.0
+Version 2.0.0
 
 
-This program fetches data from an NRCS SNOTEL site and pushes it to
-InfoEx using the new automated weather system implementation.
+This program fetches data from either an NRCS SNOTEL site or MesoWest
+weather station and pushes it to InfoEx using the new automated weather
+system implementation.
 
 It is designed to be run hourly, and it asks for the last three hours
 of data of each desired type, and selects the most recent one. This
 
 It is designed to be run hourly, and it asks for the last three hours
 of data of each desired type, and selects the most recent one. This
@@ -56,10 +57,12 @@ except:
     log.addHandler(handler)
 
 parser = OptionParser()
     log.addHandler(handler)
 
 parser = OptionParser()
+
 parser.add_option("--config",
     dest="config",
     metavar="FILE",
     help="location of config file")
 parser.add_option("--config",
     dest="config",
     metavar="FILE",
     help="location of config file")
+
 parser.add_option("--dry-run",
     action="store_true",
     dest="dry_run",
 parser.add_option("--dry-run",
     action="store_true",
     dest="dry_run",
@@ -71,16 +74,11 @@ parser.add_option("--dry-run",
 config = configparser.ConfigParser(allow_no_value=False)
 
 if not options.config:
 config = configparser.ConfigParser(allow_no_value=False)
 
 if not options.config:
-    print("Please specify a configuration file via --config")
+    print("Please specify a configuration file via --config.")
     sys.exit(1)
 
 config.read(options.config)
 
     sys.exit(1)
 
 config.read(options.config)
 
-if 'nrcs' in config and 'mesowest' in config:
-    print("Both MesoWest and NRCS configuration option specified, "
-          "please choose just one.")
-    sys.exit(1)
-
 log.debug('STARTING UP')
 
 try:
 log.debug('STARTING UP')
 
 try:
@@ -94,14 +92,18 @@ try:
     }
 
     data = dict()
     }
 
     data = dict()
+    data['provider'] = config['station']['type']
+
+    if data['provider'] not in ['nrcs', 'mesowest']:
+        print("Please specify either nrcs or mesowest as the station type.")
+        sys.exit(1)
 
 
-    if 'nrcs' in config:
-        data['provider'] = 'nrcs'
+    if data['provider'] == 'nrcs':
         data['source'] = 'https://www.wcc.nrcs.usda.gov/awdbWebService/services?WSDL'
         data['source'] = 'https://www.wcc.nrcs.usda.gov/awdbWebService/services?WSDL'
-        data['stationID'] = config['nrcs']['station_triplet']
+        data['station_id'] = config['station']['station_id']
 
         try:
 
         try:
-            desired_data = config['nrcs']['desired_data'].split(',')
+            desired_data = config['station']['desired_data'].split(',')
         except:
             # desired_data malformed or missing, setting default
             desired_data = [
         except:
             # desired_data malformed or missing, setting default
             desired_data = [
@@ -110,21 +112,24 @@ try:
                     'PREC'  # PRECIPITATION ACCUMULATION (in)
                     ]
 
                     'PREC'  # PRECIPITATION ACCUMULATION (in)
                     ]
 
-    if 'mesowest' in config:
-        data['provider'] = 'mesowest'
-        #data['source'] = 'https://api.synopticdata.com/v2/stations/latest'
+        # XXX: For NRCS, we're manually overriding units for now! Once
+        #      unit conversion is supported for NRCS, REMOVE THIS!
+        if 'units' not in data:
+            data['units'] = 'imperial'
+
+    if data['provider'] == 'mesowest':
         data['source'] = 'https://api.synopticdata.com/v2/stations/timeseries'
         data['source'] = 'https://api.synopticdata.com/v2/stations/timeseries'
-        data['stationID'] = config['mesowest']['stid']
-        data['units'] = config['mesowest']['units']
+        data['station_id'] = config['station']['station_id']
+        data['units'] = config['station']['units']
 
         try:
 
         try:
-            desired_data = config['mesowest']['desired_data']
+            desired_data = config['station']['desired_data']
         except:
             # desired_data malformed or missing, setting default
             desired_data = 'air_temp,snow_depth'
 
         except:
             # desired_data malformed or missing, setting default
             desired_data = 'air_temp,snow_depth'
 
-        # construct full API URL
-        data['source'] = data['source'] + '?token=' + config['mesowest']['token'] + '&within=60&units=' + data['units'] + '&stid=' + data['stationID'] + '&vars=' + desired_data
+        # construct full API URL (sans start/end time, added later)
+        data['source'] = data['source'] + '?token=' + config['station']['token'] + '&within=60&units=' + data['units'] + '&stid=' + data['station_id'] + '&vars=' + desired_data
 
 except KeyError as e:
     log.critical("%s not defined in %s" % (e, options.config))
 
 except KeyError as e:
     log.critical("%s not defined in %s" % (e, options.config))
@@ -184,7 +189,7 @@ fmap['hn24AutoUnit'] = 26           ; final_data[26] = 'in'
 fmap['hstAuto'] = 27                ; final_data[27] = None
 fmap['hstAutoUnit'] = 28            ; final_data[28] = 'in'
 
 fmap['hstAuto'] = 27                ; final_data[27] = None
 fmap['hstAutoUnit'] = 28            ; final_data[28] = 'in'
 
-# one final mapping, the NRCS fields that this program supports to
+# one final mapping, the NRCS/MesoWest fields that this program supports to
 # their InfoEx counterpart
 iemap = {}
 
 # their InfoEx counterpart
 iemap = {}
 
@@ -192,10 +197,35 @@ if data['provider'] == 'nrcs':
     iemap['PREC'] = 'precipitationGauge'
     iemap['TOBS'] = 'tempPres'
     iemap['SNWD'] = 'hS'
     iemap['PREC'] = 'precipitationGauge'
     iemap['TOBS'] = 'tempPres'
     iemap['SNWD'] = 'hS'
+    iemap['PRES'] = 'baro'
+    iemap['RHUM'] = 'rH'
+    iemap['WSPD'] = 'windSpeedNum'
+    iemap['WDIR'] = 'windDirectionNum'
+    # unsupported by NRCS:
+    # windGustSpeedNum
 elif data['provider'] == 'mesowest':
     iemap['precip_accum'] = 'precipitationGauge'
     iemap['air_temp'] = 'tempPres'
     iemap['snow_depth'] = 'hS'
 elif data['provider'] == 'mesowest':
     iemap['precip_accum'] = 'precipitationGauge'
     iemap['air_temp'] = 'tempPres'
     iemap['snow_depth'] = 'hS'
+    iemap['pressure'] = 'baro'
+    iemap['relative_humidity'] = 'rH'
+    iemap['wind_speed'] = 'windSpeedNum'
+    iemap['wind_direction'] = 'windDirectionNum'
+    iemap['wind_gust'] = 'windGustSpeedNum'
+
+# override units if user selected metric
+#
+# NOTE: to update this, use the fmap<->final_data mapping laid out above
+#
+# NOTE: this only 'works' with MesoWest for now, as the MesoWest API
+#       itself handles the unit conversion; in the future, we will also
+#       support NRCS unit conversion, but this must be done by this
+#       program.
+if data['units'] == 'metric':
+    final_data[fmap['tempPresUnit']] = 'C'
+    final_data[fmap['hsUnit']] = 'm'
+    final_data[fmap['windSpeedUnit']] = 'm/s'
+    final_data[fmap['windGustSpeedNumUnit']] = 'm/s'
 
 # floor time to nearest hour
 dt = datetime.datetime.now()
 
 # floor time to nearest hour
 dt = datetime.datetime.now()
@@ -220,7 +250,7 @@ if data['provider'] == 'nrcs':
 
         # get the last three hours of data for this elementCd
         tmp = client.service.getHourlyData(
 
         # get the last three hours of data for this elementCd
         tmp = client.service.getHourlyData(
-                stationTriplets=[data['stationID']],
+                stationTriplets=[data['station_id']],
                 elementCd=elementCd,
                 ordinal=1,
                 beginDate=begin_date,
                 elementCd=elementCd,
                 ordinal=1,
                 beginDate=begin_date,