diff --git a/bin/wee_config_ws28xx b/bin/wee_config_ws28xx deleted file mode 100755 index a32a0c11..00000000 --- a/bin/wee_config_ws28xx +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/env python -# $Id$ -# -# Copyright 2013 Matthew Wall -# -# This program is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free Software -# Foundation, either version 3 of the License, or any later version. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. -# -# See http://www.gnu.org/licenses/ - -"""Command line utility for configuring LaCrosse WS-28xx weather stations - -Many thanks to Eddie De Pieri, who did the first python implementation of a -WS-28xx driver, and Lucas Heijst, who sorted out the communication and timing -issues between weather station console and transceiver. -""" - -import optparse -import syslog -import time -import sys - -import weewx.drivers.ws28xx -import weewx.units -import weeutil.weeutil - -description = """Configuration utility for WS-28xx weather stations.""" - -usage = """%prog [config_file] [options] [--debug]""" - -epilog = """Mutating actions will request confirmation before proceeding.""" - -def main(): - syslog.openlog('wee_config_ws28xx', syslog.LOG_PID|syslog.LOG_CONS) - - # Create a command line parser: - parser = optparse.OptionParser(description=description, usage=usage, epilog=epilog) - - # Add the various options: - parser.add_option("--config", dest="cfgfn", type=str, metavar="FILE", - help="configuration file") - parser.add_option("--check-transceiver", dest="check", action="store_true", - help="check USB transceiver") - parser.add_option("--pair", dest="pair", action="store_true", - help="pair the USB transceiver with a station console") - parser.add_option("--info", dest="info", action="store_true", - help="display weather station configuration") - parser.add_option("--set-interval", dest="interval", type=int, metavar="N", - help="set logging interval to N minutes") - parser.add_option("--current", dest="current", action="store_true", - help="get the current weather conditions") - parser.add_option("--history-since", dest="recmin", type=int, metavar="N", - help="display history records since N minutes ago") - parser.add_option("--history", dest="nrecords", type=int, metavar="N", - help="display N history records") - parser.add_option("--maxtries", dest="maxtries", type=int, - help="maximum number of retries, 0 indicates no max") - parser.add_option("-y", dest="noprompt", action="store_true", - help="answer yes to every prompt") - parser.add_option("--debug", dest="debug", action="store_true", - help="display diagnostic information while running") - - # Now we are ready to parse the command line: - (options, args) = parser.parse_args() - if options.debug is not None: - weewx.debug = options.debug - - config_fn, config_dict = weeutil.weeutil.read_config(options.cfgfn, args) - print 'Using configuration file %s' % config_fn - - print 'Driver version %s' % weewx.drivers.ws28xx.DRIVER_VERSION - altitude_m = weewx.units.getAltitudeM(config_dict) - station = weewx.drivers.ws28xx.WS28xx(altitude=altitude_m, - **config_dict['WS28xx']) - - if options.noprompt: - prompt = False - else: - prompt = True - - maxtries = 3 if options.maxtries is None else int(options.maxtries) - if options.check: - check_transceiver(station, maxtries) - elif options.pair: - pair(station, maxtries) - elif options.interval is not None: - set_interval(station, maxtries, options.interval, prompt) - elif options.current: - current(station, maxtries) - elif options.nrecords is not None: - history(station, maxtries, count=options.nrecords) - elif options.recmin is not None: - ts = int(time.time()) - options.recmin * 60 - history(station, maxtries, ts=ts) - else: - info(station, maxtries) - - station.shutDown() - - - -def check_transceiver(station, maxtries): - """See if the transceiver is installed and operational.""" - print 'Checking for transceiver...' - ntries = 0 - while ntries < maxtries: - ntries += 1 - if station.transceiver_is_present(): - print 'Transceiver is present' - sn = station.get_transceiver_serial() - print 'serial: %s' % sn - tid = station.get_transceiver_id() - print 'id: %d (0x%04x)' % (tid, tid) - break - print 'Not found (attempt %d of %d) ...' % (ntries, maxtries) - time.sleep(5) - else: - print 'Transceiver not responding.' - -def pair(station, maxtries): - """Pair the transceiver with the station console.""" - print 'Pairing transceiver with console...' - maxwait = 90 # how long to wait between button presses, in seconds - ntries = 0 - while ntries < maxtries or maxtries == 0: - if station.transceiver_is_paired(): - print 'Transceiver is paired to console' - break - ntries += 1 - msg = 'Press and hold the [v] key until "PC" appears' - if maxtries > 0: - msg += ' (attempt %d of %d)' % (ntries, maxtries) - else: - msg += ' (attempt %d)' % ntries - print msg - now = start_ts = int(time.time()) - while now - start_ts < maxwait and not station.transceiver_is_paired(): - time.sleep(5) - now = int(time.time()) - else: - print 'Transceiver not paired to console.' - -def get_interval(station, maxtries): - cfg = get_config(station, maxtries) - if cfg is None: - return None - return weewx.drivers.ws28xx.getHistoryInterval(cfg['history_interval']) - -def get_config(station, maxtries): - start_ts = None - ntries = 0 - while ntries < maxtries or maxtries == 0: - cfg = station.get_config() - if cfg is not None: - return cfg - ntries += 1 - if start_ts is None: - start_ts = int(time.time()) - else: - dur = int(time.time()) - start_ts - print 'No data after %d seconds (press SET to sync)' % dur - time.sleep(30) - return None - -def set_interval(station, maxtries, interval, prompt): - """Set the station archive interval""" - print "This feature is not yet implemented" - -def info(station, maxtries): - """Query the station then display the settings.""" - print 'Querying the station for the configuration...' - cfg = get_config(station, maxtries) - if cfg is not None: - print_dict(cfg) - -def current(station, maxtries): - """Get current weather observation.""" - print 'Querying the station for current weather data...' - start_ts = None - ntries = 0 - while ntries < maxtries or maxtries == 0: - packet = station.get_observation() - if packet is not None: - print_dict(packet) - break - ntries += 1 - if start_ts is None: - start_ts = int(time.time()) - else: - dur = int(time.time()) - start_ts - print 'No data after %d seconds (press SET to sync)' % dur - time.sleep(30) - -def history(station, maxtries, ts=0, count=0): - """Display the indicated number of records or the records since the - specified timestamp (local time, in seconds)""" - print "Querying the station for historical records..." - ntries = 0 - last_n = n = nrem = None - last_ts = now = int(time.time()) - station.start_caching_history(since_ts=ts, num_rec=count) - while nrem is None or nrem > 0: - if ntries >= maxtries: - print 'Giving up after %d tries' % ntries - break - time.sleep(30) - ntries += 1 - now = int(time.time()) - n = station.get_num_history_scanned() - if n == last_n: - dur = now - last_ts - print 'No data after %d seconds (press SET to sync)' % dur - else: - ntries = 0 - last_ts = now - last_n = n - nrem = station.get_uncached_history_count() - ni = station.get_next_history_index() - li = station.get_latest_history_index() - msg = " scanned %s records: current=%s latest=%s remaining=%s\r" % (n, ni, li, nrem) - sys.stdout.write(msg) - sys.stdout.flush() - station.stop_caching_history() - records = station.get_history_cache_records() - station.clear_history_cache() - print - print 'Found %d records' % len(records) - for r in records: - print r - -def print_dict(data): - for x in sorted(data.keys()): - if x == 'dateTime': - print '%s: %s' % (x, weeutil.weeutil.timestamp_to_string(data[x])) - else: - print '%s: %s' % (x, data[x]) - - -if __name__=="__main__" : - main() diff --git a/bin/weewx/abstractstation.py b/bin/weewx/abstractstation.py index 47b29489..8fde1e93 100644 --- a/bin/weewx/abstractstation.py +++ b/bin/weewx/abstractstation.py @@ -59,9 +59,10 @@ class DeviceConfigurator(object): if options.debug is not None: weewx.debug = options.debug syslog.setlogmask(syslog.LOG_UPTO(syslog.LOG_DEBUG)) - self.do_config(options, config_dict) + prompt = False if options.noprompt else True + self.do_config(options, config_dict, prompt) - def do_config(self, options, config_dict): + def do_config(self, options, config_dict, prompt): raise NotImplementedError("Method 'do_config' not implemented") def get_parser(self): diff --git a/bin/weewx/drivers/cc3000.py b/bin/weewx/drivers/cc3000.py index 5894dc06..768ad76a 100644 --- a/bin/weewx/drivers/cc3000.py +++ b/bin/weewx/drivers/cc3000.py @@ -84,9 +84,8 @@ class CC3000Configurator(weewx.abstractstation.DeviceConfigurator): parser.add_option("--set-units", dest="units", metavar="UNITS", help="set units to METRIC or ENGLISH") - def do_config(self, options, config_dict): + def do_config(self, options, config_dict, prompt): self.station = CC3000Driver(**config_dict['CC3000']) - prompt = False if options.noprompt else True if options.nrecords is not None: self.show_history(options.nrecords) elif options.current: diff --git a/bin/weewx/drivers/te923.py b/bin/weewx/drivers/te923.py index 6fc5c666..94a6b199 100644 --- a/bin/weewx/drivers/te923.py +++ b/bin/weewx/drivers/te923.py @@ -247,7 +247,7 @@ class TE923Configurator(weewx.abstractstation.DeviceConfigurator): type=str, metavar="FORMAT", help="format for history: raw, table, or dict") - def do_config(self, options, config_dict): + def do_config(self, options, config_dict, prompt): if options.format is None: fmt = 'table' elif (options.format.lower() != 'raw' and @@ -257,7 +257,6 @@ class TE923Configurator(weewx.abstractstation.DeviceConfigurator): exit(1) else: fmt = options.format.lower() - prompt = False if options.noprompt else True self.station = TE923Driver(**config_dict['TE923']) if options.current: diff --git a/bin/weewx/drivers/ws23xx.py b/bin/weewx/drivers/ws23xx.py index 7056a132..e04fcd1e 100644 --- a/bin/weewx/drivers/ws23xx.py +++ b/bin/weewx/drivers/ws23xx.py @@ -294,9 +294,7 @@ class WS23xxConfigurator(weewx.abstractstation.DeviceConfigurator): parser.add_option("--clear-memory", dest="clear", action="store_true", help="clear station memory") - def do_config(self, options, config_dict): - prompt = False if options.noprompt else True - + def do_config(self, options, config_dict, prompt): self.station = WS23xxDriver(**config_dict['WS23xx']) if options.current: self.show_current() diff --git a/bin/weewx/drivers/ws28xx.py b/bin/weewx/drivers/ws28xx.py index 1b0ef177..24653e8f 100644 --- a/bin/weewx/drivers/ws28xx.py +++ b/bin/weewx/drivers/ws28xx.py @@ -924,6 +924,7 @@ Step 8. Go to step 1 to wait for state 0xde16 again. from datetime import datetime import StringIO +import sys import syslog import threading import time @@ -1002,9 +1003,9 @@ def get_next_index(idx): def get_index(idx): if idx < 0: - return idx + WS28xx.max_records - elif idx >= WS28xx.max_records: - return idx - WS28xx.max_records + return idx + WS28xxDriver.max_records + elif idx >= WS28xxDriver.max_records: + return idx - WS28xxDriver.max_records return idx def tstr_to_ts(tstr): @@ -1021,10 +1022,200 @@ def addr_to_index(addr): def index_to_addr(idx): return 18 * idx + 416 -def loader(config_dict, engine): - return WS28xx(**config_dict['WS28xx']) -class WS28xx(weewx.abstractstation.AbstractStation): +def print_dict(data): + for x in sorted(data.keys()): + if x == 'dateTime': + print '%s: %s' % (x, weeutil.weeutil.timestamp_to_string(data[x])) + else: + print '%s: %s' % (x, data[x]) + + +def config_loader(config_dict): + return WS28xxConfigurator() + +class WS28xxConfigurator(weewx.abstractstation.DeviceConfigurator): + @property + def version(self): + return DRIVER_VERSION + + def add_options(self, parser): + super(WS28xxConfigurator, self).add_options(parser) + parser.add_option("--check-transceiver", dest="check", + action="store_true", + help="check USB transceiver") + parser.add_option("--pair", dest="pair", action="store_true", + help="pair the USB transceiver with station console") + parser.add_option("--info", dest="info", action="store_true", + help="display weather station configuration") + parser.add_option("--set-interval", dest="interval", + type=int, metavar="N", + help="set logging interval to N minutes") + parser.add_option("--current", dest="current", action="store_true", + help="get the current weather conditions") + parser.add_option("--history", dest="nrecords", type=int, metavar="N", + help="display N history records") + parser.add_option("--history-since", dest="recmin", + type=int, metavar="N", + help="display history records since N minutes ago") + parser.add_option("--maxtries", dest="maxtries", type=int, + help="maximum number of retries, 0 indicates no max") + + def do_config(self, options, config_dict, prompt): + maxtries = 3 if options.maxtries is None else int(options.maxtries) + self.station = WS28xxDriver(**config_dict['WS28xx']) + if options.check: + self.check_transceiver(maxtries) + elif options.pair: + self.pair(maxtries) + elif options.interval is not None: + self.set_interval(maxtries, options.interval, prompt) + elif options.current: + self.show_current(maxtries) + elif options.nrecords is not None: + self.show_history(maxtries, count=options.nrecords) + elif options.recmin is not None: + ts = int(time.time()) - options.recmin * 60 + self.show_history(maxtries, ts=ts) + else: + self.show_info(maxtries) + self.station.closePort() + + def check_transceiver(self, maxtries): + """See if the transceiver is installed and operational.""" + print 'Checking for transceiver...' + ntries = 0 + while ntries < maxtries: + ntries += 1 + if self.station.transceiver_is_present(): + print 'Transceiver is present' + sn = self.station.get_transceiver_serial() + print 'serial: %s' % sn + tid = self.station.get_transceiver_id() + print 'id: %d (0x%04x)' % (tid, tid) + break + print 'Not found (attempt %d of %d) ...' % (ntries, maxtries) + time.sleep(5) + else: + print 'Transceiver not responding.' + + def pair(self, maxtries): + """Pair the transceiver with the station console.""" + print 'Pairing transceiver with console...' + maxwait = 90 # how long to wait between button presses, in seconds + ntries = 0 + while ntries < maxtries or maxtries == 0: + if self.station.transceiver_is_paired(): + print 'Transceiver is paired to console' + break + ntries += 1 + msg = 'Press and hold the [v] key until "PC" appears' + if maxtries > 0: + msg += ' (attempt %d of %d)' % (ntries, maxtries) + else: + msg += ' (attempt %d)' % ntries + print msg + now = start_ts = int(time.time()) + while (now - start_ts < maxwait and + not self.station.transceiver_is_paired()): + time.sleep(5) + now = int(time.time()) + else: + print 'Transceiver not paired to console.' + + def get_interval(self, maxtries): + cfg = self.get_config(maxtries) + if cfg is None: + return None + return weewx.drivers.ws28xx.getHistoryInterval(cfg['history_interval']) + + def get_config(self, maxtries): + start_ts = None + ntries = 0 + while ntries < maxtries or maxtries == 0: + cfg = self.station.get_config() + if cfg is not None: + return cfg + ntries += 1 + if start_ts is None: + start_ts = int(time.time()) + else: + dur = int(time.time()) - start_ts + print 'No data after %d seconds (press SET to sync)' % dur + time.sleep(30) + return None + + def set_interval(self, maxtries, interval, prompt): + """Set the station archive interval""" + print "This feature is not yet implemented" + + def info(self, maxtries): + """Query the station then display the settings.""" + print 'Querying the station for the configuration...' + cfg = self.get_config(maxtries) + if cfg is not None: + print_dict(cfg) + + def current(self, maxtries): + """Get current weather observation.""" + print 'Querying the station for current weather data...' + start_ts = None + ntries = 0 + while ntries < maxtries or maxtries == 0: + packet = self.station.get_observation() + if packet is not None: + print_dict(packet) + break + ntries += 1 + if start_ts is None: + start_ts = int(time.time()) + else: + dur = int(time.time()) - start_ts + print 'No data after %d seconds (press SET to sync)' % dur + time.sleep(30) + + def history(self, maxtries, ts=0, count=0): + """Display the indicated number of records or the records since the + specified timestamp (local time, in seconds)""" + print "Querying the station for historical records..." + ntries = 0 + last_n = n = nrem = None + last_ts = now = int(time.time()) + self.station.start_caching_history(since_ts=ts, num_rec=count) + while nrem is None or nrem > 0: + if ntries >= maxtries: + print 'Giving up after %d tries' % ntries + break + time.sleep(30) + ntries += 1 + now = int(time.time()) + n = self.station.get_num_history_scanned() + if n == last_n: + dur = now - last_ts + print 'No data after %d seconds (press SET to sync)' % dur + else: + ntries = 0 + last_ts = now + last_n = n + nrem = self.station.get_uncached_history_count() + ni = self.station.get_next_history_index() + li = self.station.get_latest_history_index() + msg = " scanned %s records: current=%s latest=%s remaining=%s\r" % (n, ni, li, nrem) + sys.stdout.write(msg) + sys.stdout.flush() + self.station.stop_caching_history() + records = self.station.get_history_cache_records() + self.station.clear_history_cache() + print + print 'Found %d records' % len(records) + for r in records: + print r + + +def loader(config_dict, engine): + return WS28xxDriver(**config_dict['WS28xx']) + +class WS28xxDriver(weewx.abstractstation.AbstractStation): """Driver for LaCrosse WS28xx stations.""" max_records = 1797 @@ -3367,7 +3558,7 @@ class CCommunicationService(object): hidx = self.history_cache.next_index elif self.DataStore.getLastHistoryIndex() is not None: hidx = self.DataStore.getLastHistoryIndex() - if hidx is None or hidx < 0 or hidx >= WS28xx.max_records: + if hidx is None or hidx < 0 or hidx >= WS28xxDriver.max_records: haddr = 0xffffff else: haddr = index_to_addr(hidx) @@ -3822,8 +4013,8 @@ class CCommunicationService(object): def startCachingHistory(self, since_ts=0, num_rec=0): self.history_cache.clear_records() self.history_cache.since_ts = since_ts - if num_rec > WS28xx.max_records - 2: - num_rec = WS28xx.max_records - 2 + if num_rec > WS28xxDriver.max_records - 2: + num_rec = WS28xxDriver.max_records - 2 self.history_cache.num_rec = num_rec self.command = EAction.aGetHistory