mirror of
https://github.com/weewx/weewx.git
synced 2026-04-20 17:46:58 -04:00
Started working on supporting Vantage LOOP2 packets.
This commit is contained in:
@@ -469,8 +469,10 @@ class Vantage(weewx.drivers.AbstractDevice):
|
||||
|
||||
iss_id: The station number of the ISS [Optional. Default is 1]
|
||||
|
||||
model_type: Vantage Pro model type. 1 := Vantage Pro; 2 := Vantage Pro2
|
||||
model_type: Vantage Pro model type. 1=Vantage Pro; 2=Vantage Pro2
|
||||
[Optional. Default is 2]
|
||||
|
||||
packet_request: Requested packet type. 1=LOOP; 2=LOOP2; 3=both.
|
||||
"""
|
||||
|
||||
log.debug('Driver version is %s', DRIVER_VERSION)
|
||||
@@ -478,11 +480,12 @@ class Vantage(weewx.drivers.AbstractDevice):
|
||||
self.hardware_type = None
|
||||
|
||||
# These come from the configuration dictionary:
|
||||
self.max_tries = int(vp_dict.get('max_tries', 4))
|
||||
self.max_tries = to_int(vp_dict.get('max_tries', 4))
|
||||
self.iss_id = to_int(vp_dict.get('iss_id'))
|
||||
self.model_type = int(vp_dict.get('model_type', 2))
|
||||
self.model_type = to_int(vp_dict.get('model_type', 2))
|
||||
if self.model_type not in list(range(1, 3)):
|
||||
raise weewx.UnsupportedFeature("Unknown model_type (%d)" % self.model_type)
|
||||
self.packet_request = to_int(vp_dict.get('packet_request', 1))
|
||||
|
||||
self.save_monthRain = None
|
||||
self.max_dst_jump = 7200
|
||||
@@ -536,10 +539,10 @@ class Vantage(weewx.drivers.AbstractDevice):
|
||||
|
||||
self.port.wakeup_console(self.max_tries)
|
||||
|
||||
# Request N packets:
|
||||
self.port.send_data(b"LOOP %d\n" % N)
|
||||
# Request N packets of type "packet_request":
|
||||
self.port.send_data(b"LPS %d %d\n" % (self.packet_request, N))
|
||||
|
||||
for loop in range(N): # @UnusedVariable
|
||||
for loop in range(N):
|
||||
# Fetch a packet...
|
||||
_buffer = self.port.read(99)
|
||||
# ... see if it passes the CRC test ...
|
||||
@@ -1407,51 +1410,68 @@ class Vantage(weewx.drivers.AbstractDevice):
|
||||
wait_before_retry, command_delay)
|
||||
raise weewx.UnsupportedFeature(vp_dict['type'])
|
||||
|
||||
def _unpackLoopPacket(self, raw_loop_string):
|
||||
def _unpackLoopPacket(self, raw_loop_buffer):
|
||||
"""Decode a raw Davis LOOP packet, returning the results as a dictionary in physical units.
|
||||
|
||||
raw_loop_string: The loop packet data buffer, passed in as a string.
|
||||
raw_loop_buffer: The loop packet data buffer, passed in as
|
||||
a string (Python 2), or a byte array (Python 3).
|
||||
|
||||
returns:
|
||||
|
||||
A dictionary. The key will be an observation type, the value will be
|
||||
the observation in physical units."""
|
||||
|
||||
# Unpack the data, using the compiled stuct.Struct string 'loop_fmt'
|
||||
data_tuple = loop_fmt.unpack(raw_loop_string)
|
||||
|
||||
# Put the results in a dictionary. The values will not be in physical units yet,
|
||||
# but rather using the raw values from the console.
|
||||
raw_loop_packet = dict(list(zip(loop_types, data_tuple)))
|
||||
|
||||
# Detect the kind of LOOP packet. Type 'A' has the character 'P' in this
|
||||
# position. Type 'B' contains the 3-hour barometer trend in this position.
|
||||
if raw_loop_packet['loop_type'] == byte2int(b'P'):
|
||||
raw_loop_packet['trendIcon'] = None
|
||||
raw_loop_packet['loop_type'] = 'A'
|
||||
# Get the packet type. It's in byte 4.
|
||||
packet_type = raw_loop_buffer[4]
|
||||
|
||||
if packet_type == 0:
|
||||
loop_struct = loop1_struct
|
||||
loop_types = loop1_types
|
||||
elif packet_type == 1:
|
||||
loop_struct = loop2_struct
|
||||
loop_types = loop2_types
|
||||
else:
|
||||
raw_loop_packet['trendIcon'] = raw_loop_packet['loop_type']
|
||||
raw_loop_packet['loop_type'] = 'B'
|
||||
|
||||
loop_packet = {'dateTime': int(time.time() + 0.5),
|
||||
'usUnits': weewx.US}
|
||||
|
||||
for _type in raw_loop_packet:
|
||||
# Get the mapping function for this type:
|
||||
func = _loop_map.get(_type)
|
||||
# It it exists, apply it:
|
||||
if func:
|
||||
# Call the function, with the raw value as an argument:
|
||||
val = func(raw_loop_packet[_type])
|
||||
# Skip all null values
|
||||
if val is not None:
|
||||
loop_packet[_type] = val
|
||||
|
||||
raise weewx.WeeWxIOError("Unknown LOOP packet type %d" % packet_type)
|
||||
|
||||
# Unpack the data, using the appropriate compiled stuct.Struct buffer.
|
||||
# The result will be a long tuple with just the raw values from the console.
|
||||
data_tuple = loop_struct.unpack(raw_loop_buffer)
|
||||
|
||||
# Combine it with the data types. The result will be a long iterable of 2-way
|
||||
# tuples: (type, raw-value)
|
||||
raw_loop_tuples = zip(loop_types, data_tuple)
|
||||
|
||||
# Now we need to map the raw values to physical units. This is the function that
|
||||
# will do that. It's not really necessary to use an embedded function, but it's
|
||||
# convenient to keep it close to its point-of-use.
|
||||
def map_fn(t):
|
||||
# The input t is a tuple (type, raw-value)
|
||||
# Find the function for this type. If there is no function,
|
||||
# provide a lambda function that will just return None.
|
||||
func = _loop_map.get(t[0], lambda x: None)
|
||||
# Use the function to convert to physical units.
|
||||
# If the result is None, then return None. Otherwise,
|
||||
# return a 2-way tuple (type, physical-value).
|
||||
val = func(t[1])
|
||||
return (t[0], val) if val is not None else None
|
||||
|
||||
# Run the iterable raw_loop_tuples through a mapping using the above function.
|
||||
# Filter out any None values. The result will be a new iterable of
|
||||
# 2-way tuples now in physical units: (type, physical-value).
|
||||
phys_loop_tuples = filter(lambda x: x is not None, map(map_fn, raw_loop_tuples))
|
||||
|
||||
# Last step! Convert to a dictionary.
|
||||
loop_packet = dict(phys_loop_tuples)
|
||||
|
||||
# Add time and the unit system
|
||||
loop_packet['dateTime'] = int(time.time() + 0.5)
|
||||
loop_packet['usUnits'] = weewx.US
|
||||
|
||||
# Adjust sunrise and sunset:
|
||||
start_of_day = weeutil.weeutil.startOfDay(loop_packet['dateTime'])
|
||||
loop_packet['sunrise'] += start_of_day
|
||||
loop_packet['sunset'] += start_of_day
|
||||
|
||||
|
||||
# Because the Davis stations do not offer bucket tips in LOOP data, we
|
||||
# must calculate it by looking for changes in rain totals. This won't
|
||||
# work for the very first rain packet.
|
||||
@@ -1465,7 +1485,7 @@ class Vantage(weewx.drivers.AbstractDevice):
|
||||
self.save_monthRain = loop_packet['monthRain']
|
||||
|
||||
return loop_packet
|
||||
|
||||
|
||||
def _unpackArchivePacket(self, raw_archive_string):
|
||||
"""Decode a Davis archive packet, returning the results as a dictionary.
|
||||
|
||||
@@ -1516,35 +1536,58 @@ class Vantage(weewx.drivers.AbstractDevice):
|
||||
#===============================================================================
|
||||
|
||||
# A list of all the types held in a Vantage LOOP packet in their native order.
|
||||
loop_format = [('loop', '3s'), ('loop_type', 'b'), ('packet_type', 'B'),
|
||||
('next_record', 'H'), ('barometer', 'H'), ('inTemp', 'h'),
|
||||
('inHumidity', 'B'), ('outTemp', 'h'), ('windSpeed', 'B'),
|
||||
('windSpeed10', 'B'), ('windDir', 'H'), ('extraTemp1', 'B'),
|
||||
('extraTemp2', 'B'), ('extraTemp3', 'B'), ('extraTemp4', 'B'),
|
||||
('extraTemp5', 'B'), ('extraTemp6', 'B'), ('extraTemp7', 'B'),
|
||||
('soilTemp1', 'B'), ('soilTemp2', 'B'), ('soilTemp3', 'B'),
|
||||
('soilTemp4', 'B'), ('leafTemp1', 'B'), ('leafTemp2', 'B'),
|
||||
('leafTemp3', 'B'), ('leafTemp4', 'B'), ('outHumidity', 'B'),
|
||||
('extraHumid1', 'B'), ('extraHumid2', 'B'), ('extraHumid3', 'B'),
|
||||
('extraHumid4', 'B'), ('extraHumid5', 'B'), ('extraHumid6', 'B'),
|
||||
('extraHumid7', 'B'), ('rainRate', 'H'), ('UV', 'B'),
|
||||
('radiation', 'H'), ('stormRain', 'H'), ('stormStart', 'H'),
|
||||
('dayRain', 'H'), ('monthRain', 'H'), ('yearRain', 'H'),
|
||||
('dayET', 'H'), ('monthET', 'H'), ('yearET', 'H'),
|
||||
('soilMoist1', 'B'), ('soilMoist2', 'B'), ('soilMoist3', 'B'),
|
||||
('soilMoist4', 'B'), ('leafWet1', 'B'), ('leafWet2', 'B'),
|
||||
('leafWet3', 'B'), ('leafWet4', 'B'), ('insideAlarm', 'B'),
|
||||
('rainAlarm', 'B'), ('outsideAlarm1', 'B'), ('outsideAlarm2', 'B'),
|
||||
('extraAlarm1', 'B'), ('extraAlarm2', 'B'), ('extraAlarm3', 'B'),
|
||||
('extraAlarm4', 'B'), ('extraAlarm5', 'B'), ('extraAlarm6', 'B'),
|
||||
('extraAlarm7', 'B'), ('extraAlarm8', 'B'), ('soilLeafAlarm1', 'B'),
|
||||
('soilLeafAlarm2', 'B'), ('soilLeafAlarm3', 'B'), ('soilLeafAlarm4', 'B'),
|
||||
('txBatteryStatus', 'B'), ('consBatteryVoltage', 'H'), ('forecastIcon', 'B'),
|
||||
('forecastRule', 'B'), ('sunrise', 'H'), ('sunset', 'H')]
|
||||
loop1_schema = [('loop', '3s'), ('rev_type', 'b'), ('packet_type', 'B'),
|
||||
('next_record', 'H'), ('barometer', 'H'), ('inTemp', 'h'),
|
||||
('inHumidity', 'B'), ('outTemp', 'h'), ('windSpeed', 'B'),
|
||||
('windSpeed10', 'B'), ('windDir', 'H'), ('extraTemp1', 'B'),
|
||||
('extraTemp2', 'B'), ('extraTemp3', 'B'), ('extraTemp4', 'B'),
|
||||
('extraTemp5', 'B'), ('extraTemp6', 'B'), ('extraTemp7', 'B'),
|
||||
('soilTemp1', 'B'), ('soilTemp2', 'B'), ('soilTemp3', 'B'),
|
||||
('soilTemp4', 'B'), ('leafTemp1', 'B'), ('leafTemp2', 'B'),
|
||||
('leafTemp3', 'B'), ('leafTemp4', 'B'), ('outHumidity', 'B'),
|
||||
('extraHumid1', 'B'), ('extraHumid2', 'B'), ('extraHumid3', 'B'),
|
||||
('extraHumid4', 'B'), ('extraHumid5', 'B'), ('extraHumid6', 'B'),
|
||||
('extraHumid7', 'B'), ('rainRate', 'H'), ('UV', 'B'),
|
||||
('radiation', 'H'), ('stormRain', 'H'), ('stormStart', 'H'),
|
||||
('dayRain', 'H'), ('monthRain', 'H'), ('yearRain', 'H'),
|
||||
('dayET', 'H'), ('monthET', 'H'), ('yearET', 'H'),
|
||||
('soilMoist1', 'B'), ('soilMoist2', 'B'), ('soilMoist3', 'B'),
|
||||
('soilMoist4', 'B'), ('leafWet1', 'B'), ('leafWet2', 'B'),
|
||||
('leafWet3', 'B'), ('leafWet4', 'B'), ('insideAlarm', 'B'),
|
||||
('rainAlarm', 'B'), ('outsideAlarm1', 'B'), ('outsideAlarm2', 'B'),
|
||||
('extraAlarm1', 'B'), ('extraAlarm2', 'B'), ('extraAlarm3', 'B'),
|
||||
('extraAlarm4', 'B'), ('extraAlarm5', 'B'), ('extraAlarm6', 'B'),
|
||||
('extraAlarm7', 'B'), ('extraAlarm8', 'B'), ('soilLeafAlarm1', 'B'),
|
||||
('soilLeafAlarm2', 'B'), ('soilLeafAlarm3', 'B'), ('soilLeafAlarm4', 'B'),
|
||||
('txBatteryStatus', 'B'), ('consBatteryVoltage', 'H'), ('forecastIcon', 'B'),
|
||||
('forecastRule', 'B'), ('sunrise', 'H'), ('sunset', 'H')]
|
||||
|
||||
# Extract the types and struct.Struct formats for the LOOP packets:
|
||||
loop_types, fmt = list(zip(*loop_format))
|
||||
loop_fmt = struct.Struct('<' + ''.join(fmt))
|
||||
|
||||
loop2_schema = [('loop', '3s'), ('trendIcon', 'b'), ('packet_type', 'B'),
|
||||
('_unused', 'H'), ('barometer', 'H'), ('inTemp', 'h'),
|
||||
('inHumidity', 'B'), ('outTemp', 'h'), ('windSpeed', 'B'),
|
||||
('_unused', 'B'), ('windDir', 'H'), ('windSpeed10', 'H'),
|
||||
('windSpeed2', 'H'), ('windGust10', 'H'), ('windGustDir10', 'H'),
|
||||
('_unused', 'H'), ('_unused', 'H'), ('dewpoint', 'h'),
|
||||
('_unused', 'B'), ('outHumidity', 'B'), ('_unused', 'B'),
|
||||
('heatindex', 'h'), ('windchill', 'h'), ('THSW', 'h'),
|
||||
('rainRate', 'H'), ('UV', 'B'), ('radiation', 'H'),
|
||||
('stormRain', 'H'), ('stormStart', 'H'), ('dayRain', 'H'),
|
||||
('rain15', 'H'), ('hourRain', 'H'), ('dayET', 'H'),
|
||||
('rain24', 'H'), ('bar_reduction', 'B'), ('bar_offset', 'h'),
|
||||
('bar_calibration', 'h'), ('pressure_raw', 'H'), ('pressure', 'H'),
|
||||
('altimeter', 'H'), ('_unused', 'B'), ('_unused', 'B'),
|
||||
('_unused_graph', 'B'), ('_unused_graph', 'B'), ('_unused_graph', 'B'),
|
||||
('_unused_graph', 'B'), ('_unused_graph', 'B'), ('_unused_graph', 'B'),
|
||||
('_unused_graph', 'B'), ('_unused_graph', 'B'), ('_unused_graph', 'B'),
|
||||
('_unused_graph', 'B'), ('_unused', 'H'), ('_unused', 'H'),
|
||||
('_unused', 'H'), ('_unused', 'H'), ('_unused', 'H'),
|
||||
('_unused', 'H')]
|
||||
|
||||
loop1_types, loop1_code = list(zip(*loop1_schema))
|
||||
loop1_struct = struct.Struct('<' + ''.join(loop1_code))
|
||||
loop2_types, loop2_code = list(zip(*loop2_schema))
|
||||
loop2_struct = struct.Struct('<' + ''.join(loop2_code))
|
||||
|
||||
#===============================================================================
|
||||
# archive packet
|
||||
@@ -1779,7 +1822,8 @@ _loop_map = {'barometer' : _val1000Zero,
|
||||
'forecastRule' : _null,
|
||||
'sunrise' : _stime,
|
||||
'sunset' : _stime,
|
||||
'trendIcon' : _null}
|
||||
'trendIcon' : _null,
|
||||
'next_record' : _null_int}
|
||||
|
||||
# This dictionary maps a type key to a function. The function should be able to
|
||||
# decode a sensor value held in the archive packet in the internal, Davis form into US
|
||||
|
||||
Reference in New Issue
Block a user