dan'l, you have not missed anything - i neglected to put the macosx support in cmon.
please try the attach cmon v0.15rc1 it uses psutil, so you'll have to install that: easy_install psutil or use pip or whatever python package management you are using on macos. i have only tested this on an ancient version of macosx (10.6), so please let me know how it works for you. m -- You received this message because you are subscribed to the Google Groups "weewx-user" group. To unsubscribe from this group and stop receiving emails from it, send an email to weewx-user+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
# $Id: cmon.py 1573 2016-11-22 16:01:49Z mwall $ # Copyright 2013 Matthew Wall """weewx module that records cpu, memory, disk, and network usage. This file contains both a weewx driver and a weewx service. Installation Put this file in the bin/user directory. Service Configuration Add the following to weewx.conf: [ComputerMonitor] data_binding = cmon_binding max_age = 2592000 # 30 days; None to store indefinitely [DataBindings] [[cmon_binding]] database = cmon_sqlite manager = weewx.manager.DaySummaryManager table_name = archive schema = user.cmon.schema [Databases] [[cmon_sqlite]] root = %(WEEWX_ROOT)s database_name = archive/cmon.sdb driver = weedb.sqlite [Engine] [[Services]] archive_services = ..., user.cmon.ComputerMonitor Driver Configuration Add the following to weewx.conf: [Station] station_type = ComputerMonitor [ComputerMonitor] polling_interval = 30 driver = user.cmon Schema The default schema is defined in this file. If you prefer to maintain a schema different than the default, specify the desired schema in the configuration. For example, this would be a schema that stores only memory and network data, and uses eth1 instead of the default eth0: [DataBindings] [[cmon_binding]] database = cmon_sqlite manager = weewx.manager.DaySummaryManager table_name = archive [[[schema]]] dateTime = INTEGER NOT NULL PRIMARY KEY usUnits = INTEGER NOT NULL interval = INTEGER NOT NULL mem_total = INTEGER mem_free = INTEGER mem_used = INTEGER swap_total = INTEGER swap_free = INTEGER swap_used = INTEGER net_eth1_rbytes = INTEGER net_eth1_rpackets = INTEGER net_eth1_rerrs = INTEGER net_eth1_rdrop = INTEGER net_eth1_tbytes = INTEGER net_eth1_tpackets = INTEGER net_eth1_terrs = INTEGER net_eth1_tdrop = INTEGER Another approach to maintaining a custom schema is to define the schema in the file user/extensions.py as cmonSchema: cmonSchema = [ ('dateTime', 'INTEGER NOT NULL PRIMARY KEY'), ('usUnits', 'INTEGER NOT NULL'), ('interval', 'INTEGER NOT NULL'), ('mem_total','INTEGER'), ('mem_free','INTEGER'), ('mem_used','INTEGER'), ('net_eth1_rbytes','INTEGER'), ('net_eth1_rpackets','INTEGER'), ('net_eth1_rerrs','INTEGER'), ('net_eth1_rdrop','INTEGER'), ('net_eth1_tbytes','INTEGER'), ('net_eth1_tpackets','INTEGER'), ('net_eth1_terrs','INTEGER'), ('net_eth1_tdrop','INTEGER'), ] then load it using this configuration: [DataBindings] [[cmon_binding]] database = cmon_sqlite manager = weewx.manager.DaySummaryManager table_name = archive schema = user.extensions.cmonSchema """ # FIXME: make these methods platform-independent instead of linux-specific # FIXME: deal with MB/GB in memory sizes # FIXME: save the total counts instead of the deltas # FIXME: refactor ups and rpi specialties from __future__ import with_statement import os import platform import re import syslog import time from subprocess import Popen, PIPE import weewx import weeutil.weeutil from weewx.drivers import AbstractDevice from weewx.engine import StdService DRIVER_NAME = "ComputerMonitor" DRIVER_VERSION = "0.15rc1" if weewx.__version__ < "3": raise weewx.UnsupportedFeature("weewx 3 is required, found %s" % weewx.__version__) schema = [ ('dateTime', 'INTEGER NOT NULL PRIMARY KEY'), ('usUnits', 'INTEGER NOT NULL'), ('interval', 'INTEGER NOT NULL'), ('mem_total', 'INTEGER'), ('mem_free', 'INTEGER'), ('mem_used', 'INTEGER'), ('swap_total', 'INTEGER'), ('swap_free', 'INTEGER'), ('swap_used', 'INTEGER'), ('cpu_user', 'INTEGER'), ('cpu_nice', 'INTEGER'), ('cpu_system', 'INTEGER'), ('cpu_idle', 'INTEGER'), ('cpu_iowait', 'INTEGER'), ('cpu_irq', 'INTEGER'), ('cpu_softirq', 'INTEGER'), ('load1', 'REAL'), ('load5', 'REAL'), ('load15', 'REAL'), ('proc_active', 'INTEGER'), ('proc_total', 'INTEGER'), # measure cpu temperature (not all platforms support this) ('cpu_temp', 'REAL'), # degree C ('cpu_temp1', 'REAL'), # degree C ('cpu_temp2', 'REAL'), # degree C ('cpu_temp3', 'REAL'), # degree C ('cpu_temp4', 'REAL'), # degree C # measure rpi attributes (not all platforms support this) # ('core_temp','REAL'), # degree C # ('core_volt', 'REAL'), # ('core_sdram_c', 'REAL'), # ('core_sdram_i', 'REAL'), # ('core_sdram_p', 'REAL'), # ('arm_mem', 'REAL'), # ('gpu_mem', 'REAL'), # the default interface on most linux systems is eth0 ('net_eth0_rbytes', 'INTEGER'), ('net_eth0_rpackets', 'INTEGER'), ('net_eth0_rerrs', 'INTEGER'), ('net_eth0_rdrop', 'INTEGER'), ('net_eth0_tbytes', 'INTEGER'), ('net_eth0_tpackets', 'INTEGER'), ('net_eth0_terrs', 'INTEGER'), ('net_eth0_tdrop', 'INTEGER'), # ('net_eth1_rbytes', 'INTEGER'), # ('net_eth1_rpackets', 'INTEGER'), # ('net_eth1_rerrs', 'INTEGER'), # ('net_eth1_rdrop', 'INTEGER'), # ('net_eth1_tbytes', 'INTEGER'), # ('net_eth1_tpackets', 'INTEGER'), # ('net_eth1_terrs', 'INTEGER'), # ('net_eth1_tdrop', 'INTEGER'), # some systems have a wireless interface as wlan0 ('net_wlan0_rbytes', 'INTEGER'), ('net_wlan0_rpackets', 'INTEGER'), ('net_wlan0_rerrs', 'INTEGER'), ('net_wlan0_rdrop', 'INTEGER'), ('net_wlan0_tbytes', 'INTEGER'), ('net_wlan0_tpackets', 'INTEGER'), ('net_wlan0_terrs', 'INTEGER'), ('net_wlan0_tdrop', 'INTEGER'), # if the computer is an openvpn server, track the tunnel traffic # ('net_tun0_rbytes', 'INTEGER'), # ('net_tun0_rpackets', 'INTEGER'), # ('net_tun0_rerrs', 'INTEGER'), # ('net_tun0_rdrop', 'INTEGER'), # ('net_tun0_tbytes', 'INTEGER'), # ('net_tun0_tpackets', 'INTEGER'), # ('net_tun0_terrs', 'INTEGER'), # ('net_tun0_tdrop', 'INTEGER'), # disk volumes will vary, but root is always present ('disk_root_total', 'INTEGER'), ('disk_root_free', 'INTEGER'), ('disk_root_used', 'INTEGER'), # separate partition for home is not uncommon ('disk_home_total', 'INTEGER'), ('disk_home_free', 'INTEGER'), ('disk_home_used', 'INTEGER'), # measure the ups parameters if we can ('ups_temp', 'REAL'), # degree C ('ups_load', 'REAL'), # percent ('ups_charge', 'REAL'), # percent ('ups_voltage', 'REAL'), # volt ('ups_time', 'REAL'), # seconds ] # this exension will scan for all mounted file system. these are the # filesystems we ignore. IGNORED_MOUNTS = [ '/lib/init/rw', '/proc', '/sys', '/dev', '/afs', '/mit', '/run', '/var/lib/nfs'] def logmsg(level, msg): syslog.syslog(level, 'cmon: %s' % msg) def logdbg(msg): logmsg(syslog.LOG_DEBUG, msg) def loginf(msg): logmsg(syslog.LOG_INFO, msg) def logerr(msg): logmsg(syslog.LOG_ERR, msg) def get_collector(hardware=[], ignored_mounts=IGNORED_MOUNTS): # see what we are running on s = platform.system() if s == 'Linux': return LinuxCollector(hardware, ignored_mounts) elif s == 'Darwin': return MacOSXCollector(hardware, ignored_mounts) elif s == 'BSD': return BSDCollector(hardware, ignored_mounts) raise Exception('unsupported system %s' % s) class Collector(object): _CPU_KEYS = ['user','nice','system','idle','iowait','irq','softirq'] # bytes received # packets received # packets dropped # fifo buffer errors # packet framing errors # compressed packets # multicast frames _NET_KEYS = [ 'rbytes','rpackets','rerrs','rdrop','rfifo','rframe','rcomp','rmulti', 'tbytes','tpackets','terrs','tdrop','tfifo','tframe','tcomp','tmulti' ] def __init__(self, hardware): self.hardware = hardware def get_data(self, now_ts=None): if now_ts is None: now_ts = int(time.time() + 0.5) record = dict() record['dateTime'] = now_ts record['usUnits'] = weewx.METRIC if 'rpi' in self.hardware: record.update(self._get_rpi_info()) if 'apcups' in self.hardware: record.update(self._get_apcups_info()) return record _DIGITS = re.compile('[\d.]+') @staticmethod def _get_apcups_info(): record = dict() try: cmd = '/sbin/apcaccess' p = Popen(cmd, shell=True, stdout=PIPE) o = p.communicate()[0] for line in o.split('\n'): if line.startswith('ITEMP'): m = Collector._DIGITS.search(line) if m: record['ups_temp'] = float(m.group()) elif line.startswith('LOADPCT'): m = Collector._DIGITS.search(line) if m: record['ups_load'] = float(m.group()) elif line.startswith('BCHARGE'): m = Collector._DIGITS.search(line) if m: record['ups_charge'] = float(m.group()) elif line.startswith('OUTPUTV') or line.startswith('BATTV'): m = Collector._DIGITS.search(line) if m: record['ups_voltage'] = float(m.group()) elif line.startswith('TIMELEFT'): m = Collector._DIGITS.search(line) if m: record['ups_time'] = float(m.group()) except (ValueError, IOError, KeyError), e: logerr('apcups_info failed: %s' % e) return record _RPI_VCGENCMD = '/opt/vc/bin/vcgencmd' _RPI_VOLT = re.compile('([^:]+):\s+volt=([\d.]+)') _RPI_MEM = re.compile('([^=]+)=([\d]+)') @staticmethod def _get_rpi_info(): # get raspberry pi measurements record = dict() try: cmd = '%s measure_temp' % Collector._RPI_VCGENCMD p = Popen(cmd, shell=True, stdout=PIPE) o = p.communicate()[0] record['core_temp'] = float(o.replace("'C\n", '').partition('=')[2]) cmd = '%s measure_volts' % Collector._RPI_VCGENCMD p = Popen(cmd, shell=True, stdout=PIPE) o = p.communicate()[0] for line in o.split('\n'): m = Collector._RPI_VOLT.search(line) if m: record[m.group(1) + '_volt'] = float(m.group(2)) cmd = '%s measure_volts' % Collector._RPI_VCGENCMD p = Popen(cmd, shell=True, stdout=PIPE) o = p.communicate()[0] for line in o.split('\n'): m = Collector._RPI_MEM.search(line) if m: record[m.group(1) + '_mem'] = float(m.group(2)) except (ValueError, IOError, KeyError), e: logerr('rpi_info failed: %s' % e) return record # this should work on any linux running kernel 2.2 or later class LinuxCollector(Collector): def __init__(self, hardware, ignored_mounts): super(LinuxCollector, self).__init__(hardware) # provide info about the system on which we are running loginf('sysinfo: %s' % ' '.join(os.uname())) fn = '/proc/cpuinfo' try: cpuinfo = self._readproc_dict(fn) for key in cpuinfo: loginf('cpuinfo: %s: %s' % (key, cpuinfo[key])) except Exception, e: logdbg("read failed for %s: %s" % (fn, e)) self.ignored_mounts = ignored_mounts self.last_cpu = dict() self.last_net = dict() @staticmethod def _readproc_line(filename): """read single line proc file, return the string""" info = '' with open(filename) as fp: info = fp.readline().strip() return info @staticmethod def _readproc_lines(filename): """read proc file that has 'name value' format for each line""" info = dict() with open(filename) as fp: for line in fp: line = line.replace(' ', ' ') (label, data) = line.split(' ', 1) info[label] = data return info @staticmethod def _readproc_dict(filename): """read proc file that has 'name:value' format for each line""" info = dict() with open(filename) as fp: for line in fp: if line.find(':') >= 0: (n, v) = line.split(':', 1) info[n.strip()] = v.strip() return info def get_data(self, now=None): record = super(LinuxCollector, self).get_data(now) # read memory status fn = '/proc/meminfo' try: meminfo = self._readproc_dict(fn) if meminfo: record['mem_total'] = int(meminfo['MemTotal'].split()[0]) # kB record['mem_free'] = int(meminfo['MemFree'].split()[0]) # kB record['mem_used'] = record['mem_total'] - record['mem_free'] record['swap_total'] = int(meminfo['SwapTotal'].split()[0]) # kB record['swap_free'] = int(meminfo['SwapFree'].split()[0]) # kB record['swap_used'] = record['swap_total'] - record['swap_free'] except Exception, e: logdbg("read failed for %s: %s" % (fn, e)) # get cpu usage fn = '/proc/stat' try: cpuinfo = self._readproc_lines(fn) if cpuinfo: values = cpuinfo['cpu'].split()[0:7] for i, k in enumerate(self._CPU_KEYS): if k in self.last_cpu: record['cpu_' + k] = int(values[i]) - self.last_cpu[k] self.last_cpu[k] = int(values[i]) except Exception, e: logdbg("read failed for %s: %s" % (fn, e)) # get network usage fn = '/proc/net/dev' try: netinfo = self._readproc_dict(fn) if netinfo: for iface in netinfo: values = netinfo[iface].split() for i, k in enumerate(self._NET_KEYS): if iface not in self.last_net: self.last_net[iface] = {} if k in self.last_net[iface]: x = int(values[i]) - self.last_net[iface][k] if x < 0: maxcnt = 0x100000000 # 32-bit counter if x + maxcnt < 0: maxcnt = 0x10000000000000000 # 64-bit counter x += maxcnt record['net_' + iface + '_' + k] = x self.last_net[iface][k] = int(values[i]) except Exception, e: logdbg("read failed for %s: %s" % (fn, e)) # uptimestr = _readproc_line('/proc/uptime') # (uptime,idletime) = uptimestr.split() # get load and process information fn = '/proc/loadavg' try: loadstr = self._readproc_line(fn) if loadstr: (load1, load5, load15, nproc) = loadstr.split()[0:4] record['load1'] = float(load1) record['load5'] = float(load5) record['load15'] = float(load15) (num_proc, tot_proc) = nproc.split('/') record['proc_active'] = int(num_proc) record['proc_total'] = int(tot_proc) except Exception, e: logdbg("read failed for %s: %s" % (fn, e)) # read cpu temperature tdir = '/sys/class/hwmon/hwmon0/device' # rpi keeps cpu temperature in a different location tfile = '/sys/class/thermal/thermal_zone0/temp' if os.path.exists(tdir): try: for f in os.listdir(tdir): if f.endswith('_input'): s = self._readproc_line(os.path.join(tdir, f)) if s and len(s): n = f.replace('_input', '') t_C = int(s) / 1000 # degree C record['cpu_' + n] = t_C except Exception, e: logdbg("read failed for %s: %s" % (tdir, e)) elif os.path.exists(tfile): try: s = self._readproc_line(tfile) t_C = int(s) / 1000 # degree C record['cpu_temp'] = t_C except Exception, e: logdbg("read failed for %s: %s" % (tfile, e)) # get stats on mounted filesystems fn = '/proc/mounts' disks = [] try: mntlines = self._readproc_lines(fn) if mntlines: for mnt in mntlines: mntpt = mntlines[mnt].split()[0] ignore = False if mnt.find(':') >= 0: ignore = True for m in self.ignored_mounts: if mntpt.startswith(m): ignore = True break if not ignore: disks.append(mntpt) except Exception, e: logdbg("read failed for %s: %s" % (fn, e)) for disk in disks: label = disk.replace('/', '_') if label == '_': label = '_root' st = os.statvfs(disk) free = int((st.f_bavail * st.f_frsize) / 1024) # kB total = int((st.f_blocks * st.f_frsize) / 1024) # kB used = int(((st.f_blocks - st.f_bfree) * st.f_frsize) / 1024) # kB record['disk' + label + '_free'] = free record['disk' + label + '_total'] = total record['disk' + label + '_used'] = used return record class MacOSXCollector(Collector): def __init__(self, hardware, ignored_mounts): super(MacOSXCollector, self).__init__(hardware) # provide info about the system on which we are running loginf('sysinfo: %s' % ' '.join(os.uname())) self.ignored_mounts = ignored_mounts self.last_cpu = dict() self.last_net = dict() def get_data(self, now=None): import psutil record = super(MacOSXCollector, self).get_data(now) # read memory status info = psutil.virtual_memory() record['mem_total'] = info.total record['mem_free'] = info.free record['mem_used'] = info.used record['mem_available'] = meminfo.available record['mem_active'] = meminfo.active record['mem_inactive'] = meminfo.inactive record['mem_wired'] = meminfo.wired info = psutil.swap_memory() record['swap_total'] = info.total record['swap_free'] = info.free record['swap_used'] = info.used record['swap_sin'] = info.sin record['swap_sout'] = info.sout # get cpu usage ncpu = psutil.cpu_count() record['cpu_count'] = ncpu info = psutil.cpu_times() for k in self._CPU_KEYS: v = getattr(info, k, None) if k in self.last_cpu: record['cpu_' + k] = v - self.last_cpu[k] self.last_cpu[k] = v # get network usage info = psutil.net_io_counters(pernic=True) for iface in info: for k in self._NET_KEYS: if iface not in self.last_net: self.last_net[iface] = {} v = getattr(info[iface], k, None) if k in self.last_net[iface]: x = v - self.last_net[iface][k] if x < 0: maxcnt = 0x100000000 # 32-bit counter if x + maxcnt < 0: maxcnt = 0x10000000000000000 # 64-bit counter x += maxcnt record['net_' + iface + '_' + k] = x self.last_net[iface][k] = v # uptime # FIXME: implement uptime/idletime # get load and process information # FIXME: implement load # read cpu temperature # FIXME: implement cpu temperature # get stats on mounted filesystems info = psutil.disk_partitions() for d in info: label = d.mountpoint.replace('/', '_') if label == '_': label = '_root' du = psutil.disk_usage(d.mountpoint) record['disk' + label + '_free'] = du.free / 1024 # kB record['disk' + label + '_total'] = du.total / 1024 # kB record['disk' + label + '_used'] = du.used / 1024 # kB # FIXME: add io counters per disk return record def loader(config_dict, engine): return ComputerMonitorDriver(**config_dict['ComputerMonitor']) class ComputerMonitorDriver(AbstractDevice): """Driver for collecting computer health data.""" def __init__(self, **stn_dict): loginf('driver version is %s' % DRIVER_VERSION) self.polling_interval = int(stn_dict.get('polling_interval', 10)) loginf('polling interval is %s' % self.polling_interval) self.ignored_mounts = stn_dict.get('ignored_mounts', IGNORED_MOUNTS) self.hardware = stn_dict.get('hardware', [None]) if not isinstance(self.hardware, list): self.hardware = [self.hardware] self.collector = get_collector(self.hardware, self.ignored_mounts) @property def hardware_name(self): return 'ComputerMonitor' def genLoopPackets(self): while True: p = self.collector.get_data() yield p time.sleep(self.polling_interval) class ComputerMonitor(StdService): """Collect CPU, Memory, Disk, and other computer information.""" def __init__(self, engine, config_dict): super(ComputerMonitor, self).__init__(engine, config_dict) loginf("service version is %s" % DRIVER_VERSION) d = config_dict.get('ComputerMonitor', {}) self.max_age = weeutil.weeutil.to_int(d.get('max_age', 2592000)) self.ignored_mounts = d.get('ignored_mounts', IGNORED_MOUNTS) self.hardware = d.get('hardware', [None]) if not isinstance(self.hardware, list): self.hardware = [self.hardware] # get the database parameters we need to function binding = d.get('data_binding', 'cmon_binding') self.dbm = self.engine.db_binder.get_manager(data_binding=binding, initialize=True) # be sure schema in database matches the schema we have dbcol = self.dbm.connection.columnsOf(self.dbm.table_name) dbm_dict = weewx.manager.get_manager_dict( config_dict['DataBindings'], config_dict['Databases'], binding) memcol = [x[0] for x in dbm_dict['schema']] if dbcol != memcol: raise Exception('cmon schema mismatch: %s != %s' % (dbcol, memcol)) self.last_ts = None self.collector = get_collector(self.hardware, self.ignored_mounts) self.bind(weewx.NEW_ARCHIVE_RECORD, self.new_archive_record) def shutDown(self): try: self.dbm.close() except: pass def new_archive_record(self, event): """save data to database then prune old records as needed""" now = int(time.time() + 0.5) delta = now - event.record['dateTime'] if delta > event.record['interval'] * 60: logdbg("Skipping record: time difference %s too big" % delta) return if self.last_ts is not None: self.save_data(self.get_data(now, self.last_ts)) self.last_ts = now if self.max_age is not None: self.prune_data(now - self.max_age) def save_data(self, record): """save data to database""" self.dbm.addRecord(record) def prune_data(self, ts): """delete records with dateTime older than ts""" sql = "delete from %s where dateTime < %d" % (self.dbm.table_name, ts) self.dbm.getSql(sql) try: # sqlite databases need some help to stay small self.dbm.getSql('vacuum') except Exception: pass def get_data(self, now_ts, last_ts): record = self.collector.get_data(now_ts) record['interval'] = int((now_ts - last_ts) / 60) return record # To test this extension, do the following: # # cd /home/weewx # PYTHONPATH=bin python bin/user/cmon.py # if __name__ == "__main__": usage = """%prog [options] [--help] [--debug]""" def main(): import optparse import weecfg syslog.openlog('wee_cmon', syslog.LOG_PID | syslog.LOG_CONS) parser = optparse.OptionParser(usage=usage) parser.add_option('--config', dest='cfgfn', type=str, metavar="FILE", help="Use configuration file FILE. Default is /etc/weewx/weewx.conf or /home/weewx/weewx.conf") parser.add_option('--binding', dest="binding", metavar="BINDING", default='cmon_binding', help="The data binding to use. Default is 'cmon_binding'.") parser.add_option('--test-collector', dest='tc', action='store_true', help='test the data collector') parser.add_option('--test-driver', dest='td', action='store_true', help='test the driver') parser.add_option('--test-service', dest='ts', action='store_true', help='test the service') parser.add_option('--update', dest='update', action='store_true', help='update schema to v3') (options, args) = parser.parse_args() if options.update: _, config_dict = weecfg.read_config(options.cfgfn, args) update_to_v3(config_dict, options.binding) elif options.tc: test_collector() elif options.td: test_driver() elif options.ts: test_service() def update_to_v3(config_dict, db_binding): import weedb import sqlite3 from weewx.manager import Manager # update an old schema to v3-compatible. this means adding the # interval column and populating it. mgr_dict = weewx.manager.get_manager_dict(config_dict['DataBindings'], config_dict['Databases'], db_binding) # see if update is actually required schema_name = mgr_dict.get('schema') if schema_name is None: s = schema elif isinstance(schema_name, str): s = weeutil.weeutil._get_object(schema_name) else: s = mgr_dict['schema'] if 'interval' in s: print "Column 'interval' already exists, update not necessary" return # make a copy of the database dict new_db_dict = dict(mgr_dict['database_dict']) # modify the database name new_db_dict['database_name'] = mgr_dict['database_dict']['database_name'] + '_new' # see if the new db exists. if so, see if we can delete it try: weedb.create(new_db_dict) except weedb.DatabaseExists: ans = None while ans not in ['y', 'n']: ans = raw_input("New database '%s' already exists. Delete it (y/n)? " % (new_db_dict['database_name'],)) if ans == 'y': weedb.drop(new_db_dict) elif ans == 'n': print "update aborted" return except sqlite3.OperationalError: pass # see if we really should do this ans = None while ans not in ['y', 'n']: ans = raw_input("Create new database %s (y/n)? " % new_db_dict['database_name']) if ans == 'y': break elif ans == 'n': print "update aborted" return # copy the data over cnt = 0 with Manager.open(mgr_dict['database_dict']) as old_mgr: with Manager.open_with_create(new_db_dict, schema=s) as new_mgr: last_ts = None for r in old_mgr.genBatchRecords(): cnt += 1 print "record %d\r" % cnt, ival = r['dateTime'] - last_ts if last_ts else 0 r['interval'] = ival new_mgr.addRecord(r) last_ts = r['dateTime'] print "copied %d records\n" % cnt def test_collector(): c = get_collector() while True: print c.get_data() time.sleep(5) def test_driver(): driver = ComputerMonitorDriver() for p in driver.genLoopPackets(): print p def test_service(): from weewx.engine import StdEngine config = { 'Station': { 'station_type': 'Simulator', 'altitude': [0, 'foot'], 'latitude': 0, 'longitude': 0}, 'Simulator': { 'driver': 'weewx.drivers.simulator', 'mode': 'simulator'}, 'ComputerMonitor': { 'binding': 'cmon_binding'}, 'DataBindings': { 'cmon_binding': { 'database': 'cmon_sqlite', 'manager': 'weewx.manager.DaySummaryManager', 'table_name': 'archive', 'schema': 'user.cmon.schema'}}, 'Databases': { 'cmon_sqlite': { 'root': '%(WEEWX_ROOT)s', 'database_name': '/tmp/cmon.sdb', 'driver': 'weedb.sqlite'}}, 'Engine': { 'Services': { 'archive_services': 'user.cmon.ComputerMonitor'}}} engine = StdEngine(config) svc = ComputerMonitor(engine, config) now_ts = int(time.time() + 0.5) last_ts = None for _ in range(4): if last_ts is not None: record = svc.get_data(now_ts, last_ts) print record last_ts = now_ts time.sleep(5) os.remove('/tmp/cmon.sdb') main()