
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 ]-

+------------+--------------------------------------------------------------------------------------------+
| "apparent_ | Compute apparent temperature (real feel), using formula from                               |
+------------+--------------------------------------------------------------------------------------------+
| "dew_point | Compute dew point, using formula from                                                      |
+------------+--------------------------------------------------------------------------------------------+
| "get_wind_ | Return an array to convert wind direction integer to a string.                             |
+------------+--------------------------------------------------------------------------------------------+
| "pressure_ | Convert pressure trend to a string, as used by the UK met                                  |
+------------+--------------------------------------------------------------------------------------------+
| "wind_chil | Compute wind chill, using formula from                                                     |
+------------+--------------------------------------------------------------------------------------------+

-[ Classes ]-

+------------+--------------------------------------------------------------------------------------------+
| "CUSBDrive | Low level interface to weather station via USB.                                            |
+------------+--------------------------------------------------------------------------------------------+
| "weather_s | Class that represents the weather station to user program.                                 |
+------------+--------------------------------------------------------------------------------------------+

pywws.WeatherStation.dew_point(temp, hum)

   Compute dew point, using formula from
   http://en.wikipedia.org/wiki/Dew_point.

pywws.WeatherStation.wind_chill(temp, wind)

   Compute wind chill, using formula from
   http://en.wikipedia.org/wiki/wind_chill

pywws.WeatherStation.apparent_temp(temp, rh, wind)

   Compute apparent temperature (real feel), using formula from
   http://www.bom.gov.au/info/thermal_stress/

pywws.WeatherStation.get_wind_dir_text()

   Return an array to convert wind direction integer to a string.

pywws.WeatherStation.pressure_trend_text(trend)

   Convert pressure trend to a string, as used by the UK met office.

class class pywws.WeatherStation.CUSBDrive(library)

   Low level interface to weather station via USB.

   Loosely modeled on a C++ class obtained from http://site.ambientwea
   therstore.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)

      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)

      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 class pywws.WeatherStation.weather_station(ws_type='1080', library='auto', params=None)

   Class that represents the weather station to user program.

   Connect to weather station and prepare to read data.

   avoid = 3.0

   min_pause = 0.5

   live_data(logged_only=False)

   inc_ptr(ptr)

      Get next circular buffer data pointer.

   dec_ptr(ptr)

      Get previous circular buffer data pointer.

   get_raw_data(ptr, unbuffered=False)

      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)

      Get decoded data from circular buffer.

      If unbuffered is false then a cached value that was obtained
      earlier may be returned.

   current_pos()

      Get circular buffer location where current data is being
      written.

   get_raw_fixed_block(unbuffered=False)

      Get the raw "fixed block" of settings and min/max data.

   get_fixed_block(keys=[], unbuffered=False)

      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)

      Write a set of single bytes to the weather station. Data must be
      an array of (ptr, value) pairs.

   reading_format = {'3080': {'status': (15, 'pb', None), 'hum_out': (4, 'ub', None), 'wind_gust': (10, 'wg', 0.1), 'uv': (19, 'ub', None), 'wind_ave': (9, 'wa', 0.1), 'rain': (13, 'us', 0.3), 'temp_in': (2, 'ss', 0.1), 'illuminance': (16, 'u3', 0.1), 'abs_pressure': (7, 'us', 0.1), 'delay': (0, 'ub', None), 'hum_in': (1, 'ub', None), 'temp_out': (5, 'ss', 0.1), 'wind_dir': (12, 'ub', None)}, '1080': {'status': (15, 'pb', None), 'wind_ave': (9, 'wa', 0.1), 'rain': (13, 'us', 0.3), 'hum_in': (1, 'ub', None), 'temp_out': (5, 'ss', 0.1), 'wind_dir': (12, 'ub', None), 'hum_out': (4, 'ub', None), 'wind_gust': (10, 'wg', 0.1), 'temp_in': (2, 'ss', 0.1), 'delay': (0, 'ub', None), 'abs_pressure': (7, 'us', 0.1)}}

   lo_fix_format = {'alarm_1': (21, 'bf', ('bit0', 'time', 'wind_dir', 'bit3', 'hum_in_lo', 'hum_in_hi', 'hum_out_lo', 'hum_out_hi')), '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')), 'alarm_2': (22, 'bf', ('wind_ave', 'wind_gust', 'rain_hour', 'rain_day', 'pressure_abs_lo', 'pressure_abs_hi', 'pressure_rel_lo', 'pressure_rel_hi')), 'data_changed': (26, 'ub', None), 'settings_1': (17, 'bf', ('temp_in_F', 'temp_out_F', 'rain_in', 'bit3', 'bit4', 'pressure_hPa', 'pressure_inHg', 'pressure_mmHg')), 'settings_2': (18, 'bf', ('wind_mps', 'wind_kmph', 'wind_knot', 'wind_mph', 'wind_bft', 'bit5', 'bit6', 'bit7')), 'read_period': (16, 'ub', None), 'unknown_01': (25, 'pb', None), 'timezone': (24, 'sb', None), 'data_count': (27, 'us', None), 'current_pos': (30, 'us', None), 'display_2': (20, 'bf', ('temp_out_temp', 'temp_out_chill', 'temp_out_dew', 'rain_hour', 'rain_day', 'rain_week', 'rain_month', 'rain_total')), 'display_3': (29, 'bf', ('illuminance_fc', 'bit1', 'bit2', 'bit3', 'bit4', 'bit5', 'bit6', 'bit7')), 'display_1': (19, 'bf', ('pressure_rel', 'wind_gust', 'clock_12hr', 'date_mdy', 'time_scale_24', 'show_year', 'show_day_name', 'alarm_time'))}

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

   data_start = 256

   reading_len = {'3080': 20, '1080': 16}
