From d6f8378f771930462ae0caa1a242fc6efdcd8a5f Mon Sep 17 00:00:00 2001 From: Tom Keffer Date: Mon, 5 Apr 2010 22:42:24 +0000 Subject: [PATCH] Branch to experiment with a different way of tracking units --- CHANGES.txt | 205 +- MANIFEST | 12 +- NEW_FEATURES.txt | 4 - TODO.txt | 3 + docs/customizing.htm | 2765 ++++++++++++++++++--- docs/daytemp_with_avg.png | Bin 0 -> 10457 bytes docs/readme.htm | 1655 ++---------- docs/upgrading.htm | 18 +- docs/weekgustoverlay.png | Bin 0 -> 10649 bytes examples/alarm.py | 46 +- examples/daily.py | 12 +- examples/lowBattery.py | 164 ++ examples/mygenerator.py | 45 + setup.py | 159 +- skins/Ftp/skin.conf | 2 +- skins/Standard/NOAA/NOAA-YYYY-MM.txt.tmpl | 11 +- skins/Standard/NOAA/NOAA-YYYY.txt.tmpl | 30 +- skins/Standard/RSS/weewx_rss.xml.tmpl | 99 + skins/Standard/index.html.tmpl | 26 +- skins/Standard/month.html.tmpl | 3 + skins/Standard/skin.conf | 332 +-- skins/Standard/week.html.tmpl | 3 + skins/Standard/year.html.tmpl | 3 + start_scripts/Debian/weewx | 10 +- start_scripts/SuSE/weewx | 8 + upload.py | 306 --- weeplot/genplot.py | 8 +- weeutil/ftpupload.py | 233 ++ weeutil/weeutil.py | 32 +- weewx.conf | 53 +- weewx/VantagePro.py | 33 +- weewx/__init__.py | 2 +- weewx/archive.py | 7 +- weewx/{genfiles.py => filegenerator.py} | 224 +- weewx/formatter.py | 386 ++- weewx/ftpdata.py | 77 - weewx/{genimages.py => imagegenerator.py} | 64 +- weewx/reportengine.py | 167 +- weewx/station.py | 22 +- weewx/stats.py | 201 +- weewx/units.py | 352 +-- weewx/wxengine.py | 10 +- 42 files changed, 4676 insertions(+), 3116 deletions(-) create mode 100644 docs/daytemp_with_avg.png create mode 100644 docs/weekgustoverlay.png create mode 100644 examples/lowBattery.py create mode 100644 examples/mygenerator.py create mode 100644 skins/Standard/RSS/weewx_rss.xml.tmpl delete mode 100644 upload.py create mode 100644 weeutil/ftpupload.py rename weewx/{genfiles.py => filegenerator.py} (53%) delete mode 100644 weewx/ftpdata.py rename weewx/{genimages.py => imagegenerator.py} (84%) diff --git a/CHANGES.txt b/CHANGES.txt index 4e812102..28ec14cb 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,60 +1,75 @@ CHANGE HISTORY -------------------------------- -1.6.0 03/22/10 +1.6.0 04/5/10 -Now supports skins. A skin is a collection of templates, under the -control of a 'skin configuration file.' Presentation layer specific -options have been moved out of the weewx configuration file -'weewx.conf' to the skin configuration files 'skin.conf'. The standard -distribution comes with one skin, but new ones can be added easily. +Reports now use skins for their "look or feel." Options specific to the +presentation layer have been moved out of the weewx configuration file +'weewx.conf' to a skin configuration file, 'skin.conf'. Other options have +remained behind. -FTP is treated as just another report, albeit with an unusual report -generator. +Because the configuration file weewx.conf was split, the installation script +setup.py will NOT merge your old configuration file into the new one. You will +have to reedit weewx.conf to put in your customizations. -Data files used in reports (such as weewx.css) are copied over to the -HTML directory on program startup. So, setup.py no longer deletes -public_html/#upstream.last. +FTP is treated as just another report, albeit with an unusual generator. You can +have multiple FTP sessions, each to a different server, or uploading to or from +a different area. -HTML labels are now derived from regular labels by substituting -HTML entities for non-7bit characters. For example, "\0xb0F" becomes -"°F". +Rewrote the FTP upload package so that it allows more than one FTP session to be +active in the same local directory. This version also does fewer hits on the +server, so it is significantly faster. -Moved the URL for the radar image from the configuration file to the -template index.html.tmpl. +The configuration files weewx.conf and skin.conf now expect UTF-8 characters +throughout. -Now requires configobj v4.6 (instead of V4.5). As this version was -released nearly a year ago, this should not be a problem for most -users. If it is the following command will take care of it: - easy_install --upgrade configobj +The encoding for reports generated from templates can be chosen. By default, the +day, week, month, and year HTML files are encoded using HTML entities; the NOAA +reports encoded using 'strict ascii.' Optionally, reports can be encoded using +UTF-8. + +Optional formatting was added to all tags in the templates. There are now +optional endings: + 'raw': return the underlying data with no string formatting or label. + 'formatted': return a version with string formatting, but no label. + 'format': Use the specified string format. No label. + 'string': Let Python convert to a string. + +Added an RSS feed. + +Now offer a section 'Extras' in the skin configuration file for including tags +added by the user. As an example, the tag radar_url has been moved into here. + +Data files used in reports (such as weewx.css) are copied over to the HTML +directory on program startup. + +Included an example of a low-battery alarm. + +Changed the name of the service StdProcess to StdReportService. This will affect +only users who have written custom reports. 1.5.0 03/07/10 -Added support for other units besides the U.S. Customary. Plots and -HTML reports can be prepared using any arbitrary combination of -units. For example, pressure could be in millibars, while everything -else is in U.S. Customary. +Added support for other units besides the U.S. Customary. Plots and HTML reports +can be prepared using any arbitrary combination of units. For example, pressure +could be in millibars, while everything else is in U.S. Customary. Because the configuration file weewx.conf changed significantly, the -installation script setup.py will NOT merge your old configuration -file into the new one. You will have to reedit weewx.conf to put in -your customizations. - -Added an exception handler for exception OSError, which is typically -thrown when another piece of software attempts to access the same -device port. Weewx catches the exception, waits 10 seconds, then -starts again from the top. +installation script setup.py will NOT merge your old configuration file into the +new one. You will have to reedit weewx.conf to put in your customizations. +Added an exception handler for exception OSError, which is typically thrown when +another piece of software attempts to access the same device port. Weewx +catches the exception, waits 10 seconds, then starts again from the top. 1.4.0 02/22/10 -Changed the architecture of stats.py to one that uses very late -binding. The SQL statements are not run until template -evaluation. This reduces the amount of memory required (by about 1/2), -reduces memory fragmentation, as well as greatly simplifying the code -(file stats.py shed over 150 lines of non-test code). Execution time -is slightly slower for NOAA file generation, slightly faster for HTML -file generation, the same for image generation, although your actual +Changed the architecture of stats.py to one that uses very late binding. The SQL +statements are not run until template evaluation. This reduces the amount of +memory required (by about 1/2), reduces memory fragmentation, as well as greatly +simplifying the code (file stats.py shed over 150 lines of non-test code). +Execution time is slightly slower for NOAA file generation, slightly faster for +HTML file generation, the same for image generation, although your actual results will depend on your disk speed. Now possible to tell weewx to reread the configuration file without @@ -90,31 +105,31 @@ that manages a list of 'services.' At key events, each service is given a chance to participate. Services are easy to add, to allow easy customization. An example is offered of an 'alarm' service. -Checking the clock of the weather station for drift is now a service, so -the option clock_check was moved from the station specific [VantagePro] -section to the more general [Station] section. +Checking the clock of the weather station for drift is now a service, so the +option clock_check was moved from the station specific [VantagePro] section to +the more general [Station] section. -Added an example service 'MyAlarm', which sends out an email should the -outside temperature drop below 40 degrees. +Added an example service 'MyAlarm', which sends out an email should the outside +temperature drop below 40 degrees. -In a similar manner, all generated files, images, and reports are the -product of a report engine, which can run any number of reports. New reports -are easily added. +In a similar manner, all generated files, images, and reports are the product of +a report engine, which can run any number of reports. New reports are easily +added. -Moved the compass rose used in progressive vector plots into the interior -of the plot. +Moved the compass rose used in progressive vector plots into the interior of the +plot. -Install now deletes public_html/#upstream.last, thus forcing all files to -be uploaded to the web server at the next opportunity. +Install now deletes public_html/#upstream.last, thus forcing all files to be +uploaded to the web server at the next opportunity. 1.2.0 11/22/09 Added progressive vector plots for wind data. -Improved axis scaling. The automatic axis scaling routine -now does a better job for ranges less than 1.0. The user can also -hardwire in min and max values, as well as specify a minimum increment, -through parameter 'yscale' in section [Images] in the configuration file. +Improved axis scaling. The automatic axis scaling routine now does a better job +for ranges less than 1.0. The user can also hardwire in min and max values, as +well as specify a minimum increment, through parameter 'yscale' in section +[Images] in the configuration file. Now allows the same SQL type to be used more than once in a plot. This allows, say, instantaneous and average wind speed to be shown in @@ -137,57 +152,54 @@ Added the ability to cache LOOP data. This can dramatically reduce the number of writes to the stats database, reducing wear on solid-state disk stores. -Introduced module weewx.mainloop. Introduced class weewx.mainloop.MainLoop -This class offers many opportunities to customize weewx through subclassing, -then overriding an appropriate member function. +Introduced module weewx.mainloop. Introduced class weewx.mainloop.MainLoop This +class offers many opportunities to customize weewx through subclassing, then +overriding an appropriate member function. -Refactored module weewx.wunderground so it more closely resembles the -(better) logic in wunderfixer. +Refactored module weewx.wunderground so it more closely resembles the (better) +logic in wunderfixer. -setup.py no longer installs a daemon startup script to /etc/init.d. It must -now be done by hand. +setup.py no longer installs a daemon startup script to /etc/init.d. It must now +be done by hand. -setup.py now uses the 'home' value in setup.cfg to set WEEWX_ROOT in -weewx.conf and in the daemon start up scripts +setup.py now uses the 'home' value in setup.cfg to set WEEWX_ROOT in weewx.conf +and in the daemon start up scripts Now uses FTP passive mode by default. 1.0.1 11/09/09 -Fixed bug that prevented backfilling the stats database after modifying -the main archive. +Fixed bug that prevented backfilling the stats database after modifying the main +archive. 1.0.0 10/26/09 -Took the module weewx.factory back out, as it was too complicated and -hard to understand. +Took the module weewx.factory back out, as it was too complicated and hard to +understand. -Added support for generating NOAA monthly and yearly -reports. Completely rewrote the genfiles.py module, to allow easy -subclassing and specialization. +Added support for generating NOAA monthly and yearly reports. Completely rewrote +the filegenerator.py module, to allow easy subclassing and specialization. -Completely rewrote the stats.py module. All aggregate quantities -are now calculated dynamically. +Completely rewrote the stats.py module. All aggregate quantities are now +calculated dynamically. -Labels for HTML generation are now held separately from labels used -for image generation. This allows entities such as '°' to be used -for the former. +Labels for HTML generation are now held separately from labels used for image +generation. This allows entities such as '°' to be used for the former. -LOOP mode now requests only 200 LOOP records (instead of the old -2000). It then renews the request should it run out. This was to get -around an (undocumented) limitation in the VP2 that limits the number -of LOOP records that can be requested to something like 220. This was -a problem when supporting VP2s that use long archive intervals. +LOOP mode now requests only 200 LOOP records (instead of the old 2000). It then +renews the request should it run out. This was to get around an (undocumented) +limitation in the VP2 that limits the number of LOOP records that can be +requested to something like 220. This was a problem when supporting VP2s that +use long archive intervals. -Cut down the amount of computing that went on before the processing -thread was spawned, thus allowing the main thread to get back into -LOOP mode more quickly. +Cut down the amount of computing that went on before the processing thread was +spawned, thus allowing the main thread to get back into LOOP mode more quickly. -Added type 'rainRate' to the types decoded from a Davis archive -record. For some reason it was left out. +Added type 'rainRate' to the types decoded from a Davis archive record. For some +reason it was left out. -Added retries when doing FTP uploads. It will now attempt the upload -several times before giving up. +Added retries when doing FTP uploads. It will now attempt the upload several +times before giving up. Much more extensive DEBUG analysis. @@ -197,8 +209,8 @@ Nipped and tucked here and there, trying to simplify. 0.6.5 10/11/09 Ported to Cheetah V2.2.X. Mostly, this is making sure that all strings that -cannot be converted with the 'ascii' codec are converted to Unicode first -before feeding to Cheetah. +cannot be converted with the 'ascii' codec are converted to Unicode first before +feeding to Cheetah. 0.6.4 9/22/09 @@ -214,9 +226,9 @@ PASSIVE mode. This was necessary to support Microsoft FTP servers. Exception handling in weewx/ftpdata.py used socket.error but failed to declare it. Added 'import socket' to fix. -Added more complete check for unused pages in weewx/VantagePro.py. Now the entire -record must be filled with 0xff, not just the time field. This fixes a bug where -certain time stamps could look like unused records. +Added more complete check for unused pages in weewx/VantagePro.py. Now the +entire record must be filled with 0xff, not just the time field. This fixes a +bug where certain time stamps could look like unused records. 0.6.1 6/22/09 @@ -234,11 +246,12 @@ place to inject his/her new types. 0.5.1 5/13/09 -1. Weather Underground thread now run as daemon thread, allowing the program -to exit even if it is running. +1. Weather Underground thread now run as daemon thread, allowing the program to +exit even if it is running. 2. WU queue now hold an instance of archive and the time to be published, rather than a record. This allows dailyrain to be published as well. 3. WU date is now given in the format "2009-05-13+12%3A35%3A00" rather than -"2009-05-13 12:35:00". Seems to be more reliable. But, maybe I'm imagining things... +"2009-05-13 12:35:00". Seems to be more reliable. But, maybe I'm imagining +things... diff --git a/MANIFEST b/MANIFEST index f67e29e8..28e32f99 100644 --- a/MANIFEST +++ b/MANIFEST @@ -6,16 +6,19 @@ configure.py daemon.py setup.cfg setup.py -upload.py weewx.conf weewxd.py docs/customizing.htm +docs/daytemp_with_avg.png docs/readme.htm docs/sheeva.htm docs/upgrading.htm +docs/weekgustoverlay.png examples/__init__.py examples/alarm.py examples/daily.py +examples/lowBattery.py +examples/mygenerator.py skins/Ftp/skin.conf skins/Standard/index.html.tmpl skins/Standard/month.html.tmpl @@ -25,6 +28,7 @@ skins/Standard/weewx.css skins/Standard/year.html.tmpl skins/Standard/NOAA/NOAA-YYYY-MM.txt.tmpl skins/Standard/NOAA/NOAA-YYYY.txt.tmpl +skins/Standard/RSS/weewx_rss.xml.tmpl skins/Standard/backgrounds/band.gif skins/Standard/backgrounds/butterfly.jpg skins/Standard/backgrounds/drops.gif @@ -40,15 +44,15 @@ weeutil/Almanac.py weeutil/Moon.py weeutil/Sun.py weeutil/__init__.py +weeutil/ftpupload.py weeutil/weeutil.py weewx/VantagePro.py weewx/__init__.py weewx/archive.py weewx/crc16.py +weewx/filegenerator.py weewx/formatter.py -weewx/ftpdata.py -weewx/genfiles.py -weewx/genimages.py +weewx/imagegenerator.py weewx/reportengine.py weewx/station.py weewx/stats.py diff --git a/NEW_FEATURES.txt b/NEW_FEATURES.txt index c70f9424..ec6a254b 100644 --- a/NEW_FEATURES.txt +++ b/NEW_FEATURES.txt @@ -2,10 +2,6 @@ NEW FEATURES to be added in decreasing order of importance. udev support to compensate for flaky 2101 drivers. -Support for UTF-8 characters. - -Add support for user-supplied skins. - Add support for CWOP. Allow rain-to-date to be added to a partial rainyear. diff --git a/TODO.txt b/TODO.txt index 26365e2c..52f76c66 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,3 +1,6 @@ TODO before the next release: +Add the skin subdirectory to PYTHONPATH automatically, +to allow custom skins to find their generators. +Clarify table of what can be used where. Such as windvec. \ No newline at end of file diff --git a/docs/customizing.htm b/docs/customizing.htm index 4580a2de..f040b563 100644 --- a/docs/customizing.htm +++ b/docs/customizing.htm @@ -1,12 +1,14 @@ - - - - - - - - -Customizing weewx + + + + + + + + + + +Customizing weewx - - - - -

Customizing weewx v1.5

-

Overview

-

At a high level, weewx consists of an engine that is responsible for -managing a set of services. A service consists of a Python class with a -set of member functions. The engine arranges to have appropriate member -functions called when specific events happen. For example, when a new LOOP -packet arrives, member function processLoopPacket() of -all services is called.

-

To customize, you can

- -

This document describes how to do all three.

-

The default install of weewx includes the following services:

- - - - - - - - - - - - - - - - - - - - - - - - - -
ServiceFunction
weewx.wxengine.StdWundergroundStarts thread to manage WU connection; adds new data to a Queue to - be posted to the WU by the thread.
weewx.wxengine.StdCatchUpAny data found on the weather station memory but not yet in the - archive, is retrieved and put in the archive.
weewx.wxengine.StdTimeSynchArranges to have the clock on the station synchronized at regular - intervals.
weewx.wxengine.StdPrintPrints out new LOOP and archive packets on the console.
weewx.wxengine.StdProcessLaunches a new thread to do processing after a new archive record - arrives. The thread loads zero or more reports and processes them in - order. Reports do things such as generate HTML files, generate images, - or FTP files to a web server. New reports can be added easily by the - user.
- -

Customizing a Service

-

The service weewx.wxengine.StdPrint prints out new LOOP and archive packets -to the console when they arrive. By default, it prints out time, barometer, -outside temperature, wind speed, and wind direction. Suppose you don't like -this, and want to print out humidity as well when a new LOOP packet arrives, but -leave the printing of archive packets alone. This could be done by subclassing the default -print service StdPrint and overriding member function -processLoopPacket().

-

In file myprint.py:

-

from weewx.wxengine import StdPrint
-from weeutil.weeutil import timestamp_to_string
-
-class MyPrint(StdPrint):
-
-    # Override the default processLoopPacket:
-    def processLoopPacket(self, physicalPacket):
-        print "LOOP: ", timestamp_to_string(physicalPacket['dateTime']),\
-            -physicalPacket['barometer'],\
-            -physicalPacket['outTemp'],\
-            -physicalPacket['outHumidity'],\
-            -physicalPacket['windSpeed'],\
-            -physicalPacket['windDir']

-

You then need to specify that your print service class should be loaded -instead of the default StdPrint service. This is done -by substituting your service name for the standard print service name in the -option service_list, located in -[Engines][[WxEngine]]:

-

[Engines]
-    [[WxEngine]]
-         - service_list = weewx.wxengine.StdWunderground, weewx.wxengine.StdCatchUp,
-                       weewx.wxengine.StdTimeSynch, myprint.MyPrint,
-                       weewx.wxengine.StdProcess

-

(Note that this list is shown on several lines for clarity, but in actuality -it must be all on one line. The parser ConfigObj does -not allow options to be continued on to following lines.)

-

Adding a Service

-

Suppose there is no service that can easily be customized for your needs. In -this case, a -new one can easily be created by subclassing off the abstract base class -StdService, and then adding the functionality you -need. Here's an example that implements an alarm that sends off an email -when an arbitrary expression evaluates True. This example is included in the standard -distribution in subdirectory 'examples.'

-

File examples/alarm.py:

-

import time
-import smtplib
-from email.mime.text import MIMEText
-import threading
-import syslog
-
-from weewx.wxengine import StdService
-from weeutil.weeutil import timestamp_to_string
-
-# Inherit from the base class StdService:
-class MyAlarm(StdService):
-    """Custom service that sounds an alarm if an expression -evaluates true"""
-
-    def __init__(self, engine):
-        # Pass the initialization information -on to my superclass:
-        StdService.__init__(self, engine)
-
-        # This will hold the time when the -last alarm message went out:
-        self.last_msg = None
-        self.expression = None
-
-    def setup(self):
-        try:
-            # Dig the -needed options out of the configuration dictionary.
-            # If a -critical option is missing, an exception will be thrown and
-            # the alarm -will not be set.
-            -self.expression = self.engine.config_dict['Alarm']['expression']
-            -self.time_wait = int(self.engine.config_dict['Alarm'].get('time_wait', '3600'))
-            -self.smtp_host = self.engine.config_dict['Alarm']['smtp_host']
-            -self.smtp_user = self.engine.config_dict['Alarm'].get('smtp_user')
-            -self.smtp_password = self.engine.config_dict['Alarm'].get('smtp_password')
-            self.TO = -self.engine.config_dict['Alarm']['mailto']
-            syslog.syslog(syslog.LOG_INFO, -"alarm: Alarm set for expression %s" % self.expression)
-        except:
-            -self.expression = None
-            -self.time_wait = None
-
-    def postArchiveData(self, rec):
-        # Let the super class see the record -first:
-        StdService.postArchiveData(self, rec)
-
-        # See if the alarm has been set:
-        if self.expression:
-            # To avoid a -flood of nearly identical emails, this will do
-            # the check -only if we have never sent an email, or if we haven't
-            # sent one in -the last self.time_wait seconds:
-            if not -self.last_msg or abs(time.time() - self.last_msg) >= self.time_wait :
-
-                -# Evaluate the expression in the context of 'rec'.
-                -# Sound the alarm if it evaluates true:
-                -if eval(self.expression, None, rec):        # -NOTE 1
-                    -# Sound the alarm!
-                    -# Launch in a separate thread so it doesn't block the main LOOP thread:
-                    -t = threading.Thread(target = MyAlarm.soundTheAlarm, args=(self, rec))
-                    -t.start()
-
-    def soundTheAlarm(self, rec):
-        """This function is called when the -given expression evaluates True."""
-
-        # Get the time and convert to a -string:
-        t_str = timestamp_to_string(rec['dateTime'])
-        # Form the message text:
-        msg_text = "Alarm expression %s -evaluated True at %s\nRecord:\n%s" % (self.expression, t_str, str(rec))
-        # Convert to MIME:
-        msg = MIMEText(msg_text)
-
-        # Fill in MIME headers:
-        msg['Subject'] = "Alarm message from -weewx"
-        msg['From'] = "weewx"
-        msg['To'] = self.TO
-
-        # Create an instance of class SMTP -for the given SMTP host:
-        s = smtplib.SMTP(self.smtp_host)
-        # If a username has been given, -assume that login is required for this host:
-        if self.smtp_user:
-            s.login(self.smtp_user, -self.smtp_password)
-        # Send the email:
-        s.sendmail(msg['From'], [self.TO], -msg.as_string())
-        # Log out of the server:
-        s.quit()
-        # Record when the message went out:
-        self.last_msg = time.time()
-        # Log it in the system log:
-        syslog.syslog(syslog.LOG_INFO, -"alarm: Alarm sounded for expression %s" % self.expression)
-        syslog.syslog(syslog.LOG_INFO, " *** -email sent to: %s" % self.TO)

-

This service expects all the information it needs to be in the configuration -file weewx.conf in a new section called -[Alarm]. So, add the following lines to your -configuration file:

-

[Alarm]
-    expression = "outTemp < 40.0"
-    time_wait = 1800
-    smtp_host = smtp.mymailserver.com
-    smtp_user = myusername
-    smtp_password = mypassword
-    mailto = auser@adomain.com

-

These options specify that the alarm is to be sounded when "outTemp -< 40.0" evaluates True, that is when the outside temperature is below -40.0 degrees. Any valid Python expression can be used, although the only -variables available are those in the current archive record. (The place in the -code where the expression is evaluated is marked with "Note 1".)

-

Another example expression could be:

-

    expression = "outTemp < 32.0 and windSpeed > -10.0"

-

In this case, the alarm is sounded if the outside temperature drops below -freezing and the wind speed is greater than 10.0.

-

Option time_wait is used to avoid a flood of nearly -identical emails. The new service will wait this long before sending another -email out.

-

Email will be sent through the SMTP host specified by option -smtp_host. The recipient is specified in option -mailto.

-

Many SMTP hosts require user login. If this is the case, the user and -password are specified with options smtp_user and -smtp_password, respectively.

-

To make this all work, you must tell the engine to load this new service. -This is done by adding your service name to the list -service_list, located in [Engines][[WxEngine]]:

-

[Engines]
-    [[WxEngine]]
-        -service_list = weewx.wxengine.StdWunderground, weewx.wxengine.StdCatchUp,
-                       weewx.wxengine.StdTimeSynch, weewx.wxengine.StdPrint,
-                       weewx.wxengine.StdProcess, examples.alarm.MyAlarm

-

(Again, note that this list is shown on several lines for clarity, but in -actuality it must be all on one line.)

-

Customizing the Engine

-

In this section, we look at how to install a custom Engine. In general, this -is the least desirable way to proceed, but in some cases it may be the only way -to get what you want.

-

For example, suppose you want to define a new event for when the first -archive of a day arrives. This can be done by extending the the standard engine.

-

This example is in file example/daily.py:

-

from weewx.wxengine import StdEngine, StdService
-from weeutil.weeutil import startOfArchiveDay
-
-class MyEngine(StdEngine):
-    """A customized weewx engine."""
-
-    def __init__(self, *args, **vargs):
-        # Pass on the initialization data to -my superclass:
-        StdEngine.__init__(self, *args, **vargs)
-
-        # This will record the timestamp of -the old day
-        self.old_day = None
-
-    def postArchiveData(self, rec):
-        # First let my superclass process it:
-        StdEngine.postArchiveData(self, rec)
-
-        # Get the timestamp of the start of -the day using
-        # the utility function -startOfArchiveDay
-        dayStart_ts = startOfArchiveDay(rec['dateTime'])
-
-        # Call the function firstArchiveOfDay -if either this is
-        # the first archive since startup, or -if a new day has started
-        if not self.old_day or self.old_day -!= dayStart_ts:
-            self.old_day -= dayStart_ts
-            -self.newDay(rec)                          -# Note 1
-
-    def newDay(self, rec):
-        """Called when the first archive -record of a day arrives."""
-
-        # Go through the list of service -objects. This
-        # list is actually in my superclass -StdEngine.
-        for svc_obj in self.service_obj:
-            # Because -this is a new event, not all services will
-            # be prepared -to accept it. Check first to see if the
-            # service has -a member function "firstArchiveOfDay"
-            # before -calling it:
-            if hasattr(svc_obj, -"firstArchiveOfDay"):  # Note 2
-                -# The object does have the member function. Call it:
-                -svc_obj.firstArchiveOfDay(rec)

-

This customized engine works by monitoring the arrival of archive records, -and checking their time stamp (rec['dateTime']. It -calculates the time stamp for the start of the day, and if it changes, calls -member function newDay() (Note 1).

-

The member function newDay() then goes through the -list of services (attribute self.service_obj). Because -this engine is defining a new event (first archive of the day), the existing -services may not be prepared to accept it. So, the engine checks each one to -make sure it has a function firstArchiveOfDay before -calling it (Note 2)

-

To use this engine, go into file weewxd.py and change the line

-

weewx.wxengine.main()

-

so that it uses your new engine:

-

from examples.daily import MyEngine

-# Specify that my specialized engine should be used instead
-# of the default:
-weewx.wxengine.main(EngineClass = MyEngine)

-

We now have a new engine that defines a new event ("firstArchiveOfDay"), -but there is no service to take advantage of it. We define a new service:

-

# Define a new service to take advantage of the new event
-class DailyService(StdService):
-    """This service can do something when the first archive -record of
-    a day arrives."""
-
-    def firstArchiveOfDay(self, rec):
-        """Called when the first archive -record of a day arrives."""
-
-        print "The first archive of the day -has arrived!"
-        print rec
-
-        # You might want to do something here -like run a cron job

-

This service will simply print out a notice and then print out the new -record. However, if there is some daily processing you want to do, perhaps a -backup, or running utility -wunderfixer, this would be the place to do it.

-

The final step is to go into your configuration file and specify that this -new service be loaded, by adding its class name to option -service_list:

-

[Engines]
-
-  [[WxEngine]]
-    # The list of services the main weewx engine should run:
-    service_list = weewx.wxengine.StdWunderground, -weewx.wxengine.StdCatchUp,
-                   -weewx.wxengine.StdTimeSynch, weewx.wxengine.StdPrint,
-                   -weewx.wxengine.StdProcess, examples.daily.DailyService

-

(Again, note that this list is shown on several lines for clarity, but in -actuality it must be all on one line.)

- - - - +.highlight { + background-color: #FFFF66; +} +.center { + text-align: center; +} +.Example_output { + padding: 10px; + border: thin #000000 dotted; + font-family: "Times New Roman", Times, serif; + margin-left: 40px; +} + + + + + +

Customizing weewx v1.6

+

Table of Contents

+
    +
  1. Introduction
  2. +
  3. Opportunities for customizing + reports
  4. +
  5. Reference: The + Standard skin configuration file
  6. +
  7. Customizing the + weewx service engine
  8. +
  9. Appendix A: Types
  10. +
  11. Appendix B: Units
  12. +
  13. Appendix C: Statistical aggregations
  14. +
+

1. Introduction

+

This document covers the customization of weewx. It assumes that you have read +and are reasonably familiar with the Users Guide.

+

It starts with an overview of the architecture of weewx. If you are only interested +in customizing the generated skin reports you can probably skip that section and +just have a look at the options available for the Standard skin configuration file, +described in the section +The Standard skin +configuration file. With this approach you can easily add new plot images, +change the titles of images, change the units used in the reports, and so on.

+

However, if your goal is a specialized application such as adding alarms, RSS +feeds, etc., then it would be worth your while to read about the internal architecture +and how to customize it.

+

Overview of the weewx architecture

+

At a high-level, weewx consists of an engine class +called StdEngine. It is responsible for loading any "services" +that are to be run and arranging for them to be called when key events occur, such +as the arrival of LOOP data. The default install of weewx +includes the following services:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
ServiceFunction
weewx.wxengine.StdWundergroundStarts thread to manage WU connection; adds new data to a Queue to be + posted to the WU by the thread.
weewx.wxengine.StdCatchUpAny data found on the weather station memory but not yet in the archive, + is retrieved and put in the archive.
weewx.wxengine.StdTimeSynchArranges to have the clock on the station synchronized at regular intervals.
weewx.wxengine.StdPrintPrints out new LOOP and archive packets on the console.
weewx.wxengine.StdReportServiceLaunches a new thread to do report processing after a new archive record arrives. + Reports + do things such as generate HTML files, generate images, or FTP files to + a web server. New reports can be added easily by the user.
+

It is easy to extend old services or to add new ones. The source distribution +includes an example new service called "MyAlarm," which +sends an email when an arbitrary expression evaluates True. +It is also possible to extend the internal engine. These advanced topics are +covered later in the section +Customizing the weewx service +engine.

+

The standard reporting +service, StdReportService

+

For the moment, we focus on the last service, weewx.wxengine.StdReportService, +the standard service for creating reports. This will be what most users want to +customize even if it means just changing a few options.

+

Reports

+

The Standard Report Service runs zero or more Reports. Which ones are +set in the weewx configuration file weewx.conf, in section +[Reports].

+

The default distribution of weewx includes two reports:

+ + + + + + + + + + + + + +
ReportDefault functionality
StandardReportGenerates day, week, month and year "to-date" summaries in HTML, as + well as the plot images to go along with them. Also generates NOAA monthly + and yearly summaries.
FTPArranges to upload everything in the public_html + subdirectory up to a remote webserver.
+

Note that the FTP "report" is kind of a funny report in that it doesn't actually +generate anything. Instead, it uses the reporting service engine to arrange for +things to be FTP'd to a remote server.

+

Skins
+

+

Each report has a Skin associated with it. For most reports, the relationship +with the skin is an obvious one: it contains the templates, any auxiliary files +such as background GIFs or CSS style sheets, and a skin configuration file, +skin.conf. If you will, the skin controls the look +and feel of the report. Note that more than one report can use the same skin. +For example, you might want to run a report that uses US Customary units, then run +another report against the same skin, but using metric units and put the results +in a different place. All this is possible by either overriding configuration options +in the weewx configuration file weewx.conf or the skin +configuration file skin.conf.

+

Like all reports, the FTP "Report" also uses a skin, and includes a skin configuration +file, although it is quite minimal.

+

Skins live in their own subdirectory located in $HTML_ROOT/skins.

+

Generators

+

To create their output, skins rely on one or more Generators, code that +actually create useful things such as HTML files or plot images. They can also copy +files around or FTP them to remote locations. The default install of +weewx includes the following generators:

+ + + + + + + + + + + + + + + + + + + + + +
GeneratorFunction
weewx.filegenerator.FileGeneratorGenerates files from templates. Used to generate HTML and text + files.
weewx.imagegenerator.ImageGeneratorGenerates graph plots.
weewx.reportengine.FtpGeneratorUploads data to a remote server using FTP.
weewx.reportengine.CopyGeneratorCopies files locally.
+

Note that the two generators FtpGenerator and +CopyGenerator don't actually generate anything to do with +the presentation layer. Instead, they just move files around.

+

Which generators are to be run for a given skin is specified in the skin's configuration +file skin.conf.

+

Databases

+

There are two databases used in weewx, both SQLITE3 +databases:

+ +

The important thing to remember is that the archive database contains a +record for every archive interval and, as such, represents the current +conditions at the time of the observation. The statistical database +represents the aggregation of conditions over a day. That is, it +contains the daily minimum, maximum, and the time of the minimum and maximum, +for each observation type. As you can imagine, the statistical database is much +smaller because it represents only a summary of the data.

+

The archive database is used for both generating plot data and in template +generation (where it appears as tag $current). The +statistical database is used only in template generation (where it appears as +tags $day, $week, +$month, $year, and +$rainyear, depending on the aggregation time period).

+

2. Opportunities for customizing +reports

+

This section discusses the two general strategies for customizing reports: by +changing options in one or more configuration file, or by changing the template +files. The former is generally easier, but occasionally the latter is necessary.

+

Changing options

+

Changing an option means going into either weewx.conf or the +skin.conf that comes +with the standard distribution and changing a value.

+

Changing options in skin.conf

+

With this approach, the user edits the skin configuration file for the standard +skin that comes with weewx, located in $WEEWX_ROOT/skins/Standard/skin.conf, +using a text editor. For example, suppose you wish to use metric units in the presentation +layer. Then, you would edit section [Units][[Groups]] +to read:

+
[Units]
+
  [[Groups]]
+
    group_altitude    = meter
+
    group_direction   = degree_compass
+
    group_moisture    = centibar
+
    group_percent     = percent
+
    group_pressure    = mbar
+
    group_radiation   = watt_per_meter_squared
+
    group_rain        = mm
+
    group_rainrate    = mm_per_hour
+
    group_speed       = meter_per_second
+
    group_speed2      = meter_per_second2
+
    group_temperature = degree_C
+
    group_volt        = volt
+

The options that were changed have been highlighted. Details of the various unit options are given in Appendix +B: Units.

+

Other options are available, such as changing the text label for various observation +types. Suppose your weather instrument console is actually located in a barn, not +indoors, and you want the plot for the temperature at the console to be labeled "Barn Temperature," rather than the +default "Inside Temperature." This can be done by changing the "inTemp" option located +in section [Labels][[Generic]] from

+
[Units]
+
    [[Generic]]
+
      inTemp  = Inside Temperature
+
      outTemp = Outside Temperature
+
      ...
+

so that it reads

+
[Units]
+
    [[Generic]]
+
      inTemp  = Barn Temperature
+
      outTemp = Outside Temperature
+
      ...
+

Overriding options in skin.conf from +weewx.conf

+

This approach is very similar, except that instead of changing the skin configuration +file directly, +you override its options by editing the main configuration file, +weewx.conf. The advantage of this approach is that you +can use the same skin to produce several different output, each with separate options. +

+

Revisiting our example, suppose you want two reports, one in US Customary, the +other in Metric. The former will go in the directory public_html, +the latter in a subdirectory, public_html/metric. If you +just simply modify skin.conf, you can get one, but not +both at the same time. Alternatively, you could create a whole new skin by copying +all the files to a new skin subdirectory then editing the new +skin.conf. The trouble with this approach is that you would then have +two skins you would have to maintain. If you change something, you have to +remember to change it in both places.

+

But, there's a better approach: reuse the same skin, but overriding some options. +Here's what your [Report] section in +weewx.conf would look like:

+
[Reports]
+
#
+# This section specifies what reports, using which skins, are to be generated.
+#
+
 
+
# Where the skins reside, relative to WEEWX_ROOT:
+SKIN_ROOT = skins
+
+
# Where the generated reports should go, relative to WEEWX_ROOT:
+HTML_ROOT = public_html 
+
 
+
  # This report will use US Customary Units
+
  [[USReport]]
+
    # It's based on the Standard skin
+    skin = Standard
+
 
+
  # This report will use metric units:
+  [[MetricReport]]
+    # It's also based on the Standard skin:
+    skin = Standard 
+    # However, override where the results will go and put them in a subdirectory:
+    HTML_ROOT = public_html/metric
+    
+      # And override the options that were not in metric units
+      [[[Units]]]
+        [[[[Groups]]]]
+          group_altitude    = meter
+          group_pressure    = mbar
+          group_rain        = mm
+          group_rainrate    = mm_per_hour
+          group_speed       = meter_per_second
+          group_speed2      = meter_per_second2
+          group_temperature = degree_C
+
 
+
    [[FTP]]
+
      ...
+
      ... (as before) 
+

We have done two things different from the stock reports. First (1), we've renamed +the first report from StandardReport to +USReport for clarity; and (2) we've introduced a new report +MetricReport, just like the first, except it puts its +results in a different spot and uses different units. Both use the same skin, the +Standard skin.

+

Customizing templates

+

If you cannot achieve the results you need by changing a configuration option, +you may have to modify the templates that come with weewx, +or write your own.

+

Template generation is done using the +Cheetah templating engine. This is +a very powerful engine, which essentially lets you have the full semantics of Python +available in your templates. As this would make the templates incomprehensible to +anyone but a Python programmer, weewx adopts a very small +subset of its power.

+

The key construct is a 'dot' +code, specifying what value you want. For example:

+
$month.outTemp.max
+
$month.outTemp.maxtime
+
$current.outTemp
+

would code the max outside temperature for the month, the time it occurred, and +the current outside temperature, respectively. So, an HTML file that looks like

+
<html>
+
  <head>
+
    <title>Current conditions</title>
+
  </head>
+
  <body>
+
  <p>Current temperature = $current.outTemp</p>
+
  <p>Max for the month is $month.outTemp.max, which occurred at $month.outTemp.maxtime</p>
+
  </body>
+
</html>
+

would be all you need for a very simple HTML page that would display the text +(assuming that the unit group for temperature is degree_F): +

+

Current temperature = 51.0°F
+Max for the month is 68.8°F, which occurred at 07-Oct-2009 15:15

+

The format that was used to format the temperature (51.0) +is specified in section [Units][[StringFormat]]. +The unit label °F is from section +[Units][[Labels]], while the time format is from +[Labels][[Time]].

+

The dot code

+

As we saw above, the dot codes can be very simple:

+
## Output max outside temperature using an appropriate format and label:
+$month.outTemp.max
+

Most of the time, this is all you will be using. However, +weewx offers extensive customization of the generate output for specialized applications such as XML RSS +feeds, or ridgidly formatted reports (such as the NOAA reports). This section +specifies the various options available.

+

In general, the dot code has up to four parts:

+
$period.obstype.aggregation.[optional_formatting]
+
    +
  1. The first part is the time period, and can be one of + current, day, week, + month, year, or + rainyear. For period current, + the data comes from the archive database; for all others, the data comes + from the statistical database. See section Databases above + for more information about the differences between these two databases.
  2. +
  3. The second part is the observation type, something like + 'outTemp', + 'rain', 'wind', + etc. While most observation types can be used for any of the time periods, + there are a few differences. See Appendix A, + Archive Types for a table of + types in the archive database and that can appear in time period + current, and + Appendix C, Statistical Types, for a + table of types in the statistical database and that can appear in all other + periods.
  4. +
  5. For types other than 'current' the + third position is the aggregation type and specifies the type of + aggregation that is to be done over the period specified in position 1. Examples are "min", + "mintime", or "sum". + If you ask for $month.outTemp.avg you are asking + for the average outside temperature for the month. The table + Appendix C: Statistical types + shows what aggregation types are available for which types. This position is + not valid for period 'current'.
  6. +
  7. The last position (position "3" for period "current", + position "4" for everything else) controls the optional formatting of the results. If + left out, the results will be formatted as in the example above, that is + with an appropriate string format (such as "%.1f") + and an appropriate unit label (°F), both + retrieved from skin.conf. Most of the time, this + is what you want. See the table below for a summary of formatting options.
  8. +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+

Optional formatting tag

+
+

Comment

+
+

(no tag)

+
+

Value is returned as a string, formatted using an appropriate string format + from skin.conf. A unit label from + skin.conf is also attached at the end.

+
+ .formatted + Value is returned as a string, formatted using an appropriate string + format from skin.conf. No label.
+ .format(string_format, NONE_string) + Value is returned as a string, using the string format specified with + string_format. If the value is None, + the string NONE_string will be substituted if + given, otherwise the value for NONE in + [Units][[Formats]] will be + used.
+ .string(NONE_string) + Value is returned as a string, with Python picking a format. If the + value is None, + the string NONE_string will be substituted if + given, otherwise the value for NONE in + [Units][[Formats]] + will be used.
+

.raw

+
+ Value is returned "as is" without being converted to a string and + without any formatting applied. You must be prepared to deal with a + None value unless the value is converted + directly to a string. In this case, it will be converted to the empty + string ('')
+

Here are some examples with the expected results:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TagResultComment
+ $current.outTemp + + 45.2°F + + String formatting and label from skin.conf
+ $current.outTemp.formatted + + 45.2 + String formatting from skin.conf; no label
+ $current.outTemp.format("%.3f") + + 45.200 + + Specified string format used; no label
+ $current.dateTime + 02-Apr-2010 16:25 + Time formatting and label from skin.conf
+ $current.dateTime.format("%H:%M") + 16:25 + Specified time format used; no label
+ $current.dateTime.raw + 1270250700 + Unix epoch time, converted to string by template engine.
+ $current.outTemp.string + 45.2 + Python picks string formatting. No label.
+ $current.outTemp.raw + 45.2 + Float returned, converted to string by template engine.
+ $current.UV.string + N/A + None value converted to value given by + NONE in + [Units][[Formats]]
+ $current.UV.string("No UV") + No UV + None value converted to specified string
+ $month.dateTime + 01-Apr-2010 00:00 + Time formatting and label from skin.conf
+ $month.outTemp.avg + + 40.8°F + String formatting and label from skin.conf
+ $month.outTemp.avg.formatted + + 40.8 + String formatting from skin.conf; no label
+ $month.outTemp.avg.format("%.3f") + 40.759 + Specified string format used; no label
+ $month.outTemp.avg.string + 40.7589690722 + Python picks string formatting
+ $month.outTemp.avg.raw + + 40.7589690722 + Float returned, converted to string by template engine
+ $month.UV.avg.string + N/A + None value converted to value given by + NONE in + [Units][[Formats]]
+ $month.UV.avg.string("No UV") + No UV + None value converted to specified string
+ $month.UV.avg.raw + (empty) + None value converted to empty string by template engine.
+

Note:

+ +

Type dateTime

+

While not an observation type, in many ways the time of an observation, +dateTime, can be treated as one. A tag such as +$current.dateTime represents the end time of an +archive interval, a tag such as +$month.dateTime represents the start time of the +month. Like true observation types, explicit formats can be specified, except +that they require a +strftime() +time format, rather than a string format:

+
$month.dateTime.format("%B %Y)
+

produces

+
January 2010
+

The returned string value will always be in local time.

+

The raw value of dateTime is Unix + Epoch Time (number of seconds since 00:00:00 UTC 1 Jan 1970, i.e., + a large number), which you must convert yourself to local time. It is +guaranteed to never be None, so you don't + worry have to worry about handling a None value. +

+

 

+

Unit tags

+

The unit type, label, and string formats are also available, +allowing you to do highly customized labels:

+ + + + + + + + + + + + + + + + + +
TagResults
$unit_type.outTempdegree_C
$unit_label.outTemp°C
$unit_format.outTemp%.1f
+

As a summary, the tag

+
$day.outTemp.max.formatted$unit_label.outTemp
+

would result in

+
21.2°C
+

(assuming metric values have been specified for +group_temperature), essentially reproducing the results of the simpler tag +$day.outTemp.max.

+

Iteration

+

If the first part of the dot code represents a time period longer +than a day (e.g., a week, month, year, or rainyear), then it can be iterated over +by day or month. +This example uses a Cheetah 'for' loop to iterate over all months in a year, printing +out each month's min and max temperature.

+
<html>
+
  <head>
+
    <title>Year stats by month</title>
+
  </head>
+
  <body>
+
  <p>Min, max temperatures by month:</p>
+
  #for $month in $year.months
+
    <p>$month.dateTime.format("%B"): Min, max temperatures: $month.outTemp.min $month.outTemp.max</p>
+
  #end for 
+
  </body>
+
</html>
+

Produces results:

+
Min, max temperatures by month:
+January: Min, max temperatures: 30.1°F 51.5°F
+February: Min, max temperatures: 24.4°F 58.6°F
+March: Min, max temperatures: 27.3°F 64.1°F
+April: Min, max temperatures: 33.2°F 52.5°F
+May: Min, max temperatures: N/A N/A
+June: Min, max temperatures: N/A N/A
+July: Min, max temperatures: N/A N/A
+August: Min, max temperatures: N/A N/A
+September: Min, max temperatures: N/A N/A
+October: Min, max temperatures: N/A N/A
+November: Min, max temperatures: N/A N/A
+December: Min, max temperatures: N/A N/A
+
+

See the NOAA template files NOAA/NOAA-YYYY.txt.tmpl +and NOAA/NOAA-YYYY-MM.txt.tmpl for examples using iteration, +as well as explicit formatting.

+

Writing a custom generator

+

To do more sophisticated customization it may be necessary to extend an existing +generator, or write your own.

+

Extending an existing generator

+

In the section on Customizing templates, +we have seen how you can change a template and make use of the various tags available +such as $day.outTemp.max for the maximum outside temperature +for the day. But, what if you want to introduce some new data for which no tag is +available?

+

If you wish to introduce a static tag, that is, one that will not change with +time (such as a Google analytics Tracker ID or your name), then this is very +easy: simply put it in section [Extras] +in the skin configuration file. More information on how to do this can be found +there.

+

But, what if you wish to introduce a more dynamic tag, one that requires some +calculation? Simply putting it in the [Extras] section +won't do, because then it cannot change.

+

The answer is to extend the default file generator weewx.filegenerator.FileGenerator +by subclassing, then override the function that returns the search list. +The search list is a list of dictionaries that the template engine searches through, +trying all keys in each dictionary, looking for a match for your tag. For example, +for the "ToDate" generator, you would override function getToDateSearchList(), +and add a small dictionary with your tag as the key to the list returned by the +superclass.

+

Let's look at an example. The stock weewx reports +offers statistical summaries by day, week, month, and year. Suppose we would like to add +one more: all-time statistics. This would allow us to display statistics such as +the all-time high or low temperature seen at your station.

+

This example is included in the distribution as examples/mygenerator.py:

+
from weewx.filegenerator import FileGenerator
+from weewx.stats import TimeSpanStats
+from weeutil.weeutil import TimeSpan
+
+class MyFileGenerator(FileGenerator):                    # 1
+
+    def getToDateSearchList(self, currentRec, stop_ts):  # 2
+
+        # Get a TimeSpan object that represents all time up to the stop time:
+        all_time = TimeSpan(self.start_ts, stop_ts)      # 3
+
+        # Get a TimeSpanStats object :
+        all_stats = TimeSpanStats(self.statsdb,
+                                  all_time,
+                                  self.unitTypeDict)     # 4
+
+        # Get the superclass's search list: 
+        search_list = FileGenerator.getToDateSearchList(self, currentRec, stop_ts) #5
+
+        # Now tack on my addition as a small dictionary with key 'alltime':
+        search_list += [ {'alltime' : all_stats} ]       # 6
+
+        return search_list
+
+
+

Going through the example, line by line:

+
    +
  1. Subclass from class FileGenerator. The new + class will be caled MyFileGenerator
  2. +
  3. Override member function getToDateSearchList(). + The parameters are self (Python's way of + indicating the instance we are working with), currentRec + (a dictionary with the current conditions), and stop_ts + (the ending time for the "to date" summary, in Unix epoch time).
  4. +
  5. Attribute self.start_ts is available as the + earliest time seen in the main archive database. The class TimeSpan + is a utility class that represents an interval of time. Here, we are + creating an instance of TimeSpan that represents + all time preceeding stop_ts.
  6. +
  7. Class TimeSpanStats represents a statistical calculation that can be + done against a database. It takes 3 parameters. The first, + self.statsdb is + the statistical database the calculation is to be run against; the second is + the timespan over which the calculation is to be done; and the third is the + units the results are to be returned in.
  8. +
  9. Get the search list from the superclass.
  10. +
  11. Tack on our addition and return the results. The search list will now + consist of a list of dictionaries, including a small one we added on the end + that has a single key, 'alltime', with value an instance of + TimeSpanStats.
  12. +
+

With this approach, you can now include "all time" statistics in +your HTML templates:

+
...
+
...
+<table>
+  <tr>
+    <td>Maximum temperature to date: </td>
+    <td>$alltime.outTemp.max</td>
+  </tr>
+  <tr>
+    <td>Minimum temperature to date: </td>
+    <td>$alltime.outTemp.min
+  </tr>
+  ... (more table entries)
+
 
+

One additonal step is required: to tell the report service to run your generator +instead of the default generator. Modify option generator_list +in the skin configuration file skin.conf to read:

+
generator_list = examples.mygenerator.MyFileGenerator, weewx.imagegenerator.ImageGenerator, weewx.reportengine.CopyGenerator
+

NB: If you create a custom generator some place other than where +weewxd.py resides, you may have to specify its +location in the environment variable PYTHON_PATH:

+
export PYTHON_PATH=/home/me/secret_location
+

3. Reference: The Standard +skin configuration file

+

This section is a reference to the options appearing in the Standard skin configuration +file, found in $WEEWX_ROOT/skins/Standard/skin.conf.

+

It is worth noting that, like the main configuration file +weewx.conf, U, UTF-8 is used throughout. Also, like the weewx.conf, the most +important options are up near the top of the file.  The truly important ones, +the ones you are likely to have to customize for your station, are shown in +bold face and in blue.

+

General

+

generator_list

+

This option controls which generators get run for this skin. It is a comma separated +list. The generators will be run in this order.

+

[Extras]

+

This section is available to you to add any static tags that you might want to be available +in the templates. As an example, the stock skin.conf file +includes option radar_url, which is available as tag +$Extras.radar_url. If you take a look at the template +index.html.tmpl you will see an example of testing for +this tag (search the file for the string 'radar_url' to find +it).

+

radar_url

+

If set, the NOAA radar image will be displayed. If commented out, no image will +be displayed.

+

Extending [Extras]

+

Other tags can be added in a similar manner, including subsections. For +example, say you have added a video camera and you would like to add a still +image with a hyperlink to a page with the video. You want all of these options +to be neatly contained in a subsection.

+
[Extras]
+
  [[video]]
+
    still = video_capture.jpg
+
    hyperlink = http://www.eatatjoes.com/video.html
+

Then in your template you could refer to these as:

+
<a href="$Extras.video.hyperlink">
+  <img src="$Extras.video.still" alt="Video capture"/>
+</a>
+

[Units]

+

This section deals with Units and their formatting.

+

[[Groups]]

+

This subsection lists all the Unit Groups and specifies which unit system +is to be used for each one of them.

+

As there are many different observational measurement types (such as 'outTemp', +'barometer', etc.) used in weewx +(more than 50 at last count), it would be tedious, not to say possibly inconsistent, +to specify a different measurement system for each one of them. At the other extreme, +requiring all of them to be "U.S. Customary" or "Metric" seems overly restrictive. +Weewx has taken a middle route and divided all the different +observation types into 12 different "unit groups." A unit group is something like +"group_temperature." It represents the measurement system +to be used by all observation types that are measured in temperature, such as inside +temperature (type 'inTemp'), outside temperature ('outTemp'), +dewpoint ('dewpoint'), wind chill ('windchill'), +and so on. If you decide that you want unit group group_temperature +to be measured in "degree_C" then you are saying +all members of its group will be reported in degrees Celsius.

+

Note that the unit system is always specified in the singular. That is, specify +"degree_C" or "foot", not +"degrees_C" or "feet". See the Appendix Units for more information, including a concise summary of the groups, +their members, and which options can be used for each group.

+

group_altitude

+

Which measurement unit to be used for altitude. Possible options are 'foot' +or 'meter'.

+

group_direction

+

Which measurement unit to be used for direction. The only option is "degree_compass".

+

group_moisture

+

The measurement unit to be used for soil moisture. The only option is "centibar."

+

group_percent

+

The measurement unit to be used for percentages. The only option is "percent".

+

group_pressure

+

The measurement unit to be used for pressure. Possible options are one of "inHg" +(inches of mercury), "mbar", or "hPa."

+

group_radiation

+

The measurement unit to be used for radiation. The only option is "watt_per_meter_squared."

+

group_rain

+

The measurement unit to be used for precipitation. Options are "inch", +"cm," or "mm."

+

group_rainrate

+

The measurement unit to be used for rate of precipitation. Possible options are +one of "inch_per_hour", "cm_per_hour", +or "mm_per_hour".

+

group_speed

+

The measurement unit to be used for wind speeds. Possible options are one of +"mile_per_hour", "km_per_hour", +"knot", or "meter_per_second."

+

group_speed2

+

This group is similar to group_speed, but is used for +calculated wind speeds which typically have a slightly higher resolution. Possible +options are one "mile_per_hour2", "km_per_hour2", +"knot2", or "meter_per_second2".

+

group_temperature

+

The measurement unit to be used for temperatures. Options are "degree_F" +or "degree_C."

+

group_volt

+

The measurement unit to be used for voltages. The only option is "volt."

+

[[StringFormats]]

+

This sub-section is used to specify what string format is to be used for each +unit when a quantity needs to be converted to a string. Typically, this happens +with y-axis labeling on plots and for statistics in HTML file generation. For example, +the options

+
degree_C = %.1f
+
inch     = %.2f
+

would specify that the given string formats are to be used when formatting any +temperature measured in degrees Celsius or any precipitation amount measured in +inches, respectively. The + +formatting codes are those used by Python, a, and are very similar to C's +sprintf() codes.

+

You can also specify what string to use for an invalid or unavailable +measurement (value 'None'). For example,

+
NONE = " N/A "
+

[[Labels]]

+

This subsection specifies what label is to be used for each measurement unit +type. For example, the options

+
degree_F = °F
+
inch     = ' in'
+

would cause all temperatures to have unit labels '°F' +and all precipitation to have labels  ' in'. If any special symbols are to +be used (such as the degree sign above) they should be encoded in UTF-8. This is +generally what most text editors use if you cut-and-paste from a character map. +Labels used in plot images will be converted to Latin-1 first (this is all the Python +Imaging Library can handle).

+

[Labels]

+

This section sets the various labels to use.

+

hemispheres

+

Comma separated list for the labels to be used for the four hemispheres. The +default is "N, S, E, W".

+

[[Generic]]

+

This sub-sections specifies default labels to be used for each SQL type. For +example, options

+
inTemp = Temperature inside the house
+
outTemp = Outside Temperature
+

would cause the given labels to be used for plots involving SQL types +inTemp and outTemp.

+

[[Time]]

+

This sub-section is used for time labels. It uses +strftime() +formats. For example

+
week  = %H:%M on %A
+
month = %d-%b-%Y %H:%M
+

would specify that week data should use a format such as "15:20 +on Sunday", while month data should look like "06-Oct-2009 +15:20"

+

[Almanac]

+

This section controls what text to use for the almanac. It consists of only one +entry

+

moon_phases

+

This option is a comma separated list of labels to be used for the eight phases +of the moon. Default is "New, Waxing crescent, First quarter, +Waxing gibbous, Full, Waning gibbous, Last quarter, Waning crescent".

+

[FileGenerator]

+

This section is used by generator weewx.reportengine.FileGenerator +and controls text generation from templates, specifically which files are to be +produced from which template.

+

Overview of file generation

+

Customization of file generation consists of playing with the various options +offered below and, failing that, modifying the template files that come with the +distribution.

+

Each template file is named something like D/F.E.tmpl, +where D is the subdirectory the template sits in +and will also be the subdirectory the results will be put in, and +F.E is the generated file name. So, given a template file +with name Acme/index.html.tmpl, the results will be put +in $HTML_ROOT/Acme/index.html.

+

The skin that comes with the standard distribution of weewx +contains three different kinds of generated output:

+
    +
  1. Summary by month. In addition to the naming rules above, if the template + file has the letters YYYY and MM + in its name, these will be substituted for the year and month, respectively. + So, a template with the name summary-YYYY-MM.html.tmpl + would have name summary-2010-03.html for the month + of March, 2010. The default distribution has been set up to produce NOAA monthly summaries, + one for each month, as a simple text file (no HTML).
  2. +
  3. Summary by year.  In addition to the naming rules above, if the template + file has the letters YYYY in its name, the year will + be substituted. The default distribution has been set up to produce NOAA yearly + summaries, one for each year, as a simple text file (no HTML).
  4. +
  5. Summary "To Date". The default distribution has been set up to produce reports + for the day, week, month, and year-to-date observations. These four files are + all HTML files. The first, the daily summary (output file index.html), includes + a drop-down list that allows the NOAA month and yearly summaries to be displayed.
  6. +
+

General

+

encoding

+

This option controls which encoding is to be used for the generated output. There +are 3 possible choices:

+ + + + + + + + + + + + + + + + + +
EncodingComComments
html_entitiesNon 7-bit characters will be represented as HTML entities (e.g. + the degree sign will be represented as &#176;)
utf8Non 7-bit characters will be represented in UTF-8.
strict_asciiNon 7-bit characters will be ignoredd>
+

By default, the encoding html_entities is used for +HTML files, strict_ascii for the NOAA template files. +

+

[[SummaryByMonth]]

+

This section controls which summaries-by-month are generated. For each such +summary, it should have its own sub-subsection, with option +template listing the template to be used. The default configuration +generates NOAA-by-month summaries and is summarized below as an example. +Additional "by month" summaries can be added easily by following the +same pattern.

+

[[[NOAA_month]]]

+

encoding

+

Set to strict_ascii for the NOAA monthly summary.

+

template

+

This option is set to the source template for the NOAA monthly summary, +NOAA/NOAA-YYYY-MM.txt.tmpl.

+

[[SummaryByYear]]

+

This section controls which summaries-by-year are generated. For each such +summary, it should have its own sub-subsection, with option +template listing the template to be used. The default configuration +generates NOAA-by-year summaries and is summarized below as an example. +Additional "by year" summaries can be added easily by following the pattern.

+

[[[NOAA_year]]]

+

encoding

+

Set to strict_ascii for the NOAA year summary.

+

template

+

This option is set to the source template for the NOAA yearly summary, +NOAA/NOAA-YYYY.txt.tmpl.

+

[[ToDate]]

+

This section controls which observations-to-date are generated. The default configuration +generates four files: one for day, week, month, and year. Although the +sub-subsections below have names such as 'week' or 'month', this is not used in +their generation.  Output is set by the template content, not the +name of the sub-subsection — the names below could as easily have been'Fred', +'Mary', 'Peter', and 'George' and had the same output.

+

Additional observations-to-date pages can be created easily by adding a new +sub-subsection and giving it a unique name ("Jill"?), then giving the path to +its template as option template.

+

[[[day]]]

+

template

+

Set to index.html.tmpl, which contains the template +for the day summary.

+

[[[week]]]

+

template

+

Set to week.html.tmpl, which contains the template +for the week summary.

+

[[[month]]]

+

template

+

Set to month.html.tmpl, which contains the template +for the month summary.

+

[[[year]]]

+

template

+

Set to year.html.tmpl, which contains the template +for the year summary.

+

[[[RSS]]]

+

Set to RSS/weewx_rss.xml.tmpl, which contains a +template for an RSS feed.

+

[CopyGenerator]

+

This section is used by generator weewx.reportengine.CopyGenerator +and controls which files are to be copied over from the skin subdirectory to the +destination directory. Think of it as "file generation," except that rather than +going through the template engine, the files are simply copied over.

+

copy_once

+

This option controls which files get copied over on the first invocation of the +report engine service. Typically, this is things such as style sheets or background +GIFs. Wildcards can be used.

+

copy_always

+

This is a list of files that should be copied on every invocation. Wildcards +can be used.

+

[ImageGenerator]

+

This section is used by generator weewx.reportengine.ImageGenerator +and controls which images (plots) get generated and with which options. While complicated, +it is extremely flexible and powerful.

+

Time periods

+

The section consists of one or more sub-sections, one for each time period (day, +week, month, and year). These sub-sections define the nature of aggregation and +plot types for the time period. For example, here's a typical set of options for +sub-section [[month_images]], controlling how images that +cover a month period are generated:

+
[[month_images]]
+
  x_label_format = %d
+
  bottom_label_format = %m/%d/%y %H:%M
+
  time_length = 2592000 # == 30 days
+
  aggregate_type = avg
+
  aggregate_interval = 10800 # == 3 hours
+

The option x_label_format gives a +strftime() +type format for the x-axis. In this example, it will only show days (format option +"%d"). The bottom_label_format +is the format used to time stamp the image at the bottom. In this example, it will +show the time as 10/25/09 15:35. A plot will cover a nominal +30 days, and all items included in it will use an aggregate type of averaging over +3 hours.

+

Image files

+

Within each sub-section is another nesting, one for each image to be generated. +The title of each sub-sub-section is the filename to be used for the image. Finally, +at one additional nesting level (!) are the logical names of all the line types +to be drawn in the image.  Values specified in the level above can be overridden. +For example, here's a typical set of options for sub-sub-section +[[[[[[[monthrain]]]: /p> class="tty">[[[[[[monthrain]]]

+
  plot_type = bar
+
  yscale = None, None, 0.02
+
  [[[[rain]]]]
+
    aggregate_type = sum
+
    aggregate_interval = 86400
+
    label = Rain (daily avg)
+

This will generate an image file with name monthrain.png. +It will be a bar plot. Option yscale controls the y-axis +scaling — if left out, the scale will automatically be chosen. However, in this +example we are choosing to exercise some degree of control by specifying values +explicitly. It is a 3-way tuple (ylow, +yhigh, min_interval), where +ymin and ymax are the minimum +and maximum y-axis values, respectively, and min_interval +is the minimum tick interval. If set to 'None', the corresponding +value will be automatically chosen. So, in this example, we are letting +weewx pick sensible y minimum and maximum values, but +we are requiring that the tick increment (min_interval) +be at least 0.02.

+

Continuing on with the example above, there will be only one plot "line" (it +will actually be a series of bars) and it will have logical name "rain". +Because we haven't said otherwise, the SQL data type to be used for this line will +be the same as its logical name, that is, rain, b, but +this can be overridden (see below). The aggregation type will be summing (overriding +the averaging specified in sub-section [[month_images]]), +so you get the total rain over the aggregate period (rather than the average) over +an aggregation interval of 86,400 seconds (one day). The plot line will be titled +with the indicated label ('Rain (daily avg)')

+

Including more than one SQL type in a plot

+

More than one SQL type can be included in a plot. For example, here's how to +generate a plot with the week's outside temperature as well as dewpoint:

+

[[[monthtempdew]]]

+

  [[[[outTemp]]]]

+

  [[[[dewpoint]]]]

+

This would create an image in file monthtempdew.png +that includes a line plot of both outside temperature and dewpoint.

+

Including the same SQL type more than once in a plot

+

Another example. Say you want a plot of the day's temperature, overlaid with +hourly averages. Here, you are using the same data type ('outTemp') +for both plot lines, the first with averages, the second without. If you do the +obvious it won't work:

+
## WRONG ##
+
[[[[[[daytemp_with_avg]]]
+
  [[[[outTemp]]]]
+
    aggregate_type = avg
+
    aggregate_interval = 3600
+
  [[[[outTemp]]]]  # OOPS! The same section name appears more than once!
+

The option parser does not allow the same section name ('outTemp' +in this case) to appear more than once at a given level in the configuration file, +so an error will be declared (technical reason: formally, the sections are an unordered +dictionary). If you wish for the same SQL type to appear more than once in a plot +then there is a trick you must know: use option data_type. +This will override the default action that the logical line name is used for the +SQL type. So, our example would look like this:

+
[[[daytemp_with_avg]]]
+
    [[[[a_logical_name]]]]
+
      data_type = outTemp
+
      aggregate_type = avg
+
      aggregate_interval = 3600
+
      label = Avg. Temp. 
+
    [[[[outTemp]]]]
+

Here, the first logical line has been given the name "a_logical_name" +to distinguish it from the second line "outTemp". We have +specified that the first line will use data type outTemp +and that it will use averaging over a one hour period. The second also uses +outTemp, but will not use averaging.

+

The result is a nice plot of the day's temperature, overlaid with a 3-hour smoothed +average:

+

+Daytime temperature with running average

+

Progressive vector plots

+

Weewx can produce progressive vector plots as well +as the more conventional x-y plots. To produce these, use plot type 'vector'. +You need a vector type to produce this kind of plot. There are two: 'windvec', +and 'windgustvec'. While they don't actually appear in +the SQL database, weewx understands that they represent +special vector-types. The first, 'windvec', represents +the average wind in an archive period, the second, 'windgustvec' +the max wind in an archive period. Here's how to produce a progressive vector for +one week that shows the hourly biggest wind gusts, along with hourly averages:

+
[[[weekgustoverlay]]]
+
   aggregate_interval = 3600
+
   [[[[windvec]]]]
+
     label = Hourly Wind 
+
     plot_type = vector
+
     aggregate_type = avg
+
   [[[[windgustvec]]]]
+
    label = Gust Wind 
+
     plot_type = vector
+
     aggregate_type = max
+

This will produce an image file with name weekgustoverlay.png. +It will consist of two progressive vector plots, both using hourly aggregation (3,600 +seconds). For the first set of vectors, the hourly average will be used. In the +second, the max of the gusts will be used:

+

+hourly average wind vector overlaid with gust vectors

+

By default, the sticks in the progressive wind plots point towards the wind source. +That is, the stick for a wind from the west will point left. If you have a chronic +wind direction (as I do), you may want to rotate the default direction so that all +the vectors don't line up over the x-axis, overlaying each other. Do this by using +option vector_rotate. For example, with my chronic westerlies, +I set vector_rotate to 90.0 for the plot above, so winds +out of the west point straight up.

+

If you use this kind of plot (the out-of-the-box version of +weewx includes daily, weekly, monthly, and yearly progressive +wind plots), a small compass rose will be put in the lower-left corner of the image +to show the orientation of North.

+

Overriding values

+

Remember that values at any level can override values specified at a higher level. +For example, say you want to generate the standard plots, but for a few key observation +types such as barometer, you want to also generate some oversized plots to give +you extra detail, perhaps for an HTML popup. The standard weewx.conf +file specifies plot size of 300x180 pixels, which will be used for all plots unless +overridden:

+
[Images]
+
  ...
+
  image_width=300
+
  image_height = 180
+

The standard plot of barometric pressure will appear in daybarometer.png:

+

    [[[daybarometer]]]

+

      [[[[barometer]]]]

+

We now add our special plot of barometric pressure, but specify a larger image +size. This image will be put in file an class="code" daybarometer_big.png.

+

     [[[daybarometer_big]]]

+

      image_width  = 600

+

      image_height = 360

+

      [[[[barometer]]]]

+

4. Customizing the +weewx service engine

+

This is an advance topic intended for those who wish to try their hand at extending +the internal engine in weewx. You should have a passing familiarity with Python +or, at least, be willing to learn it.

+

At a high level, weewx consists of an engine that is responsible for +managing a set of services. A service consists of a Python class with a +set of member functions. The engine arranges to have appropriate member functions +called when specific events happen. For example, when a new LOOP packet arrives, +member function processLoopPacket() of all services is +called.

+

To customize, you can

+ +

This section describes how to do all three.

+

The default install of weewx includes the following +services:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
ServiceFunction
weewx.wxengine.StdWundergroundStarts thread to manage WU connection; adds new data to a Queue to be + posted to the WU by the thread.
weewx.wxengine.StdCatchUpAny data found on the weather station memory but not yet in the archive, + is retrieved and put in the archive.
weewx.wxengine.StdTimeSynchArranges to have the clock on the station synchronized at regular intervals.
weewx.wxengine.StdPrintPrints out new LOOP and archive packets on the console.
weewx.wxengine.StdReportServiceLaunches a new thread to do processing after a new archive record arrives. + The thread loads zero or more reports and processes them in order. Reports + do things such as generate HTML files, generate images, or FTP files to + a web server. New reports can be added easily by the user.
+

Customizing a Service

+

The service weewx.wxengine.StdPrint prints out new +LOOP and archive packets to the console when they arrive. By default, it prints +out time, barometer, outside temperature, wind speed, and wind direction. Suppose +you don't like this, and want to print out humidity as well when a new LOOP packet +arrives, but leave the printing of archive packets alone. This could be done by +subclassing the default print service StdPrint and overriding +member function processLoopPacket().

+

In file myprint.py:

+
from weewx.wxengine import StdPrint
+from weeutil.weeutil import timestamp_to_string
+
+class MyPrint(StdPrint):
+
+    # Override the default processLoopPacket:
+    def processLoopPacket(self, physicalPacket):
+        print "LOOP: ", timestamp_to_string(physicalPacket['dateTime']),\
+            physicalPacket['barometer'],\
+            physicalPacket['outTemp'],\
+            physicalPacket['outHumidity'],\
+            physicalPacket['windSpeed'],\
+            physicalPacket['windDir']
+

You then need to specify that your print service class should be loaded instead +of the default StdPrint service. This is done by substituting +your service name for the standard print service name in the option +service_list, located in [Engines][[WxEngine]]:

+
[Engines]
+    [[WxEngine]]
+        service_list = weewx.wxengine.StdWunderground, weewx.wxengine.StdCatchUp, weewx.wxengine.StdTimeSynch, myprint.MyPrint, weewx.wxengine.StdReportService
+

(Note that this list must be all on one line. The parser ConfigObj +does not allow options to be continued on to following lines.)

+

Adding a Service

+

Suppose there is no service that can be easily customized for your needs. In +this case, a new one can easily be created by subclassing off the abstract base +class StdService, and then adding the functionality you +need. Here's an example that implements an alarm that sends off an email when an +arbitrary expression evaluates True. This example is included in the standard distribution +in subdirectory "examples".

+

File examples/alarm.py:

+
import time
+import smtplib
+from email.mime.text import MIMEText
+import threading
+import syslog
+
+from weewx.wxengine import StdService
+from weeutil.weeutil import timestamp_to_string
+
+# Inherit from the base class StdService:
+class MyAlarm(StdService):
+    """Custom service that sounds an alarm if an arbitrary expression evaluates true"""
+    
+    def __init__(self, engine):
+        # Pass the initialization information on to my superclass:
+        StdService.__init__(self, engine)
+        
+        # This will hold the time when the last alarm message went out:
+        self.last_msg_ts = None
+        self.expression  = None
+        
+    def setup(self):
+        
+        try:
+            # Dig the needed options out of the configuration dictionary.
+            # If a critical option is missing, an exception will be thrown and
+            # the alarm will not be set.
+            self.expression    = self.engine.config_dict['Alarm']['expression']
+            self.time_wait     = int(self.engine.config_dict['Alarm'].get('time_wait', 3600))
+            self.smtp_host     = self.engine.config_dict['Alarm']['smtp_host']
+            self.smtp_user     = self.engine.config_dict['Alarm'].get('smtp_user')
+            self.smtp_password = self.engine.config_dict['Alarm'].get('smtp_password')
+            self.TO            = self.engine.config_dict['Alarm']['mailto']
+            syslog.syslog(syslog.LOG_INFO, "alarm: Alarm set for expression: \"%s\"" % self.expression)
+        except:
+            self.expression = None
+            self.time_wait  = None
+
+    def postArchiveData(self, rec):
+        # Let the super class see the record first:
+        StdService.postArchiveData(self, rec)
+
+        # See if the alarm has been set:
+        if self.expression:
+            # To avoid a flood of nearly identical emails, this will do
+            # the check only if we have never sent an email, or if we haven't
+            # sent one in the last self.time_wait seconds:
+            if not self.last_msg_ts or abs(time.time() - self.last_msg_ts) >= self.time_wait :
+                
+                # Evaluate the expression in the context of 'rec'.
+                # Sound the alarm if it evaluates true:
+                if eval(self.expression, None, rec):            # NOTE 1
+                    # Sound the alarm!
+                    # Launch in a separate thread so it doesn't block the main LOOP thread:
+                    t  = threading.Thread(target = MyAlarm.soundTheAlarm, args=(self, rec))
+                    t.start()
+                    # Record when the message went out:
+                    self.last_msg_ts = time.time()
+
+    def soundTheAlarm(self, rec):
+        """This function is called when the given expression evaluates True."""
+        
+        # Get the time and convert to a string:
+        t_str = timestamp_to_string(rec['dateTime'])
+        # Form the message text:
+        msg_text = "Alarm expression \"%s\" evaluated True at %s\nRecord:\n%s" % (self.expression, t_str, str(rec))
+        # Convert to MIME:
+        msg = MIMEText(msg_text)
+        
+        # Fill in MIME headers:
+        msg['Subject'] = "Alarm message from weewx"
+        msg['From']    = "weewx"
+        msg['To']      = self.TO
+        
+        # Create an instance of class SMTP for the given SMTP host:
+        s = smtplib.SMTP(self.smtp_host)
+        try:
+            # Some servers (eg, gmail) require encrypted transport.
+            # Be prepared to catch an exception if the server
+            # doesn't support it.
+            s.ehlo()
+            s.starttls()
+            s.ehlo()
+        except smtplib.SMTPException:
+            pass
+        # If a username has been given, assume that login is required for this host:
+        if self.smtp_user:
+            s.login(self.smtp_user, self.smtp_password)
+        # Send the email:
+        s.sendmail(msg['From'], [self.TO],  msg.as_string())
+        # Log out of the server:
+        s.quit()
+        # Log it in the system log:
+        syslog.syslog(syslog.LOG_INFO, "alarm: Alarm sounded for expression: \"%s\"" % self.expression)
+        syslog.syslog(syslog.LOG_INFO, "       *** email sent to: %s" % self.TO) 
+

This service expects all the information it needs to be in the configuration +file weewx.conf in a new section called +[Alarm]. So, add the following lines to your configuration +file:

+
[Alarm]
+    expression = "outTemp < 40.0"
+    time_wait = 3600
+    smtp_host = smtp.mymailserver.com
+    smtp_user = myusername
+    smtp_password = mypassword
+    mailto = auser@adomain.com
+

These options specify that the alarm is to be sounded when "outTemp +< 40.0" evaluates True, that is when the outside temperature is below 40.0 +degrees. Any valid Python expression can be used, although the only variables available +are those in the current archive record. (The place in the code where the expression +is evaluated is marked with "NOTE 1".)

+

Another example expression could be:

+

    expression = "outTemp < 32.0 and windSpeed > 10.0"

+

In this case, the alarm is sounded if the outside temperature drops below freezing +and the wind speed is greater than 10.0.

+

Option time_wait is used to avoid a flood of nearly +identical emails. The new service will wait this long before sending another email +out.

+

Email will be sent through the SMTP host specified by option +smtp_host. The recipient is specified in option +mailto.

+

Many SMTP hosts require user login. If this is the case, the user and password +are specified with options smtp_user and +smtp_password, respectively.

+

To make this all work, you must tell the engine to load this new service. This +is done by adding your service name to the list service_list, +located in [Engines][[WxEngine]]:

+
[Engines]
+    [[WxEngine]]
+        service_list = weewx.wxengine.StdWunderground, weewx.wxengine.StdCatchUp, weewx.wxengine.StdTimeSynch, weewx.wxengine.StdPrint, weewx.wxengine.StdReportService, examples.alarm.MyAlarm
+

(Again, the list must be all on one line.)

+

In addition to the example above, the distribution also includes a low-battery +alarm (lowBattery.py), which is very similar, except that +it intercepts LOOP events (instead of archiving events).

+

Customizing the Engine

+

In this section, we look at how to install a custom Engine. In general, this +is the least desirable way to proceed, but in some cases it may be the only way +to get what you want.

+

For example, suppose you want to define a new event for when the first archive +of a day arrives. This can be done by extending the the standard engine.

+

This example is in file example/daily.py:

+
from weewx.wxengine import StdEngine, StdService
+from weeutil.weeutil import startOfArchiveDay
+
+class MyEngine(StdEngine):
+    """A customized weewx engine."""
+
+    def __init__(self, *args, **vargs):
+        # Pass on the initialization data to my superclass:
+        StdEngine.__init__(self, *args, **vargs)
+
+        # This will record the timestamp of the old day
+        self.old_day = None
+
+    def postArchiveData(self, rec):
+        # First let my superclass process it:
+        StdEngine.postArchiveData(self, rec)
+
+        # Get the timestamp of the start of the day using
+        # the utility function startOfArchiveDay 
+        dayStart_ts = startOfArchiveDay(rec['dateTime'])
+
+        # Call the function firstArchiveOfDay if either this is
+        # the first archive since startup, or if a new day has started
+        if not self.old_day or self.old_day != dayStart_ts:
+            self.old_day = dayStart_ts
+            self.newDay(rec)                          # NOTE 1
+
+    def newDay(self, rec):
+        """Called when the first archive record of a day arrives."""
+
+        # Go through the list of service objects. This
+        # list is actually in my superclass StdEngine.
+        for svc_obj in self.service_obj:
+            # Because this is a new event, not all services will
+            # be prepared to accept it. Be prepared for an AttributeError
+            # exception:
+            try:                                     # NOTE 2
+                svc_obj.firstArchiveOfDay(rec)
+            except AttributeError:
+                pass
+

This customized engine works by monitoring the arrival of archive records, and +checking their time stamp (rec['dateTime']. It calculates +the time stamp for the start of the day, and if it changes, calls member function +newDay() (NOTE 1).

+

The member function newDay() then goes through the +list of services (attribute self.service_obj). Because +this engine is defining a new event (first archive of the day), the existing +services may not be prepared to accept it. So, be prepared to catch an exception +AttributeError if the service does not define it (NOTE +2).

+

To use this engine, go into file weewxd.py and change +the line

+

weewx.wxengine.main()

+

so that it uses your new engine:

+
from examples.daily import MyEngine
+ 
+# Specify that my specialized engine should be used instead
+# of the default:
+weewx.wxengine.main(EngineClass = MyEngine)
+

We now have a new engine that defines a new event ("firstArchiveOfDay"), +but there is no service to take advantage of it. We define a new service:

+
# Define a new service to take advantage of the new event
+class DailyService(StdService):
+    """This service can do something when the first archive record of
+    a day arrives."""
+
+    def firstArchiveOfDay(self, rec):
+        """Called when the first archive record of a day arrives."""
+
+        print "The first archive of the day has arrived!"
+        print rec
+
+        # You might want to do something here like run a cron job
+

This service will simply print out a notice and then print out the new record. +However, if there is some daily processing you want to do, perhaps a backup, or +running utility wunderfixer, this +would be the place to do it.

+

The final step is to go into your configuration file and specify that this new +service be loaded, by adding its class name to option service_list:

+
[Engines]
+
+  [[WxEngine]]
+    # The list of services the main weewx engine should run:
+    service_list = weewx.wxengine.StdWunderground, weewx.wxengine.StdCatchUp, weewx.wxengine.StdTimeSynch, weewx.wxengine.StdPrint, weewx.wxengine.StdReportService, examples.daily.DailyService
+

(Again, the list must be all on one line.)

+

Appendix A: Archive types

+

Archive types are weather observations that have come from your +instrument and been stored in the archive database,  a SQL +database. They represent the current conditions as of some time. They +are available to be used in two places:

+ +

The following table shows all the possible archive types and whether they can +be used in tag $current or in a plot. Note that just because a type appears in the table does not necessarily mean +that it is available for your station setup. That would depend on +whether your instrument supports the type.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSQL Type
+ (appears in archive database)
Can be used
+ in plots
Can be used
+ in tag $current
altimeterXXX
barometerXXX
consBatteryVoltageXXX
dateTimeXX (represents current time)
dewpointXXX
ETXXX
extraHumid1XXX
extraHumid2XXX
extraTemp1XXX
extraTemp2XXX
extraTemp3XXX
hailXXX
hailRateXXX
heatindexXXX
heatingTempXXX
heatingVoltageXXX
inHumidityXXX
inTempXXX
inTempBatteryStatusXXX
intervalXXX
leafTemp2XXX
leafWet2XXX
outHumidityXXX
outTempXXX
outTempBatteryStatusXXX
pressureXXX
radiationXXX
rainXXX
rainBatteryStatusXXX
rainRateXXX
referenceVoltageXXX
rxCheckPercentXXX
soilMoist1XXX
soilMoist2XXX
soilMoist3XXX
soilMoist4XXX
soilTemp1XXX
soilTemp2XXX
soilTemp3XXX
soilTemp4XXX
supplyVoltageXXX
txBatteryStatusXXX
usUnitsXXX
UVXXX
windvec X (special vector type) 
windBatteryStatusXXX
windDirXXX
windGustXXX
windGustDirXXX
windSpeedXXX
windchillXXX
+

Appendix B: Units

+

The table below lists all the unit groups, their members, and which units are +options for the group.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
GroupMembersUnit options
group_altitudealtitudefoot
+ meter
group_directiongustdir
+ vecdir
+ windDir
+ windGustDir
degree_compass
group_moisturesoilMoist1
+ soilMoist2
+ soilMoist3
+ soilMoist4
centibar
group_percentextraHumid1
+ extraHumid2
+ inHumidity
+ outHumidity
+ rxCheckPercent
percent
group_pressurebarometer
+ altimeter
+ pressure
inHg
+ mbar
+ hPa
group_radiationUV
+ radiation
watt_per_meter_squared
group_rainrain
+ ET
+ hail
in
+ cm
+ mm
group_rainraterainRate
+ hailRate
in_per_hour
+ cm_per_hour
+ mm_per_hour
group_speedwind
+ windGust
+ windSpeed
+ windgustvec
+ windvec
mile_per_hour
+ km_per_hour
+ knot
+ meter_per_second
group_speed2rms
+ vecavg
mile_per_hour2
+ km_per_hour2
+ knot2
+ meter_per_second2
group_temperaturedewpoint
+ extraTemp1
+ extraTemp2
+ extraTemp3
+ heatindex
+ heatingTemp
+ inTemp
+ leafTemp1
+ leafTemp2
+ outTemp
+ soilTemp1
+ soilTemp2
+ soilTemp3
+ soilTemp4
+ windchill
degree_F
+ degree_C
group_voltconsBatteryVoltage
+ heatingVoltage
+ referenceVoltage
+ supplyVoltage
volt
group_NONENONENONE
+

Appendix C: Statistical types

+

Most of the templates are devoted to reporting statistical types, +such as temperature, wind, or rainfall, using various aggregates, +such as min, max, or sum. These are called +aggregations, because they are a summary of lots of underlying data. However, +only certain aggregates make sense for certain statistical types. For example, heat +degree days is defined on a daily basis, so while the day's average temperature +is meaningful, the day's heating degree days do not.

+

The following table defines which aggregates are available to be used in your +template for which statistical types (assuming your station supports them and you +have specified that it be stored in your stats database. See section +[Stats] in the +weewx.conf configuration file).

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Stats Typeminmintimemaxmaxtimeavgsumrmsvecavgvecdir
barometerXXXXX    
inTempXXXXX    
outTempXXXXX    
inHumidityXXXXX    
outHumidityXXXXX    
windXXXXX XXX
rainXXXXXX   
dewpointXXXXX    
windchillXXXXX    
heatindexXXXXX    
heatdeg    XX   
cooldeg    XX   
ETXXXXX    
radiationXXXXX    
UVXXXXX    
extraTemp1
+ extraTemp2
+ extraTemp3
XXXXX    
soilTemp1
+ soilTemp2
+ soilTemp3
XXXXX    
leafTemp1
+ leafTemp2
XXXXX    
extraHumid1
+ extraHumid2
XXXXX    
soilMoist1
+ soilMoist2
+ soilMoist3
+ soilMoist4
XXXXX    
leafWet1
+ leafWet2
XXXXX    
rxCheckPercentXXXXX    
+ + + + diff --git a/docs/daytemp_with_avg.png b/docs/daytemp_with_avg.png new file mode 100644 index 0000000000000000000000000000000000000000..0ed55bde014eed34912ff6a35685ddfeaee6cc60 GIT binary patch literal 10457 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 - + + + - - + + The Weewx weather system

The weewx weather system
-Version 1.5

+Version 1.6

Table of Contents

    -
  1. -

    Copyright

    -
  2. -
  3. -

    About weewx

    -
  4. -
  5. -

    Downloading - weewx

    -
  6. -
  7. -

    Prerequisites

    -
  8. -
  9. -

    Installing - weewx

    -
  10. -
  11. -

    Configuring - weewx

    -
  12. -
  13. -

    Running weewx

    -
  14. -
  15. -

    Compatibility with - wview

    -
  16. -
  17. -

    Monitoring - weewx

    -
  18. -
  19. -

    Advanced topics: types

    -
  20. -
  21. -

    Advanced topics: customizing HTML - generation

    -
  22. -
  23. -

    Advanced topics: architectural - notes

    -
  24. -
  25. -

    Advanced topics: customizing - the internal weewx engines

    -
  26. +
  27. Copyright
  28. +
  29. About weewx
  30. +
  31. Downloading weewx
  32. +
  33. Prerequisites
  34. +
  35. Installing weewx
  36. +
  37. Configuring weewx
  38. +
  39. Running weewx
  40. +
  41. Compatibility with wview
  42. +
  43. Monitoring weewx
  44. +
  45. Architectural notes
+

For information on customizing weewx, see the separate +document Customizing weewx.

+

For instructions on upgrading from various versions, see the separate +document Upgrading weewx.

+

For instructions on porting to the SheevaPlug, see the separate document +Notes on porting weewx to the +SheevaPlug.

1. Copyright

(c) 2009, 2010 by Tom Keffer <tkeffer@gmail.com>

This program is free software: you can redistribute it and/or modify it under @@ -192,34 +172,36 @@ PARTICULAR PURPOSE. See the GNU General Public License for more details.

this program. If not, see http://www.gnu.org/licenses.

2. About weewx

-

weewx is a piece of software, written in - Python, that interacts with your - weather station to produce plots, reports, and HTML pages. It can - optionally publish to the - WeatherUnderground. It uses modern software concepts, making it - simple, robust, and easy to extend. For an example station see - Hood River West.

-

I wrote weewx over the winter of 2008-2009 for two reasons: it was a wet and -miserable winter here in Oregon with not much else to do, so there was no good reason -not to, and because I wanted a simple, easy-to-understand server to run my Davis -VantagePro2 weather station on a Linux box. I had been using -wview, which is a -high-performance and feature rich system authored by Mark Teel -with lots of users. Written in C, it's an efficient system that can run on very underpowered boxes. In exchange, it's huge -(45,000+ lines of code), tightly integrated in with its companion library, radlib (another -14,000+ lines), and very complex, making it difficult to understand and reliably -customize. I wanted something more modern and much simpler.

Having made a career in C++ and Java, I was also interested in some more -modern languages, so I thought I'd try either Python or Ruby (although, truth be -told, the roots of Python are nearly as old as C++!). I ended up picking Python because -its libraries are more mature and there are many mores choices for third party -libraries.

+

weewx is a piece of software, written in +Python, that interacts with your weather station +to produce plots, reports, and HTML pages. It can optionally upload the reports +to a remote Web server as well as publish to the +WeatherUnderground. It uses modern software +concepts, making it simple, robust, and easy to extend. For an example station see +Hood River West.

+

I wrote weewx over the winter of 2008-2009 for two +reasons: it was a wet and miserable winter here in Oregon with not much else to +do, so there was no good reason not to, and because I wanted a simple, easy-to-understand +server to run my Davis VantagePro2 weather station on a Linux box. I had been using +wview, which is a high-performance and +feature rich system authored by Mark Teel with lots of users. Written in C, it's +an efficient system that can run on very underpowered boxes. In exchange, it's huge +(45,000+ lines of code), tightly integrated in with its companion library, radlib +(another 14,000+ lines), and very complex, making it difficult to understand and +reliably customize. I wanted something more modern and much simpler.

+

Having made a career in C++ and Java, I was also interested in some more modern +languages, so I thought I'd try either Python or Ruby (although, truth be told, +the roots of Python are nearly as old as C++!). I ended up picking Python because +its libraries are more mature and there are many mores choices for third party libraries.

Weewx weighs in at under 4,000 lines of code, plus -another 2,500 comment lines. Because it is pure Python, it requires no makefiles, no builds, no special installs. However, to be fair, it is missing features such as support for other weather stations. On the other hand, it offers very powerful configuration -and templating options, as well as an internally extensible engine, making it easy to customize. -Its internal modular design and use of modern exception handling make it very -robust and difficult to crash. It is also architecturally -very simple and easy to understand.

+another 2,500 comment lines. Because it is pure Python, it requires no makefiles, +no builds, no special installs. However, to be fair, at this point it supports only +the  Davis +VantagePro2 weather station. On the other hand, it offers very powerful configuration +and templating options, as well as an internally extensible engine, making it easy +to customize. Its internal modular design and use of modern exception handling make +it very robust and difficult to crash. It is also architecturally very simple and +easy to understand.

3. Downloading weewx

weewx can be downloaded from its SourceForge page: @@ -238,7 +220,7 @@ very simple and easy to understand.

  • pysqlite (Version 2.5 or greater) The Python interface to sqlite3.
  • configobj (Version - 4.6 or greater) Manages the configuration file weewx.conf.
  • + 4.5 or greater) Manages the configuration file weewx.conf.
  • pyserial (Version 1.35 or greater) Manages the serial connection to the weather station.
  • Cheetah (Version 2.0 or greater) @@ -263,8 +245,8 @@ both approaches are given below.

    apt_get, but the same package names would be used should you chose to use a graphical interface such as the Synaptic Package Manager.

    sqlite3

    -

    My Ubuntu 8.10 system came with V3.5.9, which works just fine. However, if you -need to install:

    +

    My Ubuntu 8.10 system came with V3.5.9 of sqlite, which works just fine. However, +if you need to install:

    sudo apt-get install sqlite3

    pysqlite

    Easily installed:

    @@ -330,11 +312,12 @@ of, apparently, benign warnings):

    setup tool "easy_install", part of the python-setuptools package. Refer to their instructions on how to install this tool.

    -

    Once easy_install has been installed, installing the rest of the packages is very easy.

    +

    Once easy_install has been installed, installing the +rest of the packages is very easy.

    sqlite3

    -

    My Ubuntu 8.10 system came with sqlite V3.5.9, which works just fine. If you do not -have sqlite3, refer to the sqlite webpage for -installation instructions.

    +

    My Ubuntu 8.10 system came with sqlite V3.5.9, which works just fine. If you +do not have sqlite3, refer to the sqlite webpage +for installation instructions.

    pysqlite

    While Version 2.3.X of pysqlite is included with many versions of Python, the more recent 2.5.X or greater is required @@ -395,8 +378,8 @@ the weewx directory hierarchy, then

    and scripts are installed;
  • $WEEWX_ROOT/weewx.conf is the configuration file;
  • -
  • $WEEWX_ROOT/templates is where the html - templates live;
  • +
  • $WEEWX_ROOT/skins is where the skins + live;
  • $WEEWX_ROOT/archive is the directory where the sqlite3 databases live;
  • $WEEWX_ROOT/public_html is where @@ -421,15 +404,17 @@ then type

    sudo ./setup.py install

    Upgrading

    Before upgrading from a previous version of weewx, -check the upgrade notes, found in file upgrading.htm, to see if there any -specific actions you need to do.

    +check the upgrade notes, found in file upgrading.htm, +to see if there any specific actions you need to do.

    You then generally follow the procedure above.

    In particular, before starting, be sure to set -home in the file -setup.cfg.

    +home in the file +setup.cfg.

    The build and install process will do the following for you.

      -
    • Save your old template directories as $WEEWX_ROOT/templates.YYYYMMDDHHMMSS +
    • Save your old 'skin' subdirectory as $WEEWX_ROOT/skins.YYYYMMDDHHMMSS + where YYYYMMDDHHMMSS is a timestamp;
    • +
    • Save your old 'bin' subdirectory as $WEEWX_ROOT/bin.YYYYMMDDHHMMSS where YYYYMMDDHHMMSS is a timestamp;
    • Merge any changes you've made to your old configuration file weewx.conf into the new configuration file, then install @@ -457,8 +442,9 @@ be handy when upgrading to a later version.

      6. Configuring weewx

      This section covers configuring your archive and statistical database (if necessary; this step is required only if you are moving from -wview to weewx), configuring your weather -station, and configuring the configuration file weewx.conf.

      +wview to weewx), +configuring your weather station, and configuring the configuration files +weewx.conf and skin.conf.

      In the following, $WEEWX_ROOT refers to the weewx root directory, generally /home/weewx.

      @@ -468,36 +454,38 @@ station, and configuring the configuration file weewx.confweewx.

      -

      If you wish to transfer your wview data, note that two databases are maintained by weewx:

      +

      If you wish to transfer your wview data, note that two databases are maintained +by weewx:

      • $WEEWX_ROOT/$archive_file (nominally /home/weewx/archive/weewx.sdb)
      • $WEEWX_ROOT/$stats_file (nominally /home/weewx/archive/stats.sdb)
      -

      Because wview and weewx use identical schema for the first of these (the archive -database), it can be just copied over. However, the second (the statistical databases) -is different — the weewx statistical database must be built manually and backfilled. -This is done using the configuration script configure.py. -

      +

      Because wview and weewx use identical schema for the +first of these (the archive database), it can be just copied over. However, the +second (the statistical databases) is different — the weewx +statistical database must be built manually and backfilled. This is done using the +configuration script configure.py.

      Here's a summary of how to transfer your wview data to weewx.

      mkdir $WEEWX_ROOT/archive

      -

      cp /usr/local/var/wview/archive/wview-archive.sdb $WEEWX_ROOT/archive/weewx.sdb

      +

      cp /usr/local/var/wview/archive/wview-archive.sdb +$WEEWX_ROOT/archive/weewx.sdb

      $WEEWX_ROOT/bin/configure.py --create-stats $WEEWX_ROOT/weewx.conf

      $WEEWX_ROOT/bin/configure.py --backfill-stats $WEEWX_ROOT/weewx.conf

      If your existing database is large, backfilling could take some time. On my modest -500 MHz fit-PC -with 512 MB of memory it took a little over 4 minutes for a year and a half (25 -MB) of data (while wview was running in the background).

      +500 MHz fit-PC +Slim with 512 MB of memory it took a little over 4 minutes for a year and a +half (25 MB) of data (while wview was running in the background).

      6.2 Configuring your weather station

      The only two variables weewx tries to manage on the VantagePro are the time and the archive interval.

      Time

      The time on the VP is automatically synchronized with the -weewx server nominally every four hours (changeable by the user). You should run a -NTP daemon on your -server to insure that it is synchronized with the correct time. Doing so will greatly -reduce errors, especially if you send data to services such as the Weather Underground.

      +weewx server nominally every four hours (changeable by the user). You should +run a NTP daemon on your server to insure that +it is synchronized with the correct time. Doing so will greatly reduce errors, especially +if you send data to services such as the Weather Underground.

      Archive interval

      The archive interval is set in the main configuration file $WEEWX_ROOT/weewx.conf. Look for the entry archive_interval @@ -512,12 +500,17 @@ script to set it on the VantagePro. If it differs from the old archive interval, the main memory log of the VantagePro will be cleared.

      $WEEWX_ROOT/bin/configure.py --configure-VantagePro $WEEWX_ROOT/weewx.conf

      6.3 Editing the configuration file weewx.conf

      -

      Virtually every conceivable configuration option is in the configuration file -$WEEWX_ROOT/weewx.conf. Most of the important -ones are up near the top of the file. They are all documented in this section, although -you can safely ignore most of them. The truly important ones, the ones you are likely -to have to customize for your station, are shown in -bold face and in blue.

      +

      Station specific information is set in the configuration file +$WEEWX_ROOT/weewx.conf. There is another configuration +file skin.conf for presentation-specific options, which +is described under section +Reference: +The standard skin configuration file in the document +Customizing weewx .

      +

      Most of the important options are up near the top of the file. They are all documented +in this section, although you can safely ignore most of them. The truly important +ones, the ones you are likely to have to customize for your station, are shown in +bold face and in blue.

      Default values are provided for many of them, meaning that if they are not listed in the configuration file at all, weewx will pick sensible values. When the documentation below gives a "default value" this @@ -545,40 +538,36 @@ will be set automatically by the setup script setup.py to reflect the choice you made in the configuration file setup.cfg. Required. No default.

      location

      -

      The station location should be a string that describes the geography of where -you weather station is located, such as 'Hood River, Oregon'. Required. No default.

      +

      The station location should be a UTF-8 string that describes the geography of +where you weather station is located, such as 'Hood River, Oregon'. Required. No +default.

      latitude
      longitude

      The lat/lon should be set in decimal degrees, negative for southern and eastern hemispheres, respectively. Required. No default.

      altitude

      Should be set to the altitude of the station in the same units specified in -group_altitude. Required. No default.

      +group_altitude. +Required. No default.

      rain_year_start

      If your area uses a rain year that starts on something other than the first of January, you may want to set this variable. For example, set to 10 if your rain year starts in October (as mine does). Default is 1.

      -

      radar_url

      -

      This variable is available in the HTML templates. Set it to an appropriate URL -to display a radar image for your area. No default.

      heating_base
      cooling_base

      Set to the base temperature for calculating heating and cooling degree-days, respectively, in the same units specified in -group_temperature. The default is 65.0 for both.

      +group_temperature. The default +is 65.0 for both.

      week_start

      -

      Start of the week. 0=Monday, 1= Tuesday, ... , 6 = Sunday. Default is 6 -(Sunday)

      -

      hemispheres

      -

      Set to suitable abbreviations for the four hemispheres. Default is "N", "S", -"E", "W"

      +

      Start of the week. 0=Monday, 1= Tuesday, ... , 6 = Sunday. Default is 6 (Sunday)

      clock_check

      How often to check the station's onboard clock for drift, in seconds. Default is 14400 (every 4 hours)

      cache_loop_data

      -

      Set to 1 (one) to cache LOOP data, otherwise, set to zero. Most users will -not want to change this unless you have a specialized application. Default is 1 -(i.e., cache LOOP data).

      +

      Set to 1 (one) to cache LOOP data, otherwise, set to zero. Most users will not +want to change this unless you have a specialized application. Default is 1 (i.e., +cache LOOP data).

      station_type

      Set to the type of hardware you are using. For this version, only 'VantagePro' is accepted. Required.

      @@ -613,48 +602,19 @@ up. Default is 5 seconds.

      to change it, this value should be left at the default, as it is long enough for the station to offer new data, but not so long as to go into a new loop packet (which arrive every 2 seconds). Default is 1.2 seconds.

      -

      max_retries

      +

      max_tries

      How many times to try again before giving up. Default is 4.

      max_drift

      The maximum amount of drift to tolerate, in seconds, in the VantagePro's onboard clock, before resetting the clock. Default is 5.

      unit_system

      What unit system is in use on your weather station hardware. Possible values -are '1' (U.S. Customary) or '2' (Metric). As far as I know, all Davis instruments support -only U.S.. In any case, U.S. is the only system supported by +are '1' (U.S. Customary) or '2' (Metric). As far as I know, all Davis instruments +support only U.S.. In any case, U.S. is the only system supported by weewx at this time. Default is 1.

      -

      [FTP]

      -

      If you FTP your images and HTML files to an external web server, -weewx can FTP them for you. It does an incremental update, -that is, it only FTPs any files that have changed, saving the outgoing bandwidth -of your Internet connection.

      -

      If you do not use such a server, comment out the first four options below.

      -

      user

      -

      Set to the username you use for your FTP connection to your web server. Required. -No default.

      -

      password

      -

      Set to the password you use for your FTP connection to your web server. Required. -No default.

      -

      server

      -

      Set to the name of your web server (e.g., -www.threefools.org, in my case). Required. No default

      -

      path

      -

      Set to the path where the weather data will be stored on your webserver (e.g., -'/weather'). NB: some FTP servers require a -leading slash ('/'), some don't. Required. No default.

      -

      passive

      -

      Set to 1 if you wish to use the more modern, FTP passive mode, 0 if you wish -to use active mode. Passive mode generally works better through firewalls, but not -all FTP servers do a good job of supporting it. See -Active FTP vs. Passive FTP, a Definitive -Explanation for a good explanation of the difference. Default is 1 (passive -mode).

      -

      max_retries

      -

      Weewx will try up to this many times to FTP a file -up to your server before giving up. Default is 3.

      [Wunderground]

      -

      Weewx can send your current data to the Weather -Underground. If you do not wish to do this, comment out the two options below.

      +

      Weewx can send your current data to the Weather Underground. +If you do not wish to do this, comment out the two options below.

      station

      Set to your Weather Underground station ID (e.g., KORHOODR3). Required.

      @@ -669,7 +629,7 @@ the database. Required

      unit_system

      What unit system to use inside the database. Required. The only one supported right now is '1', the U.S. Customary system

      -

      [Stats]

      +

      [Stats]

      This section is for configuring the sqlite3 database in which the station statistics are stored.

      stats_file

      @@ -681,297 +641,76 @@ be available for generating HTML pages. Optional. The default is all types, resu in a possibly much bigger than necessary stats database (do you really have four different soil moisture sensors?) The list that ships with the configuration file will work for most stations and probably will not have to be modified.

      -

      [Units]

      -

      This section deals with Units and their formatting.

      -

      Unit Groups

      -

      As there are many different observational measurement types (such as 'outTemp', -'barometer', etc.) used in weewx -(more than 50 at last count), it would be tedious, not to say possibly -inconsistent, to specify a different measurement system for each one of them. At -the other extreme, requiring them all to be "U.S. Customary" or "Metric" seems -overly restrictive. Weewx has taken a middle route and -divided all the different measurement types into 12 different "unit groups." A -unit group is something like "group_temperature." It -represents the measurement system to be used by all observation types that are -measured in temperature, such as inside temperature (type 'inTemp'), -outside temperature ('outTemp'), dewpoint ('dewpoint'), -wind chill ('windchill'), and so on. If you -decide that you want unit group group_temperature to be -measured in "degrees_C" then you are saying all -members of its group will be reported in degrees Celsius.

      -

      [[UnitGroups]]

      -

      This section lists all the Unit Groups and specifies which unit system is to -be used for each one of them. Note that the unit system is always specified in -the singular. That is, specify "foot" or "inch_per_hour", -not "feet" or "inches_per_hour."

      -

      See section Units for more information, including a concise -summary of the groups, their members, and which options can be used for each -group.

      -

      group_altitude

      -

      Which measurement unit to be used for altitude. Possible options -are 'foot' or -'meter'.

      -

      group_direction

      -

      Which measurement unit to be used for direction. The only option is -"degree_compass".

      -

      group_moisture

      -

      The measurement unit to be used for soil moisture. The only option -is "centibar."

      -

      group_percent

      -

      The measurement unit to be used for percentages. The only option is -"percent".

      -

      group_pressure

      -

      The measurement unit to be used for pressure. Possible options are -one of "inHg" (inches of mercury), -"mbar", or -"hPa."

      -

      group_radiation

      -

      The measurement unit to be used for radiation. The only option is -"watt_per_meter_squared."

      -

      group_rain

      -

      The measurement unit to be used for precipitation. Options are -"inch", -"cm," or -"mm."

      - -

      group_rainrate

      -

      The measurement unit to be used for rate of precipitation. Possible options -are one of "inch_per_hour", -"cm_per_hour", or -"mm_per_hour".

      - -

      group_speed

      -

      The measurement unit to be used for wind speeds. Possible options are one of "mile_per_hour", -"km_per_hour", "knot", or "meter_per_second."

      -

      group_speed2

      -

      This group is similar to group_speed, but is used for calculated wind speeds -which typically have a slightly higher resolution. Possible options are one of "mile_per_hour2", -"km_per_hour2", "knot2", or -"meter_per_second2."

      -

      group_temperature

      -

      The measurement unit to be used for temperatures. Options are "degree_F" -or "degree_C."

      -

      group_volt

      -

      The measurement unit to be used for voltages. The only option is "volt."

      -

      [[StringFormats]]

      -

      This sub-section is used to specify what string format is to be used for each -unit when a quantity needs to be converted to a string. Typically, this happens -with y-axis labeling on plots and for statistics in -HTML file generation. For example, the options

      -

      degree_C = %.1f

      -

      inch     = %.2f

      -

      would specify that the given string formats are to be used when formatting any temperature -measured in degrees Celsius or any precipitation amount measured in inches, respectively. The - -formatting codes are those used by Python, and are very similar to C's sprintf() -codes.

      -

      [Images]

      -

      This section, which controls which images (plots) get generated and with which -options, is by far the most complicated. However, it is extremely flexible and powerful.

      -

      Time periods

      -

      The section consists of one or more sub-sections, one for each time period (day, week, -month, and year). These sub-sections define the nature of aggregation and plot types -for the time period. For example, here's a typical set of options for sub-section -[[month_images]], controlling how images that cover a -month period are generated:

      -

      [[month_images]]

      -

        x_label_format = %d

      -

        bottom_label_format = %m/%d/%y %H:%M

      -

        time_length = 2592000 # == 30 days

      -

        aggregate_type = avg

      -

        aggregate_interval = 10800 # == 3 hours

      -

      The option x_label_format gives a -strftime() -type format for the x-axis. In this example, it will only show days (format option -"%d"). The bottom_label_format -is the format used to time stamp the image at the bottom. In this example, it will -show the time as 10/25/09 15:35. A plot will cover a nominal -30 days, and all items included in it will use an aggregate type of averaging over -3 hours.

      -

      Image files

      -

      Within each sub-section is another nesting, one for each image to be generated. -The title of each sub-sub-section is the filename to be used for the image. -Finally, at one additional nesting level (!) are the logical names of all the -line types to be drawn in the image.  Values specified in the -level above can be overridden. For example, here's a typical set of options for -sub-sub-section [[[monthrain]]]:

      -

      [[[monthrain]]]

      -

        plot_type = bar

      -

        yscale = None, None, 0.02

      -

        [[[[rain]]]]

      -

          aggregate_type = sum

      -

          aggregate_interval = 86400

      -

          label = Rain (daily avg)

      -

      This will generate an image file with name monthrain.png. -It will be a bar plot. Option yscale controls the y-axis -scaling — if left out, the scale will automatically be chosen. However, in this -example we are choosing to exercise some degree of control by specifying values -explicitly. It is a 3-way tuple (ylow, -yhigh, min_interval), where -ymin and ymax are the minimum -and maximum y-axis values, respectively, and min_interval -is the minimum tick interval. If set to 'None', the corresponding -value will be automatically chosen. So, in this example, we are letting -weewx pick sensible y minimum and maximum values, but -we are requiring that the tick increment (min_interval) -be at least 0.02.

      -

      Continuing on with the example above, there will be only one plot "line" (it -will actually be a series of bars) and it will -have logical name 'rain'. Because we haven't said -otherwise, the SQL data type to be used for this line will be the same as its -logical name, that is, rain, but this can be -overridden (see below). The aggregation type will be summing (overriding -the averaging specified in sub-section [[month_images]]), -so you get the total rain over the aggregate period (rather than the average) over -an aggregation interval of 86,400 seconds (one day). The plot line will be titled -with the indicated label ('Rain (daily avg)')

      -

      Including more than one SQL type in a plot

      -

      More than one SQL type can be included in a plot. For example, here's how to -generate a plot with the week's outside temperature as well as dewpoint:

      -

      [[[monthtempdew]]]

      -

        [[[[outTemp]]]]

      -

        [[[[dewpoint]]]]

      -

      This would create an image in file monthtempdew.png -that includes a line plot of both outside temperature and dewpoint.

      -

      Including the same SQL type more than once in a plot

      -

      Another example. Say you want a plot of the day's temperature, overlaid with -hourly averages. Here, you are using the same data type ('outTemp') -for both plot lines, the first with averages, the second without. If you do the -obvious it won't work:

      -

      ## WRONG ##

      -

      [[[daytemp_with_avg]]]

      -

        [[[[outTemp]]]]

      -

          aggregate_type = avg

      -

          aggregate_interval = 3600

      -

        [[[[outTemp]]]]  # OOPS! The same section name appears more than -once!

      -

      The option parser does not allow the same section name ('outTemp' -in this case) to appear more than once at a given level in the configuration -file, so an error will be declared (technical reason: formally, the sections are -an unordered dictionary). If you wish for the same SQL type to appear more than -once in a plot then there is a trick you must know: use option -data_type. This will override the default action that -the logical line name is used for the SQL type. So, our example would look like -this:

      -

      [[[daytemp_with_avg]]]

      -

          [[[[a_logical_name]]]]

      -

            data_type = outTemp

      -

            aggregate_type = avg

      -

            aggregate_interval = 3600

      -

          [[[[outTemp]]]]

      -

      Here, the first logical line has been given the name "a_logical_name" -to distinguish it from the second line "outTemp". We -have specified that the first line will use data type outTemp -and that it will use averaging over a one hour period. The second also uses -outTemp, but will not use averaging.

      -

      The result is a nice plot of the day's temperature, overlaid with a 3-hour -smoothed average.

      -

      Progressive vector plots

      -

      Weewx can produce progressive vector plots as well as the more conventional -x-y plots. To produce these, use plot type 'vector'. -You need a vector type to produce this kind of plot. There are two: 'windvec', -and 'windgustvec'. While they don't actually appear in -the SQL database, weewx understands that they represent special vector-types. -The first, 'windvec', represents the average wind in -an archive period, the second, 'windgustvec' the max -wind in an archive period. Here's how to produce a progressive vector plot of -the year's biggest daily wind gusts, along with daily averages:

      -

      [[[yeargustoverlay]]]

      -

        aggregate_interval = 86400

      -

        [[[[windvec]]]]

      -

          plot_type = vector

      -

          aggregate_type = avg

      -

        [[[[windgustvec]]]]

      -

          plot_type = vector

      -

          aggregate_type = max

      -

      This will produce an image file with name -yeargustoverlay.png. It will consist of two progressive vector plots, -both using daily aggregation (86,400 seconds). For the first set of vectors, the daily -average will be used. In the second, the max of the gusts will be used.

      -

      By default, the sticks in the progressive wind plots point towards the wind -source. That is, the stick for a wind from the west will point left. If you have -a chronic wind direction (as I do), you may want to rotate the default direction -so that all the vectors don't line up over the x-axis, overlaying each other. Do -this by using option vector_rotate. For example, with -my chronic westerlies, I set vector_rotate to 90.0, so -winds out of the west point straight up.

      -

      If you use this kind of plot (the out-of-the-box version of -weewx includes daily, weekly, monthly, and yearly -progressive wind plots), a small compass rose will be put in the lower-left -corner of the image to show the orientation of North.

      -

      Overriding values

      -

      Remember that values at any level can override values specified at a higher -level. For example, say you want to generate the standard plots, but for a few -key observation types such as barometer, you want to also generate some -oversized plots to give you extra detail, perhaps for an HTML popup. The -standard weewx.conf file specifies plot size of -300x180 pixels, which will be used for all plots unless overridden:

      -

      [Images]

      -

        ...

      -

        image_width=300

      -

        image_height = 180

      -

      The standard plot of barometric pressure will appear in -daybarometer.png:

      -

          [[[daybarometer]]]

      -

            [[[[barometer]]]] 

      -

      We now add our special plot of barometric pressure, but specify a larger -image size. This image will be put in file -daybarometer_big.png.

      -

          [[[daybarometer_big]]]

      -

            image_width  = 600

      -

            image_height = 360

      -

            [[[[barometer]]]]

      -

      Summary

      -

      Studying this section in the shipped version of weewx.conf -will give you ideas about the many different image plot configurations that are -possible without hacking the code.

      -

      [Labels]

      -

      This section controls how measurement values are labeled. It is generally -used in plot generation, although there are a few other areas where it is used. It consists of -two sub-sections:

      -

      [[Generic]]

      -

      This sub-sections specifies default labels to be used for each SQL type. For -example, options

      -

      inTemp = Inside Temperature

      -

      outTemp = Outside Temperature

      -

      would cause the given labels to be used for plots involving SQL types -inTemp and outTemp..

      -

      [[UnitLabels]]

      -

      This sub-section specifies what label to be used for each measurement unit -type. For example, the options

      -

      degree_F = \xb0F

      -

      inch     = ' in'

      -

      would cause all temperatures to have unit labels '°F' -and all precipitation to have labels ' in'. (NB: the code -\xb0 is the hexadecimal value b0, -which in many encodings encodes the degree sign.)

      -

      [HTML]

      -

      Section [HTML] has two options and two sub-sections. For additional information -on HTML generation see the section below.

      -

      template_root

      -

      This option specifies the directory, relative to WEEWX_ROOT, -where the HTML templates can be found. Required. No default.

      -

      html_root

      -

      This option specifies the directory, relative to WEEWX_ROOT, -where the generated HTML files should be put. Required. No default.

      -

      [[UnitLabels]]

      -

      This subsection is similar to its eponymous counterpart in section -[Labels] above, except it is used for HTML generation. -It is useful to have a separate section because HTML uses special 'entity' codes -to code special characters, such as the degree sign. For example, the options

      -

      degree_F = &deg;F

      -

      inch     = ' in'

      -

      would cause outside temperature and rain to have unit labels  '°F' -and ' in', respectively.

      -

      [[Time]]

      -

      This subsection is used for time labels in HTML generation. It uses -strftime() -formats. For example

      -

      week  = %H:%M on %A

      -

      month = %d-%b-%Y %H:%M

      -

      would specify that week data should use a format such as "15:20 -on Sunday", while month data should look like "06-Oct-2009 -15:20"

      +

      [Reports]

      +

      This section controls which reports are to be generated. While it can be highly +customized for your individual situation, this documentation describes the section +as shipped in the standard distribution.

      +

      Each report is represented by a sub-section, marked with double brackets (e.g., +[[MyReport]]). Any options for the report should be placed +under it. The standard report service will go through the sections, running each +report in order. Hence, for the stock distribution, report [[StandardReport]] +will be run first, then report [[FTP]] (which actually +optionally uploads the results to a remote web server). Details for how to customize +reports are in the section +Opportunities for +customizing reports, in the document Customizing +weewx.

      +

      SKIN_ROOT

      +

      The directory relative to $WEEWX_ROOT where +the skins live. Default is skins.

      +

      HTML_ROOT

      +

      The target directory for the generated files, relative to +$WEEWX_ROOT. Generated files and images will be put here. Default +is public_html.

      +

      [[StandardReport]]

      +

      This is the standard report that will be run on every archiving interval. It +uses the skin "Standard", which generates four HTML pages +("day", "week", "month", and "year" observations), +plot graphs for same, an RSS feed, and NOAA +monthly and yearly reports. Unless changed otherwise, it uses US Customary Units +and puts the results in public_html and subdirectory +public_html/NOAA.

      +

      [[FTP]]

      +

      While this "report" doesn't actually generate anything, it uses the report machinery +to upload files from directory $HTML_ROOT to +a remote webserver. It does an incremental update, that is, it only FTPs any files +that have changed, saving the outgoing bandwidth of your Internet connection.

      +

      If you do not use such a server, comment out the first four options below.

      +

      user

      +

      Set to the username you use for your FTP connection to your web server. Required. +No default.

      +

      password

      +

      Set to the password you use for your FTP connection to your web server. Required. +No default.

      +

      server

      +

      Set to the name of your web server (e.g., +www.threefools.org, in my case). Required. No default

      +

      path

      +

      Set to the path where the weather data will be stored on your webserver (e.g., +'/weather'). NB: some FTP servers require a leading slash +('/'), some don't. Required. No default.

      +

      passive

      +

      Set to 1 if you wish to use the more modern, FTP passive mode, 0 if you wish +to use active mode. Passive mode generally works better through firewalls, but not +all FTP servers do a good job of supporting it. See +Active FTP vs. Passive FTP, a Definitive +Explanation for a good explanation of the difference. Default is 1 (passive +mode).

      +

      max_tries

      +

      Weewx will try up to this many times to FTP a file +up to your server before giving up. Default is 3.

      +

      [Engines]

      +

      This section is used to configure the internal service engine in weewx. It is +for advanced customization. Details on how to do this is found in the section +Customizing the +weewx service engine in the document Customizing +weewx.

      +

      [[WxEngine]]

      +

      This section is for options used by the service engine.

      +
      service_list
      +

      This option is the list of services that are to be run by the service +engine. After each event (such as the arrival of LOOP data, etc.), they will be +run in the given order.

      7. Running weewx

      Weewx can be run either from the command line (useful for diagnostic purposes because it will print out a summary of every LOOP data), @@ -994,17 +733,18 @@ some reason.

      Weewx will then start monitoring LOOP data, printing a short version of the received data on standard output, about once every two seconds.

      You can tell a running instance of weewx to reread -its configuration file by sending it the HUP signal. -First run ps to find out the Process ID (PID) number -of the instance, then send it the HUP signal:

      +its configuration file by sending it the HUP signal. First +run ps to find out the Process ID (PID) number of the +instance, then send it the HUP signal:

      ps -a         # Note the PID of the weewxd.py process

      kill -HUP pid # Send it a HUP signal

      7.2 Running as a daemon

      For unattended operations it is best to have weewx -run as a daemon, started automatically when the server is rebooted. Start by selecting the appropriate run script. They can be found under -$WEEWX_ROOT/start_script.

      - +run as a daemon, started automatically when the server is rebooted. Start by selecting +the appropriate run script. They can be found under $WEEWX_ROOT/start_script. +

      +
      @@ -1019,7 +759,7 @@ inside has been set to the proper root directory for your wee installation (it should have been set to the correct value automatically by the install process, but it's worth checking).

      Copy it to the proper location for your system:

      -
      SuSE: $WEEWX_ROOT/start_script/SuSE/weewx
      +
      @@ -1030,7 +770,7 @@ install process, but it's worth checking).

      SuSE: cp $WEEWX_ROOT/start_script/SuSE/weewx /etc/init.d

      Make sure the script is executable

      - +
      @@ -1041,7 +781,7 @@ install process, but it's worth checking).

      SuSE: chmod +x /etc/init.d/weewx

      Create symbolic links in the run level directories:

      - +
      @@ -1072,8 +812,8 @@ wview

      The sqlite3 archive database used by weewx (nominally, weewx.sdb) is completely compatible with the database used by wview (usually called -wview-archive.sdb), at least as of wview Version 5.2.X. The -schema and its semantics is identical. However, the statistical file +wview-archive.sdb), at least as of wview Version 5.2.X. +The schema and its semantics is identical. However, the statistical file stats.sdb is different, and must be rebuilt

      9. Monitoring weewx

      Weewx logs many events to the system log. On Debian @@ -1083,861 +823,19 @@ When troubleshooting the system, be sure to check it!

      Setting the option debug in weewx.conf to 1 (one) will generate many more checks and output and can be useful for debugging.

      -

      10. Advanced topics: types

      +

      10. Architectural notes

      This section is not needed to get started.

      -

      The weather variables used in weewx generally fall into three different camps. -They can be a "SQL type", meaning they appear in the archive database, an -"Observational type", meaning that they either appear in the archive -database or are a derived quantity thereof, -or a "Statistical type," meaning that they appear in the statistical -database. Observational types can be used in plots, statistical types can be -aggregated and used in daily, weekly, monthly, and yearly summaries.

      -

      The following table shows all the possible types and what categories they -fall into. Note that just because a type appears in the table does not -necessarily mean that it is available for your station setup. That -would depend on whether your instrument supports the type, and whether or not -you configured weewx to use that type.

      -
      SuSE: /usr/lib/lsb/install_initd /etc/init.d/weewx
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      NameSQL Type
      - (appears in archive database)
      Observation Type
      - (can be used in plots)
      Stats Type
      - (can be used in statistical aggregations)
      altimeterXXX
      altitude
      barometerXXX
      consBatteryVoltageXXX
      dateTimeX
      dewpointXXX
      ETXXX
      extraHumid1XXx
      extraHumid2XXX
      extraTemp1XXX
      extraTemp2XXX
      extraTemp3XXX
      hailXXX
      hailRateXXX
      heatindexXXX
      heatingTempXXX
      heatingVoltageXXX
      inHumidityXXX
      inTempXXX
      inTempBatteryStatusXXX
      intervalXX
      leafTemp2XXX
      leafWet2XXX
      outHumidityXXX
      outTempXXX
      outTempBatteryStatusXXX
      pressureXXX
      radiationXXX
      rainXXX
      rainBatteryStatusXXX
      rainRateXXX
      referenceVoltageXXX
      rxCheckPercentXXX
      soilMoist1XXX
      soilMoist2XXX
      soilMoist3XXX
      soilMoist4XXX
      soilTemp1XXX
      soilTemp2XXX
      soilTemp3XXX
      soilTemp4XXX
      supplyVoltageXXX
      txBatteryStatusXXX
      usUnitsXX
      UVXX
      windXXX
      windvec X 
      windBatteryStatusXXX
      windDirXXX
      windGustXXX
      windGustDirXXX
      windSpeedXXX
      windchillXXX
      -

       

      -

      10.1 Units

      -

      The table below lists all the unit groups, their members, and which units are -options for the group.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      GroupMembersUnit options
      group_altitudealtitudefoot
      - meter
      group_directiongustdir
      - vecdir
      - windDir
      - windGustDir
      degree_compass
      group_moisturesoilMoist1
      - soilMoist2
      - soilMoist3
      - soilMoist4
      centibar
      group_percentextraHumid1
      - extraHumid2
      - inHumidity
      - outHumidity
      - rxCheckPercent
      percent
      group_pressurebarometer
      - altimeter
      - pressure
      inHg
      - mbar
      - hPa
      group_radiationUV
      - radiation
      watt_per_meter_squared
      group_rainrain
      - ET
      - hail
      in
      - cm
      - mm
      group_rainraterainRate
      - hailRate
      in_per_hour
      - cm_per_hour
      - mm_per_hour
      group_speedwind
      - windGust
      - windSpeed
      - windgustvec
      - windvec
      mile_per_hour
      - km_per_hour
      - knot
      - meter_per_second
      group_speed2rms
      - vecavg
      mile_per_hour2
      - km_per_hour2
      - knot2
      - meter_per_second2
      group_temperaturedewpoint
      - extraTemp1
      - extraTemp2
      - extraTemp3
      - heatindex
      - heatingTemp
      - inTemp
      - leafTemp1
      - leafTemp2
      - outTemp
      - soilTemp1
      - soilTemp2
      - soilTemp3
      - soilTemp4
      - windchill
      degree_F
      - degree_C
      group_voltconsBatteryVoltage
      - heatingVoltage
      - referenceVoltage
      - supplyVoltage
      volt
      -

      11. Advanced topics: customizing HTML Generation

      -

      This section is not needed to get started.

      -

      HTML generation is done using the Cheetah -templating engine. This is a very powerful engine, which essentially lets you have -the full semantics of Python available in your templates. As this would make the -templates incomprehensible to anyone but a Python programmer, -weewx adopts a very small subset of its power.

      -

      Generally, any value is specified by using a 'dot' code. For example:

      -

      $month.outTemp.max

      -

      $month.outTemp.maxtime

      -

      $current.outTemp

      -

      would code the max outside temperature for the month, the time it occurred, and -the current outside temperature, respectively. So, an HTML file that looks like

      -

      <html>

      -

        <head>

      -

          <title>Current conditions</title>

      -

        </head>

      -

        <body>

      -

        <p>Current temperature = $current.outTemp</p>

      -

        <p>Max for the month is $month.outTemp.max, which occurred at

      -

      $month.outTemp.maxtime</p>

      -

        </body>

      -

      </html>

      -

      would be all you need for a very simple HTML page that would display the text -(assuming that the unit class for temperature is -degree_F): -

      -

      Current temperature = 51.0°F
      -Max for the month is 68.8°F, which occurred at 07-Oct-2009 15:15

      -

      The format that was used to format the temperature (51.0) -is specified in section [Units][[StringFormat]]. -The unit label °F is from section -[HTML][[UnitLabels]], while the time -format is from [HTML][[Time]].

      -

      The "dot" code has up to three parts.

      -
        -
      1. The first part is the time period, and can be one of - current, day, week, - month, year, or - rainyear.
      2. -
      3. The second part is the "statistical type". This is something like 'outTemp', - 'rain', 'wind', etc. - The statistical types are generally those that appear in the SQL databases with three exceptions. First, type 'wind' - is a special hybrid type and does not appear in the SQL database. It brings - together the several different SQL types 'windSpeed', - 'windDir', windGust', and - 'windGustDir' under one roof (all are still available, - should you wish to use them for a specialized application). Exceptions number - two and three are 'heatdeg' and 'cooldeg', - heating and cooling degree-days, respectively, which are synthesized from average - outside temperature and do not appear directly in the database.
      4. -
      5. The last position is the aggregation type, available for any time period - except for 'current'. The table below in - section Statistical aggregations shows what aggregation - types are available for which types.
      6. -
      -

      In addition, if the first part of the dot code represents a time period -longer than a day (e.g., a week, month, year, or rainyear), then it can be -iterated over. This example uses a Cheetah 'for' loop to iterate over all months -in a year, printing out each month's min and max temperature.

      -

      <html>

      -

        <head>

      -

          <title>Year stats by month</title>

      -

        </head>

      -

        <body>

      -

        <p>Min, max temperatures -by month:</p>

      -

        # for $month in $year.months

      -

          $month.outTemp.min $month.outTemp.max

      -

        # end for 

      -

        </body>

      -

      </html>

      -

      See the NOAA template files NOAA_month.tmpl and -NOAA_year.tmpl for examples using iteration, as well -as explicit formatting.

      -

      11.1 Statistical aggregations

      -

      Most of the HTML templates are devoted to generating statistical -aggregates, such as monthly max temperature, or year-to-date rainfall. -These are called aggregations, because they are a summary of lots of -underlying data. However, only certain aggregates make sense for certain -statistical -types. For example, heat degree days is defined on a daily basis, so while the -day's average temperature is meaningful, the day's heating degree days do not.

      -

      The following table defines which aggregates are available to be used in your template -for which statistical types (assuming your -station supports them and you have specified that it be stored in your stats database. -See section stats_types in the weewx.conf -configuration file).

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      Stats Typeminmintimemaxmaxtimeavgsumrmsvecavgvecdir
      barometerXXXXX    
      inTempXXXXX    
      outTempXXXXX    
      inHumidityXXXXX    
      outHumidityXXXXX    
      windXXXXX XXX
      rainXXXXXX   
      dewpointXXXXX    
      windchillXXXXX    
      heatindexXXXXX    
      heatdeg    XX   
      cooldeg    XX   
      ETXXXXX    
      radiationXXXXX    
      UVXXXXX    
      extraTemp1
      - extraTemp2
      - extraTemp3
      XXXXX    
      soilTemp1
      - soilTemp2
      - soilTemp3
      XXXXX    
      leafTemp1
      - leafTemp2
      XXXXX    
      extraHumid1
      - extraHumid2
      XXXXX    
      soilMoist1
      - soilMoist2
      - soilMoist3
      - soilMoist4
      XXXXX    
      leafWet1
      - leafWet2
      XXXXX    
      rxCheckPercentXXXXX    
      -

      12. Advanced topics: architectural notes

      -

      This section is not needed to get started.

      -

      12.1 Goals

      +

      10.1 Goals

      The primary goals of weewx are:

      • Architectural simplicity. No semaphores, no named pipes, no inter-process communications, no complex multi-threading to manage.
      • One code base. The same code base should be used for all platforms, all - weather stations, all platforms, and any combination of features. Ample - configuration and customization options should be provided so the user - doesn't feel tempted to start hacking code. At worse, the user may have to - subclass, which is much easier to port to newer versions of the code base, - than customizing the base code.
      • + weather stations, all platforms, and any combination of features. Ample configuration + and customization options should be provided so the user doesn't feel tempted + to start hacking code. At worse, the user may have to subclass, which is much + easier to port to newer versions of the code base, than customizing the base + code.
      • Minimal reliance on external packages, so the user doesn't have to go chase them down all over the Web before getting started.
      • "Fast enough." In any design decision, architectural simplicity trumps speed. @@ -1948,7 +846,7 @@ configuration file).

        with two decades of experience in C++. I tried hard to not make the code base look like it was written by a C++ programmer who stumbled across a Python manual!
      -

      12.2 Strategies

      +

      10.2 Strategies

      To meet these goals, the following strategies were used:

      • A powerful configuration parser, @@ -1967,68 +865,27 @@ configuration file).

        the database, where transactions are easily controlled. This greatly reduces the chances of corrupting the data, making it much easier to understand and modify the code base.
      • -
      • An engine-driven design. Key services are actually classes that can be - loaded at runtime, making it easy for users to add or subtract features.
      • +
      • An engine-driven design. Key services are actually classes that can be loaded + at runtime, making it easy for users to add or subtract features.
      • No static variables (except read only variables) to make it easy to simultaneously support multiple weather stations and to allow some level of multithreading.
      • Pure Python. The code base is 100% Python — no underlying C libraries need - be built to install weewx. This also means no - Makefiles are needed.
      • + be built to install weewx. This also means no Makefiles + are needed.

      While weewx is nowhere near as fast at generating images and HTML as its predecessor, wview (this is partially because it uses fancier fonts and a much more powerful templating engine), it is 'fast enough' for all platforms but the slowest. I run it regularly on a 500 MHz machine where generating the 9 images used in the "Current Conditions" page takes -just under 2 seconds (compared with wview's 0.4 seconds).

      +just under 2 seconds (compared with wview's 0.4 seconds). +

      Unfortunately, the architectural goal of one code base is likely to be broken with the arrival of Python V3.X. It has so many changes that are not backwards compatible with V2.X, that a separate code base will most likely be needed. My intention is to stick with the V2.5 and V2.6 versions until V3.X is so widespread it cannot be ignored, then make a permanent switch. I doubt this will affect the average weewx user.

      -

      12.3 Run time internals

      -

      At a high-level, weewx consists of an engine class called -StdEngine. It is -responsible for loading any "services" that are to be run and arranging for them -to be called when key events occur, such as the arrival of LOOP data. The default install of weewx includes the following services:

      - - - - - - - - - - - - - - - - - - - - - - - - - -
      ServiceFunction
      weewx.wxengine.StdWundergroundStarts thread to manage WU connection; adds new data to a Queue to - be posted to the WU by the thread.
      weewx.wxengine.StdCatchUpAny data found on the weather station memory but not yet in the - archive, is retrieved and put in the archive.
      weewx.wxengine.StdTimeSynchArranges to have the clock on the station synchronized at regular - intervals.
      weewx.wxengine.StdPrintPrints out new LOOP and archive packets on the console.
      weewx.wxengine.StdProcessLaunches a new thread to do processing after a new archive record - arrives. The thread loads zero or more reports and processes them in - order. Reports do things such as generate HTML files, generate images, - or FTP files to a web server. New reports can be added easily by the - user.
      - -

      It is easy to add new services. The source distribution includes an example -new service called "MyAlarm," which sends an email -when an arbitrary expression evaluates True.

      All writes to the databases are protected by transactions. You can kill the program at any time (either Control-C if run from the command line or "/etc/init.d/weewx stop" if a daemon) without fear of corrupting the databases.

      @@ -2040,10 +897,10 @@ seconds then restarts the program from the top.

      Any "hard" exceptions, that is those that do not involve network and console timeouts and are most likely due to a logic error, are logged, reraised, and ultimately cause thread termination. If this happens in the main thread (not likely and hasn't -happened to me yet), then this causes program termination. Otherwise, the -program will keep chugging along, storing data, allowing you to fix the problem -at your leisure, without losing any data.

      -

      12.4 Terminology

      +happened to me yet), then this causes program termination. Otherwise, the program +will keep chugging along, storing data, allowing you to fix the problem at your +leisure, without losing any data.

      +

      10.3 Terminology

      This is a glossary of terminology used throughout the code.

      @@ -2101,8 +958,8 @@ at your leisure, without losing any data.

      - @@ -2112,37 +969,35 @@ at your leisure, without losing any data.

      or vecavg).
      SQL typeA type that appears in the SQL database. This usually looks - something like 'outTemp', 'barometer', + A type that appears in the SQL database. This usually looks something + like 'outTemp', 'barometer', 'extraTemp1', and so on.
      -

      12.5 Units

      -

      In general, there are three different areas where the unit system makes a -difference.: +

      10.4 Units

      +

      In general, there are three different areas where the unit system makes a difference.:

        -
      1. On the weather station. As far as I know, the Davis VantagePro series - supports only U.S. Customary units internally.
      2. -
      3. In the database. The unit system of any individual record is indicated - by the "usUnits" field. The numerical value 1 - indicates U.S. Customary, 2 indicates Metric. -
      4. +
      5. On the weather station. As far as I know, the Davis VantagePro series supports + only U.S. Customary units internally.
      6. +
      7. In the database. The unit system of any individual record is indicated by + the "usUnits" field. The numerical value 1 indicates + U.S. Customary, 2 indicates Metric.
      8. In the presentation (i.e., html and image files).

      The general strategy is that measurements are stored internally in the native measurement system of the weather instrument. That is, the units used in #1 and #2 are always the same. With Version 1.5, weewx supports translations between #2 and #3, allowing any unit system to be used in the presentation layer.

      -

      It would be easy enough to support an instrument that uses metric units -internally, although this has not been done yet.

      -

      12.6 Value "None"

      +

      It would be easy enough to support an instrument that uses metric units internally, +although this has not been done yet.

      +

      10.5 Value "None"

      The Python special value 'None' is used throughout to signal a missing data point. All functions expect it.

      However, the time value must never be 'None'. This is because it is used as the primary key in the SQL database.

      -

      12.7 Time

      +

      10.6 Time

      Weewx stores all data in UTC (roughly, "Greenwich" or "Zulu") time. However, usually one is interested in weather events in local time and want image and HTML generation to reflect that. Furthermore, most weather stations are configured in local time. This requires that many data times be converted back and forth between UTC and local time. To avoid tripping up over time zones and daylight -savings time, weeewx generally uses Python routines to +savings time, weewx generally uses Python routines to do this conversion. Nowhere in the code base is there any explicit recognition of DST. Instead, its presence is implicit in the conversions. At times, this can cause the code to be relatively inefficient.

      @@ -2162,10 +1017,6 @@ bulletproof against changes in DST algorithms, etc:

      next_dt = time_dt + delta

      next_ts = int(time.mktime(next_dt.timetuple()))

      Other time conversion problems are handled in a similar manner.

      -

      13. Advanced topic: customizing the internal -weewx engines

      -

      See file customizing.htm for details on how to -customize weewx.

      diff --git a/docs/upgrading.htm b/docs/upgrading.htm index 97986d65..a80b3f97 100644 --- a/docs/upgrading.htm +++ b/docs/upgrading.htm @@ -119,23 +119,25 @@ td { -

      Upgrading to weewx v1.5

      +

      Upgrading to weewx v1.6

      Version specific directions

      What follows are directions for upgrading from specific versions.

      V.1.5.0 or earlier

      The part of the configuration file dealing with the presentation layer has been split off into a separate file skin.conf. Hence, once again, the installation script -setup.py will NOT merge your old configuration file +setup.py will NOT merge your old +weewx.conf configuration file into the new one. You will have to re-edit weewx.conf to put in your customizations. You may also have to edit -skin.conf for whatever skin you choose (right now, only skin, Standard +skin.conf for whatever skin you choose (right now, only one skin, Standard, comes with the distribution).

      -

      The URL for the radar image is now in the template. If you don't live in the -Pacific Northwest, you will probably want to change it! You can find it in -$WEEWX_ROOT/skins/Standard/index.html.tmpl -and searching for "radar."

      -

      The directory 'templates' is no longer used, so you +

      However, a reinstall of V1.6 will merge your changes for +weewx.conf.

      +

      It will also merge any changes you have made to skin.conf +as well.

      +

      The directory 'templates' is no longer used; +it has been replaced with directory 'skins'. You may delete it if you wish:

      rm -r $WEEWX_ROOT/templates

      V1.4.0 or earlier

      diff --git a/docs/weekgustoverlay.png b/docs/weekgustoverlay.png new file mode 100644 index 0000000000000000000000000000000000000000..cb9a652782b2be0f5e0e86ce0da0ef548ab56f86 GIT binary patch literal 10649 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 U-F~LWsNANmtgTe5U>Whh0B?g&>i_@% literal 0 HcmV?d00001 diff --git a/examples/alarm.py b/examples/alarm.py index ed66acc9..b9a8a753 100644 --- a/examples/alarm.py +++ b/examples/alarm.py @@ -9,38 +9,44 @@ # """Example of how to implement an alarm in weewx. -***************************************************************************** +******************************************************************************** -To use this alarm, add the following somewhere in your -configuration file weewx.conf: +To use this alarm, add the following somewhere in your configuration file +weewx.conf: [Alarm] expression = "outTemp < 40.0" - time_wait = 1800 + time_wait = 3600 smtp_host = smtp.mymailserver.com smtp_user = myusername smtp_password = mypassword mailto = auser@adomain.com -In this example, if the outside temperature falls below 40, it -will send an email to the recipient auser@adomain.com. +In this example, if the outside temperature falls below 40, it will send an +email to the recipient auser@adomain.com. The example assumes that your SMTP email server is at smtp.mymailserver.com and -that it uses secure logins. If it does not use secure logins, leave out the lines -for smtp_user and smtp_password and no login will be attempted. +that it uses secure logins. If it does not use secure logins, leave out the +lines for smtp_user and smtp_password and no login will be attempted. -To avoid a flood of emails, one will only be sent every 1800 seconds (half hour). +To avoid a flood of emails, one will only be sent every 3600 seconds (one hour). -***************************************************************************** +******************************************************************************** To specify that this new service be loaded and run, it must be added to the configuration option service_list, located in sub-section [Engines][[WxEngine]]: [Engines] [[WxEngine]] - service_list = weewx.wxengine.StdWunderground, weewx.wxengine.StdCatchUp, weewx.wxengine.StdTimeSynch, weewx.wxengine.StdPrint, weewx.wxengine.StdProcess, examples.alarm.MyAlarm + service_list = weewx.wxengine.StdWunderground, weewx.wxengine.StdCatchUp, weewx.wxengine.StdTimeSynch, weewx.wxengine.StdPrint, weewx.wxengine.StdReportService, examples.alarm.MyAlarm -***************************************************************************** +******************************************************************************** + +If you wish to use both this example and the lowBattery.py example, simply merge +the two configuration options together under [Alarm] and add both services to +the service_list. + +******************************************************************************** """ import time @@ -54,15 +60,15 @@ from weeutil.weeutil import timestamp_to_string # Inherit from the base class StdService: class MyAlarm(StdService): - """Custom service that sounds an alarm if an expression evaluates true""" + """Custom service that sounds an alarm if an arbitrary expression evaluates true""" def __init__(self, engine): # Pass the initialization information on to my superclass: StdService.__init__(self, engine) # This will hold the time when the last alarm message went out: - self.last_msg = None - self.expression = None + self.last_msg_ts = None + self.expression = None def setup(self): @@ -71,7 +77,7 @@ class MyAlarm(StdService): # If a critical option is missing, an exception will be thrown and # the alarm will not be set. self.expression = self.engine.config_dict['Alarm']['expression'] - self.time_wait = int(self.engine.config_dict['Alarm'].get('time_wait', '3600')) + self.time_wait = int(self.engine.config_dict['Alarm'].get('time_wait', 3600)) self.smtp_host = self.engine.config_dict['Alarm']['smtp_host'] self.smtp_user = self.engine.config_dict['Alarm'].get('smtp_user') self.smtp_password = self.engine.config_dict['Alarm'].get('smtp_password') @@ -90,15 +96,17 @@ class MyAlarm(StdService): # To avoid a flood of nearly identical emails, this will do # the check only if we have never sent an email, or if we haven't # sent one in the last self.time_wait seconds: - if not self.last_msg or abs(time.time() - self.last_msg) >= self.time_wait : + if not self.last_msg_ts or abs(time.time() - self.last_msg_ts) >= self.time_wait : # Evaluate the expression in the context of 'rec'. # Sound the alarm if it evaluates true: - if eval(self.expression, None, rec): + if eval(self.expression, None, rec): # NOTE 1 # Sound the alarm! # Launch in a separate thread so it doesn't block the main LOOP thread: t = threading.Thread(target = MyAlarm.soundTheAlarm, args=(self, rec)) t.start() + # Record when the message went out: + self.last_msg_ts = time.time() def soundTheAlarm(self, rec): """This function is called when the given expression evaluates True.""" @@ -133,8 +141,6 @@ class MyAlarm(StdService): s.sendmail(msg['From'], [self.TO], msg.as_string()) # Log out of the server: s.quit() - # Record when the message went out: - self.last_msg = time.time() # Log it in the system log: syslog.syslog(syslog.LOG_INFO, "alarm: Alarm sounded for expression: \"%s\"" % self.expression) syslog.syslog(syslog.LOG_INFO, " *** email sent to: %s" % self.TO) diff --git a/examples/daily.py b/examples/daily.py index bf29168f..9777cb92 100644 --- a/examples/daily.py +++ b/examples/daily.py @@ -39,7 +39,7 @@ class MyEngine(StdEngine): # the first archive since startup, or if a new day has started if not self.old_day or self.old_day != dayStart_ts: self.old_day = dayStart_ts - self.newDay(rec) + self.newDay(rec) # NOTE 1 def newDay(self, rec): """Called when the first archive record of a day arrives.""" @@ -48,12 +48,12 @@ class MyEngine(StdEngine): # list is actually in my superclass StdEngine. for svc_obj in self.service_obj: # Because this is a new event, not all services will - # be prepared to accept it. Check first to see if the - # service has a member function "firstArchiveOfDay" - # before calling it: - if hasattr(svc_obj, "firstArchiveOfDay"): - # The object does have the member function. Call it: + # be prepared to accept it. Be prepared for an AttributeError + # exception: + try: # NOTE 2 svc_obj.firstArchiveOfDay(rec) + except AttributeError: + pass # Define a new service to take advantage of the new event class DailyService(StdService): diff --git a/examples/lowBattery.py b/examples/lowBattery.py new file mode 100644 index 00000000..f5aab480 --- /dev/null +++ b/examples/lowBattery.py @@ -0,0 +1,164 @@ +# +# Copyright (c) 2009, 2010 Tom Keffer +# +# See the file LICENSE.txt for your full rights. +# +# $Revision$ +# $Author$ +# $Date$ +# +"""Example of how to implement a low battery alarm in weewx. + +******************************************************************************** + +To use this alarm, add the following somewhere in your configuration file +weewx.conf: + +[Alarm] + time_wait = 3600 + count_threshold = 50 + smtp_host = smtp.mymailserver.com + smtp_user = myusername + smtp_password = mypassword + mailto = auser@adomain.com + +The example assumes that your SMTP email server is at smtp.mymailserver.com and +that it uses secure logins. If it does not use secure logins, leave out the +lines for smtp_user and smtp_password and no login will be attempted. + +To avoid a flood of emails, one will only be sent every 3600 seconds (one hour). + +It will also not send an email unless the low battery indicator has been on +greater than or equal to count_threshold times in an archive period. This avoids +sending out an alarm if the battery is only occasionally being signaled as bad. + +******************************************************************************** + +To specify that this new service be loaded and run, it must be added to the +configuration option service_list, located in sub-section [Engines][[WxEngine]]: + +[Engines] + [[WxEngine]] + service_list = weewx.wxengine.StdWunderground, weewx.wxengine.StdCatchUp, weewx.wxengine.StdTimeSynch, weewx.wxengine.StdPrint, weewx.wxengine.StdReportService, examples.lowBattery.BatteryAlarm + +******************************************************************************** + +If you wish to use both this example and the alarm.py example, simply merge the +two configuration options together under [Alarm] and add both services to the +service_list. + +***************************************************************************** +""" + +import time +import smtplib +from email.mime.text import MIMEText +import threading +import syslog + +from weewx.wxengine import StdService +from weeutil.weeutil import timestamp_to_string + +# Inherit from the base class StdService: +class BatteryAlarm(StdService): + """Custom service that sounds an alarm if one of the batteries is low""" + + def __init__(self, engine): + # Pass the initialization information on to my superclass: + StdService.__init__(self, engine) + + # This will hold the time when the last alarm message went out: + self.last_msg_ts = 0 + # This will hold the count of the number of times the VP2 has signaled + # a low battery alarm this archive period + self.alarm_count = 0 + + def setup(self): + + try: + # Dig the needed options out of the configuration dictionary. + # If a critical option is missing, an exception will be thrown and + # the alarm will not be set. + self.time_wait = int(self.engine.config_dict['Alarm'].get('time_wait', '3600')) + self.count_threshold = int(self.engine.config_dict['Alarm'].get('count_threshold', 50)) + self.smtp_host = self.engine.config_dict['Alarm']['smtp_host'] + self.smtp_user = self.engine.config_dict['Alarm'].get('smtp_user') + self.smtp_password = self.engine.config_dict['Alarm'].get('smtp_password') + self.TO = self.engine.config_dict['Alarm']['mailto'] + syslog.syslog(syslog.LOG_INFO, "lowBattery: LowBattery alarm turned on. Count threshold is %d" % self.count_threshold) + except: + self.time_wait = None + + def processLoopPacket(self, loopPacket): + """This function is called with the arrival of every LOOP packet.""" + + # Let the superclass have a peek first: + StdService.processLoopPacket(self, loopPacket) + + # If the Transmit Battery Status byte is non-zero, an alarm is on + if self.time_wait is not None and loopPacket['transmitBattery']: + + self.alarm_count += 1 + + # Don't panic on the first occurrence. We must see the alarm at + # least count_threshold times before sounding the alarm. + if self.alarm_count >= self.count_threshold: + # We've hit the threshold. However, to avoid a flood of nearly + # identical emails, send a new one only if it's been a long time + # since we sent the last one: + if abs(time.time() - self.last_msg_ts) >= self.time_wait : + # Sound the alarm! + # Launch in a separate thread so it doesn't block the main LOOP thread: + t = threading.Thread(target = BatteryAlarm.soundTheAlarm, args=(self, loopPacket, self.alarm_count)) + t.start() + # Record when the message went out: + self.last_msg_ts = time.time() + + def postArchiveData(self, rec): + """This function is called when it's time to post some archive data.""" + + # Let the superclass do its thing first: + StdService.postArchiveData(self, rec) + + # Reset the alarm counter + self.alarm_count = 0 + + def soundTheAlarm(self, rec, alarm_count): + """This function is called when the low battery alarm has been sounded.""" + + # Get the time and convert to a string: + t_str = timestamp_to_string(rec['dateTime']) + # Form the message text: + msg_text = """The low battery alarm (0x%04x) has been seen %d times since the last archive period.\n\n"""\ + """Alarm sounded at %s\n\n"""\ + """LOOP record:\n%s""" % (rec['transmitBattery'], alarm_count, t_str, str(rec)) + # Convert to MIME: + msg = MIMEText(msg_text) + + # Fill in MIME headers: + msg['Subject'] = "Low battery alarm message from weewx" + msg['From'] = "weewx" + msg['To'] = self.TO + + # Create an instance of class SMTP for the given SMTP host: + s = smtplib.SMTP(self.smtp_host) + try: + # Some servers (eg, gmail) require encrypted transport. + # Be prepared to catch an exception if the server + # doesn't support it. + s.ehlo() + s.starttls() + s.ehlo() + except smtplib.SMTPException: + pass + # If a username has been given, assume that login is required for this host: + if self.smtp_user: + s.login(self.smtp_user, self.smtp_password) + # Send the email: + s.sendmail(msg['From'], [self.TO], msg.as_string()) + # Log out of the server: + s.quit() + # Log it in the system log: + syslog.syslog(syslog.LOG_INFO, "lowBattery: Low battery alarm (0x%04x) sounded." % rec['transmitBattery']) + syslog.syslog(syslog.LOG_INFO, " *** email sent to: %s" % self.TO) + diff --git a/examples/mygenerator.py b/examples/mygenerator.py new file mode 100644 index 00000000..6f258011 --- /dev/null +++ b/examples/mygenerator.py @@ -0,0 +1,45 @@ +# +# Copyright (c) 2009, 2010 Tom Keffer +# +# See the file LICENSE.txt for your full rights. +# +# $Revision$ +# $Author$ +# $Date$ +# + +"""Example of how to extend a generator. + +This generator offers tag 'alltime' that makes all-time statistics available, +such as the all-time max temperature. + +To use it, in the skin's generator_list, replace weewx.filegenerator.FileGenerator +with examples.mygenerator.MyFileGenerator. + +You can then use tags such as $alltime.outTemp.max for the all-time max temperature. +""" + +from weewx.filegenerator import FileGenerator +from weewx.stats import TimeSpanStats +from weeutil.weeutil import TimeSpan + +class MyFileGenerator(FileGenerator): # 1 + + def getToDateSearchList(self, currentRec, stop_ts): # 2 + + # Get a TimeSpan object that represents all time up to the stop time: + all_time = TimeSpan(self.start_ts, stop_ts) # 3 + + # Get a TimeSpanStats object : + all_stats = TimeSpanStats(self.statsdb, + all_time, + self.unitTypeDict) # 4 + + # Get the superclass's search list: + search_list = FileGenerator.getToDateSearchList(self, currentRec, stop_ts) #5 + + # Now tack on my addition as a small dictionary with key 'alltime': + search_list += [ {'alltime' : all_stats} ] # 6 + + return search_list + diff --git a/setup.py b/setup.py index 7d8563e8..64d85bfe 100755 --- a/setup.py +++ b/setup.py @@ -32,15 +32,23 @@ 2. It merges any existing weewx.conf configuration files into the new, thus preserving any user changes. - 3. It sets the option ['Station']['WEEWX_ROOT'] in weewx.conf to reflect + 3. It merges any existing Standard/skin.conf configuration file into the new, thus + preserving any user changes. + + 4. It sets the option ['Station']['WEEWX_ROOT'] in weewx.conf to reflect the actual installation directory (as set in setup.cfg or specified in the command line to setup.py install) + + 5. In a similar manner, it sets WEEWX_ROOT in the daemon startup script. - 4. It backups up any pre-existing skin subdirectory + 6. It backs up any pre-existing skin subdirectory + + 7. It backs up any pre-existing bin subdirectory. """ -from distutils.core import setup, Command +from distutils.core import setup from distutils.command.install_data import install_data +from distutils.command.install_lib import install_lib from distutils.command.sdist import sdist import sys @@ -54,6 +62,22 @@ import configobj from weewx import __version__ as VERSION +class My_install_lib(install_lib): + """Specialized version of install_lib + + This version: + + - Backs up the old ./bin subdirectory before installing. + """ + + def run(self): + if os.path.exists(self.install_dir): + bin_backupdir = backup(self.install_dir) + print "Backed up bin subdirectory to %s" % bin_backupdir + + # Run the superclass's run: + install_lib.run(self) + class My_install_data(install_data): """Specialized version of install_data @@ -61,8 +85,11 @@ class My_install_data(install_data): - Sets WEEWX_ROOT in the configuration file to reflect the actual installation root directory; - - Merges an old configuration file into a new, + - Merges an old week.conf configuration file into a new, thus preserving any changes made by the user; + - Merges an old skin.conf file into a new, thus preserving + any changes; + - Backs up the old skin directory; - Massages the daemon start up script to reflect the choice of WEEWX_ROOT """ @@ -72,7 +99,9 @@ class My_install_data(install_data): # If this is the configuration file, then merge it instead # of copying it if f == 'weewx.conf': - rv = self.massageConfigFile(f, install_dir, **kwargs) + rv = self.massageWeewxConfigFile(f, install_dir, **kwargs) + elif f == 'skins/Standard/skin.conf': + rv = self.massageSkinConfigFile(f, install_dir, **kwargs) elif f in ('start_scripts/Debian/weewx', 'start_scripts/SuSE/weewx'): rv = self.massageStartFile(f, install_dir, **kwargs) else: @@ -80,75 +109,131 @@ class My_install_data(install_data): return rv def run(self): + # Back up the old skin directory if it exists skin_dir = os.path.join(self.install_dir, 'skins') if os.path.exists(skin_dir): - backupdir = backup(skin_dir) - print "Backed up skins subdirectory to %s" % backupdir + self.skin_backupdir = backup(skin_dir) + print "Backed up skins subdirectory to %s" % self.skin_backupdir + + # If the file #upstream.last exists, delete it, as it is no longer used. + try: + os.remove(os.path.join(self.install_dir, 'public_html/#upstream.last')) + except: + pass - # Run the superclass's run(): - install_data.run(self) - # If the file $WEEWX_ROOT/readme.htm exists, delete it. It's # the old readme (since replaced with docs/readme.htm) try: - os.remove('readme.htm') + os.remove(os.path.join(self.install_dir, 'readme.htm')) + except: + pass + + # Clean up after a bad install from earlier versions of setup.py: + try: + os.remove(os.path.join(self.install_dir, 'start_scripts/weewx')) except: pass - def massageConfigFile(self, f, install_dir, **kwargs): + # Run the superclass's run(): + install_data.run(self) + + def massageWeewxConfigFile(self, f, install_dir, **kwargs): """Merges any old config file into the new one, and sets WEEWX_ROOT If an old configuration file exists, it will merge the contents into the new file. It also sets variable ['Station']['WEEWX_ROOT'] to reflect the installation directory""" - # The path name of the final output file: - outname = os.path.join(install_dir, os.path.basename(f)) + # The path name of the weewx.conf configuration file: + config_path = os.path.join(install_dir, os.path.basename(f)) # Create a ConfigObj using the new contents: - newconfig = configobj.ConfigObj(f) - newconfig.indent_type = ' ' + new_config = configobj.ConfigObj(f) + new_config.indent_type = ' ' new_version_number = VERSION.split('.') # Check to see if there is an existing config file. # If so, merge its contents with the new one - if os.path.exists(outname): - oldconfig = configobj.ConfigObj(outname) - old_version = oldconfig.get('version') + if os.path.exists(config_path): + old_config = configobj.ConfigObj(config_path) + old_version = old_config.get('version') # If the version number does not appear at all, then # assume a very old version: if not old_version: old_version = '1.0.0' old_version_number = old_version.split('.') # Do the merge only for versions >= 1.6 if old_version_number[0:2] >= ['1','6']: - # Any user changes in oldconfig will overwrite values in newconfig + # Any user changes in old_config will overwrite values in new_config # with this merge - newconfig.merge(oldconfig) + new_config.merge(old_config) # Make sure WEEWX_ROOT reflects the choice made in setup.cfg: - newconfig['Station']['WEEWX_ROOT'] = self.install_dir + new_config['Station']['WEEWX_ROOT'] = self.install_dir # Add the version: - newconfig['version'] = VERSION + new_config['version'] = VERSION # Get a temporary file: tmpfile = tempfile.NamedTemporaryFile("w", 1) # Write the new configuration file to it: - newconfig.write(tmpfile) + new_config.write(tmpfile) # Back up the old config file if it exists: - if os.path.exists(outname): - backup_path = backup(outname) + if os.path.exists(config_path): + backup_path = backup(config_path) print "Backed up old configuration file as %s" % backup_path # Now install the temporary file (holding the merged config data) # into the proper place: - rv = install_data.copy_file(self, tmpfile.name, outname, **kwargs) + rv = install_data.copy_file(self, tmpfile.name, config_path, **kwargs) # Set the permission bits unless this is a dry run: if not self.dry_run: - shutil.copymode(f, outname) + shutil.copymode(f, config_path) + + return rv + + def massageSkinConfigFile(self, f, install_dir, **kwargs): + """Merges an old skin.conf file into the new one""" + + # The path name of the final output file: + new_skin_config_path = os.path.join(install_dir, os.path.basename(f)) + + # Create a ConfigObj using the new contents: + new_skin_config = configobj.ConfigObj(f) + new_skin_config.indent_type = ' ' + + # If the backed up skin directory doesn't exist, we'll get + # an attribute error. Skip the merge in this case. + try: + old_skin_config_path = os.path.join(self.skin_backupdir, 'Standard/skin.conf') + # Check to see if there is an existing skin.conf file. + # If so, merge its contents with the new one + if os.path.exists(old_skin_config_path): + old_skin_config = configobj.ConfigObj(old_skin_config_path) + # Any user changes in the old skin.conf will overwrite + # values in the new skin.conf file with this merge + new_skin_config.merge(old_skin_config) + except AttributeError: + pass + + # Add the version: + new_skin_config['version'] = VERSION + + # Get a temporary file: + tmpfile = tempfile.NamedTemporaryFile("w", 1) + + # Write the new configuration file to it: + new_skin_config.write(tmpfile) + + # Now install the temporary file (holding the merged config data) + # into the proper place: + rv = install_data.copy_file(self, tmpfile.name, new_skin_config_path, **kwargs) + + # Set the permission bits unless this is a dry run: + if not self.dry_run: + shutil.copymode(f, new_skin_config_path) return rv @@ -175,6 +260,8 @@ class My_install_data(install_data): return rv def backup(filepath): + # Sometimes the target has a trailing '/'. This will take care of it: + filepath = os.path.normpath(filepath) newpath = filepath + time.strftime(".%Y%m%d%H%M%S") os.rename(filepath, newpath) return newpath @@ -198,13 +285,15 @@ class My_sdist(sdist): # have any private data in it. config = configobj.ConfigObj(f) - if config.has_key('FTP') and config['FTP'].has_key('password'): + if config.has_key('Reports') and config['Reports'].has_key('FTP') and config['Reports']['FTP'].has_key('password'): sys.stderr.write("\n*** FTP password found in configuration file. Aborting ***\n\n") exit() if config.has_key('Wunderground') and config['Wunderground'].has_key('password'): sys.stderr.write("\n*** Wunderground password found in configuration file. Aborting ***\n\n") exit() + + # Pass on to my superclass: return sdist.copy_file(self, f, install_dir, **kwargs) setup(name='weewx', @@ -217,19 +306,23 @@ setup(name='weewx', author_email='tkeffer@gmail.com', url='http://www.weewx.com', packages = ['weewx', 'weeplot', 'weeutil', 'examples'], - py_modules = ['upload', 'daemon'], + py_modules = ['daemon'], scripts = ['configure.py', 'weewxd.py'], data_files = [('', ['CHANGES.txt', 'LICENSE.txt', 'README', 'weewx.conf']), ('docs', ['docs/customizing.htm', 'docs/readme.htm', - 'docs/sheeva.htm', 'docs/upgrading.htm']), + 'docs/sheeva.htm', 'docs/upgrading.htm', + 'docs/daytemp_with_avg.png', 'docs/weekgustoverlay.png']), ('skins/Ftp', ['skins/Ftp/skin.conf']), ('skins/Standard/backgrounds', ['skins/Standard/backgrounds/band.gif']), ('skins/Standard/NOAA', ['skins/Standard/NOAA/NOAA-YYYY.txt.tmpl', 'skins/Standard/NOAA/NOAA-YYYY-MM.txt.tmpl']), + ('skins/Standard/RSS', ['skins/Standard/RSS/weewx_rss.xml.tmpl']), ('skins/Standard', ['skins/Standard/index.html.tmpl', 'skins/Standard/month.html.tmpl', 'skins/Standard/skin.conf', 'skins/Standard/week.html.tmpl', 'skins/Standard/weewx.css', 'skins/Standard/year.html.tmpl']), - ('start_scripts', ['start_scripts/Debian/weewx', 'start_scripts/SuSE/weewx'])], - requires = ['configobj(>=4.6)', 'pyserial(>=1.35)', 'Cheetah(>=2.0)', 'pysqlite(>=2.5)', 'PIL(>=1.1.6)'], + ('start_scripts/Debian', ['start_scripts/Debian/weewx']), + ('start_scripts/SuSE', ['start_scripts/SuSE/weewx'])], + requires = ['configobj(>=4.5)', 'pyserial(>=1.35)', 'Cheetah(>=2.0)', 'pysqlite(>=2.5)', 'PIL(>=1.1.6)'], cmdclass = {"install_data" : My_install_data, + "install_lib" : My_install_lib, "sdist" : My_sdist} ) diff --git a/skins/Ftp/skin.conf b/skins/Ftp/skin.conf index 3ba74474..184bbd3d 100644 --- a/skins/Ftp/skin.conf +++ b/skins/Ftp/skin.conf @@ -21,5 +21,5 @@ # This isn't really a "report". Instead, we use the report engine to run an FTP service. # The list of generators that are part of this report: -generator_list = weewx.reportengine.Ftp, +generator_list = weewx.reportengine.FtpGenerator diff --git a/skins/Standard/NOAA/NOAA-YYYY-MM.txt.tmpl b/skins/Standard/NOAA/NOAA-YYYY-MM.txt.tmpl index 766bc48c..bf2eaf4b 100644 --- a/skins/Standard/NOAA/NOAA-YYYY-MM.txt.tmpl +++ b/skins/Standard/NOAA/NOAA-YYYY-MM.txt.tmpl @@ -28,18 +28,19 @@ NAME: $station.location ELEV: $station.altitude LAT: $station.latitude[0]-$station.latitude[1] $station.latitude[2] LONG: $station.longitude[0]-$station.longitude[1] $station.longitude[2] - TEMPERATURE ($station.temperature_unit_label), RAIN ($station.rain_unit_label), WIND SPEED ($station.wind_unit_label) + TEMPERATURE ($unit_label.outTemp.strip()), RAIN ($unit_label.rain.strip()), WIND SPEED ($unit_label.windSpeed.strip()) HEAT COOL AVG MEAN DEG DEG WIND DOM DAY TEMP HIGH TIME LOW TIME DAYS DAYS RAIN SPEED HIGH TIME DIR --------------------------------------------------------------------------------------- #for $day in $month.days -#if $day.barometer.count -$showDay($day.dateTime) $showTemp($day.outTemp.avg) $showTemp($day.outTemp.max) $showTime($day.outTemp.maxtime) $showTemp($day.outTemp.min) $showTime($day.outTemp.mintime) $showTemp($day.heatdeg.sum) $showTemp($day.cooldeg.sum) $showRain($day.rain.sum) $showWind($day.wind.avg) $showWind($day.wind.max) $showTime($day.wind.maxtime) $showDir($day.wind.vecdir) +#if $day.barometer.count.raw +$showDay($day.dateTime.raw) $showTemp($day.outTemp.avg.raw) $showTemp($day.outTemp.max.raw) $showTime($day.outTemp.maxtime.raw) $showTemp($day.outTemp.min.raw) $showTime($day.outTemp.mintime.raw) $showTemp($day.heatdeg.sum.raw) $showTemp($day.cooldeg.sum.raw) $showRain($day.rain.sum.raw) $showWind($day.wind.avg.raw) $showWind($day.wind.max.raw) $showTime($day.wind.maxtime.raw) $showDir($day.wind.vecdir.raw) #else -$showDay($day.dateTime) +$showDay($day.dateTime.raw) #end if #end for --------------------------------------------------------------------------------------- - $showTemp($month.outTemp.avg) $showTemp($month.outTemp.max) $showDay($month.outTemp.maxtime) $showTemp($month.outTemp.min) $showDay($month.outTemp.mintime) $showTemp($month.heatdeg.sum) $showTemp($month.cooldeg.sum) $showRain($month.rain.sum) $showWind($month.wind.avg) $showWind($month.wind.max) $showDay($month.wind.maxtime) $showDir($month.wind.vecdir) \ No newline at end of file + $showTemp($month.outTemp.avg.raw) $showTemp($month.outTemp.max.raw) $showDay($month.outTemp.maxtime.raw) $showTemp($month.outTemp.min.raw) $showDay($month.outTemp.mintime.raw) $showTemp($month.heatdeg.sum.raw) $showTemp($month.cooldeg.sum.raw) $showRain($month.rain.sum.raw) $showWind($month.wind.avg.raw) $showWind($month.wind.max.raw) $showDay($month.wind.maxtime.raw) $showDir($month.wind.vecdir.raw) + \ No newline at end of file diff --git a/skins/Standard/NOAA/NOAA-YYYY.txt.tmpl b/skins/Standard/NOAA/NOAA-YYYY.txt.tmpl index e29daf4d..1062bb0a 100644 --- a/skins/Standard/NOAA/NOAA-YYYY.txt.tmpl +++ b/skins/Standard/NOAA/NOAA-YYYY.txt.tmpl @@ -34,51 +34,51 @@ NAME: $station.location ELEV: $station.altitude LAT: $station.latitude[0]-$station.latitude[1] $station.latitude[2] LONG: $station.longitude[0]-$station.longitude[1] $station.longitude[2] - TEMPERATURE ($station.temperature_unit_label) + TEMPERATURE ($unit_label.outTemp.strip()) HEAT COOL MEAN MEAN DEG DEG MAX MAX MIN MIN YR MO MAX MIN MEAN DAYS DAYS HI DAY LOW DAY >=90 <=32 <=32 <=0 ------------------------------------------------------------------------------------------------ #for $month in $year.months -#if $month.barometer.count -$showYM($month.dateTime) $showTemp($month.outTemp.meanmax) $showTemp($month.outTemp.meanmin) $showTemp($month.outTemp.avg) $showTemp($month.heatdeg.sum) $showTemp($month.cooldeg.sum) $showTemp($month.outTemp.max) $showDay($month.outTemp.maxtime) $showTemp($month.outTemp.min) $showDay($month.outTemp.mintime) $showCount($month.outTemp.max_ge(90.0)) $showCount($month.outTemp.max_le(32.0)) $showCount($month.outTemp.min_le(32.0)) $showCount($month.outTemp.min_le(0.0)) +#if $month.barometer.count.raw +$showYM($month.dateTime.raw) $showTemp($month.outTemp.meanmax.raw) $showTemp($month.outTemp.meanmin.raw) $showTemp($month.outTemp.avg.raw) $showTemp($month.heatdeg.sum.raw) $showTemp($month.cooldeg.sum.raw) $showTemp($month.outTemp.max.raw) $showDay($month.outTemp.maxtime.raw) $showTemp($month.outTemp.min.raw) $showDay($month.outTemp.mintime.raw) $showCount($month.outTemp.max_ge(90.0).raw) $showCount($month.outTemp.max_le(32.0).raw) $showCount($month.outTemp.min_le(32.0).raw) $showCount($month.outTemp.min_le(0.0).raw) #else -$showYM($month.dateTime) +$showYM($month.dateTime.raw) #end if #end for ------------------------------------------------------------------------------------------------ - $showTemp($year.outTemp.meanmax) $showTemp($year.outTemp.meanmin) $showTemp($year.outTemp.avg) $showTemp($year.heatdeg.sum) $showTemp($year.cooldeg.sum) $showTemp($year.outTemp.max) $showDay($year.outTemp.maxtime) $showTemp($year.outTemp.min) $showDay($year.outTemp.mintime) $showCount($year.outTemp.max_ge(90.0)) $showCount($year.outTemp.max_le(32.0)) $showCount($year.outTemp.min_le(32.0)) $showCount($year.outTemp.min_le(0.0)) + $showTemp($year.outTemp.meanmax.raw) $showTemp($year.outTemp.meanmin.raw) $showTemp($year.outTemp.avg.raw) $showTemp($year.heatdeg.sum.raw) $showTemp($year.cooldeg.sum.raw) $showTemp($year.outTemp.max.raw) $showDay($year.outTemp.maxtime.raw) $showTemp($year.outTemp.min.raw) $showDay($year.outTemp.mintime.raw) $showCount($year.outTemp.max_ge(90.0).raw) $showCount($year.outTemp.max_le(32.0).raw) $showCount($year.outTemp.min_le(32.0).raw) $showCount($year.outTemp.min_le(0.0).raw) - PRECIPITATION ($station.rain_unit_label) + PRECIPITATION ($unit_label.rain.strip()) MAX ---DAYS OF RAIN--- OBS. OVER YR MO TOTAL DAY DATE 0.01 0.10 1.00 ------------------------------------------------ #for $month in $year.months -#if $month.barometer.count -$showYM($month.dateTime) $showRain($month.rain.sum) $showRain($month.rain.maxsum) $showDay($month.rain.maxsumtime) $showCount($month.rain.sum_ge(0.01)) $showCount($month.rain.sum_ge(0.10)) $showCount($month.rain.sum_ge(1.00)) +#if $month.barometer.count.raw +$showYM($month.dateTime.raw) $showRain($month.rain.sum.raw) $showRain($month.rain.maxsum.raw) $showDay($month.rain.maxsumtime.raw) $showCount($month.rain.sum_ge(0.01).raw) $showCount($month.rain.sum_ge(0.10).raw) $showCount($month.rain.sum_ge(1.00).raw) #else -$showYM($month.dateTime) +$showYM($month.dateTime.raw) #end if #end for ------------------------------------------------ - $showRain($year.rain.sum) $showRain($year.rain.maxsum) $showMonth($year.rain.maxsumtime) $showCount($year.rain.sum_ge(0.01)) $showCount($year.rain.sum_ge(0.10)) $showCount($year.rain.sum_ge(1.00)) + $showRain($year.rain.sum.raw) $showRain($year.rain.maxsum.raw) $showMonth($year.rain.maxsumtime.raw) $showCount($year.rain.sum_ge(0.01).raw) $showCount($year.rain.sum_ge(0.10).raw) $showCount($year.rain.sum_ge(1.00).raw) - WIND SPEED ($station.wind_unit_label) + WIND SPEED ($unit_label.windSpeed.strip()) DOM YR MO AVG HI DATE DIR ----------------------------------- #for $month in $year.months -#if $month.barometer.count -$showYM($month.dateTime) $showWind($month.wind.avg) $showWind($month.wind.max) $showDay($month.wind.maxtime) $showDir($month.wind.vecdir) +#if $month.barometer.count.raw +$showYM($month.dateTime.raw) $showWind($month.wind.avg.raw) $showWind($month.wind.max.raw) $showDay($month.wind.maxtime.raw) $showDir($month.wind.vecdir.raw) #else -$showYM($month.dateTime) +$showYM($month.dateTime.raw) #end if #end for ----------------------------------- - $showWind($year.wind.avg) $showWind($year.wind.max) $showMonth($year.wind.maxtime) $showDir($year.wind.vecdir) \ No newline at end of file + $showWind($year.wind.avg.raw) $showWind($year.wind.max.raw) $showMonth($year.wind.maxtime.raw) $showDir($year.wind.vecdir.raw) diff --git a/skins/Standard/RSS/weewx_rss.xml.tmpl b/skins/Standard/RSS/weewx_rss.xml.tmpl new file mode 100644 index 00000000..98c2b391 --- /dev/null +++ b/skins/Standard/RSS/weewx_rss.xml.tmpl @@ -0,0 +1,99 @@ + +## $Revision$ +## $Author$ +## $Date$ + + + $station.location, Weather Conditions + $station.webpath + Current conditions, and daily, monthly, and yearly summaries + en-us + $current.dateTime.format("%a, %d %b %Y %H:%M:%S %Z") + + $current.dateTime.format("%a, %d %b %Y %H:%M:%S %Z") + http://blogs.law.harvard.edu/tech/rss + weewx $station.version + $current.interval.string('') + + Current Weather Conditions + $station.webpath/index.html + Current weather conditions at $station.location + $current.dateTime.format("%a, %d %b %Y %H:%M:%S %Z") + + Time: $current.dateTime
      + Outside Temperature: $current.outTemp
      + Inside Temperature: $current.inTemp
      + Wind Chill: $current.windchill
      + Heat Index: $current.heatindex
      + Dewpoint: $current.dewpoint
      + Humidity: $current.outHumidity
      + Barometer: $current.barometer
      + Wind: $current.windSpeed from $current.windDir
      + Rain Rate: $current.rainRate
      +

      + ]]>
      +
      + + + Daily Weather Summary + $station.webpath/index.html + Daily summary of weather conditions at $station.location + $current.dateTime.format("%a, %d %b %Y %H:%M:%S %Z") + + Day: $day.dateTime.format("%d %b %Y")
      + Max Outside Temperature: $day.outTemp.max at $day.outTemp.maxtime
      + Min Outside Temperature: $day.outTemp.min at $day.outTemp.mintime
      + Max Inside Temperature: $day.inTemp.max at $day.inTemp.maxtime
      + Min Inside Temperature: $day.inTemp.min at $day.inTemp.mintime
      + Max Barometer: $day.barometer.max at $day.barometer.maxtime
      + Min Barometer: $day.barometer.min at $day.barometer.mintime
      + Max Wind : $day.wind.max from $day.wind.gustdir at $day.wind.maxtime
      + Rain today: $day.rain.sum
      +

      + ]]>
      +
      + + + Monthly Weather Summary + $station.webpath/month.html + Monthly summary of weather conditions at $station.location + $current.dateTime.format("%a, %d %b %Y %H:%M:%S %Z") + + Month: $month.dateTime.format("%B %Y")
      + Max Outside Temperature: $month.outTemp.max at $month.outTemp.maxtime
      + Min Outside Temperature: $month.outTemp.min at $month.outTemp.mintime
      + Max Inside Temperature: $month.inTemp.max at $month.inTemp.maxtime
      + Min Inside Temperature: $month.inTemp.min at $month.inTemp.mintime
      + Max Barometer: $month.barometer.max at $month.barometer.maxtime
      + Min Barometer: $month.barometer.min at $month.barometer.mintime
      + Max Wind : $month.wind.max from $month.wind.gustdir at $month.wind.maxtime
      + Rain total for month: $month.rain.sum
      +

      + ]]>
      +
      + + + Yearly Weather Summary + $station.webpath/year.html + Yearly summary of weather conditions at $station.location + $current.dateTime.format("%a, %d %b %Y %H:%M:%S %Z") + + Year: $year.dateTime.format("%Y")
      + Max Outside Temperature: $year.outTemp.max at $year.outTemp.maxtime
      + Min Outside Temperature: $year.outTemp.min at $year.outTemp.mintime
      + Max Inside Temperature: $year.inTemp.max at $year.inTemp.maxtime
      + Min Inside Temperature: $year.inTemp.min at $year.inTemp.mintime
      + Max Barometer: $year.barometer.max at $year.barometer.maxtime
      + Min Barometer: $year.barometer.min at $year.barometer.mintime
      + Max Wind : $year.wind.max from $year.wind.gustdir at $year.wind.maxtime
      + Rain total for year: $year.rain.sum
      +

      + ]]>
      +
      + +
      +
      \ No newline at end of file diff --git a/skins/Standard/index.html.tmpl b/skins/Standard/index.html.tmpl index 15f73eac..3efee101 100644 --- a/skins/Standard/index.html.tmpl +++ b/skins/Standard/index.html.tmpl @@ -5,6 +5,9 @@ + #if $encoding == 'utf8' + + #end if $station.location Current Weather Conditions