mirror of
https://github.com/weewx/weewx.git
synced 2026-06-09 09:35:19 -04:00
Updated driver from Chris Manton
This commit is contained in:
@@ -130,7 +130,7 @@ class UsbDevice(object):
|
||||
if not self.dev:
|
||||
return False
|
||||
|
||||
# Open the device and get a handle.
|
||||
# Open the device and get a handle.
|
||||
try:
|
||||
self.handle = self.dev.open()
|
||||
except usb.USBError, exception:
|
||||
@@ -260,13 +260,14 @@ class Packet(object):
|
||||
def __init__(self, wmr200):
|
||||
"""Initialize base elements of the packet parser."""
|
||||
self._pkt_data = []
|
||||
# Record to pass to weewx engine.
|
||||
self._record = None
|
||||
# Determines if packet may be sent to weewx engine or not
|
||||
self._yieldable = True
|
||||
# See note above
|
||||
self._bogus_packet = False
|
||||
# Add the command byte as the first field
|
||||
self.appendData(self.pkt_cmd)
|
||||
|
||||
# Keep reference to the wmr200 for any special considerations
|
||||
# or options.
|
||||
self._wmr200 = wmr200
|
||||
@@ -348,8 +349,27 @@ class Packet(object):
|
||||
return self._sizeActual() == self._sizeExpected()
|
||||
|
||||
def packetProcess(self):
|
||||
"""Returns a records field to be processed by the weewx engine."""
|
||||
"""Process the raw data and creates a record field.
|
||||
|
||||
This is a parent class method and all derivative children
|
||||
must define this method."""
|
||||
dprint('Processing %s' % self.packetName)
|
||||
# Promote this field to an empty dictionary.
|
||||
self._record = {}
|
||||
|
||||
def _packetBeenProcessed(self):
|
||||
"""Indication if packet has been processed.
|
||||
|
||||
Returns: True if packet has been processed."""
|
||||
if self._record is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
def packetRecord(self):
|
||||
"""Returns the processed record to the weewx engine."""
|
||||
if not self._packetBeenProcessed():
|
||||
print 'WARN packetRecord() Packet has not been proccessed.'
|
||||
return self._record
|
||||
|
||||
def packetYieldable(self):
|
||||
"""Not all packets are accepted by the weewx engine.
|
||||
@@ -401,8 +421,11 @@ class Packet(object):
|
||||
syslog.syslog(syslog.LOG_ERR, 'wmr200: %s' % str_val)
|
||||
raise WMR200CheckSumError(str_val)
|
||||
|
||||
def timeStampEpoch(self):
|
||||
"""The timestamp of the packet in seconds since epoch."""
|
||||
def _timeStampEpoch(self):
|
||||
"""The timestamp of the packet in seconds since epoch.
|
||||
|
||||
Must only be called by packets that have timestamps in the
|
||||
protocal packet."""
|
||||
try:
|
||||
minute = self._pkt_data[2]
|
||||
hour = self._pkt_data[3]
|
||||
@@ -410,18 +433,21 @@ class Packet(object):
|
||||
month = self._pkt_data[5]
|
||||
year = 2000 + self._pkt_data[6]
|
||||
|
||||
time_epoch = \
|
||||
self._wmr200.last_time_epoch = \
|
||||
time.mktime((year, month, day, hour, minute, 0, -1, -1, -1))
|
||||
|
||||
# Option to use PC time and not the console time.
|
||||
# Done here so that any error conditions associated with the
|
||||
# time fields will cause the same error as using pc time would.
|
||||
# Drawback is making sure the record interval boundaries that
|
||||
# weewx keeps # per loop packet are satisfied.
|
||||
if self._wmr200.usePcTime:
|
||||
return time.time()
|
||||
return time_epoch
|
||||
self._wmr200.last_time_epoch = time.time()
|
||||
|
||||
return self._wmr200.last_time_epoch
|
||||
|
||||
except IndexError:
|
||||
log_msg = 'Packet length too short to get timestamp'
|
||||
log_msg = 'Packet length too short to get timestamp len:%d' % len(self._pkt_data)
|
||||
print log_msg
|
||||
syslog.syslog(syslog.LOG_ERR, ('wmr200: %s') % log_msg)
|
||||
raise WMR200ProtocolError(log_msg)
|
||||
@@ -432,18 +458,26 @@ class Packet(object):
|
||||
raise WMR200ProtocolError(log_msg)
|
||||
|
||||
def printRaw(self, override = False):
|
||||
"""Debug method to print the raw packet."""
|
||||
out = ' Packet: '
|
||||
"""Debug method to print the raw packet.
|
||||
|
||||
May be called anytime during packet accumulation."""
|
||||
out = ' Packet Raw: '
|
||||
for byte in self._pkt_data:
|
||||
out += '%02x '% byte
|
||||
dprint(out, override)
|
||||
|
||||
def printCooked(self, override = False):
|
||||
"""Debug method to print the processed packet."""
|
||||
out = ' Packet: '
|
||||
out += '%s ' % self.packetName
|
||||
out += '%s ' % time.asctime(time.localtime(self.timeStampEpoch()))
|
||||
out += 'len:%d' % self._sizeActual()
|
||||
"""Debug method method to print the processed packet.
|
||||
|
||||
Must be called after the Process() method."""
|
||||
if self._packetBeenProcessed():
|
||||
out = ' Packet: '
|
||||
out += '%s ' % self.packetName
|
||||
out += '%s ' % time.asctime(time.localtime(self._timeStampEpoch()))
|
||||
out += 'len:%d' % self._sizeActual()
|
||||
out += str(self._record)
|
||||
else:
|
||||
out = 'WARN: printCooked() Packet has not been processed'
|
||||
dprint(out, override)
|
||||
|
||||
|
||||
@@ -472,7 +506,10 @@ class PacketHistoryReady(Packet):
|
||||
return True
|
||||
|
||||
def printCooked(self, override = False):
|
||||
"""Print the processed packet"""
|
||||
"""Print the processed packet.
|
||||
|
||||
Not much processing is done in this packet so not much
|
||||
cooked data to print."""
|
||||
out = ' Packet: '
|
||||
out += '%s ' % self.packetName
|
||||
dprint(out, override)
|
||||
@@ -493,7 +530,10 @@ class PacketHistoryData(Packet):
|
||||
self._yieldable = False
|
||||
|
||||
def printCooked(self, override = False):
|
||||
"""Print the processed packet"""
|
||||
"""Print the processed packet.
|
||||
|
||||
Not much processing is done in this packet so not much
|
||||
cooked data to print."""
|
||||
out = ' Packet: '
|
||||
out += '%s ' % self.packetName
|
||||
dprint(out, override)
|
||||
@@ -564,20 +604,19 @@ class PacketWind(Packet):
|
||||
|
||||
# The console returns wind speeds in m/s. Our metric system requires
|
||||
# kph, so the result needs to be multiplied by 3.6.
|
||||
_record = {'windSpeed' : avgSpeed * 3.60,
|
||||
'windDir' : dirDeg,
|
||||
'dateTime' : self.timeStampEpoch(),
|
||||
'usUnits' : weewx.METRIC,
|
||||
'windChill' : windchill,
|
||||
}
|
||||
self._record = {'windSpeed' : avgSpeed * 3.60,
|
||||
'windDir' : dirDeg,
|
||||
'dateTime' : self._timeStampEpoch(),
|
||||
'usUnits' : weewx.METRIC,
|
||||
'windChill' : windchill,
|
||||
}
|
||||
# Sometimes the station emits a wind gust that is less than the
|
||||
# average wind. Ignore it if this is the case.
|
||||
if gustSpeed >= _record['windSpeed']:
|
||||
_record['windGust'] = gustSpeed * 3.60
|
||||
if gustSpeed >= self._record['windSpeed']:
|
||||
self._record['windGust'] = gustSpeed * 3.60
|
||||
|
||||
# Save the wind record to be used for windchill and heat index
|
||||
self._wmr200.last_wind_record = _record
|
||||
return _record
|
||||
self._wmr200.last_wind_record = self._record
|
||||
|
||||
|
||||
class PacketRain(Packet):
|
||||
@@ -606,14 +645,12 @@ class PacketRain(Packet):
|
||||
# 0.04 inches. Per Ejeklint's notes have you divide the packet values by
|
||||
# 10, but this would result in an 0.4 inch bucket --- too big. So, I'm
|
||||
# dividing by 100.
|
||||
_record = {'rainRate' : rain_rate,
|
||||
'hourRain' : rain_hour,
|
||||
'dayRain' : rain_day,
|
||||
'totalRain' : rain_total,
|
||||
'dateTime' : self.timeStampEpoch(),
|
||||
'usUnits' : weewx.US}
|
||||
|
||||
return _record
|
||||
self._record = {'rainRate' : rain_rate,
|
||||
'hourRain' : rain_hour,
|
||||
'dayRain' : rain_day,
|
||||
'totalRain' : rain_total,
|
||||
'dateTime' : self._timeStampEpoch(),
|
||||
'usUnits' : weewx.US}
|
||||
|
||||
|
||||
class PacketUvi(Packet):
|
||||
@@ -628,10 +665,9 @@ class PacketUvi(Packet):
|
||||
"""Returns a packet that can be processed by the weewx engine."""
|
||||
super(PacketUvi, self).packetProcess()
|
||||
|
||||
_record = {'UV' : self._pkt_data[7] & 0xf,
|
||||
'dateTime' : self.timeStampEpoch(),
|
||||
'usUnits' : weewx.METRIC}
|
||||
return _record
|
||||
self._record = {'UV' : self._pkt_data[7] & 0xf,
|
||||
'dateTime' : self._timeStampEpoch(),
|
||||
'usUnits' : weewx.METRIC}
|
||||
|
||||
|
||||
class PacketPressure(Packet):
|
||||
@@ -665,12 +701,11 @@ class PacketPressure(Packet):
|
||||
dprint('Pressure unknown nibble: %d' % (unknownNibble))
|
||||
dprint('Altitude corrected Pressure: %d hPa' % (altPressure))
|
||||
|
||||
_record = {'barometer' : pressure,
|
||||
'pressure' : pressure,
|
||||
'altimeter' : forecast,
|
||||
'dateTime' : self.timeStampEpoch(),
|
||||
'usUnits' : weewx.METRIC}
|
||||
return _record
|
||||
self._record = {'barometer' : pressure,
|
||||
'pressure' : pressure,
|
||||
'altimeter' : forecast,
|
||||
'dateTime' : self._timeStampEpoch(),
|
||||
'usUnits' : weewx.METRIC}
|
||||
|
||||
|
||||
class PacketTemperature(Packet):
|
||||
@@ -685,8 +720,8 @@ class PacketTemperature(Packet):
|
||||
"""Returns a packet that can be processed by the weewx engine."""
|
||||
super(PacketTemperature, self).packetProcess()
|
||||
|
||||
_record = {'dateTime' : self.timeStampEpoch(),
|
||||
'usUnits' : weewx.METRIC}
|
||||
self._record = {'dateTime' : self._timeStampEpoch(),
|
||||
'usUnits' : weewx.METRIC}
|
||||
|
||||
# The historic data can contain data from multiple sensors. I'm not
|
||||
# sure if the 0xD7 frames can do too. I've never seen a frame with
|
||||
@@ -734,19 +769,18 @@ class PacketTemperature(Packet):
|
||||
dprint(' Heat index: %d' % (heat_index))
|
||||
|
||||
if sensor_id == 0:
|
||||
_record['inTemp'] = temp
|
||||
_record['inHumidity'] = humidity
|
||||
self._record['inTemp'] = temp
|
||||
self._record['inHumidity'] = humidity
|
||||
elif sensor_id == 1:
|
||||
_record['outTemp'] = temp
|
||||
_record['dewpoint'] = weewx.wxformulas.dewpointC(temp, humidity)
|
||||
_record['outHumidity'] = humidity
|
||||
_record['heatindex'] = weewx.wxformulas.heatindexC(temp, humidity)
|
||||
self._record['outTemp'] = temp
|
||||
self._record['dewpoint'] = weewx.wxformulas.dewpointC(temp, humidity)
|
||||
self._record['outHumidity'] = humidity
|
||||
self._record['heatindex'] = weewx.wxformulas.heatindexC(temp, humidity)
|
||||
elif sensor_id >= 2:
|
||||
# If additional temperature sensors exist (channel>=2), then
|
||||
# use observation types 'extraTemp1', 'extraTemp2', etc.
|
||||
_record['extraTemp%d' % sensor_id] = temp
|
||||
_record['extraHumid%d' % sensor_id] = humidity
|
||||
return _record
|
||||
self._record['extraTemp%d' % sensor_id] = temp
|
||||
self._record['extraHumid%d' % sensor_id] = humidity
|
||||
|
||||
|
||||
class PacketStatus(Packet):
|
||||
@@ -756,22 +790,28 @@ class PacketStatus(Packet):
|
||||
pkt_len = 0x08
|
||||
def __init__(self, wmr200):
|
||||
super(PacketStatus, self).__init__(wmr200)
|
||||
self._yieldable = False
|
||||
|
||||
def printCooked(self, override = False):
|
||||
"""Print the processed packet."""
|
||||
out = ' Packet: '
|
||||
out += '%s ' % self.packetName
|
||||
dprint(out, override)
|
||||
|
||||
def packetProcess(self):
|
||||
"""Returns a packet that can be processed by the weewx engine.
|
||||
|
||||
Currently this console status is not passed to the weewx engine.
|
||||
TODO(cmanton) Add way to push bettery status to information to the
|
||||
user. """
|
||||
Not all console status aligns with the weewx API but we try
|
||||
to make it fit."""
|
||||
super(PacketStatus, self).packetProcess()
|
||||
|
||||
# Setup defaults as good. This packet does not have
|
||||
# a timestamp so we put in the PC timestamp.
|
||||
# This may be a problem if using console timestamps.
|
||||
self._record = {
|
||||
'dateTime' : self._wmr200.last_time_epoch,
|
||||
'usUnits' : weewx.METRIC,
|
||||
'inTempBatteryStatus' : 1.0,
|
||||
'OutTempBatteryStatus' : 1.0,
|
||||
'rainBatteryStatus' : 1.0,
|
||||
'windBatteryStatus' : 1.0,
|
||||
'txBatteryStatus' : 1.0,
|
||||
'rxCheckPercent' : 1.0
|
||||
}
|
||||
|
||||
if self._pkt_data[2] & 0x2:
|
||||
dprint('Sensor 1 fault (temp/hum outdoor)')
|
||||
|
||||
@@ -786,15 +826,32 @@ class PacketStatus(Packet):
|
||||
|
||||
if self._pkt_data[4] & 0x02:
|
||||
dprint('Sensor 1: Battery low')
|
||||
self._record['outTempBatteryStatus'] = 0.0
|
||||
|
||||
if self._pkt_data[4] & 0x01:
|
||||
dprint('Wind sensor: Battery low')
|
||||
self._record['windBatteryStatus'] = 0.0
|
||||
|
||||
if self._pkt_data[5] & 0x20:
|
||||
dprint('UV sensor: Battery low')
|
||||
|
||||
if self._pkt_data[5] & 0x10:
|
||||
dprint('Rain sensor: Battery low')
|
||||
self._record['rainBatteryStatus'] = 0.0
|
||||
|
||||
# Output packet to try to understand other fields.
|
||||
self.printRaw(True)
|
||||
|
||||
def printCooked(self, override = False):
|
||||
"""Print the cooked packet."""
|
||||
if self._packetBeenProcessed():
|
||||
out = ' Packet: '
|
||||
out += '%s ' % self.packetName
|
||||
out += 'len:%d' % self._sizeActual()
|
||||
out += str(self._record)
|
||||
else:
|
||||
out = 'WARN: printCooked() Packet has not been processed'
|
||||
dprint(out, override)
|
||||
|
||||
|
||||
class PacketEraseAcknowledgement(Packet):
|
||||
@@ -823,7 +880,9 @@ class PacketEraseAcknowledgement(Packet):
|
||||
return True
|
||||
|
||||
def printCooked(self, override = False):
|
||||
"""Print the processed packet"""
|
||||
"""Print the processed packet.
|
||||
|
||||
This packet consists of a single byte and thus not much to print."""
|
||||
out = ' Packet: '
|
||||
out += '%s ' % self.packetName
|
||||
dprint(out, override)
|
||||
@@ -909,7 +968,7 @@ class RequestLiveData(threading.Thread):
|
||||
# Data is reeady to read on socket.
|
||||
buf = self.sock_rd.recv(4096)
|
||||
syslog.syslog(syslog.LOG_INFO, ('wmr200: Watchdog'
|
||||
' recieved %s') % buf)
|
||||
' received %s') % buf)
|
||||
break
|
||||
|
||||
syslog.syslog(syslog.LOG_INFO, ('wmr200: Watchdog thread exiting'))
|
||||
@@ -1063,6 +1122,8 @@ class WMR200(weewx.abstractstation.AbstractStation):
|
||||
|
||||
# Boolean to use pc timestamps or weather console timestamps.
|
||||
self._use_pc_time = int(stn_dict.get('use_pc_time', '0'), 0) == 1
|
||||
syslog.syslog(syslog.LOG_INFO, ('wmr200: Using PC Time:'
|
||||
'%d') % self._use_pc_time);
|
||||
|
||||
# Buffer of bytes read from weather console device.
|
||||
self._buf = []
|
||||
@@ -1181,7 +1242,7 @@ class WMR200(weewx.abstractstation.AbstractStation):
|
||||
def _writeD0(self):
|
||||
"""Write a command across the USB bus.
|
||||
|
||||
Write a single byte 0xD0 and recieve a single byte back
|
||||
Write a single byte 0xD0 and receive a single byte back
|
||||
acknowledging the command, 0xD1
|
||||
"""
|
||||
buf = [0x01, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||
@@ -1198,7 +1259,7 @@ class WMR200(weewx.abstractstation.AbstractStation):
|
||||
def _writeDB(self):
|
||||
"""Write a command across the USB bus.
|
||||
|
||||
Write a single byte 0xDB and recieve a single byte back
|
||||
Write a single byte 0xDB and receive a single byte back
|
||||
acknowledging the command, 0xDB
|
||||
"""
|
||||
buf = [0x01, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||
@@ -1286,7 +1347,6 @@ class WMR200(weewx.abstractstation.AbstractStation):
|
||||
self.pkt.printRaw(True)
|
||||
else:
|
||||
self.pkt.printRaw()
|
||||
self.pkt.printCooked()
|
||||
# The packets are fixed lengths and flag if they
|
||||
# are incorrect.
|
||||
if self.pkt.packetVerifyLength():
|
||||
@@ -1295,7 +1355,9 @@ class WMR200(weewx.abstractstation.AbstractStation):
|
||||
if self.pkt.packetYieldable():
|
||||
# Only send commands weewx engine will handle.
|
||||
self._stat_pkts_sent += 1
|
||||
yield self.pkt.packetProcess()
|
||||
self.pkt.packetProcess()
|
||||
self.pkt.printCooked(override=True)
|
||||
yield self.pkt.packetRecord()
|
||||
|
||||
# Reset this packet as its complete or bogus.
|
||||
self.pkt = None
|
||||
|
||||
Reference in New Issue
Block a user