Files
weewx/docs/customizing.htm

1023 lines
56 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" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<!-- $Revision$ -->
<!-- $Author$ -->
<!-- $Date$ -->
<head>
<meta http-equiv="Content-Language" content="en-us" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Customizing weewx</title>
<style type="text/css">
body {
font: 11pt Verdana,arial,sans-serif;
color: black;
}
p {
font: 11pt Verdana,arial,sans-serif;
color: black;
}
ol {
font: 11pt Verdana,arial,sans-serif;
color: black;
}
ul {
font: 11pt Verdana,arial,sans-serif;
color: black;
}
li {
font: 11pt Verdana,arial,sans-serif;
color: black;
}
dl {
font: 11pt Verdana,arial,sans-serif;
color: black;
}
dt {
font: 11pt Verdana,arial,sans-serif;
color: black;
}
dd {
font: 11pt Verdana,arial,sans-serif;
color: black;
}
h1 {
font: normal normal bold xx-large Verdana, arial, sans-serif;
color: #FFFFFF;
border: 1px solid black;
border-bottom: 2px solid black;
border-right: 2px solid black;
background-color: #008080;
padding-left: .5em;
padding-right: .5em;
margin-top: 60pt;
border-right-width: medium;
border-bottom-width: medium;
}
h2 {
font: x-large Verdana, arial, sans-serif;
color: teal;
border: 1px solid black;
background-color: #e8e8e8;
padding-left: .5em;
padding-right: .5em;
margin-top: 30pt;
}
h3 {
font: medium Verdana, arial, sans-serif;
color: teal;
border: 1px solid black;
background-color: #e8e8e8;
padding-left: .5em;
padding-right: .5em;
}
h4 {
font: medium Verdana, arial, sans-serif;
color: black;
font-weight: bold;
}
.code {
font-family: "Courier New", Courier, monospace;
}
table {
border-style: solid;
border-width: 1px;
border-collapse: collapse;
}
td {
border-style: solid;
border-width: 1px;
}
.indent {
margin-left: 40px;
}
.tty {
font-family: "Courier New", Courier, monospace;
margin-left: 40px;
margin-top: 0px;
margin-bottom: 0px;
background-color: #FFFFCC;
}
.title {
text-align: center;
margin-top: 0px;
}
.config_option {
font-family: "Courier New", Courier, monospace;
font-weight: normal;
}
.config_section {
font-family: "Courier New", Courier, monospace;
}
.config_important {
font-family: "Courier New", Courier, monospace;
font-weight: bold;
color: #0000FF;
}
.bold_n_blue {
color: #0000FF;
}
.xxsmall {
font-size: xx-small;
}
</style>
</head>
<body>
<h1 class="title">Customizing weewx v1.6</h1>
<h1>Table of Contents</h1>
<ol>
<li>Overview</li>
</ol>
<h1>Introduction</h1>
<p>This document covers the customization of weewx. </p>
<p>For users who are only interested in customizing the generated reports,
modifying the options in the Standard skin configuration file skin.conf will give you all
the flexibility you need. You can easily add new plot images, change the titles
of images, change the units used in the reports, and so on, just by changing
options in this file. If this is your goal, skip the sections on the run-time
internals and just peruse the section on skin.conf.</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>
<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 any &quot;<em>services</em>&quot; that are to be run and arranging for them
to be called when key events occur, such as the arrival of LOOP data. The default install of <span class="code">weewx</span> includes the following services:</p>
<table style="width: 60%" align="center">
<tr>
<td><strong>Service</strong></td>
<td><strong>Function</strong></td>
</tr>
<tr>
<td><span class="code">weewx.wxengine.StdWunderground</span></td>
<td>Starts thread to manage WU connection; adds new data to a Queue to
be posted to the WU by the thread.</td>
</tr>
<tr>
<td><span class="code">weewx.wxengine.StdCatchUp</span></td>
<td>Any data found on the weather station memory but not yet in the
archive, is retrieved and put in the archive.</td>
</tr>
<tr>
<td><span class="code">weewx.wxengine.StdTimeSynch</span></td>
<td>Arranges to have the clock on the station synchronized at regular
intervals.</td>
</tr>
<tr>
<td><span class="code">weewx.wxengine.StdPrint</span></td>
<td>Prints out new LOOP and archive packets on the console.</td>
</tr>
<tr>
<td><span class="code">weewx.wxengine.StdProcess</span></td>
<td>Launches a new thread to do processing after a new archive record
arrives. The thread loads zero or more reports and processes them in
order. Reports do things such as generate HTML files, generate images,
or FTP files to a web server. New reports can be added easily by the
user.</td>
</tr>
</table>
<p>It is easy to extend old services or to add new ones. The source distribution includes an example
new service called &quot;<span class="code">MyAlarm</span>,&quot; which sends an email
when an arbitrary expression evaluates <span class="code">True</span>. It is
also possible to extend the internal engine. These advanced topics are covered
later in section XX.</p>
<h3>Reports</h3>
<p>For the moment, we focus on the last service, <span class="code">weewx.wxengine.StdProcess</span>, the
standard service for creating reports. This will be what most users want to
customize even if it means just changing a few options.</p>
<p>This service runs zero or more <em>
Reports</em>, each using one or more <em>Generators</em>. A generator does
things such as generate HTML from a template, create plot images from the data
file, or upload data to a webserver via FTP. The default install of weewx
includes the following generators:</p>
<table style="width: 60%" align="center" >
<tr>
<td>Generator</td>
<td><strong>Function</strong></td>
</tr>
<tr>
<td class="code">weewx.reportengine.FileGenerator</td>
<td>Generates HTML files from templates. </td>
</tr>
<tr>
<td class="code">weewx.reportengine.ImageGenerator</td>
<td>Generates image plots</td>
</tr>
<tr>
<td><span class="code">weewx.reportengine.Ftp</span></td>
<td>FTP uploads data</td>
</tr>
<tr>
<td><span class="code">weewx.reportengine.Copy</span></td>
<td>Copies files locally.</td>
</tr>
</table>
<p>A 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>, skin.conf. 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 customizing or
overriding the skin configuration file skin.conf.
</p>
<p>Note that the Ftp and Copy &quot;Reports&quot; are a kind of funny report in that they
don&#39;t actually generate anything to do with the presentation layer. Instead,
they make use of the same reporting machinery, but use it to move files around.
They fit nicely into the report architecture, insuring that they can be well
integrated with the generation of the actual presentation.
</p>
<p>The out-of-the-box weewx configuration file weewx.conf has been set up to run
two reports, the first called StandardReport, and the second called FTP.
StandardReport runs against a skin called Standard, which is included in the
distribution. FTP runs against a &quot;skin&quot; (note the quotes) called Ftp, also
included. Each has its own configuration file, skin.conf. While it is unlikely
you will need to change the Ftp skin configuration file, you very much will want
to look at modifying the Standard skin configuration file.</p>
<h1>The Standard skin configuration file</h1>
<p>This section is a reference to the options appearing in the Standard skin configuration file, found in <span class="code"><em>$WEEWX_ROOT</em>/skins/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.</p>
<h3>Root 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 order.</p>
<p class="config_option">singleton_list</p>
<p>Similar to generator_list, except the generators in this list get run only
once, the first time the report engine service is started. It is useful for
doing one-time chores, such as copying files to the destination directory or
initializing some service.<br />
</p>
<h2 class="config_section">[Units]</h2>
<p>This section deals with Units and their formatting. </p>
<h3 class="config_section">[[Groups]]</h3>
<p>This subsection 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 &#39;<span class="code">outTemp</span>&#39;,
&#39;<span class="code">barometer</span>&#39;, 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 &quot;U.S. Customary&quot; or &quot;Metric&quot; seems
overly restrictive. <span class="code">Weewx</span> has taken a middle route and
divided all the different observation types into 12 different &quot;unit groups.&quot; A
unit group is something like &quot;<span class="code">group_temperature</span>.&quot; It
represents the measurement system to be used by all observation types that are
measured in temperature, such as inside temperature (type &#39;<span class="code">inTemp</span>&#39;),
outside temperature (&#39;<span class="code">outTemp</span>&#39;), dewpoint (&#39;<span class="code">dewpoint</span>&#39;),
wind chill (&#39;<span class="code">windchill</span>&#39;), and so on. If you
decide that you want unit group <span class="code">group_temperature</span> to be
measured in &quot;<span class="code">degrees_C</span>&quot; 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 &quot;<span class="code">foot</span>&quot; or &quot;<span class="code">inch_per_hour</span>&quot;,
not &quot;<span class="code">feet</span>&quot; or &quot;<span class="code">inches_per_hour</span>.&quot; See section <a href="#Units">Units</a> for more information, including a concise
summary of the groups, their members, and which options can be used for each
group.</p>
<h4 class="config_important"><a name="group_altitude">group_altitude</a></h4>
<p>Which measurement unit to be used for altitude. Possible options
are &#39;<span class="code">foot</span>&#39; or
&#39;<span class="code">meter</span>&#39;.</p>
<h4 class="config_option">group_direction</h4>
<p>Which measurement unit to be used for direction. The only option is
&quot;<span class="code">degree_compass</span>&quot;.</p>
<h4 class="config_option">group_moisture</h4>
<p>The measurement unit to be used for soil moisture. The only option
is &quot;<span class="code">centibar</span>.&quot;</p>
<h4 class="config_option">group_percent</h4>
<p>The measurement unit to be used for percentages. The only option is
&quot;<span class="code">percent</span>&quot;.</p>
<h4 class="config_important">group_pressure</h4>
<p>The measurement unit to be used for pressure. Possible options are
one of &quot;<span class="code">inHg</span>&quot; (inches of mercury),
&quot;<span class="code">mbar</span>&quot;, or
&quot;<span class="code">hPa</span>.&quot;</p>
<h4 class="config_option">group_radiation</h4>
<p>The measurement unit to be used for radiation. The only option is
&quot;<span class="code">watt_per_meter_squared</span>.&quot;</p>
<h4 class="config_important">group_rain</h4>
<p>The measurement unit to be used for precipitation. Options are
&quot;<span class="code">inch</span>&quot;,
&quot;<span class="code">cm</span>,&quot; or
&quot;<span class="code">mm</span>.&quot;</p>
<h4 class="config_important">group_rainrate</h4>
<p>The measurement unit to be used for rate of precipitation. Possible options
are one of &quot;<span class="code">inch_per_hour</span>&quot;,
&quot;<span class="code">cm_per_hour</span>&quot;, or
&quot;<span class="code">mm_per_hour</span>&quot;.</p>
<h4 class="config_important">group_speed</h4>
<p>The measurement unit to be used for wind speeds. Possible options are one of &quot;<span class="code">mile_per_hour</span>&quot;,
&quot;<span class="code">km_per_hour</span>&quot;, &quot;<span class="code">knot</span>&quot;, or &quot;<span class="code">meter_per_second</span>.&quot;</p>
<h4 class="config_important">group_speed2</h4>
<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 of &quot;<span class="code">mile_per_hour2</span>&quot;,
&quot;<span class="code">km_per_hour2</span>&quot;, &quot;<span class="code">knot2</span>&quot;, or
&quot;<span class="code">meter_per_second2</span>.&quot;</p>
<h4 class="config_important"><a name="group_temperature">group_temperature</a></h4>
<p>The measurement unit to be used for temperatures. Options are &quot;<span class="code">degree_F</span>&quot;
or &quot;<span class="code">degree_C</span>.&quot;</p>
<h4 class="config_option">group_volt</h4>
<p>The measurement unit to be used for voltages. The only option is &quot;<span class="code">volt</span>.&quot;</p>
<h3 class="config_section"><a name="Units_StringFormats">[[StringFormats]]</a></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>
<p class="tty">degree_C = %.1f</p>
<p class="tty">inch&nbsp;&nbsp;&nbsp;&nbsp; =&nbsp;%.2f</p>
<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>, and are very similar to C&#39;s
<span class="code">sprintf()</span>
codes.</p>
<h3 class="config_section">[[Labels]]</h3>
<p>This subsection specifies what label is to be used for each measurement unit
type. For example, the options</p>
<p class="tty">degree_F = °F</p>
<p class="tty">inch&nbsp;&nbsp;&nbsp;&nbsp; = &#39; in&#39;</p>
<p>would cause all temperatures to have unit labels &#39;<span class="code">°F</span>&#39;
and all precipitation to have labels &#39;<span class="code"> in</span>&#39;. 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.)</p>
<h2 class="config_section">[Files]</h2>
<p>This section controls text generation from templates, specifically which
files are to be produced from which template.</p>
<h3>Overview of file generation</h3>
<p>Customization of file generation consists of playing with the various options offered below and,
failing that, modifying the template files that come with the distribution.</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 subdirectory the template sits in&nbsp;
and will also be the subdirectory 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="code"><em>$HTML_ROOT</em>/Acme/index.html</span>.</p>
<p>The skin that comes with the standard distribution of <span class="code">weewx</span> contains three
different kinds of generated output:</p>
<ol>
<li>Summary by month. In addition to the naming rules above, if the template
file has the letters <span class="code">YYYY</span> and <span class="code">
MM</span> in its name, these will be substituted for the year and month,
respectively. The default distribution has been set up to produce NOAA
monthly summaries, one for each month, as a simple text file (no HTML). </li>
<li>Summary by year.&nbsp; In addition to the naming rules above, if the
template file has the letters <span class="code">YYYY</span> in its name,
the year will be substituted. The default distribution has been set up to
produce NOAA yearly summaries, one for each year, as a simple text file (no
HTML). </li>
<li>Summary &quot;To Date&quot;. The default distribution has been set up to produce reports for the day,
week, month, and year-to-date observations. These four files are all HTML
files. The first, the daily summary (output file index.html), includes a
drop-down list that allows the NOAA month and yearly summaries to be
displayed.</li>
</ol>
<h3>Root options</h3>
<h4 class="config_option">encoding</h4>
<p>This option controls which encoding is to be used for the generated output.
There are 3 possible choices:</p>
<table style="width: 60%" align="center" >
<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>
</table>
<p>By default, the encoding <span class="code">html_entities</span> is used for
HTML files, <span class="code">strict_ascii</span> for the NOAA template files.
</p>
<h3>[[SummaryByMonth]]</h3>
<p>This section controls how summaries-by-month are generated. The default
configuration generates NOAA-by-month summaries. </p>
<h3>[[SummaryByYear]]</h3>
<p>This section controls how summaries-by-year are generated. The default
configuration generates NOAA-by-year summaries. </p>
<h3>[[ToDate]]</h3>
<p>This section controls how observations-to-date are generated. The default
configuration generates four files, one for day, week, month, and year-to-date.</p>
<h3>[[Copy]]</h3>
<p>This section controls which files are to be copied over from the skin
subdirectory to the destination directory. Think of it as &quot;file generation,&quot;
except that rather than going through the template engine, the files are simply
copied over. </p>
<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>
<h2 class="config_section">[Images]</h2>
<p>This section, which controls which images (plots) get generated and with which
options, is by far the most complicated. However, it is extremely flexible and powerful.</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&#39;s 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>
<p class="tty">[[month_images]]</p>
<p class="tty">&nbsp; x_label_format = %d</p>
<p class="tty">&nbsp; bottom_label_format = %m/%d/%y %H:%M</p>
<p class="tty">&nbsp; time_length = 2592000 # == 30 days</p>
<p class="tty">&nbsp; aggregate_type = avg</p>
<p class="tty">&nbsp; aggregate_interval = 10800 # == 3 hours</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 days (format option
&quot;<span class="code">%d</span>&quot;). The <span class="code">bottom_label_format</span>
is the format used to time stamp the image at the bottom. In this example, it will
show the time as <span class="code">10/25/09 15:35</span>. A plot will cover a nominal
30 days, and all items included in it will use an aggregate type of averaging over
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.&nbsp; Values specified in the
level above can be overridden. For example, here&#39;s a typical set of options for
sub-sub-section <span class="code">[[[monthrain]]]</span>: </p>
<p class="tty">[[[monthrain]]]</p>
<p class="tty">&nbsp; plot_type = bar</p>
<p class="tty">&nbsp; yscale = None, None, 0.02</p>
<p class="tty">&nbsp; [[[[rain]]]]</p>
<p class="tty">&nbsp;&nbsp;&nbsp; aggregate_type = sum</p>
<p class="tty">&nbsp;&nbsp;&nbsp; aggregate_interval = 86400</p>
<p class="tty">&nbsp;&nbsp;&nbsp; label = Rain (daily avg)</p>
<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 &mdash; if left out, the scale will automatically be chosen. However, in this
example we are choosing to exercise some degree of control by specifying values
explicitly. It is a 3-way tuple (<span class="code">ylow</span>,
<span class="code">yhigh</span>, <span class="code">min_interval</span>), where
<span class="code">ymin</span> and <span class="code">ymax</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 &#39;<span class="code">None</span>&#39;, the corresponding
value will be automatically chosen. So, in this example, we are letting
<span class="code">weewx</span> pick sensible y minimum and maximum values, but
we are requiring 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 &quot;line&quot; (it
will actually be a series of bars) and it will
have logical name &#39;<span class="code">rain</span>&#39;. Because we haven&#39;t 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 (&#39;<span class="code">Rain (daily avg)</span>&#39;)</p>
<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&#39;s how to
generate a plot with the week&#39;s outside temperature as well as dewpoint:</p>
<p class="tty">[[[monthtempdew]]]</p>
<p class="tty">&nbsp; [[[[outTemp]]]]</p>
<p class="tty">&nbsp; [[[[dewpoint]]]]</p>
<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&#39;s temperature, overlaid with
hourly averages. Here, you are using the same data type (&#39;<span class="code">outTemp</span>&#39;)
for both plot lines, the first with averages, the second without. If you do the
obvious it won&#39;t work:</p>
<p class="tty">## WRONG ##</p>
<p class="tty">[[[daytemp_with_avg]]]</p>
<p class="tty">&nbsp; [[[[outTemp]]]]</p>
<p class="tty">&nbsp;&nbsp;&nbsp; aggregate_type = avg</p>
<p class="tty">&nbsp;&nbsp;&nbsp; aggregate_interval = 3600</p>
<p class="tty">&nbsp; [[[[outTemp]]]]&nbsp; # OOPS! The same section name appears more than
once!</p>
<p>The option parser does not allow the same section name (&#39;<span class="code">outTemp</span>&#39;
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>
<p class="tty">[[[daytemp_with_avg]]]</p>
<p class="tty">&nbsp;&nbsp;&nbsp; [[[[a_logical_name]]]]</p>
<p class="tty">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data_type = outTemp</p>
<p class="tty">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aggregate_type = avg</p>
<p class="tty">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aggregate_interval = 3600</p>
<p class="tty">&nbsp;&nbsp;&nbsp; [[[[outTemp]]]]</p>
<p>Here, the first logical line has been given the name &quot;<span class="code">a_logical_name</span>&quot;
to distinguish it from the second line &quot;<span class="code">outTemp</span>&quot;. 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&#39;s temperature, overlaid with a 3-hour
smoothed average.</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 &#39;<span class="code">vector</span>&#39;.
You need a vector type to produce this kind of plot. There are two: &#39;<span class="code">windvec</span>&#39;,
and &#39;<span class="code">windgustvec</span>&#39;. While they don&#39;t actually appear in
the SQL database, <span class="code">weewx</span> understands that they represent special vector-types.
The first, &#39;<span class="code">windvec</span>&#39;, represents the average wind in
an archive period, the second, &#39;<span class="code">windgustvec</span>&#39; the max
wind in an archive period. Here&#39;s how to produce a progressive vector plot of
the year&#39;s biggest daily wind gusts, along with daily averages:</p>
<p class="tty">[[[yeargustoverlay]]]</p>
<p class="tty">&nbsp; aggregate_interval = 86400</p>
<p class="tty">&nbsp; [[[[windvec]]]]</p>
<p class="tty">&nbsp;&nbsp;&nbsp; plot_type = vector</p>
<p class="tty">&nbsp;&nbsp;&nbsp; aggregate_type = avg</p>
<p class="tty">&nbsp; [[[[windgustvec]]]]</p>
<p class="tty">&nbsp;&nbsp;&nbsp; plot_type = vector</p>
<p class="tty">&nbsp;&nbsp;&nbsp; aggregate_type = max</p>
<p>This will produce an image file with name <span class="code">
yeargustoverlay.png</span>. It will consist of two progressive vector plots,
both using daily aggregation (86,400 seconds). For the first set of vectors, the daily
average will be used. In the second, the max of the gusts will be used.</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 don&#39;t 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, 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>
<p class="tty">[Images]</p>
<p class="tty">&nbsp; ...</p>
<p class="tty">&nbsp; image_width=300</p>
<p class="tty">&nbsp; image_height = 180</p>
<p>The standard plot of barometric pressure will appear in <span class="code">
daybarometer.png</span>:</p>
<p class="tty">&nbsp;&nbsp;&nbsp; [[[daybarometer]]]</p>
<p class="tty">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [[[[barometer]]]]&nbsp;</p>
<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>
<p class="tty">&nbsp; &nbsp; [[[daybarometer_big]]]</p>
<p class="tty">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; image_width&nbsp; = 600</p>
<p class="tty">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; image_height = 360</p>
<p class="tty">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [[[[barometer]]]]</p>
<h3>Summary</h3>
<p>Studying this section in the shipped version of <span class="code">weewx.conf</span>
will give you ideas about the many different image plot configurations that are
possible without hacking the code. </p>
<h2>[Labels]</h2>
<p>This section controls what label to use for each type of measurement unit. It is generally
used in plot generation, although there are a few other areas where it is used. It consists of
two sub-sections:</p>
<h4 class="config_option">[[Generic]]</h4>
<p>This sub-sections specifies default labels to be used for each SQL type. For
example, options</p>
<p class="tty">inTemp = Inside Temperature</p>
<p class="tty">outTemp = Outside Temperature</p>
<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>
<h4 class="config_option">[[<a name="Units_StringFormats1">UnitLabels]]</a></h4>
<p>This sub-section specifies what label to be used for each measurement unit
type. For example, the options</p>
<p class="tty">degree_F = \xb0F</p>
<p class="tty">inch&nbsp;&nbsp;&nbsp;&nbsp; = &#39; in&#39;</p>
<p>would cause all temperatures to have unit labels &#39;<span class="code">°F</span>&#39;
and all precipitation to have labels &#39;<span class="code"> in</span>&#39;. (NB: the code
<span class="code">\xb0</span> is the hexadecimal value <span class="code">b0</span>,
which in many encodings encodes the degree sign.)</p>
<h3 class="config_section">[HTML]</h3>
<p>Section [HTML] has two options and two sub-sections. For additional information
on HTML generation <a href="#HTML_Generation">see the section below</a>.</p>
<h4 class="config_option">template_root</h4>
<p>This option specifies the directory, relative to <span class="code"><em>WEEWX_ROOT</em></span>,
where the HTML templates can be found. Required. No default.</p>
<h4 class="config_option">html_root</h4>
<p>This option specifies the directory, relative to <span class="code"><em>WEEWX_ROOT</em></span>,
where the generated HTML files should be put. Required. No default.</p>
<h4 class="config_option"><a name="HTML_UnitLabels">[[UnitLabels]]</a></h4>
<p>This subsection is similar to its eponymous counterpart in section
<span class="code">[Labels]</span> above, except it is used for HTML generation.
It is useful to have a separate section because HTML uses special &#39;entity&#39; codes
to code special characters, such as the degree sign. For example, the options</p>
<p class="tty">degree_F = &amp;deg;F</p>
<p class="tty">inch&nbsp;&nbsp;&nbsp;&nbsp; = &#39; in&#39;</p>
<p>would cause outside temperature and rain to have unit labels&nbsp; &#39;<span class="code">°F</span>&#39;
and &#39;<span class="code"> in</span>&#39;, respectively.</p>
<h4 class="config_option"><a name="HTML_Time">[[Time]]</a></h4>
<p>This subsection is used for time labels in HTML generation. It uses
<a href="http://docs.python.org/library/datetime.html#strftime-behavior">strftime()</a>
formats. For example</p>
<p class="tty">week&nbsp; = %H:%M on %A</p>
<p class="tty">month = %d-%b-%Y %H:%M</p>
<p>would specify that week data should use a format such as &quot;<span class="code">15:20
on Sunday</span>&quot;, while month data should look like &quot;<span class="code">06-Oct-2009
15:20</span>&quot;</p>
<p>&nbsp;</p>
<h1>Customizing the weewx service engine</h1>
<p>At a high level, weewx consists of an <em>engine</em> that is responsible for
managing a set of <em>services</em>. A service consists of a Python class with a
set of member functions. The engine arranges to have appropriate member
functions called when specific events happen. For example, when a new LOOP
packet arrives, member function <span class="code">processLoopPacket()</span> of
all services is called.</p>
<p>To customize, you can</p>
<ul>
<li>Customize a service</li>
<li>Add a service</li>
<li>Customize the engine</li>
</ul>
<p>This document describes how to do all three.</p>
<p>The default install of <span class="code">weewx</span> includes the following services:</p>
<table style="width: 100%">
<tr>
<td><strong>Service</strong></td>
<td><strong>Function</strong></td>
</tr>
<tr>
<td><span class="code">weewx.wxengine.StdWunderground</span></td>
<td>Starts thread to manage WU connection; adds new data to a Queue to
be posted to the WU by the thread.</td>
</tr>
<tr>
<td><span class="code">weewx.wxengine.StdCatchUp</span></td>
<td>Any data found on the weather station memory but not yet in the
archive, is retrieved and put in the archive.</td>
</tr>
<tr>
<td><span class="code">weewx.wxengine.StdTimeSynch</span></td>
<td>Arranges to have the clock on the station synchronized at regular
intervals.</td>
</tr>
<tr>
<td><span class="code">weewx.wxengine.StdPrint</span></td>
<td>Prints out new LOOP and archive packets on the console.</td>
</tr>
<tr>
<td><span class="code">weewx.wxengine.StdProcess</span></td>
<td>Launches a new thread to do processing after a new archive record
arrives. The thread loads zero or more reports and processes them in
order. Reports do things such as generate HTML files, generate images,
or FTP files to a web server. New reports can be added easily by the
user.</td>
</tr>
</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 time, barometer,
outside temperature, wind speed, and wind direction. Suppose you don&#39;t like
this, and want to print out humidity as well when a new LOOP packet arrives, but
leave the printing of archive packets alone. This could be done by subclassing the default
print service <span class="code">StdPrint</span> and overriding member function <span class="code">
processLoopPacket()</span>. </p>
<p>In file <span class="code">myprint.py</span>:</p>
<p class="tty">from weewx.wxengine import StdPrint<br />
from weeutil.weeutil import timestamp_to_string<br />
<br />
class MyPrint(StdPrint):<br />
<br />
&nbsp;&nbsp;&nbsp; # Override the default processLoopPacket:<br />
&nbsp;&nbsp;&nbsp; def processLoopPacket(self, physicalPacket):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print &quot;LOOP: &quot;, timestamp_to_string(physicalPacket[&#39;dateTime&#39;]),\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
physicalPacket[&#39;barometer&#39;],\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
physicalPacket[&#39;outTemp&#39;],\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
physicalPacket[&#39;outHumidity&#39;],\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
physicalPacket[&#39;windSpeed&#39;],\<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
physicalPacket[&#39;windDir&#39;]</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 the standard print service name in the
option <span class="code">service_list</span>, located in <span class="code">
[Engines][[WxEngine]]</span>:</p>
<p class="tty">[Engines]<br />
&nbsp;&nbsp;&nbsp; [[WxEngine]]<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
service_list = weewx.wxengine.StdWunderground, weewx.wxengine.StdCatchUp,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; weewx.wxengine.StdTimeSynch, myprint.MyPrint,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; weewx.wxengine.StdProcess</p>
<p>(Note that this list is shown on several lines for clarity, but in actuality
it must be all on one line. The parser <span class="code">ConfigObj</span> does
not allow options to be continued on to following lines.)</p>
<h2>Adding a Service</h2>
<p>Suppose there is no service that can easily be customized for your needs. In
this case, a
new one can easily be created by subclassing off the abstract base class
<span class="code">StdService</span>, and then adding the functionality you
need. Here&#39;s an example that implements an alarm that sends off an email
when an arbitrary expression evaluates True. This example is included in the standard
distribution in subdirectory &#39;<span class="code">examples</span>.&#39;</p>
<p>File <span class="code">examples/alarm.py</span>:</p>
<p class="tty">import time<br />
import smtplib<br />
from email.mime.text import MIMEText<br />
import threading<br />
import syslog<br />
<br />
from weewx.wxengine import StdService<br />
from weeutil.weeutil import timestamp_to_string<br />
<br />
# Inherit from the base class StdService:<br />
class MyAlarm(StdService):<br />
&nbsp;&nbsp;&nbsp; &quot;&quot;&quot;Custom service that sounds an alarm if an expression
evaluates true&quot;&quot;&quot;<br />
<br />
&nbsp;&nbsp;&nbsp; def __init__(self, engine):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Pass the initialization information
on to my superclass:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StdService.__init__(self, engine)<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # This will hold the time when the
last alarm message went out:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.last_msg = None<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.expression = None<br />
<br />
&nbsp;&nbsp;&nbsp; def setup(self):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Dig the
needed options out of the configuration dictionary.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # If a
critical option is missing, an exception will be thrown and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # the alarm
will not be set.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
self.expression = self.engine.config_dict[&#39;Alarm&#39;][&#39;expression&#39;]<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
self.time_wait = int(self.engine.config_dict[&#39;Alarm&#39;].get(&#39;time_wait&#39;, &#39;3600&#39;))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
self.smtp_host = self.engine.config_dict[&#39;Alarm&#39;][&#39;smtp_host&#39;]<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
self.smtp_user = self.engine.config_dict[&#39;Alarm&#39;].get(&#39;smtp_user&#39;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
self.smtp_password = self.engine.config_dict[&#39;Alarm&#39;].get(&#39;smtp_password&#39;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.TO =
self.engine.config_dict[&#39;Alarm&#39;][&#39;mailto&#39;]<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; syslog.syslog(syslog.LOG_INFO,
&quot;alarm: Alarm set for expression %s&quot; % self.expression)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; except:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
self.expression = None<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
self.time_wait = None<br />
<br />
&nbsp;&nbsp;&nbsp; def postArchiveData(self, rec):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Let the super class see the record
first:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StdService.postArchiveData(self, rec)<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # See if the alarm has been set:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if self.expression:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # To avoid a
flood of nearly identical emails, this will do<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # the check
only if we have never sent an email, or if we haven&#39;t<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # sent one in
the last self.time_wait seconds:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if not
self.last_msg or abs(time.time() - self.last_msg) &gt;= self.time_wait :<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
# Evaluate the expression in the context of &#39;rec&#39;.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
# Sound the alarm if it evaluates true:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
if eval(self.expression, None, rec):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #
NOTE 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
# Sound the alarm!<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
# Launch in a separate thread so it doesn&#39;t block the main LOOP thread:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
t = threading.Thread(target = MyAlarm.soundTheAlarm, args=(self, rec))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
t.start()<br />
<br />
&nbsp;&nbsp;&nbsp; def soundTheAlarm(self, rec):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;&quot;&quot;This function is called when the
given expression evaluates True.&quot;&quot;&quot;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Get the time and convert to a
string:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t_str = timestamp_to_string(rec[&#39;dateTime&#39;])<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Form the message text:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; msg_text = &quot;Alarm expression %s
evaluated True at %s\nRecord:\n%s&quot; % (self.expression, t_str, str(rec))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Convert to MIME:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; msg = MIMEText(msg_text)<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Fill in MIME headers:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; msg[&#39;Subject&#39;] = &quot;Alarm message from
weewx&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; msg[&#39;From&#39;] = &quot;weewx&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; msg[&#39;To&#39;] = self.TO<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Create an instance of class SMTP
for the given SMTP host:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s = smtplib.SMTP(self.smtp_host)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # If a username has been given,
assume that login is required for this host:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if self.smtp_user:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.login(self.smtp_user,
self.smtp_password)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Send the email:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.sendmail(msg[&#39;From&#39;], [self.TO],
msg.as_string())<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Log out of the server:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.quit()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Record when the message went out:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.last_msg = time.time()<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Log it in the system log:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; syslog.syslog(syslog.LOG_INFO,
&quot;alarm: Alarm sounded for expression %s&quot; % self.expression)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; syslog.syslog(syslog.LOG_INFO, &quot; ***
email sent to: %s&quot; % self.TO)</p>
<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>
<p class="tty">[Alarm]<br />
&nbsp;&nbsp;&nbsp; expression = &quot;outTemp &lt; 40.0&quot;<br />
&nbsp;&nbsp;&nbsp; time_wait = 1800<br />
&nbsp;&nbsp;&nbsp; smtp_host = smtp.mymailserver.com<br />
&nbsp;&nbsp;&nbsp; smtp_user = myusername<br />
&nbsp;&nbsp;&nbsp; smtp_password = mypassword<br />
&nbsp;&nbsp;&nbsp; mailto = auser@adomain.com</p>
<p>These options specify that the alarm is to be sounded when &quot;<span class="code">outTemp
&lt; 40.0</span>&quot; evaluates True, that is when the outside temperature is below
40.0 degrees. Any valid Python expression can be used, although the only
variables available are those in the current archive record. (The place in the
code where the expression is evaluated is marked with &quot;<span class="code">Note 1</span>&quot;.)</p>
<p>Another example expression could be:</p>
<p class="tty">&nbsp;&nbsp;&nbsp; expression = &quot;outTemp &lt; 32.0 and windSpeed &gt;
10.0&quot;</p>
<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 is specified in 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>To make this all work, you must tell the engine to load this new service.
This is done by adding your service name to the list <span class="code">
service_list</span>, located in <span class="code">[Engines][[WxEngine]]</span>:</p>
<p class="tty">[Engines]<br />
&nbsp;&nbsp;&nbsp; [[WxEngine]]<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
service_list = weewx.wxengine.StdWunderground, weewx.wxengine.StdCatchUp,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; weewx.wxengine.StdTimeSynch, weewx.wxengine.StdPrint,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; weewx.wxengine.StdProcess, examples.alarm.MyAlarm</p>
<p>(Again, note that this list is shown on several lines for clarity, but in
actuality it must be all on one line.)</p>
<h2>Customizing the Engine</h2>
<p>In this section, we look at how to install a custom Engine. In general, this
is the least desirable way to proceed, but in some cases it may be the only way
to get what you want.</p>
<p>For example, suppose you want to define a new event for when the first
archive of a day arrives. This can be done by extending the the standard engine. </p>
<p>This example is in file <span class="code">example/daily.py</span>:</p>
<p class="tty">from weewx.wxengine import StdEngine, StdService<br />
from weeutil.weeutil import startOfArchiveDay<br />
<br />
class MyEngine(StdEngine):<br />
&nbsp;&nbsp;&nbsp; &quot;&quot;&quot;A customized weewx engine.&quot;&quot;&quot;<br />
<br />
&nbsp;&nbsp;&nbsp; def __init__(self, *args, **vargs):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Pass on the initialization data to
my superclass:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StdEngine.__init__(self, *args, **vargs)<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # This will record the timestamp of
the old day<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.old_day = None<br />
<br />
&nbsp;&nbsp;&nbsp; def postArchiveData(self, rec):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # First let my superclass process it:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StdEngine.postArchiveData(self, rec)<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Get the timestamp of the start of
the day using<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # the utility function
startOfArchiveDay <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dayStart_ts = startOfArchiveDay(rec[&#39;dateTime&#39;])<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Call the function firstArchiveOfDay
if either this is<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # the first archive since startup, or
if a new day has started<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if not self.old_day or self.old_day
!= dayStart_ts:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.old_day
= dayStart_ts<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
self.newDay(rec)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
# Note 1<br />
<br />
&nbsp;&nbsp;&nbsp; def newDay(self, rec):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;&quot;&quot;Called when the first archive
record of a day arrives.&quot;&quot;&quot;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Go through the list of service
objects. This<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # list is actually in my superclass
StdEngine.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for svc_obj in self.service_obj:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Because
this is a new event, not all services will<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # be prepared
to accept it. Check first to see if the<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # service has
a member function &quot;firstArchiveOfDay&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # before
calling it:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if hasattr(svc_obj,
&quot;firstArchiveOfDay&quot;):&nbsp; # Note 2<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
# The object does have the member function. Call it:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
svc_obj.firstArchiveOfDay(rec)</p>
<p>This customized engine works by monitoring the arrival of archive records,
and checking their time stamp (<span class="code">rec[&#39;dateTime&#39;]</span>. It
calculates the time stamp for the start of the day, and if it changes, calls
member function <span class="code">newDay()</span> (Note 1). </p>
<p>The member function <span class="code">newDay()</span> then goes through the
list of services (attribute <span class="code">self.service_obj</span>). Because
this engine is defining a new event (first archive of the day), the existing
services may not be prepared to accept it. So, the engine checks each one to
make sure it has a function <span class="code">firstArchiveOfDay</span> before
calling it (Note 2)</p>
<p>To use this engine, go into file <span class="code">weewxd.py</span> and change the line</p>
<p class="tty">weewx.wxengine.main()</p>
<p>so that it uses your new engine:</p>
<p class="tty">from examples.daily import MyEngine<br />
&nbsp;<br />
# Specify that my specialized engine should be used instead<br />
# of the default:<br />
weewx.wxengine.main(EngineClass = MyEngine)</p>
<p>We now have a new engine that defines a new event (&quot;<span class="code">firstArchiveOfDay</span>&quot;),
but there is no service to take advantage of it. We define a new service:</p>
<p class="tty"># Define a new service to take advantage of the new event<br />
class DailyService(StdService):<br />
&nbsp;&nbsp;&nbsp; &quot;&quot;&quot;This service can do something when the first archive
record of<br />
&nbsp;&nbsp;&nbsp; a day arrives.&quot;&quot;&quot;<br />
<br />
&nbsp;&nbsp;&nbsp; def firstArchiveOfDay(self, rec):<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;&quot;&quot;Called when the first archive
record of a day arrives.&quot;&quot;&quot;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print &quot;The first archive of the day
has arrived!&quot;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print rec<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # You might want to do something here
like run a cron job</p>
<p>This service will simply print out a notice and then print out the new
record. However, if there is some daily processing you want to do, perhaps a
backup, or running utility <a href="http://www.weewx.com/wunderfixer">
wunderfixer</a>, this would be the place to do it.</p>
<p>The final step is to go into your configuration file and specify that this
new service be loaded, by adding its class name to option <span class="code">
service_list</span>:</p>
<p class="tty">[Engines]<br />
<br />
&nbsp; [[WxEngine]]<br />
&nbsp;&nbsp;&nbsp; # The list of services the main weewx engine should run:<br />
&nbsp;&nbsp;&nbsp; service_list = weewx.wxengine.StdWunderground,
weewx.wxengine.StdCatchUp,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
weewx.wxengine.StdTimeSynch, weewx.wxengine.StdPrint,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
weewx.wxengine.StdProcess, examples.daily.DailyService</p>
<p>(Again, note that this list is shown on several lines for clarity, but in
actuality it must be all on one line.)</p>
</body>
</html>