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, type, multiplier) tuple or another dictionary. So, for example, the _reading_format dictionary entry 'rain' : (13, 'us', 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 = WeatherStation.weather_station()
print ws.get_fixed_block()

To get the stored minimum external temperature, get_fixed_block is called with a sequence of keys:

ws = WeatherStation.weather_station()
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 WeatherStation module, see the TestWeatherStation program.

Detailed API

Functions

decode_status(status)

Classes

CUSBDrive() Low level interface to weather station via USB.
DriftingClock(logger, name, status, period, …)
weather_station([ws_type, status, avoid]) Class that represents the weather station to user program.
pywws.WeatherStation.decode_status(status)[source]
class pywws.WeatherStation.CUSBDrive[source]

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
read_block(address)[source]

Read 32 bytes from the weather station.

If the read fails for any reason, None is returned.

Parameters:address (int) – address to read from.
Returns:the data from the weather station.
Return type:list(int)
write_byte(address, data)[source]

Write a single byte to the weather station.

Parameters:
  • address (int) – address to write to.
  • data (int) – the value to write.
Returns:

success status.

Return type:

bool

class pywws.WeatherStation.DriftingClock(logger, name, status, period, margin)[source]
before(now)[source]
avoid()[source]
set_clock(now)[source]
invalidate()[source]
class pywws.WeatherStation.weather_station(ws_type='1080', status=None, avoid=3.0)[source]

Class that represents the weather station to user program.

Connect to weather station and prepare to read data.

min_pause = 0.5
margin = 0.9
live_data(logged_only=False)[source]
inc_ptr(ptr)[source]

Get next circular buffer data pointer.

dec_ptr(ptr)[source]

Get previous circular buffer data pointer.

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.

current_pos()[source]

Get circular buffer location where current data is being written.

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 = {'settings_1': (17, 'bf', ('temp_in_F', 'temp_out_F', 'rain_in', 'bit3', 'bit4', 'pressure_hPa', 'pressure_inHg', 'pressure_mmHg')), 'current_pos': (30, 'us', None), 'timezone': (24, 'sb', None), 'data_count': (27, 'us', None), 'data_changed': (26, 'ub', None), 'alarm_2': (22, 'bf', ('wind_ave', 'wind_gust', 'rain_hour', 'rain_day', 'pressure_abs_lo', 'pressure_abs_hi', 'pressure_rel_lo', 'pressure_rel_hi')), 'read_period': (16, 'ub', None), 'display_1': (19, 'bf', ('pressure_rel', 'wind_gust', 'clock_12hr', 'date_mdy', 'time_scale_24', 'show_year', 'show_day_name', 'alarm_time')), 'display_3': (29, 'bf', ('illuminance_fc', 'bit1', 'bit2', 'bit3', 'bit4', 'bit5', 'bit6', 'bit7')), 'alarm_3': (23, 'bf', ('temp_in_lo', 'temp_in_hi', 'temp_out_lo', 'temp_out_hi', 'wind_chill_lo', 'wind_chill_hi', 'dew_point_lo', 'dew_point_hi')), 'settings_2': (18, 'bf', ('wind_mps', 'wind_kmph', 'wind_knot', 'wind_mph', 'wind_bft', 'bit5', 'bit6', 'bit7')), 'unknown_01': (25, 'pb', None), 'alarm_1': (21, 'bf', ('bit0', 'time', 'wind_dir', 'bit3', 'hum_in_lo', 'hum_in_hi', 'hum_out_lo', 'hum_out_hi')), 'display_2': (20, 'bf', ('temp_out_temp', 'temp_out_chill', 'temp_out_dew', 'rain_hour', 'rain_day', 'rain_week', 'rain_month', 'rain_total'))}
fixed_format = {'unknown_18': (97, 'pb', None), 'settings_2': (18, 'bf', ('wind_mps', 'wind_kmph', 'wind_knot', 'wind_mph', 'wind_bft', 'bit5', 'bit6', 'bit7')), 'magic_0': (0, 'pb', None), 'magic_1': (1, 'pb', None), 'data_changed': (26, 'ub', None), 'alarm': {'hum_out': {'hi': (54, 'ub', None), 'lo': (55, 'ub', None)}, 'illuminance': (89, 'u3', 0.1), 'temp_out': {'hi': (56, 'ss', 0.1), 'lo': (58, 'ss', 0.1)}, 'wind_dir': (82, 'ub', None), 'windchill': {'hi': (60, 'ss', 0.1), 'lo': (62, 'ss', 0.1)}, 'rain': {'hour': (83, 'us', 0.3), 'day': (85, 'us', 0.3)}, 'dewpoint': {'hi': (64, 'ss', 0.1), 'lo': (66, 'ss', 0.1)}, 'rel_pressure': {'hi': (72, 'us', 0.1), 'lo': (74, 'us', 0.1)}, 'temp_in': {'hi': (50, 'ss', 0.1), 'lo': (52, 'ss', 0.1)}, 'wind_ave': {'bft': (76, 'ub', None), 'ms': (77, 'ub', 0.1)}, 'uv': (92, 'ub', None), 'hum_in': {'hi': (48, 'ub', None), 'lo': (49, 'ub', None)}, 'abs_pressure': {'hi': (68, 'us', 0.1), 'lo': (70, 'us', 0.1)}, 'wind_gust': {'bft': (79, 'ub', None), 'ms': (80, 'ub', 0.1)}, 'time': (87, 'tt', None)}, 'alarm_2': (22, 'bf', ('wind_ave', 'wind_gust', 'rain_hour', 'rain_day', 'pressure_abs_lo', 'pressure_abs_hi', 'pressure_rel_lo', 'pressure_rel_hi')), 'read_period': (16, 'ub', None), 'display_1': (19, 'bf', ('pressure_rel', 'wind_gust', 'clock_12hr', 'date_mdy', 'time_scale_24', 'show_year', 'show_day_name', 'alarm_time')), 'display_3': (29, 'bf', ('illuminance_fc', 'bit1', 'bit2', 'bit3', 'bit4', 'bit5', 'bit6', 'bit7')), 'alarm_3': (23, 'bf', ('temp_in_lo', 'temp_in_hi', 'temp_out_lo', 'temp_out_hi', 'wind_chill_lo', 'wind_chill_hi', 'dew_point_lo', 'dew_point_hi')), 'lux_wm2_coeff': (36, 'us', 0.1), 'abs_pressure': (34, 'us', 0.1), 'max': {'hum_out': {'date': (151, 'dt', None), 'val': (100, 'ub', None)}, 'temp_out': {'date': (171, 'dt', None), 'val': (106, 'ss', 0.1)}, 'rain': {'total': {'date': (251, 'dt', None), 'val': (138, 'us', 0.3)}, 'hour': {'date': (231, 'dt', None), 'val': (130, 'us', 0.3)}, 'month': {'date': (246, 'dt', None), 'val': (136, 'us', 0.3)}, 'day': {'date': (236, 'dt', None), 'val': (132, 'us', 0.3)}, 'week': {'date': (241, 'dt', None), 'val': (134, 'us', 0.3)}}, 'windchill': {'date': (181, 'dt', None), 'val': (110, 'ss', 0.1)}, 'illuminance': {'val': (94, 'u3', 0.1)}, 'dewpoint': {'date': (191, 'dt', None), 'val': (114, 'ss', 0.1)}, 'rel_pressure': {'date': (211, 'dt', None), 'val': (122, 'us', 0.1)}, 'temp_in': {'date': (161, 'dt', None), 'val': (102, 'ss', 0.1)}, 'wind_ave': {'date': (221, 'dt', None), 'val': (126, 'us', 0.1)}, 'hum_in': {'date': (141, 'dt', None), 'val': (98, 'ub', None)}, 'abs_pressure': {'date': (201, 'dt', None), 'val': (118, 'us', 0.1)}, 'wind_gust': {'date': (226, 'dt', None), 'val': (128, 'us', 0.1)}, 'uv': {'val': (93, 'ub', None)}}, 'min': {'hum_out': {'date': (156, 'dt', None), 'val': (101, 'ub', None)}, 'temp_out': {'date': (176, 'dt', None), 'val': (108, 'ss', 0.1)}, 'rel_pressure': {'date': (216, 'dt', None), 'val': (124, 'us', 0.1)}, 'windchill': {'date': (186, 'dt', None), 'val': (112, 'ss', 0.1)}, 'temp_in': {'date': (166, 'dt', None), 'val': (104, 'ss', 0.1)}, 'dewpoint': {'date': (196, 'dt', None), 'val': (116, 'ss', 0.1)}, 'hum_in': {'date': (146, 'dt', None), 'val': (99, 'ub', None)}, 'abs_pressure': {'date': (206, 'dt', None), 'val': (120, 'us', 0.1)}}, 'unknown_01': (25, 'pb', None), 'settings_1': (17, 'bf', ('temp_in_F', 'temp_out_F', 'rain_in', 'bit3', 'bit4', 'pressure_hPa', 'pressure_inHg', 'pressure_mmHg')), 'timezone': (24, 'sb', None), 'display_2': (20, 'bf', ('temp_out_temp', 'temp_out_chill', 'temp_out_dew', 'rain_hour', 'rain_day', 'rain_week', 'rain_month', 'rain_total')), 'data_count': (27, 'us', None), 'rel_pressure': (32, 'us', 0.1), 'date_time': (43, 'dt', None), 'current_pos': (30, 'us', None), 'alarm_1': (21, 'bf', ('bit0', 'time', 'wind_dir', 'bit3', 'hum_in_lo', 'hum_in_hi', 'hum_out_lo', 'hum_out_hi'))}
data_start = 256
reading_len = {'3080': 20, '1080': 16}

Comments or questions? Please subscribe to the pywws mailing list http://groups.google.com/group/pywws and let us know.