Gary,
Many thanks for your reply. This is very helpful.
First issue fixed. units from Wunderground url as you say are in in/hr so I
fixed that, i.e. /25.4.
I have written my own driver based on one called socketlogge from here
https://github.com/poblabs/weewx-socketlogger
in my weewx.conf file i specify the driver and parameters.
[W8681]
# This section is for the W8681 wifi series of weather stations.
# The driver to use:
driver = weewx.drivers.w8681
[SocketLogger]
# This section is for the SocketLogger driver
# Station info:
hardware = Generic intercept for wunderground urls
# IP address, port number and timeout in seconds of the socket to connect to
mode = sniff # driver mode either sniff of listen
host_ip = XX.XX.XX.XX # in sniff mode ip of weather station else should be
localhost for listen
host_iface = eth1 # only required in sniff mode
host_port = 80 # port to listen on or port to sniff
timeout = 320 # timeout in seconds
# this should be greater than the interval set on your pws
# The driver to use
driver = user.socketlogger
I have attached the py script (still work in progress so use at own risk).
Passes data to weewex with (for example) _packet['temp']. Not sure if this
simply inserts data into the database or whether weewx actually "processes" the
data.
I am just trying to figure out how the rainrate is handled.
Is weewx smart enough to know if I provide a rain rate of mm/hr every 5 minutes
what the cumulative rainfall is? Just started running my script with a constant
rainrate for 24hrs, will see what happens. So far the rain rate on the web
interface is correct. Although the "Today's Rain" still says zero. Will let it
run for a few days (we should have a total of 48mm tomorrow).
Pete.
--
You received this message because you are subscribed to the Google Groups
"weewx-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.
### Instruction for packet sniffing
##
## You need set your router to send copies of wunderground packets to the server running weewx
## I did this on my router that runs dd-wrt by running the following under Administration->Commands
##
## iptables -A PREROUTING -t mangle -s 192.168.1.13 -j ROUTE --gw 192.168.1.24 --tee
##
## this tells the router to make a copy of any packets sent from the PWS (first ip) to the
## machine running weewx (second ip)
##
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
import socket
import syslog
from time import gmtime, strftime
import time
import urlparse
from scapy.all import IP, sniff
from scapy.layers import http
import string
import weedb
import weewx.drivers
import weeutil.weeutil
import weewx.wxformulas
def logmsg(dst, msg):
syslog.syslog(dst, 'SocketLogger: %s' % msg)
def loginf(msg):
logmsg(syslog.LOG_INFO, msg)
def logerror(msg):
logmsg(syslog.LOG_ERR, msg)
def logdebug(msg):
logmsg(syslog.LOG_DEBUG, msg)
def loader(config_dict, engine):
station = SocketLogger(**config_dict['SocketLogger'])
return station
def _fmt_bytes(data):
return ' '.join(['%02x' % ord(x) for x in data])
def makeDic():
dictionary = {
'tempf' : 'outTemp',
'outtemp' : 'outTemp',
'humidity' : 'outHumidity',
'outhumi' : 'outHumidity',
'inhumi' : 'inHumidity',
'dewpoint' : 'dewpoint',
'dewptf' : 'dewpoint',
'winddir' : 'windDir',
'windspeed' : 'WindSpeed',
'windspeedmph' : 'windSpeed',
'windgustmph' : 'windGust',
'windgust' : 'windGust',
'windgustdir' : 'windGustDir',
'windchill' : 'windchill',
'windchillf' : 'windchill',
#'rainin' : 'rainRate',
#'rainrate' : 'rainRate',
#'dailyrainin' : 'dayRain',
#'dailyrain' : 'dayRain',
#'yearlyrainin' : 'yearRain',
#'monthlyrainin' : 'monthRain',
#'monthlyrain' : 'monthlyrain',
'solarradiation' : 'radiation',
'UV' : 'UV',
'indoortempf' : 'inTemp',
'intemp' : 'InTemp',
'indoorhumidity' : 'inHumidity',
'baromin' : 'barometer',
'absbaromin' : 'pressure',
'absbaro' : 'pressure',
'relbaro' : 'barometer',
'soiltempf' : 'soilTemp1',
'soiltemp2f' : 'soilTemp2',
'soiltemp3f' : 'soilTemp3',
'soiltemp4f' : 'soilTemp4',
'soilmoisture' : 'soilMoist1',
'soilmoisture2' : 'soilMoist2',
'soilmoisture3' : 'soilMoist3',
'soilmoisture3' : 'soilMoist3',
'soilmoisture4' : 'soilMoist4',
'leafwetness' : 'leafWet1',
'leafwetness2' : 'leafWet2',
'lowbatt' : 'txBatteryStatus' }
return dictionary
class SocketLogger(weewx.drivers.AbstractDevice):
#def archive_interval(self, config_dict):
# archive_interval = config_dict['StdArchive'].get('archive_interval', 300)
#config_dict.setdefault('StdWXCalculate', {})
#config_dict['StdWXCalculate'].setdefault('Calculatios', {})
#config_dict['StdWXCalculate']['Calculations']['rainRate'] = 'hardware'
# return archive_interval
SNAPLEN = 85535
PROMISCUOUS = 0
TIMEOUT_MS = 100
def __init__(self, **stn_dict):
import pcap
self.mode = stn_dict.get('mode')
self.host_ip = stn_dict.get('host_ip')
self.host_port = int(stn_dict.get('host_port'))
self.timeout = float(stn_dict.get('timeout'))
#self.archive_interval = config_dict['StdArchive'].get('archive_interval', 300)
self.interval = 200 #self.archive_interval
self.station_hardware = stn_dict.get('hardware')
self.checkParams = [ "intemp", "tempf" ]
self.lastrain = None
self.port = None
#self.openPort()
if self.mode != 'sniff' and self.mode != 'listen':
logerror("Socketlogger mode error " + self.mode + " is in valid. Should be \"sniff\" or \"listen\" ")
raise weewx.WeeWxIOError()
if self.mode == 'sniff':
self.host_iface = stn_dict.get('host_iface')
self.pcap_filter = "src host " + self.host_ip + " && " + "dst port " + str(self.host_port)
self.packet_sniffer = pcap.pcapObject()
#loginf("sniff iface %s" % self.host_iface)
self.packet_sniffer.open_live(
self.host_iface, self.SNAPLEN, self.PROMISCUOUS, self.TIMEOUT_MS)
loginf("Socket logger in sniff mode")
loginf("Sniffing on iface %s. Filter '%s'" %(self.host_iface, self.pcap_filter) )
self.packet_sniffer.setfilter(self.pcap_filter, 0, 0)
self.running = False
self.query_string = ''
self.lastpacket = 'xx'
self.newpacket = 'xx'
self.last_data = ''
elif self.mode == 'listen':
loginf("Socket logger in listen mode")
self.openPort()
self.paramMap = makeDic()
def run(self):
self.running = True
while self.running:
self.packet_sniffer.dispatch(1, self.decode_ip_packet)
if self.newpacket != self.lastpacket:
self.lastpacket = self.newpacket
yield self._process_message( self.newpacket )
#no point in collecting data more frequently than archive update interval
time.sleep(self.interval)
def openPort(self):
try:
loginf("Listenning on ip:port '%s:%s'" %(self.host_ip, self.host_port) )
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.settimeout(self.timeout)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((self.host_ip, self.host_port))
self.socket.listen(1)
#loginf("Listening on ip:port " + str(self.host_ip) + ":" + str(self.host_port) )
#self.socket.connect( (self.host_ip, self.host_port) )
except (socket.error, socket.timeout, socket.herror), ex:
logerror("Socket error while opening port %d to ethernet host %s." % (self.host_port, self.host_ip))
# Reraise as a weewx I/O error:
raise weewx.WeeWxIOError(ex)
except:
logerror("Unable to connect to host %s on port %d." % (self.host_ip, self.host_port))
raise
logdebug("Connected to host %s on port %d" % (self.host_ip, self.host_port))
self.port = self.socket.makefile()
#def stop(self):
# logdebug("stop sniff server")
# self.running = False
# self.packet_sniffer.close()
# self.packet_sniffer = None
def caught_error(self):
a=1
def decode_ip_packet(self, _pktlen, data, _timestamp):
if not data:
loginf("no data")
return
logdebug("sniff: timestamp=%s pktlen=%s data=%s" %
(_timestamp, _pktlen, _fmt_bytes(data)))
if len(data) >= 15 and data[12:14] == '\x08\x00':
header_len = ord(data[14]) & 0x0f
idx = 4 * header_len + 34
if len(data) >= idx:
_data = data[idx:]
if _data == self.last_data:
logdebug("sniff: dup ignore %s" % _fmt_bytes(_data))
if 'GET' in _data:
logdebug("sniff: start %s" % _fmt_bytes(_data))
self.query_string = _data
self.newpacket = self.query_string
#loginf("sniff: final %s" % self.query_string)
try:
self.packet_sniffer.breakloop()
except AttributeError:
self.caught_error()
#elif 'HTTP' in data: # and len(self.query_string):
# loginf("sniff: final %s" % _fmt_bytes(self.query_string))
# data = urlparse.urlparse(self.query_string).query
# logdebug("SNIFF: %s" % _obfuscate_passwords(data))
#Consumer.queue.put(data)
# self.query_string = ''
elif len(self.query_string):
printable = set(string.printable)
fdata = filter(lambda x: x in printable, _data)
if fdata == _data:
logdebug("sniff: append %s" % _fmt_bytes(_data))
self.query_string += _data
else:
logdebug("sniff: ignore %s" % _fmt_bytes(_data))
def hardware_name(self):
return self.station_hardware
#def closePort(self):
# self.port.close()
def check_rain(self, daily_rain_counter):
# *** DO NOT use the &rainin= data! ***
# Handle the rain accum by taking the &dailyrainin= reading ONLY.
# Then submit those minor increments of daily rain to weewx.
rain = 0.0
current_rain = float(daily_rain_counter)
if self.lastrain is not None:
if (current_rain >= self.lastrain):
rain = float(current_rain) - float(self.lastrain)
#loginf("Checking for new rain accumulation")
#loginf(rain)
self.lastrain = current_rain
return rain
#===============================================================================
# LOOP record decoding functions
#===============================================================================
def genPackets(self):
""" Generate measurement packets from the socket. """
while True:
try:
csock, caddr = self.socket.accept()
_line = csock.recv(4096)
#_line = self.port.readline(4096)
#oginf(_line)
except (socket.timeout, socket.error), ex:
raise weewx.WeeWxIOError(ex)
if _line == None:
break
if self.checkParams in _line:
loginf("New line on socket, processing weather data.")
csock.sendall("""HTTP/1.0 200 OK
Content-Type: text/html
<html>
<head>
<title>Success</title>
</head>
<body>
success
</body>
</html>
""")
#csock.close()
yield self._process_message(_line)
else:
#loginf("New line on socket, but did not start with 'outTemp='. Ignoring line.")
pass
def genLoopPackets(self):
""" Generator function that continuously returns loop packets """
if self.mode == 'sniff':
loginf("Socket logger now sniffing....")
for _packet in self.run():
yield _packet
elif self.mode == 'listen':
for _packet in self.genPackets():
yield _packet
def _process_message(self, message):
_packet = {}
#loginf("process: " + message)
parsed = urlparse.urlparse(message)
data = urlparse.parse_qsl(parsed.query)
_packet['dateTime'] = int(time.time())
_packet['usUnits'] = weewx.US
for x,y in data:
#loginf( x + " " + y )
#convert rain inch to mm
if 'rainin' in x :
loginf( x + "---" + str( y ) )
_packet[ 'rainRate' ] = 2.0/25.4
#_packet[ 'rainRate' ] = float ( y ) / 25.4
#_packet['rain'] = self.check_rain( y )
#_packet['rain'] = float ( x['rainin'] ) / 25.4
# _packet[ self.paramMap[x] ] = float( y ) / 25.4
#else:
if x in self.paramMap:
#loginf( x + " " + self.paramMap[x] )
#loginf( x + " " + self.paramMap[x] + " " + str( y ) )
_packet[ self.paramMap[x] ] = float( y )
#loginf("Date packet read %s" %(time.time() ) )
loginf("Weather date packet read %s" %( strftime("%a, %d %b %Y %X +0000", gmtime()) ) )
return _packet