pywws.weatherstation¶
Get data from WH1080/WH3080 compatible weather stations.
Derived from wwsr.c by Michael Pendec (michael.pendec@gmail.com), wwsrdump.c by Svend Skafte (svend@skafte.net), modified by Dave Wells, and other sources.
Introduction¶
This is the module that actually talks to the weather station base unit. I don’t have much understanding of USB, so copied a lot from Michael Pendec’s C program wwsr.
The weather station memory has two parts: a “fixed block” of 256 bytes
and a circular buffer of 65280 bytes. As each weather reading takes 16
bytes the station can store 4080 readings, or 14 days of 5-minute
interval readings. (The 3080 type stations store 20 bytes per reading,
so store a maximum of 3264.) As data is read in 32-byte chunks, but
each weather reading is 16 or 20 bytes, a small cache is used to
reduce USB traffic. The caching behaviour can be over-ridden with the
unbuffered
parameter to get_data
and get_raw_data
.
Decoding the data is controlled by the static dictionaries
_reading_format
, lo_fix_format
and fixed_format
. The keys
are names of data items and the values can be an (offset, class,
kwds)
tuple or another dictionary. So, for example, the
_reading_format dictionary entry 'rain' : (13, WSFloat, {'signed':
False, 'scale': 0.3})
means that the rain value is an unsigned short
(two bytes), 13 bytes from the start of the block, and should be
multiplied by 0.3 to get a useful value.
The use of nested dictionaries in the fixed_format
dictionary
allows useful subsets of data to be decoded. For example, to decode
the entire block get_fixed_block
is called with no parameters:
ws = pywws.weatherstation.WeatherStation()
print(ws.get_fixed_block())
To get the stored minimum external temperature, get_fixed_block
is
called with a sequence of keys:
ws = pywws.weatherstation.WeatherStation()
print(ws.get_fixed_block(['min', 'temp_out', 'val']))
Often there is no requirement to read and decode the entire fixed
block, as its first 64 bytes contain the most useful data: the
interval between stored readings, the buffer address where the current
reading is stored, and the current date & time. The
get_lo_fix_block
method provides easy access to these.
For more examples of using the pywws.weatherstation module, see the pywws.testweatherstation module.
Detailed API¶
Classes
CUSBDrive () |
Low level interface to weather station via USB. |
DriftingClock (name, status, period, margin) |
|
WSBits |
|
WSDateTime |
|
WSFloat |
|
WSInt |
|
WSStatus |
|
WSTime |
|
WeatherStation ([context]) |
Class that represents the weather station to user program. |
-
class
pywws.weatherstation.
WSStatus
[source]¶ Bases:
pywws.weatherstation.WSBits
-
keys
= ('bit0', 'bit1', 'bit2', 'bit3', 'bit4', 'bit5', 'lost_connection', 'rain_overflow')¶
-
-
class
pywws.weatherstation.
WSDateTime
[source]¶ Bases:
datetime.datetime
-
class
pywws.weatherstation.
CUSBDrive
[source]¶ Bases:
object
Low level interface to weather station via USB.
Loosely modeled on a C++ class obtained from http://site.ambientweatherstore.com/easyweather/ws_1080_2080_protocol.zip. I don’t know the provenance of this, but it looks as if it may have come from the manufacturer.
-
EndMark
= 32¶
-
ReadCommand
= 161¶
-
WriteCommand
= 160¶
-
WriteCommandWord
= 162¶
-
-
class
pywws.weatherstation.
WeatherStation
(context=None)[source]¶ Bases:
object
Class that represents the weather station to user program.
-
min_pause
= 0.5¶
-
margin
= 0.9¶
-
get_raw_data
(ptr, unbuffered=False)[source]¶ Get raw data from circular buffer.
If unbuffered is false then a cached value that was obtained earlier may be returned.
-
get_data
(ptr, unbuffered=False)[source]¶ Get decoded data from circular buffer.
If unbuffered is false then a cached value that was obtained earlier may be returned.
-
get_raw_fixed_block
(unbuffered=False)[source]¶ Get the raw “fixed block” of settings and min/max data.
-
get_fixed_block
(keys=[], unbuffered=False)[source]¶ Get the decoded “fixed block” of settings and min/max data.
A subset of the entire block can be selected by keys.
-
write_data
(data)[source]¶ Write a set of single bytes to the weather station. Data must be an array of (ptr, value) pairs.
-
lo_fix_format
= {'alarm_1': (21, <function WSBits.from_raw>, {'keys': ('bit0', 'time', 'wind_dir', 'bit3', 'hum_in_lo', 'hum_in_hi', 'hum_out_lo', 'hum_out_hi')}), 'alarm_2': (22, <function WSBits.from_raw>, {'keys': ('wind_ave', 'wind_gust', 'rain_hour', 'rain_day', 'pressure_abs_lo', 'pressure_abs_hi', 'pressure_rel_lo', 'pressure_rel_hi')}), 'alarm_3': (23, <function WSBits.from_raw>, {'keys': ('temp_in_lo', 'temp_in_hi', 'temp_out_lo', 'temp_out_hi', 'wind_chill_lo', 'wind_chill_hi', 'dew_point_lo', 'dew_point_hi')}), 'current_pos': (30, <function WSInt.from_2>, {'signed': False}), 'data_changed': (26, <function WSInt.from_1>, {'signed': False}), 'data_count': (27, <function WSInt.from_2>, {'signed': False}), 'display_1': (19, <function WSBits.from_raw>, {'keys': ('pressure_rel', 'wind_gust', 'clock_12hr', 'date_mdy', 'time_scale_24', 'show_year', 'show_day_name', 'alarm_time')}), 'display_2': (20, <function WSBits.from_raw>, {'keys': ('temp_out_temp', 'temp_out_chill', 'temp_out_dew', 'rain_hour', 'rain_day', 'rain_week', 'rain_month', 'rain_total')}), 'display_3': (29, <function WSBits.from_raw>, {'keys': ('illuminance_fc', 'alarm_illuminance_hi', 'alarm_uv_hi', 'bit3', 'bit4', 'illuminance_wm2', 'bit6', 'bit7')}), 'read_period': (16, <function WSInt.from_1>, {'signed': False}), 'settings_1': (17, <function WSBits.from_raw>, {'keys': ('temp_in_F', 'temp_out_F', 'rain_in', 'bit3', 'bit4', 'pressure_hPa', 'pressure_inHg', 'pressure_mmHg')}), 'settings_2': (18, <function WSBits.from_raw>, {'keys': ('wind_mps', 'wind_kmph', 'wind_knot', 'wind_mph', 'wind_bft', 'bit5', 'bit6', 'bit7')}), 'timezone': (24, <function WSInt.from_1>, {'signed': True}), 'unknown_01': (25, <function WSInt.from_1>, {'signed': False})}¶
-
fixed_format
= {'abs_pressure': (34, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1}), 'alarm': {'abs_pressure': {'hi': (68, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1}), 'lo': (70, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1})}, 'dewpoint': {'hi': (64, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1}), 'lo': (66, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1})}, 'hum_in': {'hi': (48, <function WSInt.from_1>, {'signed': False}), 'lo': (49, <function WSInt.from_1>, {'signed': False})}, 'hum_out': {'hi': (54, <function WSInt.from_1>, {'signed': False}), 'lo': (55, <function WSInt.from_1>, {'signed': False})}, 'illuminance': (89, <function WSFloat.from_3>, {'signed': False, 'scale': 0.1}), 'rain': {'day': (85, <function WSFloat.from_2>, {'signed': False, 'scale': 0.3}), 'hour': (83, <function WSFloat.from_2>, {'signed': False, 'scale': 0.3})}, 'rel_pressure': {'hi': (72, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1}), 'lo': (74, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1})}, 'temp_in': {'hi': (50, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1}), 'lo': (52, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1})}, 'temp_out': {'hi': (56, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1}), 'lo': (58, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1})}, 'time': (87, <function WSTime.from_raw>, {}), 'uv': (92, <function WSInt.from_1>, {'signed': False}), 'wind_ave': {'bft': (76, <function WSInt.from_1>, {'signed': False}), 'ms': (77, <function WSFloat.from_1>, {'signed': False, 'scale': 0.1})}, 'wind_dir': (82, <function WSInt.wind_dir>, {}), 'wind_gust': {'bft': (79, <function WSInt.from_1>, {'signed': False}), 'ms': (80, <function WSFloat.from_1>, {'signed': False, 'scale': 0.1})}, 'windchill': {'hi': (60, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1}), 'lo': (62, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1})}}, 'alarm_1': (21, <function WSBits.from_raw>, {'keys': ('bit0', 'time', 'wind_dir', 'bit3', 'hum_in_lo', 'hum_in_hi', 'hum_out_lo', 'hum_out_hi')}), 'alarm_2': (22, <function WSBits.from_raw>, {'keys': ('wind_ave', 'wind_gust', 'rain_hour', 'rain_day', 'pressure_abs_lo', 'pressure_abs_hi', 'pressure_rel_lo', 'pressure_rel_hi')}), 'alarm_3': (23, <function WSBits.from_raw>, {'keys': ('temp_in_lo', 'temp_in_hi', 'temp_out_lo', 'temp_out_hi', 'wind_chill_lo', 'wind_chill_hi', 'dew_point_lo', 'dew_point_hi')}), 'current_pos': (30, <function WSInt.from_2>, {'signed': False}), 'data_changed': (26, <function WSInt.from_1>, {'signed': False}), 'data_count': (27, <function WSInt.from_2>, {'signed': False}), 'date_time': (43, <function WSDateTime.from_raw>, {}), 'display_1': (19, <function WSBits.from_raw>, {'keys': ('pressure_rel', 'wind_gust', 'clock_12hr', 'date_mdy', 'time_scale_24', 'show_year', 'show_day_name', 'alarm_time')}), 'display_2': (20, <function WSBits.from_raw>, {'keys': ('temp_out_temp', 'temp_out_chill', 'temp_out_dew', 'rain_hour', 'rain_day', 'rain_week', 'rain_month', 'rain_total')}), 'display_3': (29, <function WSBits.from_raw>, {'keys': ('illuminance_fc', 'alarm_illuminance_hi', 'alarm_uv_hi', 'bit3', 'bit4', 'illuminance_wm2', 'bit6', 'bit7')}), 'lux_wm2_coeff': (36, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1}), 'magic_0': (0, <function WSInt.from_1>, {'signed': False}), 'magic_1': (1, <function WSInt.from_1>, {'signed': False}), 'max': {'abs_pressure': {'date': (201, <function WSDateTime.from_raw>, {}), 'val': (118, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1})}, 'dewpoint': {'date': (191, <function WSDateTime.from_raw>, {}), 'val': (114, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1})}, 'hum_in': {'date': (141, <function WSDateTime.from_raw>, {}), 'val': (98, <function WSInt.from_1>, {'signed': False})}, 'hum_out': {'date': (151, <function WSDateTime.from_raw>, {}), 'val': (100, <function WSInt.from_1>, {'signed': False})}, 'illuminance': {'date': (11, <function WSDateTime.from_raw>, {}), 'val': (94, <function WSFloat.from_3>, {'signed': False, 'scale': 0.1})}, 'rain': {'day': {'date': (236, <function WSDateTime.from_raw>, {}), 'val': (132, <function WSFloat.from_2>, {'signed': False, 'scale': 0.3})}, 'hour': {'date': (231, <function WSDateTime.from_raw>, {}), 'val': (130, <function WSFloat.from_2>, {'signed': False, 'scale': 0.3})}, 'month': {'date': (246, <function WSDateTime.from_raw>, {}), 'val': (136, <function WSFloat.from_2>, {'signed': False, 'scale': 0.3, 'nibble_pos': 140, 'nibble_high': True})}, 'total': {'date': (251, <function WSDateTime.from_raw>, {}), 'val': (138, <function WSFloat.from_2>, {'signed': False, 'scale': 0.3, 'nibble_pos': 140, 'nibble_high': False})}, 'week': {'date': (241, <function WSDateTime.from_raw>, {}), 'val': (134, <function WSFloat.from_2>, {'signed': False, 'scale': 0.3})}}, 'rel_pressure': {'date': (211, <function WSDateTime.from_raw>, {}), 'val': (122, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1})}, 'temp_in': {'date': (161, <function WSDateTime.from_raw>, {}), 'val': (102, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1})}, 'temp_out': {'date': (171, <function WSDateTime.from_raw>, {}), 'val': (106, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1})}, 'uv': {'date': (6, <function WSDateTime.from_raw>, {}), 'val': (93, <function WSInt.from_1>, {'signed': False})}, 'wind_ave': {'date': (221, <function WSDateTime.from_raw>, {}), 'val': (126, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1})}, 'wind_gust': {'date': (226, <function WSDateTime.from_raw>, {}), 'val': (128, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1})}, 'windchill': {'date': (181, <function WSDateTime.from_raw>, {}), 'val': (110, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1})}}, 'min': {'abs_pressure': {'date': (206, <function WSDateTime.from_raw>, {}), 'val': (120, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1})}, 'dewpoint': {'date': (196, <function WSDateTime.from_raw>, {}), 'val': (116, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1})}, 'hum_in': {'date': (146, <function WSDateTime.from_raw>, {}), 'val': (99, <function WSInt.from_1>, {'signed': False})}, 'hum_out': {'date': (156, <function WSDateTime.from_raw>, {}), 'val': (101, <function WSInt.from_1>, {'signed': False})}, 'rel_pressure': {'date': (216, <function WSDateTime.from_raw>, {}), 'val': (124, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1})}, 'temp_in': {'date': (166, <function WSDateTime.from_raw>, {}), 'val': (104, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1})}, 'temp_out': {'date': (176, <function WSDateTime.from_raw>, {}), 'val': (108, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1})}, 'windchill': {'date': (186, <function WSDateTime.from_raw>, {}), 'val': (112, <function WSFloat.from_2>, {'signed': True, 'scale': 0.1})}}, 'rain_factor_raw': (2, <function WSFloat.from_2>, {'signed': False}), 'read_period': (16, <function WSInt.from_1>, {'signed': False}), 'rel_pressure': (32, <function WSFloat.from_2>, {'signed': False, 'scale': 0.1}), 'settings_1': (17, <function WSBits.from_raw>, {'keys': ('temp_in_F', 'temp_out_F', 'rain_in', 'bit3', 'bit4', 'pressure_hPa', 'pressure_inHg', 'pressure_mmHg')}), 'settings_2': (18, <function WSBits.from_raw>, {'keys': ('wind_mps', 'wind_kmph', 'wind_knot', 'wind_mph', 'wind_bft', 'bit5', 'bit6', 'bit7')}), 'timezone': (24, <function WSInt.from_1>, {'signed': True}), 'unknown_01': (25, <function WSInt.from_1>, {'signed': False}), 'unknown_18': (97, <function WSInt.from_1>, {'signed': False}), 'wind_factor_raw': (4, <function WSFloat.from_2>, {'signed': False})}¶
-
data_start
= 256¶
-
reading_len
= {'1080': 16, '3080': 20}¶
-
Comments or questions? Please subscribe to the pywws mailing list http://groups.google.com/group/pywws and let us know.