Files
weewx/docs/customizing.htm

4918 lines
195 KiB
HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<!-- $Id$ -->
<head>
<meta content="en-us" http-equiv="Content-Language" />
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<title>weewx: Customization Guide</title>
<link href="weewx_docs.css" rel="stylesheet" />
<script src="jquery.min.js" type="text/javascript"></script>
<script src="jquery.toc-1.1.4.min.js" type="text/javascript"></script>
<style>
table#stattypes td {
width: 60px;
}
</style>
</head>
<body>
<a href='http://weewx.com'>
<img src='logo-weewx.png' class='logo' align='right' />
</a>
<h1 class="title">Customizing weewx<br />
<span class="version"> Version:
2.5.0
</span>
</h1>
<h1>Table of Contents</h1>
<div id="toc"></div>
<div id="technical_content">
<h1 id="introduction">Introduction</h1>
<p>
This document covers the customization of <span class="code">weewx</span>.
It assumes that you have read and are reasonably familiar with
the <a href="usersguide.htm"> Users Guide</a>.
</p>
<p>
It starts with an overview of the architecture of weewx. If you
are only interested in customizing the generated reports you can
probably skip the overview and proceed directly to the section <em>
<a href="#standard_skin"> The Standard skin configuration
file</a>
</em>. 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.
</p>
<p>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.</p>
<p>Most of the guide will cover any weather hardware, but the
exact data types are specific to the Davis Vantage series.
Unless you are using an unusual type you are unlikely to run
into trouble.</p>
<p class="warning">
<strong>Warning!</strong><br /> <span class="code">weewx</span>
is still an experimental system and, as such, its internal
design is subject to change. Be prepared to do updates to any
code or customization you do!
</p>
<h2>Overview of the weewx architecture</h2>
<p>
At a high-level, <span class="code">weewx</span> consists of an
engine class called <span class="code">StdEngine</span>. It is
responsible for loading "<em>services</em>", then arranging for
them to be called when key events occur, such as the arrival of
LOOP data. The default install of <span class="code">
weewx</span> includes the following services:
</p>
<table class="indent" style="width: 80%"
summary="Overview of the weewx architecture">
<tbody>
<tr>
<td><strong>Service</strong></td>
<td><strong>Function</strong></td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdConvert</td>
<td>Converts the units of the input to a target unit
system (such as US or Metric).</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdCalibrate</td>
<td>Adjust new LOOP and archive packets using
calibration expressions.</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdQC</td>
<td>Check quality of incoming data, making sure values
fall within a specified range.</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdArchive</td>
<td>Archive any new data to the SQL databases.</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdTimeSynch</td>
<td>Arrange to have the clock on the station
synchronized at regular intervals.</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdPrint</td>
<td>Print out new LOOP and archive packets on the
console.</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdRESTful</td>
<td>Start a thread to manage <a
href="http://en.wikipedia.org/wiki/Representational_State_Transfer">
RESTful</a> (simple stateless client-server protocols)
connections; such as those used by the Weather Underground
or CWOP.
</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdReport</td>
<td>Launch 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/rsync files
to a web server. New reports can be added easily by the
user.</td>
</tr>
</tbody>
</table>
<p>
It is easy to extend old services or to add new ones. The source
distribution includes an example new service called "<span
class="code">MyAlarm</span>," which sends an email when an
arbitrary expression evaluates <span class="code"> True</span>.
These advanced topics are covered later in the section <em><a
href="#service_engine"> Customizing the weewx service
engine</a></em>.
</p>
<h2 id="Standard_reporting_service_StdReport">
The standard reporting service, <span class="code">StdReport</span>
</h2>
<p>
For the moment, let us focus on the last service, <span
class="code"> weewx.wxengine.StdReport</span>, the standard
service for creating reports. This will be what most users will
want to customize even if it means changing just a few options.
</p>
<h3>Reports</h3>
<p>
The Standard Report Service runs zero or more <em>Reports.</em>
The specific reports which get run are set in the configuration
file <span class="code">weewx.conf</span>, in section <span
class="code"> [StdReport]</span>.
</p>
<p>
The default distribution of <span class="code">weewx</span>
includes three reports:
</p>
<table class="indent" style="width: 80%"
summary="Standard reports included in weewx">
<tbody>
<tr>
<td><strong>Report</strong></td>
<td><strong>Default functionality</strong></td>
</tr>
<tr>
<td class="code">StandardReport</td>
<td>Generates 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.</td>
</tr>
<tr>
<td class="code">FTP</td>
<td>Arranges to upload everything in the <span
class="symcode"> $HTML_ROOT</span> directory up to a
remote webserver.
</td>
</tr>
<tr>
<td class="code">RSYNC</td>
<td>Like FTP, but uses rsync for transferring files to
a remote webserver.</td>
</tr>
</tbody>
</table>
<p>Note that the FTP and RSYNC "reports" are a funny kind of
report in that it they do not actually generate anything.
Instead, they use the reporting service engine to arrange for
things to be transferred to a remote server.</p>
<h3>Skins</h3>
<p>
Each report has a <em>Skin</em> 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 <em>skin configuration file</em>,
<span class="code"> skin.conf</span>. If you will, the skin
controls the <em>look and feel </em> 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 <span
class="code">weewx</span> configuration file <span
class="code">weewx.conf</span> or the skin configuration file
<span class="code">skin.conf</span>.
</p>
<p>Like all reports, the FTP and RSYNC "Reports" also use a
skin, and include a skin configuration file, although they are
quite minimal.</p>
<p>
Skins live in their own directory located in <span
class="symcode"> $SKIN_ROOT</span>.
</p>
<h3>Generators</h3>
<p>
To create their output, skins rely on one or more <em>Generators,</em>
code that actually create useful things such as HTML files or
plot images. Generators can also copy files around or FTP/rsync
them to remote locations. The default install of <span
class="code">weewx</span> includes the following generators:
</p>
<table class="indent" style="width: 80%"
summary="Generators included in weewx">
<tbody>
<tr>
<td><strong>Generator</strong></td>
<td><strong>Function</strong></td>
</tr>
<tr>
<td class="code">weewx.cheetahgenerator.CheetahGenerator</td>
<td>Generates files from templates, using the Cheetah
template engine. Used to generate HTML and text files.</td>
</tr>
<tr>
<td class="code">weewx.imagegenerator.ImageGenerator</td>
<td>Generates graph plots.</td>
</tr>
<tr>
<td class="code">weewx.reportengine.FtpGenerator</td>
<td>Uploads data to a remote server using FTP.</td>
</tr>
<tr>
<td class="code">weewx.reportengine.RsyncGenerator</td>
<td>Uploads data to a remote server using rsync.</td>
</tr>
<tr>
<td class="code">weewx.reportengine.CopyGenerator</td>
<td>Copies files locally.</td>
</tr>
</tbody>
</table>
<p>
Note that the three generators <span class="code">FtpGenerator</span>,
<span class="code">RsyncGenerator</span>, and <span class="code">
CopyGenerator</span> do not actually generate anything having to do
with the presentation layer. Instead, they just move files
around.
</p>
<p>
Which generators are to be run for a given skin is specified in
the skin's configuration file <span class="code">skin.conf</span>,
in section <a href="#generators"><span class="code">
[Generators]</span></a>.
</p>
<h2>Databases</h2>
<p>
There are two databases used by <span class="code">weewx</span>,
which can be implemented either by using <a
href="http://www.sqlite.org/"> SQLITE3</a>, an open-source,
lightweight SQL database, or <a href="http://www.mysql.com/">
MySQL</a>, an open-source, full-featured database server, or some
combination of the two of them.
</p>
<ul>
<li>The <em>archive database</em>, given symbolic name '<span
class="code">archive_database</span>'. It is a big flat table,
one record for each archive interval, keyed by <span
class="code">dateTime</span>, the time at the end of the
archive interval.
</li>
<li>The <em>statistical database</em>, given symbolic name
'<span class="code">stats_database</span>'. It consists of a
separate table for each observation type (that is, one for '<span
class="code">outTemp</span>', one for '<span class="code">barometer</span>',
<em>etc.</em>), each containing one record per day, keyed by
the start time of the day.
</li>
</ul>
<p>
How these abstract databases are bound to the real database is
covered in the <a href="usersguide.htm">Weewx User's Guide</a>,
in section '<a href="usersguide.htm#StdArchive">[StdArchive]</a>'.
</p>
<p>
The important thing to remember is that the archive database
contains a record for every archive interval and, as such,
represents the <em>current conditions</em> at the time of the
observation. By contrast, the statistical database represents
the <em>aggregation of conditions over a day</em>. 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.
</p>
<p>
The archive database is used for both generating plot data and
in template generation (where it appears as tag <span
class="code">$current</span>). The statistical database is
used only in template generation (where it appears as tags <span
class="code">$day</span>, <span class="code">$week</span>, <span
class="code">$month</span>, <span class="code">$year</span>,
and <span class="code">$rainyear</span>, depending on the
aggregation time period).
</p>
<h2>Where to put customizations</h2>
<p>
For configuration changes, simply modify the <span class="code">weewx</span>
configuration file <span class="code">weewx.conf</span>, and
possibly modify the skin configuration file <span class="code">skin.conf</span>
as described later in this document. These files will be
preserved when you upgrade.
</p>
<p>Other customizations require new Python code or
modifications of example code. Where should you put the code? If
you simply modify the examples in place, then your changes will
be overwritten the next time you do an upgrade.</p>
<p>
A better idea is to put the code in the <span class="symcode">$BIN_ROOT</span><span
class="code">/user</span>, directory. For example, copy
example code from the <span class="code">examples</span>
directory to the <span class="code">user</span> directory, then
modify it there. If your modification does not contain much
code, consider putting it in the extensions.py file in the <span
class="code">user</span> directory. The <span class="code">user</span>
directory is preserved through upgrades, so you won't have to
redo any changes you might have made.
</p>
<h1 id="reports">Customizing reports</h1>
<p>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.</p>
<h2>Changing options</h2>
<p>
Changing an option means either modifying the main configuration
file <span class="code">weewx.conf</span>, or the skin
configuration file for the standard skin that comes with the
distribution (nominally, file <span class="symcode">
$SKIN_ROOT</span><span class="code">/Standard/skin.conf</span>).
</p>
<h3>Changing options in <span class="code">skin.conf</span></h3>
<p>
With this approach, the user edits the skin configuration file
for the standard skin that comes with <span class="code">weewx</span>,
located in <span class="symcode"> $SKIN_ROOT</span><span
class="code">/Standard/skin.conf</span>, using a text editor.
For example, suppose you wish to use metric units in the
presentation layer, instead of the default US Customary Units.
The section that controls units is <span class="code">[Units][[Groups]]</span>.
It looks like this:
</p>
<pre class="tty">[Units]
[[Groups]]
group_altitude = foot
group_degree_day = degree_F_day
group_direction = degree_compass
group_moisture = centibar
group_percent = percent
group_pressure = inHg
group_radiation = watt_per_meter_squared
group_rain = inch
group_rainrate = inch_per_hour
group_speed = mile_per_second
group_speed2 = mile_per_second2
group_temperature = degree_F
group_uv = uv_index
group_volt = volt</pre>
<p>To use metric units, you would edit this section to read:</p>
<pre class="tty">[Units]
[[Groups]]
<span class="highlight"> group_altitude = meter</span>
<span class="highlight"> group_degree_day = degree_C_day</span>
group_direction = degree_compass
group_moisture = centibar
group_percent = percent
<span class="highlight"> group_pressure = mbar</span>
group_radiation = watt_per_meter_squared
<span class="highlight"> group_rain = mm</span>
<span class="highlight"> group_rainrate = mm_per_hour</span>
<span class="highlight"> group_speed = meter_per_second</span>
<span class="highlight"> group_speed2 = meter_per_second2</span>
<span class="highlight"> group_temperature = degree_C</span>
group_uv = uv_index
group_volt = volt</pre>
<p>
The options that were changed have been <span class="highlight">
&nbsp;highlighted&nbsp;</span>. Details of the various unit options
are given in <em><a href="#units"> Appendix B: Units</a></em>.
</p>
<p>
Other options are available, such as changing the text label for
various observation types. For example, suppose your weather
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 "<span class="code">inTemp</span>"
option located in section <span class="code">
[Labels][[Generic]]</span> from the default
</p>
<pre class="tty">[Units]
[[Generic]]
inTemp = Inside Temperature
outTemp = Outside Temperature
...</pre>
<p>to:</p>
<pre class="tty">[Units]
[[Generic]]
<span class="highlight"> inTemp = Barn Temperature</span>
outTemp = Outside Temperature
...</pre>
<h3>Overriding options in <span class="code">skin.conf</span> from <span class="code">weewx.conf</span></h3>
<p>
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, <span class="code">weewx.conf</span>.
The advantage of this approach is that you can use the same skin
to produce several different output, each with separate options.
</p>
<p>
Revisiting our example, suppose you want two reports, one in US
Customary, the other in Metric. The former will go in the
directory <span class="symcode"> $HTML_ROOT</span>, the latter
in a directory, <span class="symcode"> $HTML_ROOT</span><span
class="code">/metric</span>. If you just simply modify <span
class="code">skin.conf</span>, 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 directory then
editing the new <span class="code">skin.conf</span>. The trouble
with this approach is that you would then have <em>two</em>
skins you would have to maintain. If you change something, you
have to remember to change it in both places.
</p>
<p>
But, there's a better approach: reuse the same skin, but
override some of its options. Here is what your <span
class="code">[StdReport]</span> section in <span class="code">weewx.conf</span>
would look like:
</p>
<pre class="tty">[StdReport]
#
# 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 is based on the Standard skin
skin = Standard
# This report will use metric units:
[[MetricReport]]
# It is also based on the Standard skin:
skin = Standard
# However, override where the results will go and put them in a directory:
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) </pre>
<p>
We have done two things different from the stock reports. First
(1), we have renamed the first report from <span class="code">StandardReport</span>
to <span class="code">USReport</span> for clarity; and (2) we
have introduced a new report <span class="code">MetricReport</span>,
just like the first, except it puts its results in a different
spot and uses different units. Both use the same skin, the <span
class="code">Standard</span> skin.
</p>
<h2 id="templates">Customizing templates</h2>
<p>
If you cannot achieve the results you need by changing a
configuration option, you may have to modify the templates that
come with <span class="code"> weewx</span>, or write your own.
</p>
<p>
Template modifications are preserved across upgrades (indeed,
everything in the <span class="code">./skins</span> directory is
preserved), so you don't have to worry about losing changes.
</p>
<p>
Template generation is done using the <a
href="http://www.cheetahtemplate.org/"> Cheetah</a> 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, <span class="code">weewx</span>
adopts a very small subset of its power.
</p>
<h3>The dot code</h3>
<p>The key construct is a 'dot' code, specifying what value
you want. For example:</p>
<pre class="tty">$month.outTemp.max
$month.outTemp.maxtime
$current.outTemp</pre>
<p>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</p>
<pre class="tty">&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Current conditions&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;Current temperature = $current.outTemp&lt;/p&gt;
&lt;p&gt;Max for the month is $month.outTemp.max, which occurred at $month.outTemp.maxtime&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>
would be all you need for a very simple HTML page that would
display the text (assuming that the unit group for temperature
is <span class="code"> degree_F</span>):
</p>
<p class="example_output">
Current temperature = 51.0&deg;F <br /> Max for the month is
68.8&deg;F, which occurred at 07-Oct-2009 15:15
</p>
<p>
The format that was used to format the temperature (<span
class="code">51.0</span>) is specified in section <span
class="code"><a href="#Units_StringFormats">
[Units][[StringFormat]]</a></span>. The unit label <span class="code">&deg;F</span>
is from section <span class="code"><a
href="#Units_Labels">[Units][[Labels]]</a></span>, while the time
format is from <span class="code"><a
href="#Units_TimeFormats"> [Units][[TimeFormats]]</a></span>.
</p>
<p>As we saw above, the dot codes can be very simple:</p>
<pre class="tty">## Output max outside temperature using an appropriate format and label:
$month.outTemp.max</pre>
<p>
Most of the time, the dot code will "do the right thing" and is
all you will need. However, <span class="code">weewx</span>
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.
</p>
<p>There are two different versions of the dot code, depending
on whether the data is "current", or an aggregation over time.
However, both versions are similar.</p>
<h3>Time period <span class="code">$current</span></h3>
<p>
Time period <span class="code">$current</span> represents a <em>current
observation</em>. An example would be the current barometric
pressure:
</p>
<pre class="tty">$current.barometer</pre>
<p>The dot code for a current observation looks like:</p>
<pre class="tty"><em>$current.obstype[.optional_unit_conversion][.optional_formatting]</em></pre>
<p>Where:</p>
<p class="indent">
<span class="code"><em>obstype</em></span> is an observation
type, such as <span class="code">barometer</span>. See <em>
<a href="#archive_types">Appendix A, Archive Types</a>
</em> for a table of observation types valid for time period <span
class="code"> current.</span>
</p>
<p class="indent">
<span class="code">optional_unit_conversion</span> is an
optional unit conversion tag. If provided, the results will be
converted into the specified units, otherwise the default units
specified in the skin configuration file (in section <span
class="code">[Units][[Groups]]</span>) will be used. See the
section <em><a href="#unit_conversion_options">Unit
Conversion Options</a></em> below.
</p>
<p class="indent">
<span class="code"><em>optional_formatting</em></span> is an
optional formatting tag that controls how the value will appear.
See the section <em><a href="#formatting_options">Formatting
Options</a></em> below.
</p>
<h3 id="Aggregation_periods_day_etc">
Aggregation periods <span class="code">$day</span>, <span
class="code">$week</span>, <span class="code">$month</span>, <span
class="code">$year</span>, <span class="code">$rainyear</span>
</h3>
<p>
The other time periods represent an <em>aggregation over
time</em>. In addition to the time period over which the
aggregation will occur, they also require an <em>aggregation
type</em>. An example would be the week's total precipitation
(where the aggregation type is <span class="code"><em>
sum</em></span>):
</p>
<pre class="tty">$week.rain.sum</pre>
<p>The dot code for an aggregation over time looks like:</p>
<pre class="tty"><em>$period.statstype.aggregation[.optional_unit_conversion][.optional_formatting]</em></pre>
<p>Where:</p>
<p class="indent">
<span class="code"><em>period</em></span> is the time period
over which the aggregation is to be done. Possible choices are <span
class="code">day</span>, <span class="code">week</span>, <span
class="code"> month</span>, <span class="code">year</span>, <span
class="code">rainyear</span>.
</p>
<p class="indent">
<span class="code"><em>statstype</em></span> is a statistical
type. See <em><a href="#statistical_types">Appendix C,
Statistical Types</a></em>, for a table of statistical types.
</p>
<p class="indent">
<span class="code"><em>aggregation</em></span> is an aggregation
type. This is something like '<span class="code">min</span>', '<span
class="code">sum</span>', '<span class="code">mintime</span>'.
If you ask for <span class="code">$month.outTemp.avg</span> you
are asking for the <em>average</em> outside temperature for the
month. The table <em> <a href="#statistical_types">Appendix
C: Statistical Types</a></em> shows what aggregation types are
available for which types.
</p>
<p class="indent">
<span class="code">optional_unit_conversion</span> is an
optional unit conversion tag. If provided, the results will be
converted into the specified units, otherwise the default units
specified in the skin configuration file (in section <span
class="code">[Units][[Groups]]</span>) will be used. See the
section <em><a href="#unit_conversion_options">Unit
Conversion Options</a></em> below.
</p>
<p class="indent">
<span class="code"><em>optional_formatting</em></span> is an
optional formatting tag that controls how the value will appear.
See the section <em><a href="#formatting_options">Formatting
Options</a></em> below.
</p>
<h3 id="unit_conversion_options">Unit conversion options</h3>
<p>
The tag <span class="code"><em>optional_unit_conversion</em></span>
can be used with either current observations or aggregations. If
supplied, the results will be converted to the specified units.
For example, if you have set <span class="code">group_pressure</span>
to inches of mercury (<span class="code">inHg</span>), then the
tag
</p>
<pre class="tty">Today's average pressure=$day.barometer.avg </pre>
<p>would normally give a result such as</p>
<p class="example_output">Today's average pressure=30.05 inHg
</p>
<p>
However, if you add "<span class="code">mbar</span>" to the end,
</p>
<pre class="tty">$day.barometer.avg.mbar </pre>
<p>then the results will be in millibars:</p>
<p class="example_output">Today's average pressure=1017.5 mbar
</p>
<h4>Wind ordinals</h4>
<p>Using this method, you can output compass ordinals for wind
direction. For example, the template</p>
<pre class="tty">Current wind direction is $current.windDir ($current.windDir.ordinal_compass)</pre>
<p>would result in:</p>
<p class="example_output">Current wind direction is 138&deg; (SW)</p>
<p>
The ordinal abbreviations that are used are set by option <span
class="code"> directions</span> in the skin configuration file
<span class="code">skin.conf</span>.
</p>
<h4>Illegal conversions</h4>
<p>
If an inappropriate conversion is asked for, <em>e.g.</em>,
</p>
<pre class="tty">Today's average pressure=$day.barometer.degree_C </pre>
<p>then the offending tag will be put in the output:</p>
<p class="example_output">Today's average
pressure=$day.barometer.degree_C</p>
<h3 id="formatting_options">Formatting options</h3>
<p>
The tag <span class="code"><em>optional_formatting</em></span>
can be used with either current observations or aggregations. It
can be one of:
</p>
<table class="indent" style="width: 90%" summary="Formatting Options">
<tbody>
<tr>
<td><strong>Optional Formatting Tag</strong></td>
<td><strong>Comment</strong></td>
</tr>
<tr>
<td><em>(no tag)</em></td>
<td>Value is returned as a string, formatted using an
appropriate string format from <span class="code">skin.conf</span>.
A unit label from <span class="code">skin.conf</span> is
also attached at the end.
</td>
</tr>
<tr>
<td class="code">.string(<em>NONE_string</em>)
</td>
<td>Value is returned as a string, formatted using an
appropriate string format from <span class="code">skin.conf</span>.
If the value is <span class="code">None</span>, the string
<span class="code"> NONE_string</span> will be substituted
if given, otherwise the value for <span class="code">
NONE</span> in <span class="code"> <a
href="#Units_StringFormats">
[Units][[StringFormats]]</a>
</span> will be used. A unit label from <span class="code">
skin.conf</span> will be attached at the end.
</td>
</tr>
<tr>
<td class="code"><span class="code">.formatted</span></td>
<td>Value is returned as a string, formatted using an
appropriate string format and <span class="code">None</span>
value from <span class="code">skin.conf</span>. No label.
</td>
</tr>
<tr>
<td class="code">.format(<em>string_format</em>,&nbsp;<em>NONE_string</em>)
</td>
<td>Value is returned as a string, using the string
format specified with <em>string_format</em>. If the value
is <span class="code">None</span>, the string <span
class="code">NONE_string</span> will be substituted if
given, otherwise the value for <span class="code">NONE</span>
in <span class="code"> <a
href="#Units_StringFormats">
[Units][[StringFormats]]</a>
</span> will be used. A unit label from <span class="code">
skin.conf</span> will be attached at the end.
</td>
</tr>
<tr>
<td class="code">.nolabel(string_format,&nbsp;NONE_string)</td>
<td>Value is returned as a string, using the string
format specified with <em>string_format</em>. If the value
is <span class="code">None</span>, the string <span
class="code">NONE_string</span> will be substituted if
given, otherwise the value for <span class="code">NONE</span>
in <span class="code"> <a
href="#Units_StringFormats">
[Units][[StringFormats]]</a>
</span> will be used. No label will be attached at the end.
</td>
</tr>
<tr>
<td class="code"><span class="code">.raw</span></td>
<td>Value is returned "as is" without being converted
to a string and without any formatting applied. You must
be prepared to deal with a <span class="code">None</span>
value unless the value is converted directly to a string.
In this case, it will be converted to the empty string (<span
class="code">''</span>)
</td>
</tr>
</tbody>
</table>
<p>Summary:</p>
<table class="indent" style="width: 80%"
summary="Summary of formatting options">
<tbody>
<tr>
<td><strong>Formatting Tag</strong></td>
<td><strong>Format Used</strong></td>
<td><strong>Label Used</strong></td>
<td><strong>NONE String</strong></td>
<td><strong>Returned Value</strong></td>
</tr>
<tr>
<td><em>(no tag)</em></td>
<td>From <span class="code">skin.conf</span></td>
<td>From <span class="code">skin.conf</span></td>
<td>From <span class="code">skin.conf</span></td>
<td>string</td>
</tr>
<tr>
<td class="code">.string</td>
<td>From <span class="code">skin.conf</span></td>
<td>From <span class="code">skin.conf</span></td>
<td>Optional user-supplied</td>
<td>string</td>
</tr>
<tr>
<td class="code">.formatted</td>
<td>From <span class="code">skin.conf</span></td>
<td>No label</td>
<td>From <span class="code">skin.conf</span></td>
<td>string</td>
</tr>
<tr>
<td class="code">.format</td>
<td>User-supplied</td>
<td>From <span class="code">skin.conf</span></td>
<td>Optional user-supplied</td>
<td>string</td>
</tr>
<tr>
<td class="code">.nolabel</td>
<td>User-supplied</td>
<td>No label</td>
<td>Optional user-supplied</td>
<td>string</td>
</tr>
<tr>
<td class="code">.raw</td>
<td>None</td>
<td>No label</td>
<td>None</td>
<td>native value</td>
</tr>
</tbody>
</table>
<p>Here are some examples with the expected results:</p>
<table class="indent"
summary="Formatting options with expected results">
<tbody>
<tr>
<td><strong>Tag</strong></td>
<td><strong>Result</strong></td>
<td><strong>Comment</strong></td>
</tr>
<tr>
<td class="code">$current.outTemp</td>
<td class="code">45.2&deg;F</td>
<td>String formatting and label from <span class="code">skin.conf</span></td>
</tr>
<tr>
<td class="code">$current.outTemp.string</td>
<td class="code">45.2&deg;F</td>
<td>String formatting and label from <span class="code">skin.conf</span></td>
</tr>
<tr>
<td class="code">$current.UV.string</td>
<td class="code">N/A</td>
<td>This example assumes that the instrument has no UV
sensor, resulting in a <span class="code">None</span>
value. The string specified by <span class="code">NONE</span>
in <span class="code"> <a
href="#Units_StringFormats">[Units][[StringFormats]]</a></span>
is substituted.
</td>
</tr>
<tr>
<td class="code">$current.UV.string("No UV")</td>
<td class="code">No UV</td>
<td>This example assumes that the instrument has no UV
sensor, resulting in a <span class="code">None</span>
value. The string supplied by the user is substituted.
</td>
</tr>
<tr>
<td class="code">$current.outTemp.formatted</td>
<td class="code">45.2</td>
<td>String formatting from <span class="code">skin.conf</span>;
no label
</td>
</tr>
<tr>
<td class="code">$current.outTemp.format("%.3f")</td>
<td class="code">45.200&deg;F</td>
<td>Specified string format used; label from <span
class="code"> skin.conf</span>.
</td>
</tr>
<tr>
<td class="code">$current.dateTime</td>
<td class="code">02-Apr-2010&nbsp;16:25</td>
<td>Time formatting and label from <span class="code">skin.conf</span></td>
</tr>
<tr>
<td class="code">$current.dateTime.format("%H:%M")</td>
<td class="code">16:25</td>
<td>Specified time format used; label from <span
class="code"> skin.conf</span>.
</td>
</tr>
<tr>
<td class="code">$current.dateTime.raw</td>
<td class="code">1270250700</td>
<td>Unix epoch time, converted to string by template
engine.</td>
</tr>
<tr>
<td class="code">$current.outTemp.raw</td>
<td class="code">45.2</td>
<td>Float returned, converted to string by template
engine.</td>
</tr>
<tr>
<td class="code">$month.dateTime</td>
<td class="code">01-Apr-2010 00:00</td>
<td>Time formatting and label from <span class="code">skin.conf</span></td>
</tr>
<tr>
<td class="code">$month.outTemp.avg</td>
<td class="code">40.8&deg;F</td>
<td>String formatting and label from <span class="code">skin.conf</span></td>
</tr>
<tr>
<td class="code">$month.outTemp.avg.string</td>
<td class="code">40.8&deg;F</td>
<td>Time formatting and label from <span class="code">skin.conf</span></td>
</tr>
<tr>
<td class="code">$month.UV.avg.string</td>
<td class="code">N/A</td>
<td>This example assumes that the instrument has no UV
sensor, resulting in a <span class="code">None</span>
value. The string specified by <span class="code">NONE</span>
in <span class="code"> <a
href="#Units_StringFormats">[Units][[StringFormats]]</a></span>
is substituted.
</td>
</tr>
<tr>
<td class="code">$month.UV.avg.string("No UV")</td>
<td class="code">No UV</td>
<td>This example assumes that the instrument has no UV
sensor, resulting in a <span class="code">None</span>
value. The string supplied by the user is substituted.
</td>
</tr>
<tr>
<td class="code">$month.outTemp.avg.formatted</td>
<td class="code">40.8</td>
<td>String formatting from <span class="code">skin.conf</span>;
no label
</td>
</tr>
<tr>
<td class="code">$month.outTemp.avg.format("%.3f")</td>
<td class="code">40.759&deg;F</td>
<td>Specified string format used; no label</td>
</tr>
<tr>
<td class="code">$month.outTemp.avg.raw</td>
<td class="code">40.7589690722</td>
<td>Float returned, converted to string by template
engine</td>
</tr>
<tr>
<td class="code">$month.UV.avg.raw</td>
<td class="code"><em>(empty)</em></td>
<td><span class="code">None</span> value converted to
empty string by template engine.</td>
</tr>
</tbody>
</table>
<p>Note:</p>
<ul>
<li>Tags that take an argument (such as <span class="code">
.string(NONE_string)</span>) do not require parenthesis if the
argument is omitted.Thus, you can specify either <span
class="code"> $month.outTemp.string()</span> or <span
class="code">$month.outTemp.string</span>, if you want the
default value of <span class="code">NONE_string</span>. They
produce the same results.
</li>
</ul>
<h3>Type <span class="code">dateTime</span></h3>
<p>
While not an observation type, in many ways the time of an
observation, <span class="code">dateTime</span>, can be treated
as one. A tag such as <span class="code">$current.dateTime</span>
represents the <em>current time</em> (more properly, the time as
of the end of the last archive interval). Similarly, a tag such
as <span class="code">$month.dateTime</span> represents the
start time of the month. Like true observation types, explicit
formats can be specified, except that they require a <a
href="http://docs.python.org/library/datetime.html#strftime-strptime-behavior">
strftime() <em>time format</em>
</a>, rather than a <em>string format:</em>
</p>
<pre class="tty">$month.dateTime.format("%B %Y)</pre>
<p>produces</p>
<pre class="tty">January 2010</pre>
<p>
The returned string value will always be in <em>local time</em>.
</p>
<p>
The raw value of <span class="code">dateTime</span> is Unix
Epoch Time (number of seconds since 00:00:00 UTC 1 Jan 1970, <em>i.e.</em>,
a large number), which you must convert yourself to local time.
It is guaranteed to never be <span class="code">None</span>, so
you don't worry have to worry about handling a <span
class="code">None</span> value.
</p>
<h3>Tag <span class="code">$trend</span></h3>
<p>
The tag <span class="code">$trend</span> is available for time
trends, such as barometer trends. Here are some examples:
</p>
<table class="indent" style="width: 40%"
summary="Examples of using unit formats">
<tbody>
<tr>
<td><strong>Tag</strong></td>
<td><strong>Results</strong></td>
</tr>
<tr>
<td class="code">$trend.barometer</td>
<td class="code">-.02 inHg</td>
</tr>
<tr>
<td class="code">$trend.outTemp</td>
<td class="code">1.1&deg;C</td>
</tr>
<tr>
<td class="code">$trend.time_delta</td>
<td class="code">10800 secs</td>
</tr>
<tr>
<td class="code">$trend.time_delta.hour</td>
<td class="code">3 hrs</td>
</tr>
</tbody>
</table>
<p>
Note that the time delta over which the trend is calculated is
also available. This time delta is set by an option in the skin
configuration file, <span class="code"><a href="#trend">time_delta</a></span>.
</p>
<p>As a summary, the template expression</p>
<pre class="tty">&lt;p&gt;The barometer trend over $trend.time_delta.hour is $trend.barometer.format("%+.2f").&lt;/p&gt;</pre>
<p>would result in</p>
<p class="example_output">The barometer trend over 3 hrs is
+.02 inHg.</p>
<h3>Tag <span class="code">$unit</span></h3>
<p>The unit type, label, and string formats are also
available, allowing you to do highly customized labels:</p>
<table class="indent" style="width: 40%"
summary="Examples of using unit formats">
<tbody>
<tr>
<td><strong>Tag</strong></td>
<td><strong>Results</strong></td>
</tr>
<tr>
<td class="code">$unit.unit_type.outTemp</td>
<td class="code">degree_C</td>
</tr>
<tr>
<td class="code">$unit.label.outTemp</td>
<td class="code">&deg;C</td>
</tr>
<tr>
<td class="code">$unit.format.outTemp</td>
<td class="code">%.1f</td>
</tr>
</tbody>
</table>
<p>As a summary, the tag</p>
<pre class="tty">$day.outTemp.max.formatted$unit.label.outTemp</pre>
<p>would result in</p>
<pre class="tty">21.2&deg;C</pre>
<p>
(assuming metric values have been specified for <span
class="code"> group_temperature</span>), essentially
reproducing the results of the simpler tag <span class="code">$day.outTemp.max</span>.
</p>
<h3>Iteration</h3>
<p>
For dot codes using an aggregation (<em>e.g.</em>, <span
class="code"> $day</span>, <span class="code">$week</span>, <span
class="code">$month</span>, <span class="code">$year</span>, <span
class="code">$rainyear</span>, then the aggregation period can
be iterated over by day or month. These are the only two
iteration periods available as of this version.
</p>
<p>
This example uses a Cheetah '<span class="code">for</span>' loop
to iterate over all months in a year, printing out each month's
min and max temperature (the iteration loop is <span
class="highlight">&nbsp;highlighted&nbsp;</span>):
</p>
<pre class="tty">&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Year stats by month&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;Min, max temperatures by month:&lt;/p&gt;
<span class="highlight"> #for $month in $year.months</span>
&lt;p&gt;$month.dateTime.format("%B"): Min, max temperatures: $month.outTemp.min $month.outTemp.max&lt;/p&gt;
<span class="highlight"> #end for</span>
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>Produces results:</p>
<pre class="tty">Min, max temperatures by month:
January: Min, max temperatures: 30.1&deg;F 51.5&deg;F
February: Min, max temperatures: 24.4&deg;F 58.6&deg;F
March: Min, max temperatures: 27.3&deg;F 64.1&deg;F
April: Min, max temperatures: 33.2&deg;F 52.5&deg;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</pre>
<p>
See the NOAA template files <span class="code">NOAA/NOAA-YYYY.txt.tmpl</span>
and <span class="code">NOAA/NOAA-YYYY-MM.txt.tmpl</span> for
examples using iteration, as well as explicit formatting.
</p>
<h3>Almanac</h3>
<p>
If module <a href="http://rhodesmill.org/pyephem">pyephem</a>
has been installed, then <span class="code">weewx</span> can
generate extensive almanac information for the Sun, Moon, Venus,
Mars, Jupiter, and other heavenly bodies, including their rise,
transit and set times, as well as their azimuth and altitude.
Other information is also available.
</p>
<p>Here is a small sampling:</p>
<pre class="tty">&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Almanac data&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;p&gt;Current time is $current.dateTime&lt;p&gt;
#if $almanac.hasExtras
&lt;p&gt;Sunrise, transit, sunset: $almanac.sun.rise $almanac.sun.transit $almanac.sun.set&lt;/p&gt;
&lt;p&gt;Moonrise, transit, moonset: $almanac.moon.rise $almanac.moon.transit $almanac.moon.set&lt;/p&gt;
&lt;p&gt;Mars rise, transit, set: $almanac.mars.rise $almanac.mars.transit $almanac.mars.set&lt;/p&gt;
&lt;p&gt;Azimuth, altitude of mars: $almanac.mars.az $almanac.mars.alt&lt;/p&gt;
&lt;p&gt;Next new, full moon: $almanac.next_new_moon $almanac.next_full_moon&lt;/p&gt;
&lt;p&gt;Next summer, winter solstice: $almanac.next_summer_solstice $almanac.next_winter_solstice&lt;/p&gt;
#else
&lt;p&gt;Sunrise, sunset: $almanac.sunrise $almanac.sunset&lt;/p&gt;
#end if
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>If your installation has pyephem installed this would
result in:</p>
<pre class="tty">Current time is 29-Mar-2011 09:20
Sunrise, transit, sunset: 06:51 13:11 19:30
Moonrise, transit, moonset: 04:33 09:44 15:04
Mars rise, transit, set: 06:35 12:30 18:26
Azimuth, altitude of mars: 124.354959275 26.4808431952
Next new, full moon: 03-Apr-2011 07:32 17-Apr-2011 19:43
Next summer, winter solstice: 21-Jun-2011 10:16 21-Dec-2011 21:29</pre>
<p>Otherwise, a fallback position is used, resulting in</p>
<pre class="tty">Current time is 29-Mar-2011 09:20
Sunrise, sunset: 06:51 19:30</pre>
<p>
As shown in the example, you can test whether this extended
almanac information is available with the value <span
class="code"> $almanac.hasExtras</span>.
</p>
<p>The almanac information falls in two categories:</p>
<ul>
<li>Calendar events</li>
<li>Heavenly bodies</li>
</ul>
<p>We will cover each of these separately.</p>
<h4>Calendar events</h4>
<p>
"Calendar events" do not require a heavenly body. They cover
things such as "<span class="code">next_solstice</span>", or "<span
class="code">next_first_quarter_moon</span>". The syntax here
is
</p>
<pre class="tty">$almanac.next_solstice</pre>
<p>or</p>
<pre class="tty">$almanac.next_first_quarter_moon</pre>
<p>Here is a table of the information that falls into this
category:</p>
<table class="indent code" style="width: 60%">
<tbody>
<tr>
<td>previous_equinox</td>
<td>next_equinox</td>
<td>previous_solstice</td>
<td>next_solstice</td>
</tr>
<tr>
<td>previous_autumnal_equinox</td>
<td>next_autumnal_equinox</td>
<td>previous_vernal_equinox</td>
<td>next_vernal_equinox</td>
</tr>
<tr>
<td>previous_winter_solstice</td>
<td>next_winter_solstice</td>
<td>previous_summer_solstice</td>
<td>next_summer_solstice</td>
</tr>
<tr>
<td>previous_new_moon</td>
<td>next_new_moon</td>
<td>previous_first_quarter_moon</td>
<td>next_first_quarter_moon</td>
</tr>
<tr>
<td>previous_full_moon</td>
<td>next_full_moon</td>
<td>previous_last_quarter_moon</td>
<td>next_last_quarter_moon</td>
</tr>
</tbody>
</table>
<h4>Heavenly bodies</h4>
<p>The second category does require a heavenly body. This
covers queries such as, "When does Jupiter rise?" or, "When does
the sun transit?" Examples are</p>
<pre class="tty">$almanac.jupiter.rise</pre>
<p>or</p>
<pre class="tty">$almanac.sun.transit</pre>
<p>
To accurately calculate these times, <span class="code">weewx</span>
automatically uses the present temperature and pressure to
calculate refraction effects. However, you can override these
values, which will be necessary if you wish to match the almanac
times published by the Naval Observatory <a
href="http://rhodesmill.org/pyephem/rise-set.html">as
explained in the pyephem documentation</a>. For example, to match
the sunrise time as published by the Observatory, instead of
</p>
<pre class="tty">$almanac.sun.rise</pre>
<p>use</p>
<pre class="tty">$almanac(pressure=0, horizon=-34.0/60.0).sun.rise</pre>
<p>By setting pressure to zero we are bypassing the refraction
calculations and manually setting the horizon to be 34
arcminutes lower than the normal horizon. This is what the Navy
uses.</p>
<p>
If you wish to calculate the start of civil twilight, you can
set the horizon to -6 degrees, and also tell <span class="code">weewx</span>
to use the center of the sun (instead of the upper limb, which
it normally uses) to do the calcuation:
</p>
<pre class="tty">$almanac(pressure=0, horizon=-6).sun(use_center=1).rise</pre>
<p>The general syntax is:</p>
<pre class="tty">$almanac(pressure=<em>pressure</em>, horizon=<em>horizon</em>,
temperature=<em>temperature_C</em>).<em>heavenly_body</em>(use_center=[01]).<em>attribute</em></pre>
<p>As you can see, in addition to the horizon angle, you can
also override atmospheric pressure and temperature (degrees
Celsius).</p>
<p>
PyEphem offers an extensive list of objects that can be used for
the <span class="code"><em>heavenly_body</em></span> tag. All
the planets and many stars are in the list.
</p>
<p>
The <span class="code">attribute</span> tag can be one of
</p>
<table class="indent code" style="width: 60%">
<tbody>
<tr>
<td>az</td>
<td>alt</td>
<td>a_ra</td>
<td>a_dec</td>
</tr>
<tr>
<td>g_ra</td>
<td>ra</td>
<td>g_dec</td>
<td>dec</td>
</tr>
<tr>
<td>elong</td>
<td>radius</td>
<td>hlong</td>
<td>hlat</td>
</tr>
<tr>
<td>sublat</td>
<td>sublong</td>
<td>next_rising</td>
<td>next_setting</td>
</tr>
<tr>
<td>next_transit</td>
<td>next_antitransit</td>
<td>previous_rising</td>
<td>previous_setting</td>
</tr>
<tr>
<td>previous_transit</td>
<td>previous_antitransit</td>
<td>rise</td>
<td>set</td>
</tr>
<tr>
<td>transit</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<h2 id="defining_new_tags">Defining new tags</h2>
In the section on <em><a href="#templates">Customizing
templates</a></em>, we have seen how you can change a template and make
use of the various tags available such as <span class="code">$day.outTemp.max</span>
for the maximum outside temperature for the day. But, what if you
want to introduce some new data for which no tag is available?
<p>
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 <span
class="code"><a href="#Extras">[Extras]</a></span> in the skin
configuration file. More information on how to do this can be
found there.
</p>
<p>
But, what if you wish to introduce a more dynamic tag, one that
requires some calculation, or perhaps uses the database? Simply
putting it in the <span class="code">[Extras]</span> section
won't do, because then it cannot change.
</p>
<p>
The answer is to write a <em>search list extension</em>.
</p>
<h3>Search list</h3>
<p>
The Cheetah template engine (which <span class="code">weewx</span>
uses) finds tags by scanning a <em>search list</em>, a
Python list of objects. For example, for a tag <span
class="code">$foo</span>, the engine will scan down the list,
trying each object <span class="code">obj</span> in turn. First
it tries using <span class="code">foo</span> as an attribute,
that is, <span class="code">obj.foo</span>. If that raises an
exception <span class="code"> AttributeError</span>, then it
will try <span class="code">foo</span> as a key, that is <span
class="code">obj[key]</span>. If that raises a <span
class="code">KeyError</span>, then it moves on to the next
item in the list. The first match that does not raise an
exception is returned. If no match is found, it raises a <span
class="code">NameMapper.NotFound</span> exception.
</p>
<h3>Extending the list</h3>
<p>Weewx comes with a number of objects already in the search
list, but you can extend it. To do so, you should have some
familiarity with Python, in particular, how to write new classes
and member functions for them.</p>
<p>
Let's look at an example. The regular version of <span
class="code"> weewx</span> offers statistical summaries by
day, week, month, and year. Suppose we would like to add two
more:
</p>
<ul>
<li>All-time statistics. This would allow us to display
statistics such as the all-time high or low temperature seen
at your station;</li>
<li>Seven days statistics. While <span class="code">weewx</span>
offers the tag <span class="code">$week</span>, this is
statistics <em>since Sunday at midnight</em>. We would like to
have statistics for a full week, that is since midnight seven
days ago.
</li>
</ul>
<p>
This example is included in the distribution as <span
class="symcode"> $BIN_ROOT</span><span class="code">/examples/xsearch.py</span>:
</p>
<pre class="tty">import datetime
import time
from weewx.cheetahgenerator import SearchList
from weewx.stats import TimeSpanStats
from weeutil.weeutil import TimeSpan
class MyXSearch(SearchList): #1
def __init__(self, generator): #2
SearchList.__init__(self, generator)
def get_extension(self, valid_timespan, archivedb, statsdb): #3
# First, get a TimeSpanStats object for all time. This one is easy
# because the object valid_timespan already holds all valid times to be
# used in the report.
all_stats = TimeSpanStats(valid_timespan,
statsdb,
formatter=self.generator.formatter,
converter=self.generator.converter) # 4
# Now get a TimeSpanStats object for the last seven days. This one we
# will have to calculate. First, calculate the time at midnight, seven
# days ago. The variable week_dt will be an instance of datetime.date.
week_dt = datetime.date.fromtimestamp(valid_timespan.stop) - datetime.timedelta(weeks=1) #5
# Now convert it to unix epoch time:
week_ts = time.mktime(week_dt.timetuple()) # 6
# Now form a TimeSpanStats object, using the time span we just calculated:
seven_day_stats = TimeSpanStats(TimeSpan(week_ts, valid_timespan.stop),
statsdb,
formatter=self.generator.formatter,
converter=self.generator.converter) #7
# Now create a small dictionary with keys 'alltime' and 'seven_day':
search_list_extension = {'alltime' : all_stats,
'seven_day' : seven_day_stats} # 8
return search_list_extension</pre>
<p>Going through the example, line by line:</p>
<ol>
<li>Create a new class called <span class="code">MyXSearch</span>,
which will inherit from class <span class="code">SearchList</span>.
All search list extensions inherit from this class.
</li>
<li>Create an initializer for our new class. In this case,
the initializer does nothing except pass its only parameter, <span
class="code">generator</span>, a reference to the calling
generator, to its superclass, <span class="code">SearchList</span>.
The superclass will store it in <span class="code">self</span>.
</li>
<li>Override member function <span class="code">get_extension()</span>.
This function will be called when the generator is ready to
accept your new search list extension. The parameters that
will be passed in are:
<ul>
<li><span class="code">self</span> Python's way of
indicating the instance we are working with;</li>
<li><span class="code">valid_timespan</span> An
instance of the utility class <span class="code">TimeSpan</span>.
This will contain the valid start and ending times used by
the template. Normally, this is all valid times;</li>
<li><span class="code">archivedb</span> An instance of
<span class="code">weewx.archive.Archive</span>, holding
the archive database;</li>
<li><span class="code">statsdb</span> An instance of <span
class="code">weewx.stats.StatsDb</span>, holding the
statistical database.</li>
</ul>
</li>
<li>The class <span class="code">TimeSpanStats</span>
represents a statistical calculation over a time period. In
our case, we will set it up to represent the statistics over
all possible times. The class takes 4 parameters.
<ul>
<li>The first is the timespan over which the
calculation is to be done. Here, we have a lucky
coincidence: the variable <span class="code">valid_timespan</span>
already holds a <span class="code">TimeSpan</span> object
representing the domain of all valid timespans, so we
simply pass it in.
</li>
<li>The second is the statistical database the
calculation is to be run against. We simply pass in <span
class="code">statsdb</span>.
</li>
<li>The third should be an instance of class <span
class="code">weewx.units.Formatter</span>, which contains
information about how the results should be formatted. We
just pass in the formatter set up by the generator, <span
class="code">self.generator.formatter</span>.
</li>
<li>The fourth should be an instance of <span
class="code">weewx.units.Converter</span>, which contains
information about the target units (<em>e.g.</em>, "<span
class="code">degree_C</span>") that are to be used. Again,
we just pass in the instance set up by the generator, <span
class="code">self.generator.converter</span>.
</li>
</ul>
</li>
</ol>
<p>
That one was relatively easy because we already had an instance
of <span class="code">TimeSpan</span>, <span class="code">valid_timespan</span>,
that represented the time over which we wanted to do the
calculations. Setting up an instance that will work for the last
seven days is a bit trickier. Continuing our example...
</p>
<ol start="5">
<li>The object <span class="code">valid_timespan</span>
holds the domain of all valid times, but in order to calculate
statistics for the last seven days, we need not the earliest
valid time, but the time at midnight seven days ago. So, we do
a little Python date arithmetic to calculate this. The object
<span class="code">week_dt</span> will be an instance of <span
class="code">datetime.date</span>.
</li>
<li>We convert it to unix epoch time.</li>
<li>Now we are ready to initialize an appropriate <span
class="code">TimeSpanStats</span> object. It's the same as in
step #4, except we use our new timespan object.
</li>
<li>Create a small dictionary with two keys, '<span
class="code">alltime</span>', and '<span class="code">seven_day</span>'
and return it.
</li>
</ol>
<p>
The last step is to tell the template engine where to find our
extension. You do that by going into the skin configuration
file, <span class="code">skin.conf</span>, and adding the option
<span class="code">search_list_extensions</span> with our new
extension. When you're done, it will look something like this:
</p>
<pre class="tty">[CheetahGenerator]
# This section is used by the generator CheetahGenerator, and specifies
# which files are to be generated from which template.
# Possible encodings are 'html_entities', 'utf8', or 'strict_ascii'
encoding = html_entities
<span class="highlight">search_list_extensions = examples.xsearch.MyXSearch</span>
[[SummaryByMonth]]
...
</pre>
<p>
Our addition has been <span class="highlight">&nbsp;highlighted&nbsp;</span>.
Note that it is in the section <span class="code">[CheetahGenerator]</span>.
(This section was called <span class="code">
[FileGenerator]</span> in earlier versions of <span class="code">weewx</span>,
a name which will still work.)
</p>
<p>
Now, if the Cheetah engine encounters the tag <span class="code">
$alltime</span>, it will scan the search list, looking for an
attribute or key that matches <span class="code">alltime</span>.
When it gets to the little dictionary we provided, it will find
a matching key, allowing it to retrieve the appropriate <span
class="code">TimeSpanStats</span> object.
</p>
<p>With this approach, you can now include "all time" or
"seven day" statistics in your HTML templates:</p>
<pre class="tty">...
&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;Maximum temperature to date: &lt;/td&gt;
&lt;td&gt;$alltime.outTemp.max&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimum temperature to date: &lt;/td&gt;
&lt;td&gt;$alltime.outTemp.min
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rain over the last seven days: &lt;/td&gt;
&lt;td&gt;$seven_day.rain.sum
&lt;/tr&gt;
... (more table entries)</pre>
<p>
If you place a custom generator somewhere other than the <span
class="symcode">$BIN_ROOT</span> hierarchy where <span
class="code">weewxd</span> resides, you may have to specify
its location in the environment variable <span class="code">PYTHONPATH</span>
in the shell where you start weewx:
</p>
<pre class="tty">export PYTHONPATH=/home/me/secret_location</pre>
<h1 id="standard_skin">Customizing skins: the Standard skin</h1>
<p>
This section is a reference to the options appearing in the
Standard skin configuration file, found in <span class="symcode">$SKIN_ROOT</span><span
class="code">/Standard/skin.conf</span>.
</p>
<p>
It is worth noting that, like the main configuration file <span
class="code">weewx.conf</span>, UTF-8 is used throughout. 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 <span class="config_important"><strong>
highlighted</strong></span>.
</p>
<h2 class="config_section" id="Extras">[Extras]</h2>
<p>This section is available to you to add any static tags
that you might want to be available in the templates.</p>
<h3>An Example: the Standard skin</h3>
<p>
As an example, the Standard <span class="code">skin.conf</span>
file includes two options: <span class="code">radar_url</span>,
which is available as tag <span class="code">$Extras.radar_url</span>,
and <span class="code">googleAnalyticsId</span>, available as
tag <span class="code">$Extras.googleAnalyticsId</span>. If you
take a look at the template <span class="code">index.html.tmpl</span>
you will see examples of testing for these tags (search the file
for the string <span class="code">radar_url</span> or <span
class="code">googleAnalyticsId</span> to find them).
</p>
<p class="config_option">radar_url</p>
<p>If set, the NOAA radar image will be displayed. If
commented out, no image will be displayed.</p>
<p class="config_option">googleAnalyticsId</p>
<p>
If you have a <a href="http://www.google.com/analytics/">Google
Analytics ID</a>, you can set it here. The Google Analytics
Javascript code will then be included, enabling analytics of
your website usage. If commented out, the code will not be
included.
</p>
<h3>Extending <span class="code">[Extras]</span></h3>
<p>Other tags can be added in a similar manner, including
sub-sections. 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 sub-section.</p>
<pre class="tty">[Extras]
[[video]]
still = video_capture.jpg
hyperlink = <a href="http://www.eatatjoes.com/video.html">http://www.eatatjoes.com/video.html</a></pre>
<p>Then in your template you could refer to these as:</p>
<pre class="tty">&lt;a href="$Extras.video.hyperlink"&gt;
&lt;img src="$Extras.video.still" alt="Video capture"/&gt;
&lt;/a&gt;</pre>
<h2 class="config_section">[Units]</h2>
<p>This section deals with Units and their formatting.</p>
<h3 class="config_section">[[Groups]]</h3>
<p>
This sub-section lists all the <em>Unit Groups</em> and
specifies which unit system is to be used for each one of them.
</p>
<p>
As there are many different observational measurement types
(such as '<span class="code">outTemp</span>', '<span
class="code">barometer</span>', etc.) used in <span
class="code">weewx</span> (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. <span class="code">Weewx</span>
has taken a middle route and divided all the different
observation types into 12 different "<em>unit groups</em>." A
unit group is something like "<span class="code">group_temperature</span>."
It represents the measurement system to be used by all
observation types that are measured in temperature, such as
inside temperature (type '<span class="code">inTemp</span>'),
outside temperature ('<span class="code">outTemp</span>'),
dewpoint ('<span class="code">dewpoint</span>'), wind chill ('<span
class="code">windchill</span>'), and so on. If you decide that
you want unit group <span class="code"> group_temperature</span>
to be measured in "<span class="code">degree_C</span>" then you
are saying <em>all</em> members of its group will be reported in
degrees Celsius.
</p>
<p>
Note that the unit system is always specified in the singular.
That is, specify "<span class="code">degree_C</span>" or "<span
class="code">foot</span>", not "<span class="code">degrees_C</span>"
or "<span class="code">feet</span>". See the Appendix <em>
<a href="#units">Units</a>
</em> for more information, including a concise summary of the
groups, their members, and which options can be used for each
group.
</p>
<p class="config_important">
<a class="config_option" id="group_altitude"> group_altitude</a>
</p>
<p>
Which measurement unit to be used for altitude. Possible options
are '<span class="code">foot</span>' or '<span class="code">meter</span>'.
</p>
<p class="config_option">group_direction</p>
<p>
Which measurement unit to be used for direction. The only option
is "<span class="code">degree_compass</span>".
</p>
<p class="config_option">group_moisture</p>
<p>
The measurement unit to be used for soil moisture. The only
option is "<span class="code">centibar</span>."
</p>
<p class="config_option">group_percent</p>
<p>
The measurement unit to be used for percentages. The only option
is "<span class="code">percent</span>".
</p>
<p class="config_important">group_pressure</p>
<p>
The measurement unit to be used for pressure. Possible options
are one of "<span class="code">inHg</span>" (inches of mercury),
"<span class="code">mbar</span>", or "<span class="code">hPa</span>."
</p>
<p class="config_option">group_radiation</p>
<p>
The measurement unit to be used for radiation. The only option
is "<span class="code">watt_per_meter_squared</span>."
</p>
<p class="config_important">group_rain</p>
<p>
The measurement unit to be used for precipitation. Options are "<span
class="code">inch</span>", "<span class="code">cm</span>," or
"<span class="code">mm</span>."
</p>
<p class="config_important">group_rainrate</p>
<p>
The measurement unit to be used for rate of precipitation.
Possible options are one of "<span class="code">inch_per_hour</span>",
"<span class="code">cm_per_hour</span>", or "<span class="code">mm_per_hour</span>".
</p>
<p class="config_important">group_speed</p>
<p>
The measurement unit to be used for wind speeds. Possible
options are one of "<span class="code">mile_per_hour</span>", "<span
class="code">km_per_hour</span>", "<span class="code">knot</span>",
or "<span class="code">meter_per_second</span>."
</p>
<p class="config_important">group_speed2</p>
<p>
This group is similar to <span class="code">group_speed</span>,
but is used for calculated wind speeds which typically have a
slightly higher resolution. Possible options are one "<span
class="code">mile_per_hour2</span>", "<span class="code">km_per_hour2</span>",
"<span class="code">knot2</span>", or "<span class="code">meter_per_second2</span>".
</p>
<p>
<a class="config_important" id="group_temperature">group_temperature</a>
</p>
<p>
The measurement unit to be used for temperatures. Options are "<span
class="code">degree_F</span>" or "<span class="code">degree_C</span>."
</p>
<p class="config_option">group_volt</p>
<p>
The measurement unit to be used for voltages. The only option is
"<span class="code">volt</span>."
</p>
<h3 class="config_section" id="Units_StringFormats">[[StringFormats]]</h3>
<p>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</p>
<pre class="tty">degree_C = %.1f
inch = %.2f</pre>
<p>
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 <a
href="http://docs.python.org/library/string.html#format-specification-mini-language">
formatting codes are those used by Python</a>, a, and are very
similar to C's <span class="code">sprintf()</span> codes.
</p>
<p>
You can also specify what string to use for an invalid or
unavailable measurement (value '<span class="code">None</span>').
For example,
</p>
<pre class="tty">NONE = " N/A "</pre>
<h3 class="config_section" id="Units_Labels">[[Labels]]</h3>
<p>This sub-section specifies what label is to be used for
each measurement unit type. For example, the options</p>
<pre class="tty">degree_F = &deg;F
inch = ' in'</pre>
<p>
would cause all temperatures to have unit labels <span
class="code">°F</span> and all precipitation to have labels <span
class="code">in</span>. 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).
</p>
<h3 class="config_section" id="Units_TimeFormats">[[TimeFormats]]</h3>
<p>
This sub-section is used for date and time labels, using <a
href="http://docs.python.org/library/datetime.html#strftime-behavior">
strftime()</a> formats. The values that come with the default <span
class="code"> skin.conf</span> will work for most locales, <a
href="#Environment_variable_LANG">provided you set the
environment variable LANG</a> before running <span class="code">weewx</span>.
</p>
<p>While they give acceptable results in all locales, the
defaults are not the most attractive. For example, the format
used for the month summary is</p>
<pre class="tty">month = %x %X</pre>
<p>which (in the U.S. locale) results in something that looks
like</p>
<p class="example_output">11/09/09 05:20:00 PM</p>
<p>You may wish to do something fancier. For example, try this
one</p>
<pre class="tty">month = %d-%b-%Y %H:%M</pre>
<p>which results in a label that looks like:</p>
<p class="example_output">06-Oct-2009 15:20</p>
<p>
Section <span class="code">[[TimeFormats]]</span> also allows
the formatting to be set for almanac times:
</p>
<pre class="tty">ephem_day = %X
ephem_year = %x %X</pre>
<p>
The first of these, <span class="code">ephem_day</span>, is used
for almanac times within the day, such as sunrise or sunset. The
second, <span class="code">ephem_year</span>, is used for
almanac times within the year, such as the next equinox or full
moon.
</p>
<h3 class="config_section">[[Ordinates]]</h3>
<p class="config_option">directions</p>
<p>
Set to the abbreviations to be used for ordinal directions. By
default, this is <span class="code">N, NNE, NE, ENE, E,
ESE, SE, SSE, S, SSW, SW, WSW, W, WNW, NW, NNW, N</span>.
</p>
<h3 class="config_section">[[DegreeDays]]</h3>
<p class="config_important">
heating_base <br /> cooling_base
</p>
<p>Set to the base temperature for calculating heating and
cooling degree-days, along with the unit to be used. Examples:</p>
<pre class="tty">heating_base = 65.0, degree_F
cooling_base = 20.0, degree_C</pre>
<h3 class="config_section" id="trend">[[Trend]]</h3>
<p class="config_option">time_delta</p>
<p>Set to the time difference over which you want trends to be
calculated. The default is 3 hours.</p>
<p class="config_option">time_grace</p>
<p>
When searching for a previous record to be used in calculating a
trend, a record within this amount of <span class="code">time_delta</span>
will be accepted. Default is 300 seconds.
</p>
<h2 class="config_section">[Labels]</h2>
<p>This section sets the various labels to use.</p>
<p class="config_option">hemispheres</p>
<p>
Comma separated list for the labels to be used for the four
hemispheres. The default is "<span class="code">N, S, E,
W</span>".
</p>
<p class="config_option">latlon_formats</p>
<p>Comma separated list for the formatting to be used when
converting latitude and longitude to strings. There should be
three elements:</p>
<ol>
<li>The format to be used for whole degrees of latitude</li>
<li>The format to be used for whole degrees of longitude</li>
<li>The format to be used for minutes.</li>
</ol>
<p>This allows you to decide whether or not you want leading
zeroes. The default includes leading zeroes and is "%02d",
"%03d", "%05.2f"</p>
<h3 class="config_section">[[Generic]]</h3>
<p>This sub-section specifies default labels to be used for
each SQL type. For example, options</p>
<pre class="tty">inTemp = Temperature inside the house
outTemp = Outside Temperature</pre>
<p>
would cause the given labels to be used for plots involving SQL
types <span class="code">inTemp</span> and <span class="code">outTemp</span>.
</p>
<h2 class="config_section">[Almanac]</h2>
<p>This section controls what text to use for the almanac. It
consists of only one entry</p>
<p class="config_option">moon_phases</p>
<p>
This option is a comma separated list of labels to be used for
the eight phases of the moon. Default is "<span class="code">New,
Waxing crescent, First quarter, Waxing gibbous, Full, Waning
gibbous, Last quarter, Waning crescent</span>".
</p>
<h2 class="config_section">[CheetahGenerator]</h2>
<p>
This section is used by generator <span class="code">weewx.cheetahgenerator.CheetahGenerator</span>
and controls text generation from templates, specifically which
files are to be produced from which template.
</p>
<p>
Before V2.5, this section was called <span class="code">[FileGenerator]</span>.
Older versions and names are 100% backwards compatible.
</p>
<h3>Overview of file generation</h3>
<p>
Files are generated from templates, and each template is
identified by the <span class="config_option">template</span>
parameter.
</p>
<p>
Each template file is named something like <span class="code"><em>D/F.E.tmpl</em></span>,
where <span class="code">D</span> is the (optional) directory
the template sits in and will also be the directory the results
will be put in, and <span class="code"> F.E</span> is the
generated file name. So, given a template file with name <span
class="code"> Acme/index.html.tmpl</span>, the results will be
put in <span class="symcode"> $HTML_ROOT</span><span
class="code">/Acme/index.html</span>.
</p>
<p>The configuration for a group of templates will look
something like this:</p>
<pre class="tty">[CheetahGenerator]
[[index]]
template = index.html.tmpl
[[textfile]]
template = filename.txt.tmpl
[[xmlfile]]
template = filename.xml.tmpl</pre>
<p>
There can be only one <span class="config_option">template</span>
in each block. In most cases, the block name does not matter -
it is used only to isolate each template. However, there are two
block names that have speacial meaning: SummaryByMonth and
SummaryByYear. These are described below.
</p>
<p>The file generator runs on each new archive record. In a
default weewx installation, that would be every 5 minutes.</p>
<p>
Cheetah processes each template to generate a file. Cheetah
follows any logic defined by directives such as <span
class="code">for</span> or <span class="code">if ...
else</span>, and it replaces variables such as <span class="code">$Extras.radar_url</span>
or <span class="code">$current.outTemp.max</span>.
</p>
<p>Variables are defined by objects in weewx. Some variables
are static, others are linked to data in databases. The list of
variables can be extended.</p>
<h3>File generation options</h3>
<p class="config_option">search_list</p>
<p>
This is the list of search list objects that will be scanned by
the template engine, looking for tags. See the section <em>
<a href="#defining_new_tags">Defining new tags</a>
</em> and the <a
href="http://cheetahtemplate.org/docs/users_guide_html/users_guide.html">
Cheetah documentation</a> for details on search lists. If no <span
class="config_option"> search_list</span> is specified, a
default list will be used. The default list is:
</p>
<pre class="tty">search_list = weewx.cheetahgenerator.Almanac, weewx.cheetahgenerator.Station, weewx.cheetahgenerator.Stats, weewx.cheetahgenerator.UnitInfo, weewx.cheetahgenerator.Extras, weewx.cheetahgenerator.Current</pre>
<p class="config_option">search_list_extensions</p>
<p>
This defines one or more search list objects that will be
appended to the <span class="config_option">search_list</span>.
For example, the following adds alltime and forecast variables
to the search list.
</p>
<pre class="tty">search_list_extensions = examples.xsearch.MyXSearch, user.forecast.ForecastVariables</pre>
<p class="config_option">encoding</p>
<p>This option controls which encoding is to be used for the
generated output. The encoding can be specified for individual
files. There are 3 possible choices:</p>
<table class="indent" style="width: 90%">
<tbody>
<tr>
<td><strong>Encoding</strong></td>
<td><strong>Comments</strong></td>
</tr>
<tr>
<td class="code">html_entities</td>
<td>Non 7-bit characters will be represented as HTML
entities (<em>e.g.</em>, the degree sign will be
represented as <span class="code">&amp;#176;</span>)
</td>
</tr>
<tr>
<td class="code">utf8</td>
<td>Non 7-bit characters will be represented in UTF-8.</td>
</tr>
<tr>
<td class="code">strict_ascii</td>
<td>Non 7-bit characters will be ignored.</td>
</tr>
</tbody>
</table>
<p>
The encoding <span class="code">html_entities</span> is the
default.
</p>
<p class="config_option">template</p>
<p>
The name of a template file. A template filename must end with <span
class="code">.tmpl</span>. Filenames are case-sensitive. If
the template filename has the letters <span class="code">YYYY</span>
or <span class="code">MM</span> in its name, these will be
substituted for the year and month, respectively. So, a template
with the name <span class="code">summary-YYYY-MM.html.tmpl</span>
would have name <span class="code">summary-2010-03.html</span>
for the month of March, 2010.
</p>
<p class="config_option">stale_age</p>
<p>File staleness age, in seconds. If the file is older than
this age it will be generated from the template. If no stale_age
is specified the file will be generated each time the generator
runs.</p>
<p class="config_option">[[SummaryByMonth]]</p>
<p>
The <span class="code">SummaryByMonth</span> section defines
some special behavior. Each template in this section will be
used multiple times, each time with a different per-month
timespan. Be sure to include <span class="code"> YYYY</span> and
<span class="code">MM</span> in the filename of any template in
this section.
</p>
<p class="config_option">[[SummaryByYear]]</p>
<p>
The <span class="code">SummaryByYear</span> section defines some
special behavior. Each template in this section will be used
multiple times, each time with a different per-year timespan. Be
sure to include <span class="code"> YYYY</span> in the filename
of any template in this section.
</p>
<h3>Customizing file generation</h3>
<p>The best way to customization file generation is to make a
copy of a working report/skin, then make incremental changes.</p>
<p>
When there is an error during template generation, the error
will show up in the log file. Many errors are obvious - Cheetah
will display a line number and list the template file in which
the error occurred. In some cases the error reporting is rather
obscure. So make small changes and tests often. Use <span
class="code">wee_report</span> to test modifications to the
generator configuration and/or the template contents.
</p>
<h3>An Example: the Standard skin</h3>
<p>
Here is the <span class="code">[CheetahGenerator]</span> section
from the Standard <span class="code">skin.conf</span>
</p>
<pre class="tty">[CheetahGenerator]
# This section is used by the generator CheetahGenerator, and specifies
# which files are to be generated from which template.
encoding = html_entities
[[SummaryByMonth]]
# Reports that summarize "by month"
[[[NOAA_month]]]
encoding = strict_ascii
template = NOAA/NOAA-YYYY-MM.txt.tmpl
[[SummaryByYear]]
# Reports that summarize "by year"
[[[NOAA_year]]]
encoding = strict_ascii
template = NOAA/NOAA-YYYY.txt.tmpl
[[ToDate]]
# Reports that show statistics "to date", such as day-to-date,
# week-to-date, month-to-date, etc.
[[[day]]]
template = index.html.tmpl
[[[week]]]
template = week.html.tmpl
[[[month]]]
template = month.html.tmpl
[[[year]]]
template = year.html.tmpl
[[[RSS]]]
template = RSS/weewx_rss.xml.tmpl
[[[Mobile]]]
template = mobile.html.tmpl</pre>
<p>The Standard skin contains three different kinds of
generated output:</p>
<ol>
<li>Summary by Month. The Standard skin uses <span
class="code">SummaryByMonth</span> to produce NOAA summaries,
one for each month, as a simple text file.
</li>
<li>Summary by Year. The Standard skin uses <span
class="code">SummaryByYear</span> to produce NOAA summaries,
one for each year, as a simple text file.
</li>
<li>Summary "To Date". The Standard skin produce reports
for the day, week, month, and year-to-date observations. These
files are HTML. The first, the daily summary (output filename
is <span class="code">index.html</span>), includes a drop-down
list that displays the NOAA month and yearly summaries.
</li>
</ol>
<p>
The encoding for text files is <span class="code">strict_ansii</span>,
whereas the encoding for html files is <span class="code">html_entities</span>.
In the Standard skin this is specified by declaring <span
class="code">encoding = html_entities</span> at the top level
of <span class="code"> [CheetahGenerator]</span> then <span
class="code">encoding = strict_ansii</span> for each text
file.
</p>
<p>
Other than <span class="code">SummaryByMonth</span> and <span
class="code">SummaryByYear</span>, the section names are
arbitrary. <span class="code">ToDate</span> could just as well
have been called <span class="code">files_to_date</span>, and
the sections <span class="code">day</span>, <span class="code">week</span>,
and <span class="code">month</span> could just as well have been
called <span class="code">tom</span>, <span class="code">dick</span>,
and <span class="code">harry</span>.
</p>
<h2 class="config_section">[CopyGenerator]</h2>
<p>
This section is used by generator <span class="code">
weewx.reportengine.CopyGenerator</span> and controls which files are
to be copied over from the skin directory 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.
</p>
<h3>Copy generator options</h3>
<p class="config_option">copy_once</p>
<p>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.</p>
<p class="config_option">copy_always</p>
<p>This is a list of files that should be copied on every
invocation. Wildcards can be used.</p>
<h3>An Example: the Standard skin</h3>
<p>
Here is the <span class="code">[CopyGenerator]</span> section
from the Standard <span class="code">skin.conf</span>
</p>
<pre class="tty">[CopyGenerator]
# This section is used by the generator CopyGenerator
# List of files to be copied only the first time the generator runs
copy_once = backgrounds/*, weewx.css, mobile.css, favicon.ico
# List of files to be copied each time the generator runs
# copy_always = </pre>
<p>The Standard skin includes some background images, CSS
files, and icons that need to be copied once. There are no files
that need to be copied each time the generator runs.</p>
<h2 class="config_section">[ImageGenerator]</h2>
<p>
This section is used by generator <span class="code">
weewx.reportengine.ImageGenerator</span> and controls which images
(plots) get generated and with which options. While complicated,
it is extremely flexible and powerful.
</p>
<h3>Time periods</h3>
<p>
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 is a typical set of options for
sub-section <span class="code">[[month_images]]</span>,
controlling how images that cover a month period are generated:
</p>
<pre class="tty">[[month_images]]
x_label_format = %d
bottom_label_format = %x %X
time_length = 2592000 # == 30 days
aggregate_type = avg
aggregate_interval = 10800 # == 3 hours</pre>
<p>Here's a description of these options.</p>
<p>
The option <span class="code">x_label_format</span> gives a <a
href="http://docs.python.org/library/datetime.html#strftime-behavior">
strftime()</a> type format for the x-axis. In this example, it
will only show the day of the month (format option "<span
class="code">%d</span>").
</p>
<p>
The <span class="code">bottom_label_format</span> is the format
used for the time stamp at the bottom of the image. In this
example, it has been specified as <span class="code">"%x&nbsp;%X"</span>,
which will use date and time formatting appropriate for your
locale, <a href="#Environment_variable_LANG">provided you
set environment variable LANG first</a>. For example, in the U.S.,
it will show the time as <span class="code">10/25/09&nbsp;05:20:00&nbsp;PM</span>,
while in Europe it might show <span class="code">25/10/09
17:20:00</span>.
</p>
<p>
The option <span class="code">time_length&nbsp;=&nbsp;2592000</span>
says that the plot will cover a nominal 30 days.
</p>
<p>
The option <span class="code">aggregate_type&nbsp;=&nbsp;avg</span>
says that each point in the plot will be the average over a time
period. The option <span class="code">aggregate_interval&nbsp;=&nbsp;10800</span>
says that time period will be 3 hours.
</p>
<h3>Image files</h3>
<p>
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 is a typical set of options for
sub-sub-section <span class="code"> [[[monthrain]]]</span>:
</p>
<pre class="tty">[[[monthrain]]]
plot_type = bar
yscale = None, None, 0.02
[[[[rain]]]]
aggregate_type = sum
aggregate_interval = 86400
label = Rain (daily avg)</pre>
<p>
This will generate an image file with name <span class="code">
monthrain.png</span>. It will be a bar plot. Option <span
class="code"> yscale</span> 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. The option takes a 3-way tuple
(<span class="code">ylow</span>, <span class="code">
yhigh</span>, <span class="code">min_interval</span>), where <span
class="code">ylow</span> and <span class="code">yhigh</span>
are the minimum and maximum y-axis values, respectively, and <span
class="code">min_interval</span> is the minimum tick interval.
If set to '<span class="code">None</span>', the corresponding
value will be automatically chosen. So, in this example, the
setting
</p>
<pre class="tty">yscale = None, None, 0.02</pre>
<p>
will cause <span class="code">weewx</span> to pick sensible y
minimum and maximum values, but require that the tick increment
(<span class="code">min_interval</span>) be at least 0.02.
</p>
<p>
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 "<span class="code">rain</span>". Because we
have not said otherwise, the SQL data type to be used for this
line will be the same as its logical name, that is, <span
class="code">rain</span>, but this can be overridden (see
below). The aggregation type will be summing (overriding the
averaging specified in sub-section <span class="code">[[month_images]]</span>),
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 ('<span
class="code">Rain (daily avg)</span>')
</p>
<p>
If there is a time gap in the data, the options <span
class="code"> line_gap_fraction</span> and <span class="code">bar_gap_fraction</span>
control how it will be drawn. The former, <span class="code">line_gap_fraction</span>,
is used for line graphs, the latter, <span class="code">bar_gap_fraction</span>,
for bar graphs. Here's what the resultant plots look like
without and with this option being specified:
</p>
<div class="center" style="width: 80%; margin: 0 auto;">
<div style="float: left">
<img width="334" height="197" src="day-gap-not-shown.png"
alt="Gap not shown" />
<p class="caption">
No <span class="code">line_gap_fraction</span> specified
</p>
</div>
<div>
<img width="334" height="197" src="day-gap-showing.png"
alt="Gap showing" />
<p class="caption">
With <span class="code">line_gap_fraction=0.01</span>
</p>
</div>
</div>
<div style="clear: both"></div>
<h3>Including more than one SQL type in a plot</h3>
<p>More than one SQL type can be included in a plot. For
example, here is how to generate a plot with the week's outside
temperature as well as dewpoint:</p>
<pre class="tty">[[[monthtempdew]]]
[[[[outTemp]]]]
[[[[dewpoint]]]]</pre>
<p>
This would create an image in file <span class="code">monthtempdew.png</span>
that includes a line plot of both outside temperature and
dewpoint.
</p>
<h3>Including the same SQL type more than once in a plot</h3>
<p>
Another example. Say you want a plot of the day's temperature,
overlaid with hourly averages. Here, you are using the same data
type ('<span class="code">outTemp</span>') for both plot lines,
the first with averages, the second without. If you do the
obvious it won't work:
</p>
<pre class="tty">## WRONG ##
[[[daytemp_with_avg]]]
[[[[outTemp]]]]
aggregate_type = avg
aggregate_interval = 3600
[[[[outTemp]]]] # OOPS! The same section name appears more than once!</pre>
<p>
The option parser does not allow the same section name ('<span
class="code">outTemp</span>' 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 <span class="code">data_type</span>. This will
override the default action that the logical line name is used
for the SQL type. So, our example would look like this:
</p>
<pre class="tty">[[[daytemp_with_avg]]]
[[[[a_logical_name]]]]
data_type = outTemp
aggregate_type = avg
aggregate_interval = 3600
label = Avg. Temp.
[[[[outTemp]]]]</pre>
<p>
Here, the first logical line has been given the name "<span
class="code">a_logical_name</span>" to distinguish it from the
second line "<span class="code">outTemp</span>". We have
specified that the first line will use data type <span
class="code"> outTemp</span> and that it will use averaging
over a one hour period. The second also uses <span class="code">outTemp</span>,
but will not use averaging.
</p>
<p>The result is a nice plot of the day's temperature,
overlaid with a 3-hour smoothed average:</p>
<p class="center">
<img width="300" height="180"
alt="Daytime temperature with running average"
src="daytemp_with_avg.png" />
</p>
<p>One more example. This one shows daily high and low
temperatures for a year:</p>
<pre class="tty">[[year_images]]
...
[[[yearhilow]]]
[[[[hi]]]]
data_type = outTemp
aggregate_type = max
label = High
[[[[low]]]]
date_type = outTemp
aggregate_type = min
label = Low Temperature</pre>
<p>
This results in the plot <span class="code">yearhilow.png</span>:
</p>
<p class="center">
<img width="300" height="180" alt="Daily highs and lows"
src="yearhilow.png" />
</p>
<h3>Progressive vector plots</h3>
<p>
<span class="code">Weewx</span> can produce progressive vector
plots as well as the more conventional x-y plots. To produce
these, use plot type '<span class="code">vector</span>'. You
need a vector type to produce this kind of plot. There are two:
'<span class="code">windvec</span>', and '<span class="code">windgustvec</span>'.
While they do not actually appear in the SQL database, <span
class="code">weewx</span> understands that they represent
special vector-types. The first, '<span class="code">windvec</span>',
represents the average wind in an archive period, the second, '<span
class="code">windgustvec</span>' 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:
</p>
<pre class="tty">[[[weekgustoverlay]]]
aggregate_interval = 3600
[[[[windvec]]]]
label = Hourly Wind
plot_type = vector
aggregate_type = avg
[[[[windgustvec]]]]
label = Gust Wind
plot_type = vector
aggregate_type = max</pre>
<p>
This will produce an image file with name <span class="code">
weekgustoverlay.png</span>. 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:
</p>
<p class="center">
<img width="300" height="180"
alt="hourly average wind vector overlaid with gust vectors"
src="weekgustoverlay.png" />
</p>
<p>
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 do not line up over the x-axis, overlaying each
other. Do this by using option <span class="code">
vector_rotate</span>. For example, with my chronic westerlies, I set
<span class="code"> vector_rotate</span> to 90.0 for the plot
above, so winds out of the west point straight up.
</p>
<p>
If you use this kind of plot (the out-of-the-box version of <span
class="code"> weewx</span> 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.
</p>
<h3>Overriding values</h3>
<p>
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 <span
class="code">weewx.conf</span> file specifies plot size of
300x180 pixels, which will be used for all plots unless
overridden:
</p>
<pre class="tty">[Images]
...
image_width = 300
image_height = 180</pre>
<p>
The standard plot of barometric pressure will appear in <span
class="code"> daybarometer.png</span>:
</p>
<pre class="tty">[[[daybarometer]]]
[[[[barometer]]]] </pre>
<p>
We now add our special plot of barometric pressure, but specify
a larger image size. This image will be put in file <span
class="code"> daybarometer_big.png</span>.
</p>
<pre class="tty">[[[daybarometer_big]]]
image_width = 600
image_height = 360
[[[[barometer]]]]</pre>
<h3>An Example: the Standard skin</h3>
<p>
Here is part of the <span class="code">[ImageGenerator]</span>
section from the Standard <span class="code">skin.conf</span>
</p>
<pre class="tty">[ImageGenerator]
image_width = 300
image_height = 180
...
plot_type = line # default to line, can be overridden at any level
aggregate_type = none # default to none, can be overridden at any level
width = 1 # default to skinny lines
time_length = 86400 # == 24 hours
[[day_images]]
x_label_format = %H:%M
bottom_label_format = %x %X
time_length = 97200 # == 27 hours
[[[daybarometer]]]
[[[[barometer]]]]
[[[dayrain]]]
# Make sure the y-axis increment is at least 0.02 for the rain plot
yscale = None, None, 0.02
plot_type = bar
[[[[rain]]]]
aggregate_type = sum
aggregate_interval = 3600
label = Rain (hourly total)
[[[daywinddir]]]
# Hardwire in the y-axis scale for wind direction
yscale = 0.0, 360.0, 45.0
[[[[windDir]]]]
[[[daywindvec]]]
[[[[windvec]]]]
plot_type = vector
[[[dayradiation]]]
[[[[radiation]]]]
[[week_images]]
x_label_format = %d
bottom_label_format = %x %X
time_length = 604800 # == 7 days
aggregate_type = avg
aggregate_interval = 3600
[[[weekbarometer]]]
[[[[barometer]]]]
[[[weekrain]]]
yscale = None, None, 0.02
plot_type = bar
[[[[rain]]]]
aggregate_type = sum
aggregate_interval = 86400
label = Rain (daily total)
[[[weekwinddir]]]
yscale = 0.0, 360.0, 45.0
[[[[windDir]]]]
[[[weekwindvec]]]
[[[[windvec]]]]
plot_type = vector
[[[weekradiation]]]
[[[[radiation]]]]
...
</pre>
<p>The Standard skin defines many different types of plots.
Note that each plot will be created only if there are data that
match the definition. For example, if a station does not produce
radiation data, no radiation images will be generated. This
means that a single report can be used for many different
stations, even if the stations have different sensors or
capabilities.</p>
<h2 class="config_section" id="generators">[Generators]</h2>
<p>This section defines the generators that should be run as
well as options for specific generators.</p>
<h3>Generator options</h3>
<p class="config_option">generator_list</p>
<p>This option controls which generators get run for this
skin. It is a comma separated list. The generators will be run
in this order.</p>
<h3>An Example: the Standard skin</h3>
<p>
Here is the <span class="code">[Generators]</span> section from
the Standard <span class="code">skin.conf</span>
</p>
<pre class="tty">[Generators]
generator_list = weewx.cheetahgenerator.CheetahGenerator, weewx.imagegenerator.ImageGenerator, weewx.reportengine.CopyGenerator</pre>
<p>The Standard skin uses three generators: CheetahGenerator,
ImageGenerator, and CopyGenerator.</p>
<h1 id="localization">Localization</h1>
<p>Weewx has been designed to make localization fairly
straightforward. What follows is a guide to localizing to a
non-English language and/or locale.</p>
<h2>Translate the templates</h2>
<p>
First, you will need to go through the templates and translate
to your target language. Obvious text strings such as "<span
class="code">Current Weather Conditions</span>" will need to
be translated.
</p>
<h2>Modify skin.conf to reflect local conventions</h2>
<p>
Next, you will need to go through <span class="code">skin.conf</span>
and modify options to follow local conventions. This includes
dates and labels.
</p>
<h3>Dates</h3>
<p>
The date and time formats used in the default distribution of <span
class="code">weewx</span> are locale independent, so they
should be OK.
</p>
<pre class="tty"> [[TimeFormats]]
#
# This section sets the string format to be used for
# each time scale.
#
day = %X
week = %X (%A)
month = %x %X
year = %x %X
rainyear = %x %X
current = %x %X
ephem_day = %X
ephem_year = %x %X</pre>
<p>The specifiers "%X" and "%x" say to format the time and
date, respectively, in a way that is suitable for your locale.</p>
<p>The bottom label format used in plots should also be OK:</p>
<pre class="tty">bottom_label_format = %x %X</pre>
<h3>Labels</h3>
<p>There are several almanac labels that may need to be
changed. These include:</p>
<pre class="tty">hemisphere = N, S, E, W</pre>
<p>and</p>
<pre class="tty">moon_phases = New, Waxing crescent, First quarter, Waxing gibbous, Full, Waning gibbous, Last quarter, Waning crescent</pre>
<p>Most of the unit labels either follow ISO conventions, or
are unlikely to be used outside English speaking countries (an
example would be "foot"). But, there are two exceptions:</p>
<pre class="tty">hour = " hrs"
second = " secs"</pre>
<p>You will also have to change the generic labels given to
your weather observations:</p>
<pre class="tty">barometer = Barometer
dewpoint = Dew Point
heatindex = Heat Index
inHumidity = Inside Humidity
inTemp = Inside Temperature
outHumidity = Outside Humidity
outTemp = Outside Temperature
radiation = Radiation
rain = Rain
rainRate = Rain Rate
rxCheckPercent = ISS Signal Quality
windDir = Wind Direction
windGust = Gust Speed
windGustDir = Gust Direction
windSpeed = Wind Speed
windchill = Wind Chill
windgustvec = Gust Vector
windvec = Wind Vector
extraTemp1 = Pond Temperature</pre>
<h2 id="Environment_variable_LANG">
Environment variable <span class="code"> LANG</span>
</h2>
<p>
Finally, you will need to set the environment variable <span
class="code"> LANG</span> to reflect your locale. For example,
assuming you set
</p>
<pre class="tty">$ export LANG=es_ES.UTF-8</pre>
<p>
before running <span class="code">weewx</span>, then the local
Spanish names for days of the week and months of the year will
be used. The decimal point for numbers will also be modified
appropriately.
</p>
<h1 id="service_engine">
Customizing the <span class="code">weewx</span> service engine
</h1>
<p>This is an advanced 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.</p>
<p>
<em>Please note that the service engine is likely to change
in future versions! </em>
</p>
<p>
At a high level, <span class="code">weewx</span> consists of an
<em> engine</em> that is responsible for managing a set of <em>services</em>.
A service consists of a Python class which binds its member
functions to various <em> events</em>. The engine arranges to
have the bound member function called when a specific event
happens, such as a new LOOP packet arriving.
</p>
<p>To customize, you can</p>
<ul>
<li>Customize a service</li>
<li>Add a service</li>
</ul>
<p>
The default install of <span class="code">weewx</span> includes
the following services, shown in the order they are normally
run:
</p>
<table class="indent" style="width: 80%">
<tbody>
<tr>
<td><strong>Service</strong></td>
<td><strong>Function</strong></td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdConvert</td>
<td>Converts the units of the input to a target unit
system (such as US or Metric).</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdCalibrate</td>
<td>Adjust new LOOP and archive packets using
calibration expressions.</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdQC</td>
<td>Check that observation values fall within a
specified range.</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdArchive</td>
<td>Archive any new data to the SQL databases.</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdTimeSynch</td>
<td>Arrange to have the clock on the console
synchronized at regular intervals.</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdPrint</td>
<td>Print out new LOOP and archive packets on the
console.</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdRESTful</td>
<td>Start a thread to manage <a
href="http://en.wikipedia.org/wiki/Representational_State_Transfer">
RESTful</a> (simple stateless client-server protocols)
connections; such as those used by the Weather Underground
or PWSweather.
</td>
</tr>
<tr>
<td class="code">weewx.wxengine.StdReport</td>
<td>Launch 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/rsync files
to a web server. New reports can be added easily by the
user.</td>
</tr>
</tbody>
</table>
<h2>Customizing a Service</h2>
<p>
The service <span class="code">weewx.wxengine.StdPrint</span>
prints out new LOOP and archive packets to the console when they
arrive. By default, it prints out the entire record, which
generally includes a lot of possibly distracting information and
can be rather messy. Suppose you do not like this, and want it
to print out only the time, barometer reading, and the outside
temperature whenever a new LOOP packet arrives. This could be
done by subclassing the default print service <span class="code">
StdPrint</span> and overriding member function <span class="code">new_loop_packet()</span>.
</p>
<p>
Create the file <span class="symcode">$BIN_ROOT</span><span
class="code">/user/myprint.py</span>:
</p>
<pre class="tty">from weewx.wxengine import StdPrint
from weeutil.weeutil import timestamp_to_string
class MyPrint(StdPrint):
# Override the default new_loop_packet member function:
def new_loop_packet(self, event):
packet = event.packet
print "LOOP: ", timestamp_to_string(packet['dateTime']),
"BAR=", packet.get('barometer', 'N/A'),
"TEMP=", packet.get('outTemp', 'N/A')</pre>
<p>
This service substitutes a new implementation for the member
function <span class="code">new_loop_packet</span>. This
implementation prints out the time, then the barometer reading
(or '<span class="code">N/A</span>' if it is not available) and
the outside temperature (or '<span class="code">N/A</span>').
</p>
<p>
You then need to specify that your print service class should be
loaded instead of the default <span class="code">StdPrint</span>
service. This is done by substituting your service name for <span
class="code">StdPrint</span> in the list of reporting services,
found in option <span class="code">report_services</span>,
located in <span class="code"> [Engines][[WxEngine]]</span>. When you're done,
it will look something like this:
</p>
<pre class="tty">
[Engines]
# This section configures the internal weewx engines.
# It is for advanced customization.
[[WxEngine]]
# The list of services the main weewx engine should run:
prep_services = weewx.wxengine.StdTimeSynch
process_services = weewx.wxengine.StdConvert, weewx.wxengine.StdCalibrate, weewx.wxengine.StdQC
archive_services = weewx.wxengine.StdArchive,
restful_services = weewx.restx.StdWunderground, weewx.restx.StdPWSweather, weewx.restx.StdCWOP, weewx.restx.StdStationRegistry
report_services = <span class="highlight">weewx.wxengine.StdPrint, </span>weewx.wxengine.StdReport</pre>
<h2>Adding a Service</h2>
<p>
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 <span class="code">StdService</span>,
and then adding the functionality you need. Here is an example
that implements an alarm that sends off an email when an
arbitrary expression evaluates <span class="code">True</span>.
This example is included in the standard distribution in
directory <span class="symcode">$BIN_ROOT</span><span
class="code">/examples</span>.
</p>
<p>
File <span class="code">examples/alarm.py</span>:
</p>
<pre class="tty">import time
import smtplib
from email.mime.text import MIMEText
import threading
import syslog
import weewx
from weewx.wxengine import StdService
from weeutil.weeutil import timestamp_to_string, option_as_list
# 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, config_dict):
# Pass the initialization information on to my superclass:
super(MyAlarm, self).__init__(engine, config_dict)
# This will hold the time when the last alarm message went out:
self.last_msg_ts = 0
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 = config_dict['Alarm']['expression']
self.time_wait = int(config_dict['Alarm'].get('time_wait', 3600))
self.smtp_host = config_dict['Alarm']['smtp_host']
self.smtp_user = config_dict['Alarm'].get('smtp_user')
self.smtp_password = config_dict['Alarm'].get('smtp_password')
self.SUBJECT = config_dict['Alarm'].get('subject', "Alarm message from weewx")
self.FROM = config_dict['Alarm'].get('from', 'alarm@weewx.com')
self.TO = option_as_list(config_dict['Alarm']['mailto'])
syslog.syslog(syslog.LOG_INFO, "alarm: Alarm set for expression: \"%s\"" % self.expression)
# If we got this far, it's ok to start intercepting events:
self.bind(weewx.NEW_ARCHIVE_RECORD, self.newArchiveRecord) # NOTE 1
except Exception, e:
syslog.syslog(syslog.LOG_INFO, "alarm: No alarm set. %s" % e)
def newArchiveRecord(self, event):
"""Gets called on a new archive record event."""
# 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) &gt;= self.time_wait :
# Get the new archive record:
record = event.record
# Evaluate the expression in the context of the event archive record.
# Sound the alarm if it evaluates true:
if eval(self.expression, None, record): # NOTE 2
# 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, record))
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'])
# Log it in the system log:
syslog.syslog(syslog.LOG_INFO, "alarm: Alarm expression \"%s\" evaluated True at %s" % (self.expression, t_str))
# 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'] = self.SUBJECT
msg['From'] = self.FROM
msg['To'] = ','.join(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()
syslog.syslog(syslog.LOG_DEBUG, " **** using encrypted transport")
except smtplib.SMTPException:
syslog.syslog(syslog.LOG_DEBUG, " **** using unencrypted transport")
try:
# 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)
syslog.syslog(syslog.LOG_DEBUG, " **** logged in with user name %s" % (self.smtp_user,))
# Send the email:
s.sendmail(msg['From'], self.TO, msg.as_string())
# Log out of the server:
s.quit()
except Exception, e:
syslog.syslog(syslog.LOG_ERR, "alarm: SMTP mailer refused message with error %s" % (e,))
raise
# Log sending the email:
syslog.syslog(syslog.LOG_INFO, " **** email sent to: %s" % self.TO) </pre>
<p>
This service expects all the information it needs to be in the
configuration file <span class="code">weewx.conf</span> in a new
section called <span class="code">[Alarm]</span>. So, add the
following lines to your configuration file:
</p>
<pre class="tty">[Alarm]
expression = "outTemp &lt; 40.0"
time_wait = 3600
smtp_host = smtp.mymailserver.com
smtp_user = myusername
smtp_password = mypassword
mailto = auser@adomain.com, anotheruser@someplace.com
from = me@mydomain.com
subject = "Alarm message from weewx!"</pre>
<p>
There are two important points to be noted in this example, each
marked with a "<span class="code">NOTE</span>" flag in the code.
</p>
<ol>
<li>Here is where the binding happens between an event,
weewx.NEW_ARCHIVE_RECORD in this example, and a member
function, self.newArchiveRecord. There are other events that
can be interecepted. Look in the file <span class="symcode">$BIN_ROOT</span><span
class="code">/weewx/__init__.py</span>.
</li>
<li>This is where the test is done of whether or not to
sound the alarm. The <span class="code">[Alarm]</span>
configuration options specify that the alarm be sounded when "<span
class="code">outTemp &lt; 40.0</span>" evaluates <span
class="code">True</span>, 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.
</li>
</ol>
<p>Another example expression could be:</p>
<pre class="tty">expression = "outTemp &lt; 32.0 and windSpeed &gt; 10.0"</pre>
<p>In this case, the alarm is sounded if the outside
temperature drops below freezing and the wind speed is greater
than 10.0.</p>
<p>
Option <span class="code">time_wait</span> is used to avoid a
flood of nearly identical emails. The new service will wait this
long before sending another email out.
</p>
<p>
Email will be sent through the SMTP host specified by option <span
class="code"> smtp_host</span>. The recipient(s) are specified
by the comma separated option <span class="code">mailto</span>.
</p>
<p>
Many SMTP hosts require user login. If this is the case, the
user and password are specified with options <span class="code">smtp_user</span>
and <span class="code">smtp_password</span>, respectively.
</p>
<p>
The last two options, "<span class="code">from</span>" and "<span
class="code">subject</span>" are optional. If not supplied, <span
class="code">weewx</span> will supply something sensible.
Note, however, that some mailers require a valid "from" email
address and the one <span class="code">weewx</span> supplies may
not satisfy its requirements.
</p>
<p>
To make this all work, you must tell the engine to load this new
service. You must also make sure it gets its events at the right
time. Because this is a kind of "reporting" service, that is, it
consumes and interprets archived data, it is best put among the
other reporting services, specified by option <span class="code">report_services</span>,
located in section <span class="code">[Engines][[WxEngine]]</span>.
When you're done, that section will look something like this:
</p>
<pre class="tty">[Engines]
# This section configures the internal weewx engines.
# It is for advanced customization.
[[WxEngine]]
# The list of services the main weewx engine should run:
prep_services = weewx.wxengine.StdTimeSynch
process_services = weewx.wxengine.StdConvert, weewx.wxengine.StdCalibrate, weewx.wxengine.StdQC
archive_services = weewx.wxengine.StdArchive,
restful_services = weewx.restx.StdWunderground, weewx.restx.StdPWSweather, weewx.restx.StdCWOP, weewx.restx.StdStationRegistry
report_services = weewx.wxengine.StdPrint, weewx.wxengine.StdReport<span class="highlight">, examples.alarm.MyAlarm</span></pre>
<p>
Note that each of these lists must be all on one line.
Unfortunately, the parser <span class="code">ConfigObj</span> does
not allow options to be continued on to following lines.
</p>
<p>
In addition to the example above, the distribution also includes
a low-battery alarm (<span class="code">lowBattery.py</span>),
which is similar, except that it intercepts LOOP events (instead
of archiving events).
</p>
<h1 id="archive_database">Customizing the archive database</h1>
<p>
For most users the default database will work just fine. It has
the added advantage of being compatible with the wview database.
Nevertheless, there may be occasions where you may want to add a
SQL type to your database, or change its unit system. This
section shows you how to do this, using the utility <span
class="symcode">$BIN_ROOT</span><span class="code">/wee_config_database</span>.
</p>
<p>
Before starting, it's worth running the utility with the <span
class="code"> --help</span> flag to see how it is used:
</p>
<pre class="tty"><span class="symcode">$BIN_ROOT</span>/wee_config_database --help</pre>
<h2>Adding a new observation type</h2>
<p>Suppose you have installed an electric meter at your house
and you wish to correlate electrical usage with the weather. The
meter has some sort of connection to your computer, allowing you
to download the consumption. At the end of every archive
interval you want to sample the meter for the electricity
consumed during the interval, then store the results in the
archive database, along with the weather data. How would you do
this?</p>
<p>
First, you would write a custom service that retrieves the
electrical consumption data and adds it to the archive record.
See the section <a href="#service_engine">Customizing the
weewx service engine</a> for details on how to write a custom
service. However, when you are done it will look something like
this:
</p>
<p>File <span class="code">user/electricity.py</span>:</p>
<pre class="tty">class AddElectricity(StdService):
def new_archive_packet(self, event):
(code that downloads the consumption data from the connection to the meter)
event.record['electricity'] = retrieved_value</pre>
<p>
This adds a new key '<span class="code">electricity</span>' to
the record dictionary and sets it equal to some value. As an
aside, if you do something like this, you would want to make
sure that the code to retrieve the current electrical
consumption does not delay very long so it does not slow down
the main loop. If it's going to cause a delay of more than a
couple seconds you might want to put it in a separate thread and
feed the results to <span class="code">AddElectricity</span>
through a queue.
</p>
<p>
As usual, to make sure your new service actually gets run, you
must include it in one of the service lists in section <span
class="code">[Engine][[WxEngine]]</span> of <span class="code">weewx.conf</span>.
Because your service will <em>produce</em> new data, it's
important that it be included before the archiving services.
Hence, it's best included amongst the other services that produce
and process data, in the list <span class="code">process_service</span>.
When you're done, the section would look something like this:
</p>
<pre class="tty">
[Engines]
# This section configures the internal weewx engines.
# It is for advanced customization.
[[WxEngine]]
# The list of services the main weewx engine should run:
prep_services = weewx.wxengine.StdTimeSynch
process_services = weewx.wxengine.StdConvert, weewx.wxengine.StdCalibrate, weewx.wxengine.StdQC<span class="highlight">, user.electricity.AddElectricity</span>
archive_services = weewx.wxengine.StdArchive,
restful_services = weewx.restx.StdWunderground, weewx.restx.StdPWSweather, weewx.restx.StdCWOP, weewx.restx.StdStationRegistry
report_services = weewx.wxengine.StdPrint, weewx.wxengine.StdReport</pre>
<h3 id="add_archive_type">Adding a new type to the archive database</h3>
<p>
So, now you have created a new observation type, '<span
class="code">electricity</span>'. Trouble is, there is no
corresponding type in the schema of the SQL database and,
therefore, it won't be stored there. How would you add such a
type?
</p>
<p>Here's our general strategy:</p>
<ol>
<li>Add a new type to the database schema.</li>
<li>Make sure you have the necessary permissions to create
the new database.</li>
<li>Populate it with data from the old database.</li>
<li>Shuffle databases around so <span class="code">weewx</span>
will use the new database.
</li>
<li>Modify the stats database so it includes the new type
as well (Optional).</li>
</ol>
<p>
1. <strong>Adding a new type to the schema.</strong> When
creating a database the schema is obtained from file <span
class="symcode">$BIN_ROOT</span><span class="code">/user/schemas.py</span>.
Take a look at it now. You will see a list called <span
class="code">defaultArchiveSchema</span> that holds all the
observation names and their SQL types. It looks something like:
</p>
<pre class="tty">defaultArchiveSchema = [('dateTime', 'INTEGER NOT NULL UNIQUE PRIMARY KEY'),
('usUnits', 'INTEGER NOT NULL'),
('interval', 'INTEGER NOT NULL'),
('barometer', 'REAL'),
('pressure', 'REAL'),
('altimeter', 'REAL'),
('inTemp', 'REAL'),
('outTemp', 'REAL'),
...
('inTempBatteryStatus', 'REAL')]</pre>
<p>
Let's modify it to add our new type, '<span class="code">electricity</span>'.
Now it looks like this:
</p>
<pre class="tty">defaultArchiveSchema = [('dateTime', 'INTEGER NOT NULL UNIQUE PRIMARY KEY'),
('usUnits', 'INTEGER NOT NULL'),
('interval', 'INTEGER NOT NULL'),
('barometer', 'REAL'),
('pressure', 'REAL'),
('altimeter', 'REAL'),
('inTemp', 'REAL'),
('outTemp', 'REAL'),
...
<span class="highlight"> ('electricity', 'REAL'),</span>
('inTempBatteryStatus', 'REAL')]</pre>
<p>
The new line has been <span class="highlight">&nbsp;highlighted&nbsp;</span>.
</p>
<p>
2. <strong>Check permissions.</strong> The reconfiguration
utility will create a new database with the same name as the
old, except with the suffix '<span class="code">_new</span>'
attached to the end. Make sure you have the necessary
permissions to do this. In particular, if you are using MySQL,
you will need '<span class="code">CREATE</span>' privileges.
</p>
<p>
3. <strong>Run <span class="code">wee_config_database</span>.
</strong> Now run the utility <span class="code">wee_config_database</span>
with the <span class="code">--reconfigure</span> option and the
path to the configuration file:
</p>
<pre class="tty"><span class="symcode">$BIN_ROOT</span>/wee_config_database --reconfigure <span
class="symcode">$CONFIG_ROOT</span>/weewx.conf</pre>
<p>
This will create a new database (nominally, <span class="code">
weewx.sdb_new</span> if you are using sqlite, <span class="code">weewx_new</span>
if you are using MySQL) using the new schema and populate it
with data from the old database.
</p>
<p>
4. <strong>Shuffle the databases.</strong> Now arrange things so
<span class="code">weewx</span> can find the new database.
</p>
<p class="warning">
<strong>Warning!</strong><br /> Make a backup of the data
before doing any of the next steps!
</p>
<p>
You can either shuffle the databases around so the new database
has the same name as the old database, or edit <span
class="code">weewx.conf</span> to use the new database name.
To do the former:
</p>
<p>For sqlite:</p>
<pre class="tty">cd <span class="symcode">$SQLITE_ROOT</span>
mv weewx.sdb_new weewx.sdb</pre>
<p>For MySQL:</p>
<pre class="tty">mysql -u &lt;username&gt; --password=&lt;mypassword&gt;
<span class="prompt">mysql&gt;</span> DROP DATABASE weewx; # Drops the old database
<span class="prompt">mysql&gt;</span> CREATE DATABASE weewx; # Create a new one with the same name
<span class="prompt">mysql&gt;</span> RENAME TABLE weewx_new.archive TO weewx.archive; # Rename to the nominal name</pre>
<p>
5. <strong>Modify the stats database.</strong> At this point,
you can use the new observation type in the plots. However, if
you wish to use it in the statistical summaries, you will also
have to add it to the stats database. To do this, add the type
to the Python list <span class="code"> stats_types</span>, which
can be found in <span class="symcode">$BIN_ROOT</span><span
class="code">/user/schemas.py</span>, so it reads something
like this:
</p>
<pre class="tty">stats_types = ['barometer', 'inTemp', 'outTemp',
'inHumidity', 'outHumidity',
'rainRate', 'rain', 'dewpoint', 'windchill', 'heatindex', 'ET',
'radiation', 'UV', 'extraTemp1', 'rxCheckPercent', 'wind',
<span class="highlight">'electricity'</span>]</pre>
<p>
Now delete the stats database (nominally <span class="code">stats.sdb</span>
for sqlite, <span class="code">stats</span> for MySQL). Weewx
will automatically rebuild it, including your new type.
</p>
<h3>Using the new type</h3>
<p>Now you've added a new type. How do you use it?</p>
<p>
Pretty much like any other type. For example, to do a plot of
the month's electric consumption, totaled by day, add this
section to the <span class="code">[[month_images]]</span>
section of <span class="code"> skin.conf</span>:
</p>
<pre class="tty">[[[monthelectric]]]
[[[[electricity]]]]
aggregate_type = sum
aggregate_interval = 86400
label = Electric consumption (daily total)</pre>
<p>
This will cause the generation of an image <span class="code">
monthelectric.png</span>, showing a plot of each day's consumption
for the past month.
</p>
<p>If you wish to use the new type in the templates, it will
be available using the same syntax as any other type. Here are
some other tags that might be useful:</p>
<table class="indent" style="width: 80%">
<tbody>
<tr>
<td><strong>Tag</strong></td>
<td><strong>Meaning</strong></td>
</tr>
<tr>
<td class="code">$day.electricity.sum</td>
<td>Total consumption since midnight</td>
</tr>
<tr>
<td class="code">$year.electricity.sum</td>
<td>Total consumption since the first of the year</td>
</tr>
<tr>
<td class="code">$year.electricity.max</td>
<td>The most consumed during any archive period</td>
</tr>
<tr>
<td class="code">$year.electricity.maxsum</td>
<td>The most consumed during a day</td>
</tr>
<tr>
<td class="code">$year.electricity.maxsumtime</td>
<td>The day it happened.</td>
</tr>
<tr>
<td class="code">$year.electricity.sum_ge(5.0)</td>
<td>The number of days where more than 5.0 kWH of
energy was consumed.</td>
</tr>
</tbody>
</table>
<h2 id="Changing_the_unit_system">Changing the unit system</h2>
<p>Normally, data is stored in the databases using US
Customary units and, normally, you don't care --- data can
always be displayed using any units you choose. It's an
"implementation detail." Nevertheless, there may be special
situations where you wish to store the data in Metric units. For
example, you may need to allow direct programmatic access to the
databases from another piece of software that expects metric
units.</p>
<p>
Weewx does not allow you to change the database unit system
midstream. You can't start with one unit system then, in the
middle of the database, switch to another. See the section <span
class="code"> <a href="usersguide.htm#StdConvert">
[StdConvert]</a></span> in the Weewx User's Guide. However, you can
reconfigure the database by coping it to a new database,
performing the unit conversion along the way. You then use this
new database.
</p>
<p>
The steps are pretty much the same as <a
href="#add_archive_type"> Adding a New Type to the
ArchiveDatabase</a>, described above.
</p>
<ol>
<li>Modify <span class="code">weewx.conf</span> to reflect
your choice of the new unit system to use.
</li>
<li>Make sure you have the necessary permissions to create
the new database.</li>
<li>Populate it with data from the old database.</li>
<li>Shuffle databases around so <span class="code">weewx</span>
will use the new database.
</li>
<li>Rebuild the stats database to use the new unit system.</li>
</ol>
<p>
1. <strong>Modify <span class="code">weewx.conf</span>.
</strong> Edit the configuration file to change option <span class="code">target_unit</span>
in section <span class="code"><a
href="usersguide.htm#StdConvert"> [StdConvert]</a></span> to reflect
your choice. If you are switching to metric units, the option
will look like
</p>
<pre class="tty">[StdConvert]
target_unit = METRIC</pre>
<p>
2. <strong>Check permissions.</strong> The reconfiguration
utility will create a new database with the same name as the
old, except with the suffix '<span class="code">_new</span>'
attached to the end. Make sure you have the necessary
permissions to do this. In particular, if you are using MySQL,
you will need '<span class="code">CREATE</span>' privileges.
</p>
<p>
3. <strong>Run <span class="code">wee_config_database</span>.
</strong> Now run the utility <span class="code">wee_config_database</span>
with the <span class="code">--reconfigure</span> option:
</p>
<pre class="tty"><span class="symcode">$BIN_ROOT</span>/wee_config_database --reconfigure <span
class="symcode">$CONFIG_ROOT</span>/weewx.conf</pre>
<p>
This will create a new database (nominally, <span class="code">
weewx.sdb_new</span> if you are using sqlite, <span class="code">weewx_new</span>
if you are using MySQL), using the schema found in <span
class="symcode">$BIN_ROOT</span><span class="code">/user/schemas.py</span>,
and populate it with data from the old database, while
performing the unit conversion.
</p>
<p>
4. <strong>Shuffle the databases.</strong> This is identical to
the description above.
</p>
<p>
5. <strong>Recreate the stats database.</strong> Delete the
stats database, then let <span class="code">weewx</span>
regenerate it. It will use the new unit system.
</p>
<h1 id="porting">Porting to new weather station hardware</h1>
<p>Naturally, this is an advanced topic but, nevertheless, I'd
really like to encourage any Python wizards out there to give it
a try. Of course, I have selfish reasons for encouraging you: I
don't want to have to go out and buy every type of hardware
there is! It's expensive and my roof would look like a weather
station farm.</p>
<p>Here's the general strategy for doing a port.</p>
<h2>Implement the driver</h2>
<p>
Inherit from the abstract base class <span class="code">weewx.abstractstation.AbstractStation</span>.
Try to implement as many of its methods as you can. At the very
minimum, you must implement <span class="code">hardware_name</span>
and <span class="code">genLoopPackets</span>.
</p>
<p>
<span class="code">hardware_name</span>: Return a string with a
short nickname for the hardware, such as "<span class="code">ACME
X90</span>"
</p>
<p>
<span class="code">genLoopPackets</span>: This should be a
generator function that yields loop packets, one after another.
Don't worry about stopping it: the engine will do this when an
archive record is due. A loop packet is a dictionary. At the
very minimum it must contain keys
</p>
<table class="indent" style="width: 60%">
<tbody>
<tr>
<td class="code">dateTime</td>
<td>The time of the observation in unix epoch time.</td>
</tr>
<tr>
<td class="code">usUnits</td>
<td>The unit system used. <span class="code">weewx.US</span>
for US customary, <span class="code">weewx.METRIC</span>
for metric. See the file <span class="code">units.py</span>,
dictionaries <span class="code">USUnits</span> and <span
class="code">MetricUnits</span> for the exact definition
of each.
</td>
</tr>
</tbody>
</table>
<p>
Then include any observation types you have in the dictionary.
Every packet need not contain the same set of observation types.
Different packets can use different unit systems, but all
observations within a packet must use the same unit system. If
your hardware has an error and you don't have a value, you can
either leave it out of the dictionary or (preferred) set its
value to <span class="code">None</span>.
</p>
<p>
A couple of observation types are tricky. In particular, rain.
Generally, <span class="code">weewx</span> expects to see a
packet with the amount of rain that fell in that packet period
included as observation '<span class="code">rain</span>'. It
then sums up all the values to get the total rainfall and emits
that in the archive record. If your hardware does not provide
this value, you might have to infer it from changes in whatever
value it provides, for example changes in the daily or monthly
rainfall. I know this is not the best solution, but it is the
most general solution. Any alternatives are welcome!
</p>
<p>
Wind is another tricky one. It is actually broken up into four
different observations: '<span class="code">windSpeed</span>', '<span
class="code">windDir</span>', '<span class="code">windGust</span>',
and '<span class="code">windGustDir</span>'. Supply as many as
you can. The directions should be compass directions in degrees
(0=North, 90=East, etc.).
</p>
<p>Be careful when reporting pressure. There are three
observations related to pressure. Some stations report only the
station pressure, others calculate and report sea level
pressures.</p>
<table class="indent" style="width: 60%">
<tbody>
<tr>
<td class="code">pressure</td>
<td>The <em>Station Pressure</em> (SP), which is the
raw, absolute pressure measured by the station. This is
the true barometric pressure for the station.
</td>
</tr>
<tr>
<td class="code">barometer</td>
<td>The <em>Sea Level Pressure</em> (SLP) obtained by
correcting the <em> Station Pressure</em> for altitude and
local temperature. This is the pressure reading most
commonly used by meteorologist to track weather systems at
the surface, and this is the pressure that is uploaded to
weather services by <span class="code">weewx</span>. It is
the station pressure reduced to mean sea level using local
altitude and local temperature.
</td>
</tr>
<tr>
<td class="code">altimeter</td>
<td>The <em>Altimeter Setting</em> (AS) obtained by
correcting the <em>Station Pressure</em> for altitude.
This is the pressure reading most commonly heard in
weather reports. It is not the true barometric pressure of
a station, but rather the station pressure reduced to mean
sea level using altitude and an assumed temperature
average.
</td>
</tr>
</tbody>
</table>
<p></p>
<p>
<span class="code">genArchiveRecords:</span> If your hardware
does not have an archive record logger, then <span class="code">weewx</span>
can do the record generation for you. It will automatically
collect all the types it sees in your loop packets then emit a
record with the averages (in some cases the sum or max value) of
all those types. If it doesn't see a type, then it won't appear
in the emitted record. If your hardware does have a logger, then
you should implement method <span class="code">genArchiveRecords</span>
as well. It should be a generator function that returns all the
records since a given time.
</p>
<p>
<span class="code">closePort:</span> If the driver needs to
close a serial port, terminate a thread, close a database, or
perform any other activity before the application terminates, do
it in this method.
</p>
<p>
<span class="code">loader:</span> This is a factory function
that returns an instance of your driver. It has two arguments:
the configuration dictionary, and a reference to the weewx
engine.
</p>
<h2>Define the configuration</h2>
<p>
You then include a new section in the configuration file <span
class="code"> weewx.conf</span> that includes any options your
driver needs. It should also include an entry '<span
class="code">driver</span>' that points to where your driver
can be found. Set option <span class="code">station_type</span>
to your new section type and your driver will be loaded.
</p>
<h2>Examples</h2>
<p>
Take a look at the simulator code in <span class="code">simulator.py</span>
for a dirt simple example of a driver. The next most complicated
is the driver for the WMR100 series, located in <span
class="code">wmr100.py</span>. The driver for the Vantage
series is by far the most complicated. It actually
multi-inherits from not only <span class="code">AbstractStation</span>,
but also <span class="code">StdService</span>. That is, it also
participates in the engine as a service.
</p>
<p>Naturally, there are a lot of subtleties that I've glossed
over in this high-level description. If you're game, give it a
try - I'm happy to help you out!</p>
<h1 id="archive_types">Appendix A: Archive Types</h1>
<p>
<em>Archive types</em> are weather observations that have come
from your instrument and been stored in the <em>archive
database</em>, a SQL database. They represent the <em>current
conditions</em> as of some time. They are available to be used in
two places:
</p>
<ul>
<li>In your template files as a tag with period <span
class="code"> $current</span>. Hence, the tag <span
class="code">$current.outTemp</span> represents the latest
current outside temperature. There is no aggregation involved
(see <a href="#statistical_types">statistical types</a> for
aggregation).
</li>
<li>In your plot graphs. Here, a line in the graph
represents the set of current observations over a time period.
While each plot point in a graph may represent an aggregation,
do not confuse this aggregation with the statistical
aggregation. The former is done with the archive database, the
latter with the statistical database.</li>
</ul>
<p>
The following table shows all the possible archive types and
whether they can be used in tag <span class="code">$current</span>
or in a plot. Note that just because a type appears in the table
does not necessarily mean that it is available for <em>your</em>
station setup. That would depend on whether your instrument
supports the type.
</p>
<table class="indent" style="width: 80%">
<tbody>
<tr>
<td><strong>Archive Type</strong></td>
<td style="width: 200px"><strong>SQL Type</strong> <br />
<span style="font-size: 80%">(appears in archive
database)</span></td>
<td><strong>Can be used <br /> in plots
</strong></td>
<td><strong>Can be used <br /> in tag <span
class="code">$current</span></strong></td>
</tr>
<tr>
<td class="code">altimeter</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">barometer</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">consBatteryVoltage</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">dateTime</td>
<td>X</td>
<td><br /></td>
<td>X (represents current time)</td>
</tr>
<tr>
<td class="code">dewpoint</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">ET</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">extraHumid1</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">extraHumid2</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">extraTemp1</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">extraTemp2</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">extraTemp3</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">hail</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">hailRate</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">heatindex</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">heatingTemp</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">heatingVoltage</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">inHumidity</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">inTemp</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">inTempBatteryStatus</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">interval</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">leafTemp2</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">leafWet2</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">outHumidity</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">outTemp</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">outTempBatteryStatus</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">pressure</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">radiation</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">rain</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">rainBatteryStatus</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">rainRate</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">referenceVoltage</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">rxCheckPercent</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">soilMoist1</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">soilMoist2</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code" style="height: 33px">soilMoist3</td>
<td style="height: 33px">X</td>
<td style="height: 33px">X</td>
<td style="height: 33px">X</td>
</tr>
<tr>
<td class="code">soilMoist4</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">soilTemp1</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">soilTemp2</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">soilTemp3</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">soilTemp4</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">supplyVoltage</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">txBatteryStatus</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">usUnits</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">UV</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">windvec</td>
<td></td>
<td>X (special vector type)</td>
<td></td>
</tr>
<tr>
<td class="code">windBatteryStatus</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">windDir</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">windGust</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">windGustDir</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">windSpeed</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">windchill</td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
</tbody>
</table>
<h1 id="units">Appendix B: Units</h1>
<p>The table below lists all the unit groups, their members,
and which units are options for the group.</p>
<table class="indent" style="width: 60%">
<tbody>
<tr>
<td><strong>Group</strong></td>
<td><strong>Members</strong></td>
<td><strong>Unit options</strong></td>
</tr>
<tr>
<td class="code">group_altitude</td>
<td class="code">altitude</td>
<td class="code">foot <br /> meter
</td>
</tr>
<tr class="code">
<td>group_degree_day</td>
<td>cooldeg<br /> heatdeg
</td>
<td>degree_F_day<br /> degree_C_day
</td>
</tr>
<tr class="code">
<td>group_direction</td>
<td>gustdir <br /> vecdir <br /> windDir <br />
windGustDir
</td>
<td>degree_compass</td>
</tr>
<tr class="code">
<td>group_interval</td>
<td>interval</td>
<td>minute</td>
</tr>
<tr class="code">
<td>group_moisture</td>
<td>soilMoist1 <br /> soilMoist2 <br /> soilMoist3 <br />
soilMoist4
</td>
<td>centibar</td>
</tr>
<tr class="code">
<td>group_percent</td>
<td>extraHumid1 <br /> extraHumid2 <br /> inHumidity
<br /> outHumidity <br /> rxCheckPercent
</td>
<td>percent</td>
</tr>
<tr class="code">
<td>group_pressure</td>
<td>barometer <br /> altimeter <br /> pressure
</td>
<td>inHg <br /> mbar <br /> hPa
</td>
</tr>
<tr class="code">
<td>group_radiation</td>
<td>UV <br /> radiation
</td>
<td>watt_per_meter_squared</td>
</tr>
<tr class="code">
<td>group_rain</td>
<td>rain <br /> ET <br /> hail
</td>
<td>in <br /> cm <br /> mm
</td>
</tr>
<tr class="code">
<td>group_rainrate</td>
<td>rainRate <br /> hailRate
</td>
<td>in_per_hour <br /> cm_per_hour <br /> mm_per_hour
</td>
</tr>
<tr class="code">
<td>group_speed</td>
<td>wind <br /> windGust <br /> windSpeed <br />
windgustvec <br /> windvec
</td>
<td>mile_per_hour <br /> km_per_hour <br /> knot <br />
meter_per_second
</td>
</tr>
<tr class="code">
<td>group_speed2</td>
<td>rms <br /> vecavg
</td>
<td>mile_per_hour2 <br /> km_per_hour2 <br /> knot2 <br />
meter_per_second2
</td>
</tr>
<tr class="code">
<td>group_temperature</td>
<td>dewpoint <br /> extraTemp1 <br /> extraTemp2 <br />
extraTemp3 <br /> heatindex <br /> heatingTemp <br />
inTemp <br /> leafTemp1 <br /> leafTemp2 <br /> outTemp
<br /> soilTemp1 <br /> soilTemp2 <br /> soilTemp3 <br />
soilTemp4 <br /> windchill
</td>
<td>degree_F <br /> degree_C
</td>
</tr>
<tr class="code">
<td>group_time</td>
<td>dateTime</td>
<td>unix_epoch <br /> dublin_jd
</td>
</tr>
<tr class="code">
<td>group_uv</td>
<td>UV</td>
<td>uv_index</td>
</tr>
<tr class="code">
<td>group_volt</td>
<td>consBatteryVoltage <br /> heatingVoltage <br />
referenceVoltage <br /> supplyVoltage
</td>
<td>volt</td>
</tr>
<tr class="code">
<td>group_NONE</td>
<td>NONE</td>
<td>NONE</td>
</tr>
</tbody>
</table>
<h1 id="statistical_types">Appendix C: Statistical Types</h1>
<p>
Most of the templates are devoted to reporting <em>statistical
types</em>, such as temperature, wind, or rainfall, using various <em>aggregates</em>,
such as min, max, or sum. These are called <em>aggregations</em>,
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.
</p>
<p>
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 <span class="code"><a
href="usersguide.htm#Stats"> [Stats]</a></span> in the <span
class="code">weewx.conf</span> configuration file).
</p>
<table class="indent" id="stattypes">
<tbody>
<tr>
<td><strong>Type</strong></td>
<td class="code">min</td>
<td class="code">mintime</td>
<td class="code">max</td>
<td class="code">maxtime</td>
<td class="code">avg</td>
<td class="code">sum</td>
<td class="code">rms</td>
<td class="code">vecavg</td>
<td class="code">vecdir</td>
</tr>
<tr>
<td class="code">barometer</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">inTemp</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">outTemp</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">inHumidity</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">outHumidity</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">wind</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td>X</td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td class="code">rain</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">dewpoint</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">windchill</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">heatindex</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">heatdeg</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">cooldeg</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">ET</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">radiation</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">UV</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">extraTemp1 <br /> extraTemp2 <br />
extraTemp3
</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">soilTemp1 <br /> soilTemp2 <br />
soilTemp3
</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">leafTemp1 <br /> leafTemp2
</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">extraHumid1 <br /> extraHumid2
</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">soilMoist1 <br /> soilMoist2 <br />
soilMoist3 <br /> soilMoist4
</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">leafWet1 <br /> leafWet2
</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td class="code">rxCheckPercent</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td>X</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<h1 id="packet_types">Appendix D: Packet Types</h1>
<p>
<em>Packets</em> are the raw data coming off the instrument (as
opposed to <em>records</em>, which are stored on the database).
The observation types available in a packet are useful when
setting <em><a href="usersguide.htm#StdQC"> quality
control rules</a></em> and when doing <em> <a
href="usersguide.htm#Calibrate"> calibrations</a></em>.
</p>
<p>
They may also be useful if you are writing your own custom
service. In particular, for subclasses of <span class="code">StdService</span>,
member function <span class="code">newLoopPacket</span> is
called when new LOOP packets arrive, and member function <span
class="code">newArchivePacket</span> is called when new
archive packets arrive. For both functions, the only argument
(besides <span class="code">self</span>) is a dictionary, where
the key is the type listed below, and the value is the
observation value.
</p>
<p>
See the guide from <em> <a
href="http://www.davisnet.com/support/weather/download/VantageSerialProtocolDocs_v250.pdf">
Vantage Pro and Pro2 Serial Communications Reference</a></em>
(available on the Davis website) for more information about
these types.
</p>
<table class="indent" style="width: 60%">
<tbody>
<tr>
<td><strong>Type</strong></td>
<td><strong>Loop packet</strong></td>
<td><strong>Archive Packet</strong></td>
<td width="15%"><strong>US</strong></td>
<td width="15%"><strong>METRIC</strong></td>
</tr>
<tr>
<td><span class="code">barometer</span></td>
<td>X</td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">consBatteryVoltage</span></td>
<td>X</td>
<td><br /></td>
<td>volt</td>
<td>volt</td>
</tr>
<tr>
<td><span class="code">dateTime</span></td>
<td>X</td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">dayET</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">dayRain</span></td>
<td>X</td>
<td><br /></td>
<td>in</td>
<td>cm</td>
</tr>
<tr>
<td><span class="code">dewpoint</span></td>
<td>X</td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">ET</span> (hourly)</td>
<td><br /></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">extraAlarm1</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">extraAlarm2</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">extraAlarm3</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">extraAlarm4</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">extraAlarm5</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">extraAlarm6</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">extraAlarm7</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">extraAlarm8</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">extraHumid1</span></td>
<td>X</td>
<td>X</td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">extraHumid2</span></td>
<td>X</td>
<td>X</td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">extraHumid3</span></td>
<td>X</td>
<td><br /></td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">extraHumid4</span></td>
<td>X</td>
<td><br /></td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">extraHumid5</span></td>
<td>X</td>
<td><br /></td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">extraHumid6</span></td>
<td>X</td>
<td><br /></td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">extraHumid7</span></td>
<td>X</td>
<td><br /></td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">extraTemp1</span></td>
<td>X</td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">extraTemp2</span></td>
<td>X</td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">extraTemp3</span></td>
<td>X</td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">extraTemp4</span></td>
<td>X</td>
<td><br /></td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">extraTemp5</span></td>
<td>X</td>
<td><br /></td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">extraTemp6</span></td>
<td>X</td>
<td><br /></td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">extraTemp7</span></td>
<td>X</td>
<td><br /></td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">forecastIcon</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">forecastRule</span></td>
<td>X</td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">heatIndex</span></td>
<td>X</td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">highOutTemp</span></td>
<td><br /></td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">highRadiation</span></td>
<td><br /></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">highUV</span></td>
<td><br /></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">inHumidity</span></td>
<td>X</td>
<td><br /></td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">inTemp</span></td>
<td>X</td>
<td><br /></td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">interval</span></td>
<td><br /></td>
<td>X</td>
<td>minute</td>
<td>minute</td>
</tr>
<tr>
<td><span class="code">insideAlarm</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">leafTemp1</span></td>
<td>X</td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">leafTemp2</span></td>
<td>X</td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">leafTemp3</span></td>
<td>X</td>
<td><br /></td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">leafTemp4</span></td>
<td>X</td>
<td><br /></td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">leafWet1</span></td>
<td>X</td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">leafWet2</span></td>
<td>X</td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">leafWet3</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">leafWet4</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">lowOutTemp</span></td>
<td><br /></td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">monthET</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">monthRain</span></td>
<td>X</td>
<td><br /></td>
<td>in</td>
<td>cm</td>
</tr>
<tr>
<td><span class="code">outHumidity</span></td>
<td>X</td>
<td>X</td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">outTemp</span></td>
<td>X</td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">outsideAlarm1</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">outsideAlarm2</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">radiation</span></td>
<td>X</td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">rain</span></td>
<td><br /></td>
<td>X</td>
<td>in</td>
<td>cm</td>
</tr>
<tr>
<td><span class="code">rainAlarm</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">rainRate</span></td>
<td>X</td>
<td><br /></td>
<td>in/hr</td>
<td>cm/hr</td>
</tr>
<tr>
<td><span class="code">rxCheckPercent</span></td>
<td><br /></td>
<td>X</td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">soilLeafAlarm1</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">soilLeafAlarm2</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">soilLeafAlarm3</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">soilLeafAlarm4</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">soilMoist1</span></td>
<td>X</td>
<td>X</td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">soilMoist2</span></td>
<td>X</td>
<td>X</td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">soilMoist3</span></td>
<td>X</td>
<td>X</td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">soilMoist4</span></td>
<td>X</td>
<td>X</td>
<td>%</td>
<td>%</td>
</tr>
<tr>
<td><span class="code">soilTemp1</span></td>
<td>X</td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">soilTemp2</span></td>
<td>X</td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">soilTemp3</span></td>
<td>X</td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">soilTemp4</span></td>
<td>X</td>
<td>X</td>
<td>F</td>
<td>C</td>
</tr>
<tr>
<td><span class="code">stormRain</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">stormStart</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">sunrise</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">sunset</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">txBatteryStatus</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">usUnits</span></td>
<td>X</td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">UV</span></td>
<td>X</td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">windChill</span></td>
<td>X</td>
<td>X</td>
<td><br /></td>
<td>C</td>
</tr>
<tr>
<td><span class="code">windDir</span></td>
<td>X</td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">windGust</span></td>
<td><br /></td>
<td>X</td>
<td><br /></td>
<td>km/h</td>
</tr>
<tr>
<td><span class="code">windGustDir</span></td>
<td><br /></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">windSpeed10</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">windSpeed</span></td>
<td>X</td>
<td>X</td>
<td><br /></td>
<td>km/h</td>
</tr>
<tr>
<td><span class="code">yearET</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td><br /></td>
</tr>
<tr>
<td><span class="code">yearRain</span></td>
<td>X</td>
<td><br /></td>
<td><br /></td>
<td>cm</td>
</tr>
</tbody>
</table>
</div>
<p class="copyright">
&copy; <a href="copyright.htm">Copyright</a> Tom Keffer
</p>
<script type="text/javascript">
$('#toc').toc({
exclude : 'h4, h5, h6',
autoId : true,
context : '#technical_content',
numerate: true
});
</script>
</body>
</html>