Porting to new hardware¶
Naturally, this is an advanced topic but, nevertheless, I'd like to encourage any Python wizards out there to give it a try. Of course, I have selfish reasons for this: I don't want to have to buy every weather station ever invented, and I don't want my roof to look like a weather station farm!
A driver communicates with hardware. Each driver is a single python file that contains the code that is the interface between a device and WeeWX. A driver may communicate directly with hardware using a MODBus, USB, serial, or other physical interface. Or it may communicate over a network to a physical device or a web service.
- The driver should emit data as it receives it from the hardware (no caching).
- The driver should emit only data it receives from the hardware (no "filling in the gaps").
- The driver should not modify the data unless the modification is directly related to the hardware (e.g., decoding a hardware-specific sensor value).
- If the hardware flags "bad data", then the driver should emit a
null value for that datum (Python
- The driver should not calculate any derived variables (such as
dewpoint). The service
StdWXServicewill do that.
- However, if the hardware emits a derived variable, then the driver should emit it.
Implement the driver¶
Create a file in the user directory, say
mydriver.py. This file
will contain the driver class as well as any hardware-specific code. Do
not put it in the
weewx/drivers directory, or it will be deleted
when you upgrade WeeWX.
Inherit from the abstract base class
weewx.drivers.AbstractDevice. Try to implement as many of its
methods as you can. At the very minimum, you must implement the first
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.
This is an attribute that should return a string with a short nickname for the
hardware, such as
This should be a Python 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 for the observation time and for the units used within the packet.
|The time of the observation in unix epoch time.
|The unit system used. weewx.US for US customary, weewx.METRICWX, or weewx.METRIC for metric. See the Units for their exact definitions. The dictionaries USUnits, MetricWXUnits, and MetricUnits in file units.py, can also be useful.
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 is capable of
measuring an observation type but, for whatever reason, its value is bad
(maybe a bad checksum?), then set its value to
None. If your
hardware is incapable of measuring an observation type, then leave it
out of the dictionary.
A couple of observation types are tricky, in particular,
rain in a LOOP packet should be the amount of rain
that has fallen since the last packet. Because LOOP packets are
emitted fairly frequently, this is likely to be a small number. 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.
Wind is another tricky one. It is actually broken up into four different
windGustDir. Supply as many as you can. The directions
should be compass directions in degrees (0=North, 90=East, etc.).
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.
|The Station Pressure (SP), which is the raw, absolute pressure measured by the station. This is the true barometric pressure for the station.
|The Sea Level Pressure (SLP) obtained by correcting the Station Pressure 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 WeeWX. It is the station pressure reduced to mean sea level using local altitude and local temperature.
|The Altimeter Setting (AS) obtained by correcting the Station Pressure 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.
If your hardware does not have an archive record logger, then WeeWX 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.
However, if your hardware does have a logger, then you should implement
genArchiveRecords() as well. It should be a generator
function that returns all the records since a given time.
If you implement function
genArchiveRecords(), then you should
archive_interval as either an attribute, or as a
should return the archive interval in seconds.
If your hardware has an onboard clock and supports reading the time from it, then you may want to implement this method. It takes no argument. It should return the time in Unix Epoch Time.
If your hardware has an onboard clock and supports setting it, then you may want to implement this method. It takes no argument and does not need to return anything.
If the driver needs to close a serial port, terminate a thread, close a database, or perform any other activity before the application terminates, then you must supply this function. WeeWX will call it if it needs to shut down your console (usually in the case of an error).
Define the configuration¶
You then include a new section in the configuration file
weewx.conf that includes any options your driver needs. It
should also include an entry
driver that points to where your
driver can be found. Set option
station_type to your new
section type and your driver will be loaded.
fileparse driver is perhaps the simplest example of a WeeWX
driver. It reads name-value pairs from a file and uses the values as
sensor 'readings'. The code is actually packaged as an extension,
examples/fileparse, making it a good example of not
only writing a device driver, but also of how to package an extension.
The actual driver itself is in
Another good example is the simulator code located in
weewx/drivers/simulator.py. It's dirt simple, and you can
easily play with it. Many people have successfully used it as a starting
point for writing their own custom driver.
The Ultimeter (
ultimeter.py) and WMR100 (
drivers illustrate how to communicate with serial and USB hardware,
respectively. They also show different approaches for decoding data.
Nevertheless, they are pretty straightforward.
The driver for the Vantage series is by far the most complicated. It
actually multi-inherits from not only
AbstractDevice, but also
StdService. That is, it also participates in the engine as a
Naturally, there are a lot of subtleties that have been glossed over in this high-level description. If you run into trouble, look for help in the weewx-development group.