From 1b67d896bc51da24faac60de501535ff3edc4e95 Mon Sep 17 00:00:00 2001 From: John Kline <51084020+chaunceygardiner@users.noreply.github.com> Date: Fri, 27 Dec 2019 03:42:39 -0800 Subject: [PATCH] Cleanup of log messages in CC3000 driver. (#479) * Robustness enhancements to cc3000 driver including py2/py3 support cc300.py: - changes to work on both py2/py3 (tested on both) - change timeout to 1s (more not needed) - retry commands when reading cmd echo times out (the signal of failure) - use timeout of 20s for memory clear (which takes about 12s) - various changes as assumptions of commands not currently correct (tried on 2 units) - use weewx.wxformulas.calculate_rain for rain delta - use weewx.crc16 engine.py: - Failure to get time from driver is not treated as an error. rsyncupload.py: - Add timeout option. - Change to allow single file to be rsynced. util/logwatch/scripts/services/weewx: - Changes to organize very detailed cc3000 output in logwatch reports. * cc3000 driver cleanup and fixes * rework genStartup, implement history-since, don't convert to unicode on py2 * fix typo * fix typo * Change logwatch script to match capitalization change in wxformulas.calculate_rain * logwatch change to match engine.py log message change * Another round of cleaning up the logwatch script. * remove tabs in logwatch script --- bin/weewx/drivers/cc3000.py | 142 +++++++++++++-------------- util/logwatch/scripts/services/weewx | 102 ++++++++++++++----- 2 files changed, 146 insertions(+), 98 deletions(-) diff --git a/bin/weewx/drivers/cc3000.py b/bin/weewx/drivers/cc3000.py index f19f7874..1b2bc951 100644 --- a/bin/weewx/drivers/cc3000.py +++ b/bin/weewx/drivers/cc3000.py @@ -377,7 +377,7 @@ class CC3000Driver(weewx.drivers.AbstractDevice): } def __init__(self, **stn_dict): - log.info('driver version is %s' % DRIVER_VERSION) + log.info('Driver version is %s' % DRIVER_VERSION) global DEBUG_SERIAL DEBUG_SERIAL = int(stn_dict.get('debug_serial', 0)) @@ -389,18 +389,18 @@ class CC3000Driver(weewx.drivers.AbstractDevice): self.max_tries = int(stn_dict.get('max_tries', 5)) self.model = stn_dict.get('model', 'CC3000') port = stn_dict.get('port', CC3000.DEFAULT_PORT) - log.info('using serial port %s' % port) + log.info('Using serial port %s' % port) self.polling_interval = float(stn_dict.get('polling_interval', 1)) - log.info('polling interval is %s seconds' % self.polling_interval) + log.info('Polling interval is %s seconds' % self.polling_interval) self.use_station_time = weeutil.weeutil.to_bool( stn_dict.get('use_station_time', True)) - log.info('using %s time for loop packets' % + log.info('Using %s time for loop packets' % ('station' if self.use_station_time else 'computer')) # start with the default sensormap, then augment with user-specified self.sensor_map = dict(self.DEFAULT_SENSOR_MAP) if 'sensor_map' in stn_dict: self.sensor_map.update(stn_dict['sensor_map']) - log.info('sensor map is %s' % self.sensor_map) + log.info('Sensor map is %s' % self.sensor_map) # periodically check the logger memory, then clear it if necessary. # these track the last time a check was made, and how often to make @@ -410,7 +410,7 @@ class CC3000Driver(weewx.drivers.AbstractDevice): self.last_mem_check = 0 self.mem_interval = 7 * 24 * 3600 if self.logger_threshold is not None: - log.info('clear logger at %s records' % self.logger_threshold) + log.info('Clear logger at %s records' % self.logger_threshold) # track the last rain counter value so we can determine deltas self.last_rain = None @@ -420,16 +420,16 @@ class CC3000Driver(weewx.drivers.AbstractDevice): # report the station configuration settings = self._init_station_with_retries(self.station, self.max_tries) - log.info('firmware: %s' % settings['firmware']) + log.info('Firmware: %s' % settings['firmware']) self.arcint = settings['arcint'] - log.info('archive_interval: %s' % self.arcint) + log.info('Archive interval: %s' % self.arcint) self.header = settings['header'] - log.info('header: %s' % self.header) + log.info('Header: %s' % self.header) self.units = weewx.METRICWX if settings['units'] == 'METRIC' else weewx.US - log.info('units: %s' % settings['units']) - log.info('channel: %s' % settings['channel']) - log.info('charger status: %s' % settings['charger']) - log.info('memory: %s' % self.station.get_memory_status()) + log.info('Units: %s' % settings['units']) + log.info('Channel: %s' % settings['channel']) + log.info('Charger status: %s' % settings['charger']) + log.info('Memory: %s' % self.station.get_memory_status()) def genLoopPackets(self): cmd_mode = True @@ -445,12 +445,12 @@ class CC3000Driver(weewx.drivers.AbstractDevice): values = self.station.get_current_data(cmd_mode) now = int(time.time()) ntries = 0 - log.debug("values: %s" % values) + log.debug("Values: %s" % values) if values: logged_nodata = False packet = self._parse_current( values, self.header, self.sensor_map) - log.debug("parsed: %s" % packet) + log.debug("Parsed: %s" % packet) if packet and 'dateTime' in packet: if not self.use_station_time: packet['dateTime'] = int(time.time() + 0.5) @@ -460,12 +460,12 @@ class CC3000Driver(weewx.drivers.AbstractDevice): packet['day_rain_total'], self.last_rain) self.last_rain = packet['day_rain_total'] else: - log.debug("no rain in packet: %s" % packet) - log.debug("packet: %s" % packet) + log.debug("No rain in packet: %s" % packet) + log.debug("Packet: %s" % packet) yield packet else: if not logged_nodata: - log.info("no data from sensors") + log.info("No data from sensors") logged_nodata = True # periodically check memory, clear if necessary @@ -473,13 +473,13 @@ class CC3000Driver(weewx.drivers.AbstractDevice): nrec = self.station.get_history_usage() self.last_mem_check = time.time() if nrec is None: - log.info("memory check: cannot determine memory usage") + log.info("Memory check: Cannot determine memory usage") else: - log.info("logger is at %d records, " + log.info("Logger is at %d records, " "logger clearing threshold is %d" % (nrec, self.logger_threshold)) if self.logger_threshold is not None and nrec >= self.logger_threshold: - log.info("clearing all records from logger") + log.info("Clearing all records from logger") self.station.clear_memory() if self.polling_interval: @@ -501,12 +501,12 @@ class CC3000Driver(weewx.drivers.AbstractDevice): - the archive interval is constant for entire history. - the HDR for archive records is the same as current HDR """ - log.debug("genStartupRecords: since_ts=%s" % since_ts) + log.debug("GenStartupRecords: since_ts=%s" % since_ts) log.info('Downloading new records (if any).') last_rain = None new_records = 0 for pkt in self.gen_records_since_ts(since_ts): - log.debug("pkt: %s" % pkt) + log.debug("Packet: %s" % pkt) pkt['usUnits'] = self.units pkt['interval'] = self.arcint if 'day_rain_total' in pkt: @@ -514,8 +514,8 @@ class CC3000Driver(weewx.drivers.AbstractDevice): pkt['day_rain_total'], last_rain) last_rain = pkt['day_rain_total'] else: - log.debug("no rain in record: %s" % r) - log.debug("packet: %s" % pkt) + log.debug("No rain in record: %s" % r) + log.debug("Packet: %s" % pkt) new_records += 1 yield pkt log.info('Downloaded %d new records.' % new_records) @@ -583,7 +583,7 @@ class CC3000Driver(weewx.drivers.AbstractDevice): a failure for any one value, then the entire record fails.""" pkt = dict() if len(values) != len(header) + 1: - log.info("values/header mismatch: %s %s" % (values, header)) + log.info("Values/header mismatch: %s %s" % (values, header)) return pkt for i, v in enumerate(values): if i >= len(header): @@ -600,7 +600,7 @@ class CC3000Driver(weewx.drivers.AbstractDevice): else: pkt[label] = float(v) except ValueError as e: - log.error("parse failed for '%s' '%s': %s (idx=%s values=%s)" % + log.error("Parse failed for '%s' '%s': %s (idx=%s values=%s)" % (header[i], v, e, i, values)) return dict() return pkt @@ -634,10 +634,10 @@ def _check_crc(buf): try: cs = buf[idx+1:idx+5] if DEBUG_CHECKSUM: - log.debug("found checksum at %d: %s" % (idx, cs)) + log.debug("Found checksum at %d: %s" % (idx, cs)) a = crc16(buf[0:idx]) # calculate checksum if DEBUG_CHECKSUM: - log.debug("calculated checksum %x" % a) + log.debug("Calculated checksum %x" % a) b = int(cs, 16) # checksum provided in data if a != b: raise ChecksumMismatch(a, b, buf) @@ -667,7 +667,7 @@ class CC3000(object): def open(self, timeoutOverride=None): if DEBUG_OPENCLOSE: - log.debug("open serial port %s" % self.port) + log.debug("Open serial port %s" % self.port) to = timeoutOverride if timeoutOverride is not None else self.timeout self.serial_port = serial.Serial(self.port, self.baudrate, timeout=to) @@ -675,7 +675,7 @@ class CC3000(object): def close(self): if self.serial_port is not None: if DEBUG_OPENCLOSE: - log.debug("close serial port %s" % self.port) + log.debug("Close serial port %s" % self.port) self.serial_port.close() self.serial_port = None @@ -686,7 +686,7 @@ class CC3000(object): # command does not do what is expected. data = data.encode('ascii', 'ignore') if DEBUG_SERIAL: - log.debug("write: '%s'" % data) + log.debug("Write: '%s'" % data) n = self.serial_port.write(data) if n is not None and n != len(data): raise weewx.WeeWxIOError("Write expected %d chars, sent %d" % @@ -699,7 +699,7 @@ class CC3000(object): """ data = self.serial_port.readline() if DEBUG_SERIAL: - log.debug("read: '%s' (%s)" % (data, _format_bytes(data))) + log.debug("Read: '%s' (%s)" % (data, _format_bytes(data))) data = data.strip() _check_crc(data) if sys.version_info[0] >= 3: @@ -713,11 +713,11 @@ class CC3000(object): self.flush_output() def flush_input(self): - log.debug("flush input buffer") + log.debug("Flush input buffer") self.serial_port.flushInput() def flush_output(self): - log.debug("flush output buffer") + log.debug("Flush output buffer") self.serial_port.flushOutput() def queued_bytes(self): @@ -855,7 +855,7 @@ class CC3000(object): log.info("%s: The resetting of timeout to %d took %f seconds." % (cmd, self.timeout, close_open_time)) def get_version(self): - log.debug("get firmware version") + log.debug("Get firmware version") return self.command("VERSION") # give the station some time to wake up. when we first hit it with a @@ -868,13 +868,13 @@ class CC3000(object): self.command('ECHO=?') def set_echo(self, cmd='ON'): - log.debug("set echo to %s" % cmd) + log.debug("Set echo to %s" % cmd) data = self.command('ECHO=%s' % cmd) if data != 'OK': raise weewx.WeeWxIOError("Set ECHO failed: %s" % data) def get_header(self): - log.debug("get header") + log.debug("Get header") data = self.command("HEADER") cols = data.split(',') if cols[0] != 'HDR': @@ -900,7 +900,7 @@ class CC3000(object): # unlike all of the other accessor methods, the TIME command returns # OK after it returns the requested parameter. so we have to pop the # OK off the serial so it does not trip up other commands. - log.debug("get time") + log.debug("Get time") tstr = self.command("TIME=?") if tstr not in ['ERROR', 'OK']: data = self.read() @@ -912,7 +912,7 @@ class CC3000(object): def _compose_set_time_command(): ts = time.time() tstr = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(ts)) - log.info("set time to %s (%s)" % (tstr, ts)) + log.info("Set time to %s (%s)" % (tstr, ts)) return "TIME=%s" % tstr def set_time(self): @@ -923,11 +923,11 @@ class CC3000(object): (s, data)) def get_dst(self): - log.debug("get daylight saving") + log.debug("Get daylight saving") return self.command("DST=?") def set_dst(self, dst): - log.debug("set DST to %s" % dst) + log.debug("Set DST to %s" % dst) # Firmware 1.3 Build 022 Dec 02 2016 returns 3 lines (,'',OK) data = self.command("DST=%s" % dst) # echoed input dst if data != dst: @@ -941,33 +941,33 @@ class CC3000(object): (dst, data)) def get_units(self): - log.debug("get units") + log.debug("Get units") return self.command("UNITS=?") def set_units(self, units): - log.debug("set units to %s" % units) + log.debug("Set units to %s" % units) data = self.command("UNITS=%s" % units) if data != 'OK': raise weewx.WeeWxIOError("Failed to set units to %s: %s" % (units, data)) def get_interval(self): - log.debug("get logging interval") + log.debug("Get logging interval") return int(self.command("LOGINT=?")) def set_interval(self, interval=5): - log.debug("set logging interval to %d minutes" % interval) + log.debug("Set logging interval to %d minutes" % interval) data = self.command("LOGINT=%d" % interval) if data != 'OK': raise weewx.WeeWxIOError("Failed to set logging interval: %s" % data) def get_channel(self): - log.debug("get channel") + log.debug("Get channel") return self.command("STATION") def set_channel(self, channel): - log.debug("set channel to %d" % channel) + log.debug("Set channel to %d" % channel) if channel < 0 or 3 < channel: raise ValueError("Channel must be 0-3") data = self.command("STATION=%d" % channel) @@ -975,15 +975,15 @@ class CC3000(object): raise weewx.WeeWxIOError("Failed to set channel: %s" % data) def get_charger(self): - log.debug("get charger") + log.debug("Get charger") return self.command("CHARGER") def get_baro(self): - log.debug("get baro") + log.debug("Get baro") return self.command("BARO") def set_baro(self, offset): - log.debug("set barometer offset to %d" % offset) + log.debug("Set barometer offset to %d" % offset) if offset != '0': parts = offset.split('.') if (len(parts) != 2 or @@ -997,7 +997,7 @@ class CC3000(object): def get_memory_status(self): # query for logger memory use. output is something like this: # 6438 bytes, 111 records, 0% - log.debug("get memory status") + log.debug("Get memory status") return self.command("MEM=?") def get_history_usage(self): @@ -1009,7 +1009,7 @@ class CC3000(object): return None def clear_memory(self): - log.debug("clear memory") + log.debug("Clear memory") data = self.command("MEM=CLEAR") # It's a long wait for the OK. With a greatly increased timeout # just for MEM=CLEAR, we should be able to read the OK. @@ -1019,7 +1019,7 @@ class CC3000(object): raise weewx.WeeWxIOError("Failed to clear memory: %s" % data) def get_rain(self): - log.debug("get rain total") + log.debug("Get rain total") # Firmware 1.3 Build 022 Dec 02 2017 returns OK after the rain count # This is like TIME=? rstr = self.command("RAIN") @@ -1030,7 +1030,7 @@ class CC3000(object): return rstr def reset_rain(self): - log.debug("reset rain counter") + log.debug("Reset rain counter") data = self.command("RAIN=RESET") if data != 'OK': raise weewx.WeeWxIOError("Failed to reset rain: %s" % data) @@ -1123,7 +1123,7 @@ class CC3000(object): if need_cmd: cmd_cnt += 1 if cmd_cnt > cmd_max: - msg = "download failed after %d attempts" % cmd_max + msg = "Download failed after %d attempts" % cmd_max log.error(msg) raise weewx.WeeWxIOError(msg) cmd = "DOWNLOAD" @@ -1136,7 +1136,7 @@ class CC3000(object): try: data = self.read() if data == 'OK': - log.debug("downloaded %d records, yielded %d" % (n, yielded)) + log.debug("Downloaded %d records, yielded %d" % (n, yielded)) break elif ',' in data: values = data.split(',') @@ -1153,20 +1153,20 @@ class CC3000(object): elif values[0] == '': # FIXME: observed 'input overrun' on rpi2 with debian 7 # so try sending another download command to unhang it. - log.error("download hung, initiate another download") + log.error("Download hung, initiate another download") need_cmd = True else: cmd_cnt = 0 - log.error("bad record %s '%s' (%s)" % + log.error("Bad record %s '%s' (%s)" % (n, values[0], data)) elif 'DOWNLOAD' in data: # some firmware echos the command, but not always pass else: - log.error("unexpected response to DOWNLOAD: '%s'" % data) + log.error("Unexpected response to DOWNLOAD: '%s'" % data) need_cmd = True except ChecksumError as e: - log.error("download failed for record %s: %s" % (n, e)) + log.error("Download failed for record %s: %s" % (n, e)) class CC3000ConfEditor(weewx.drivers.AbstractConfEditor): @@ -1283,16 +1283,16 @@ if __name__ == '__main__': if options.getver: print(s.get_version()) if options.status: - print("firmware:", s.get_version()) - print("time:", s.get_time()) - print("dst:", s.get_dst()) - print("units:", s.get_units()) - print("memory:", s.get_memory_status()) - print("interval:", s.get_interval() * 60) - print("channel:", s.get_channel()) - print("charger:", s.get_charger()) - print("baro:", s.get_baro()) - print("rain:", s.get_rain()) + print("Firmware:", s.get_version()) + print("Time:", s.get_time()) + print("DST:", s.get_dst()) + print("Units:", s.get_units()) + print("Memory:", s.get_memory_status()) + print("Interval:", s.get_interval() * 60) + print("Channel:", s.get_channel()) + print("Charger:", s.get_charger()) + print("Baro:", s.get_baro()) + print("Rain:", s.get_rain()) if options.getch: print(s.get_channel()) if options.setch is not None: diff --git a/util/logwatch/scripts/services/weewx b/util/logwatch/scripts/services/weewx index 5dc0a724..82763df9 100755 --- a/util/logwatch/scripts/services/weewx +++ b/util/logwatch/scripts/services/weewx @@ -50,7 +50,7 @@ my $ACURITE_BOGUS_STRENGTH = 'acurite: R1: bogus signal strength'; # BARO, CHARGER, DST=?, HEADER, LOGINT=?, MEM=?, NOW, STATION, TIME=?, UNITS=?, RAIN, VERSION -my $CC3000_VALUES_HEADER_MISMATCHES = 'cc3000: values/header mismatch'; +my $CC3000_VALUES_HEADER_MISMATCHES = 'cc3000: Values/Header mismatch'; my $CC3000_BARO_CMD_ECHO_TIMED_OUT = 'cc3000: BARO cmd echo timed out'; my $CC3000_BARO_MISSING_COMMAND_ECHO = 'cc3000: BARO echoed as empty string'; @@ -323,7 +323,7 @@ while(defined($_ = )) { $counts{$ACURITE_BOGUS_STRENGTH} += 1; } elsif (/purpleair: /) { push @purpleair, $_; - } elsif (/cc3000: values/) { + } elsif (/cc3000: Values\/header mismatch/) { $counts{$CC3000_VALUES_HEADER_MISMATCHES} += 1; # BARO, CHARGER, DST=?, HEADER, LOGINT=?, MEM=?, NOW, RAIN, STATION, TIME=?, UNITS=?, VERSION @@ -440,10 +440,10 @@ while(defined($_ = )) { } elsif (/cc3000: VERSION: Retry failed./) { $counts{$CC3000_VERSION_FAILED_RETRIES} += 1; - } elsif (/engine: error reading time: Failed to get time/) { + } elsif (/engine: Error reading time: Failed to get time/) { $counts{$CC3000_GET_TIME_FAILED} += 1; - } elsif (/cc3000: set time to /) { + } elsif (/cc3000: Set time to /) { $counts{$CC3000_SET_TIME_SUCCEEDED} += 1; } elsif (/cc3000: MEM=CLEAR succeeded./) { @@ -456,11 +456,11 @@ while(defined($_ = )) { } elsif (/cc3000: .*: times: .*/) { push @cc3000_timings, $_; - # cc3000: logger is at 11475 records, logger clearing threshold is 10000 - } elsif (/cc3000: logger is at.*/) { + # cc3000: Logger is at 11475 records, logger clearing threshold is 10000 + } elsif (/cc3000: Logger is at.*/) { push @cc3000_mem_clear_info, $_; - # cc3000: clearing all records from logger - } elsif (/cc3000: clearing all records from logger/) { + # cc3000: Clearing all records from logger + } elsif (/cc3000: Clearing all records from logger/) { push @cc3000_mem_clear_info, $_; # cc3000: MEM=CLEAR: The resetting of timeout to 20 took 0.001586 seconds. # cc3000: MEM=CLEAR: The resetting of timeout to 1 took 0.001755 seconds. @@ -570,7 +570,11 @@ while(defined($_ = )) { /engine: Daily summaries up to date/ || /engine: Using binding/ || /engine: Archive will use/ || - /engine: Starting backfill of daily summaries/ || + /engine: Platform/ || + /engine: Locale is/ || + /wxservices: The following values will be calculated:/ || + /wxservices: The following algorithms will be used for calculations:/ || + /manager: Starting backfill of daily summaries/ || /manager: Created daily summary tables/ || /cheetahgenerator: Running / || /cheetahgenerator: skip/ || @@ -711,24 +715,57 @@ while(defined($_ = )) { /te923: STT / || /te923: Bad read \(attempt \d of/ || # normal when debugging /te923: usb error.* No data available/ || # normal when debug=1 - /cc3000: found checksum at/ || - /cc3000: calculated checksum/ || - /cc3000: record/ || - /cc3000: got bytes/ || - /cc3000: open serial port/ || - /cc3000: close serial port/ || - /cc3000: station replied to command with/ || - /cc3000: driver version is/ || - /cc3000: polling interval is/ || - /cc3000: pressure offset is/ || - /cc3000: header is/ || - /cc3000: units are/ || - /cc3000: using station time/ || - /cc3000: using computer time/ || - /cc3000: using serial port/ || - /cc3000: set echo to/ || - /cc3000: get header/ || - /cc3000: get units/ || + /cc3000: Archive interval:/ || + /cc3000: Calculated checksum/ || + /cc3000: Channel:/ || + /cc3000: Charger status:/ || + /cc3000: Clear logger at/ || + /cc3000: Clear memory/ || + /cc3000: Close serial port/ || + /cc3000: Downloaded \d+ new records/ || + /cc3000: Downloaded \d+ records, yielded \d+/ || + /cc3000: Downloading new records/ || + /cc3000: Driver version is/ || + /cc3000: Firmware:/ || + /cc3000: Found checksum at/ || + /cc3000: Flush input bugger/ || + /cc3000: Flush output bugger/ || + /cc3000: gen_records: Requested \d+ latest of \d+ records/ || + /cc3000: gen_records_since_ts: Asking for \d+ records/ || + /cc3000: GenStartupRecords: since_ts/ || + /cc3000: Get baro/ || + /cc3000: Get channel/ || + /cc3000: Get charger/ || + /cc3000: Get daylight saving/ || + /cc3000: Get firmware version/ || + /cc3000: Get header/ || + /cc3000: Get logging interval/ || + /cc3000: Get memory status/ || + /cc3000: Get rain total/ || + /cc3000: Get time/ || + /cc3000: Get units/ || + /cc3000: Header:/ || + /cc3000: Memory:/ || + /cc3000: No rain in packet:/ || + /cc3000: Open serial port/ || + /cc3000: Packet:/ || + /cc3000: Parsed:/ || + /cc3000: Polling interval is/ || + /cc3000: Read:/ || + /cc3000: Reset rain counter/ || + /cc3000: Sensor map is/ || + /cc3000: Set barometer offset to/ || + /cc3000: Set channel to/ || + /cc3000: Set DST to/ || + /cc3000: Set echo to/ || + /cc3000: Set logging interval to/ || + /cc3000: Set units to/ || + /cc3000: Units:/ || + /cc3000: Using computer time/ || + /cc3000: Using serial port/ || + /cc3000: Using station time/ || + /cc3000: Values:/ || + /cc3000: Write:/ || /owfs: driver version is / || /owfs: interface is / || /owfs: polling interval is / || @@ -755,6 +792,7 @@ while(defined($_ = )) { /forecast: XTideThread: XTide: got no tidal events/ || /forecast: .*: forecast version/ || /restx: StationRegistry: Station will/ || + /restx: StationRegistry: Registration not requested/ || /restx: .*Data will be uploaded/ || /restx: .*wait interval/ || /restx: Shut down/ || @@ -762,6 +800,16 @@ while(defined($_ = )) { /restx: \S+: service version is/ || /restx: \S+: skipping upload/ || /restx: \S+: desired unit system is/ || + /restx: AWEKAS: Posting not enabled/ || + /restx: Wunderground: Posting not enabled/ || + /restx: PWSweather: Posting not enabled/ || + /restx: CWOP: Posting not enabled/ || + /restx: WOW: Posting not enabled/ || + /restx: AWEKAS: Data for station/ || + /restx: Wunderground: Data for station/ || + /restx: PWSweather: Posting not enabled/ || + /restx: CWOP: Posting not enabled/ || + /restx: WOW: Data for station/ || /restx: AWEKAS: url/ || /restx: EmonCMS: url/ || /restx: EmonCMS: prefix is/ ||