On Monday, April 2, 2018 at 11:21:05 AM UTC-4, Graham Seward wrote: > > Thanks Bob > I look forward to seeing it - like you though I'm not a programmer and not > sure I'll be able to hack the driver myself - still I'll have a go! > As you say, hopefully Susan will see this. > Many thanks > Graham >
Graham, Here is a copy of the driver I'm using. There are only 3 sections I modified starting about line 261, 314, and 346. I hope it works for you Bob -- 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 weewx-user+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.
# # Copyright (c) 2017 Susan Mackay # acknowledging that this driver code originated from and # is structured in a similar way to the Weewx 'Simulator' driver code # that carries the following line: # Copyright (c) 2009-2015 Tom Keffer <tkef...@gmail.com> # # """hp1000 driver for the weewx weather system""" import math import time import datetime import weedb import weewx.drivers import weeutil.weeutil from weewx.units import convertStd import syslog import sys import socket import struct DRIVER_NAME = 'HP1000' DRIVER_VERSION = "1.0" UDP_BROADCAST_PORT = 6000 TCP_PORT = 6500 def loader(config_dict, engine): station = HP1000Driver(**config_dict[DRIVER_NAME]) return station def logmsg(level, msg): syslog.syslog(level, 'HP1000: %s' % (msg)) def logdbg(msg): logmsg(syslog.LOG_DEBUG, msg) def loginf(msg): logmsg(syslog.LOG_ERR, msg) def logerr(msg): logmsg(syslog.LOG_ERR, msg) class HP1000Driver(weewx.drivers.AbstractDevice): """HP1000 Driver""" def __init__(self, **stn_dict): """Initialize the HP1000 driver""" self.ws_name = "HP1000" loginf('HP1000 Starting') self.internal_test_mode = False # Save the configuration parameters try: self.ip_address_mask = stn_dict['ip_address_mask'] except KeyError, e: raise Exception( "Required parameter 'ip_address_mask' has not been specified") self.retry_count = int(stn_dict.get('retry_count', 5)) self.socket_timeout = float(stn_dict.get('socket_timeout', 5)) self.loop_delay = float( stn_dict.get('loop_delay', None)) self.retry_wait = int(stn_dict.get('retry_wait', 5)) self.max_retry = int(stn_dict.get('max_retry', 5)) self.last_rain_value = None self.last_rain_time = None loginf('Address Mask = %s' % self.ip_address_mask) loginf('Retry count = %f' % self.retry_count) loginf('Socket timeout = %f' % self.socket_timeout) if self.loop_delay is None: loginf('No loop delay') else: loginf('Loop delay = %f' % self.loop_delay) loginf('Retry Wait = %f' % self.retry_wait) loginf('Max Retry = %f' % self.max_retry) # Show that we are not connected to a weather station self.ws_socket = None def string_to_null_padded(self, source, max_length, padd_char='\0'): # Create a byte array passed_string = source.ljust(max_length, '\0') return passed_string def create_cmd_string(self, cmd="READ", argument="NOWRECORD"): # Create the complete packet cmd_packet = '{0:<8s}{1:<8s}{2:<12s}'.format( self.string_to_null_padded('PC2000', 8), self.string_to_null_padded(cmd, 8), self.string_to_null_padded(argument, 12)) cmd_packet = self.string_to_null_padded(cmd_packet, 40) return cmd_packet def convert_units(self, source, target): """Suppress errors when the conversion cannot occcur such as when the source and target unit are the same - it happens a lot in this code""" try: result = convertStd(source, target) except: result = source return result def genLoopPackets(self): network_retry_count = self.max_retry # Local network failure retry counter while True: if self.ws_socket is None: # Search for a weather station on the specified if not self.internal_test_mode: # Broadcast for a weather station sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) sock.settimeout(self.socket_timeout) bcData = "PC2000\0\0SEARCH\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" retry_counter = self.retry_count sender_addr = None while True: # loginf('Sending broadcast request') try: sock.sendto(bcData, (self.ip_address_mask, UDP_BROADCAST_PORT)) except socket.error: network_retry_count -= 1 if network_retry_count > 0: # Try accessing the network again after a short break time.sleep(self.retry_wait) break else: # Run out of attempts raise weewx.RetriesExceeded try: # Receive the response form the weather station data, sender_addr = sock.recvfrom(512) # loginf( 'Received ack from {0}'.format(sender_addr)) break except socket.timeout: retry_counter -= 1 # loginf('Timeout') if retry_counter == 0: sender_addr = None loginf('Timed out too many times') break except socket.error: network_retry_count -= 1 if network_retry_count > 0: # Try accessing the network again after a short break time.sleep(self.retry_wait) break else: # Run out of attempts raise weewx.RetriesExceeded except Exception as e: sender_addr = None # loginf('Unknown error: {0}'.format(e)) break sock.close() # make sure we found something if sender_addr is None: continue # Get the data sent backby the weather station self.ws_name = data[0:8].decode().rstrip('\0') self.ws_MAC_address = data[40:64].decode().rstrip('\0') self.ws_IP_address = data[64:80].decode().rstrip('\0') # loginf( 'WS Name = %s' % self.ws_name) # loginf( 'MAC Address = %s' % self.ws_MAC_address) # loginf( 'IP Address = %s' % self.ws_IP_address) # Connect to the weather station self.ws_socket = None sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(self.socket_timeout) try: sock.bind(("0.0.0.0", TCP_PORT)) except socket.error: network_retry_count -= 1 if network_retry_count > 0: sock.close() # Try accessing the network again after a short break time.sleep(self.retry_wait) continue else: # Run out of attempts raise weewx.RetriesExceeded retry_counter = self.retry_count while True: sock.listen(5) try: # Wait until we a talked to (or timeout) (self.ws_socket, address) = sock.accept() loginf('Connected to address {0}'.format(address)) break except socket.timeout: retry_counter -= 1 loginf('accept timeout') if retry_counter == 0: self.ws_socket = None break except socket.error: network_retry_count -= 1 if network_retry_count > 0: # Try accessign the network again after a short break time.sleep(self.retry_wait) break else: # Run out of attempts raise weewx.RetriesExceeded except Exception as e: self.ws_socket = None loginf('Listening error: {0}'.format(e)) break sock.close() # Make sure that we are talking to a weather station if self.ws_socket is None: loginf('Going around again') continue loginf('Established contact at %s' % datetime.datetime.now().strftime('%d/%m/%y %H:%M:%S')) # Now read the SETUP packet to find the current value units try: self.ws_socket.send(self.create_cmd_string(argument='SETUP')) except socket.error: network_retry_count -= 1 if network_retry_count > 0: # Try accessign the network again after a short break time.sleep(self.retry_wait) break else: # Run out of attempts raise weewx.RetriesExceeded try: rxData = self.ws_socket.recv(1024) except: self.ws_socket.close() self.ws_socket = None continue # Interpret the data ### added by rmc ******************************************************************* if len(rxData) == 55: interp_data = struct.unpack("8s8s8s8s8s15b", rxData) else: self.ws_socket.close() self.ws_socket = None continue ### end added by rmc *************************************************************** # The fields are: # [0] - The device ID # [1] - The command (WRITE) # [2] - The argument (SETUP) # [3] - some part of the command that is echoed # [4] - some response that overlaps the command and is not yet understood # [5] - Time format (1 = 'H:mm:ss', 2 = 'h:mm:ss AM', 4 = 'AM h:mm:ss') # [6] - Date format (16 = 'DD-MM-YYYY', 32 = 'MM-DD-YYYY', 64 = 'YYYY-MM-DD') # [7] - Temperature unit (0 = Celcius, 1 = Fahrenheit) # [8] - Pressure Unit (0 = hPa, 1 = inHg, 2 = mmHg) # [9] - Wind speed (0=m/s, 1=km/h, 2=knot, 3=mph, 4=bft, 5=ft/s) # [10]- Rainfall unit (0 = mm, 1 = in) # [11]- Solar Radiation (0=lux, 1=fc, 2=w/m^2) # [12]- Rain display (0=rain rate,1=daily, 2-weekly, 3=monthly, 4=yearly) # [13]- Graph Time (0=12h, 1=24h, 2=48h, 3=72h) # [14]- Barometer display (0=Abs, 1=Rel) # [15]- Weather Threshold (number) # [16]- Storm Threshold (number) # [17]- Current Weather (0=Sun, 1=partly cloudy, 2=cloudy, 3=rain, 4=storm threshold) # [18]- Rainfall reset month (1=January...) # [19]- Update interval (number, minutes) # We are interested in the temperature, pressure, wind speed, rainfall and solar radiation values self.temperature_unit = interp_data[7] self.pressure_unit = interp_data[8] self.wind_unit = interp_data[9] self.rain_unit = interp_data[10] self.solar_unit = interp_data[11] else: # Internal test mode print('HP1000 - Test Mode - Pretending connection has been made') self.temperature_unit = 0 # C self.pressure_unit = 0 # hPa self.wind_unit = 0 # m/s self.rain_unit = 0 # mm self.solar_unit = 2 # w/m^2 self.rainfall_amount = 0 self.max_iterations = 2 * 3 * 6 * 2 * 3 * 2 self.iteration_count = 0 self.ws_socket = 1 # Any old junk will do as we don't reference this in test mode # See if we need to delay requesting the packet ### if self.loop_delay is not None: Comment out by rmc ### time.sleep(self.loop_delay) Comment out by rmc ### added by rmc ************************************************************************* try: time.sleep(self.loop_delay) except: continue ### end added by rmc ******************************************************************** # Get current data if not self.internal_test_mode: try: self.ws_socket.send(self.create_cmd_string(argument='NOWRECORD')) except socket.error: network_retry_count -= 1 if network_retry_count > 0: # Try accessing the network again after a short break time.sleep(self.retry_wait) self.ws_socket.close() self.ws_socket = None continue else: # Run out of attempts raise weewx.RetriesExceeded try: rxData = self.ws_socket.recv(1024) except: self.ws_socket.close() self.ws_socket = None continue if len(rxData) == 104: interp_data = struct.unpack("8s8s12s12shbbffffffffffffffbbh", rxData) ### Added by rmc ************************************************************************ else: self.ws_socket.close() self.ws_socket = None continue ### end added by rmc ******************************************************************* else: interp_data = [0] * 25 # Internal Test Mode # interp_data[0] - device ID # interp_data[1] - Command # interp_data[2] - Argument # interp_data[3] - junk (yet to be understood) interp_data[4] = 95 # Wind direction in degrees interp_data[5] = 49 # Inside humidity in percent interp_data[6] = 71 # Outside humidity in percent if self.temperature_unit == 0: interp_data[7] = 24.5 # inside temp in C interp_data[10] = 15.9 # outside temp in C interp_data[11] = 7.4 # dewpoint in C interp_data[12] = 15.8 # Windchill in C else: interp_data[7] = 24.5 * 9 / 5 + 32 # F interp_data[10] = 15.9 * 9 / 5 + 32 interp_data[11] = 7.4 * 9 / 5 + 32 interp_data[12] = 15.8 * 9 / 5 + 32 if self.pressure_unit == 0: interp_data[8] = 1014.3 # Pressure in hPa interp_data[9] = 998.4 # Barometer in hPa elif self.pressure_unit == 1: interp_data[8] = 1014.3 * 0.02953 # Pressure in inHg interp_data[9] = 998.4 * 0.02953 # Barometer in inHg else: interp_data[8] = 1014.3 * 0.75006156 # Pressure in mmHg interp_data[9] = 998.4 * 0.75006156 # Barometer in mmHg if self.wind_unit == 0: interp_data[13] = 1.5 # Wind speed in m/s interp_data[14] = 3.8 # Wind gust in m/s elif self.wind_unit == 1: interp_data[13] = 1.5 * 3.6 # Wind speed in km/hr interp_data[14] = 3.8 * 3.6 # Wind gust in km/hr elif self.wind_unit == 2: interp_data[13] = 1.5 * 1.94384 # Wind speed in knots interp_data[14] = 3.8 * 1.94384 # Wind gust in knots elif self.wind_unit == 3: interp_data[13] = 1.5 * 2.23694 # Wind speed in mph interp_data[14] = 3.8 * 2.23694 # Wind gust in mph elif self.wind_unit == 4: # Formula taken from https://en.wikipedia.org/wiki/Beaufort_scale # and inverted for a rough approximation interp_data[13] = int(round(math.pow(1.5 / 0.836, 2.0 / 3.0))) interp_data[14] = int(round(math.pow(3.8 / 0.836, 2.0 / 3.0))) else: interp_data[13] = 1.5 * 3.28084 # Wind speed in ft/s interp_data[14] = 3.8 * 3.28084 # Wind gust in fts if self.solar_unit == 0: interp_data[20] = 532.7 / 4.02 # Sunlight in Lux elif self.solar_unit == 1: interp_data[20] = 532.7 / 0.04358 # Sunlight in fc else: interp_data[20] = 532.7 # w/m2 if self.rain_unit == 0: interp_data[16] = self.rainfall_amount # Rain in mm else: interp_data[16] = self.rainfall_amount / 25.4 # Rain in inches self.rainfall_amount += 0.1 # Heavy rain!!! - At least it will register interp_data[21] = 3 # Actually the UVI print('Temperature Unit: %d' % self.temperature_unit) print(' Pressure Unit: %d' % self.pressure_unit) print(' Wind Unit: %d' % self.wind_unit) print(' Solar Unit: %d' % self.solar_unit) print(' Rain Unit: %d' % self.rain_unit) # End of loading up the test data # Build the LOOP packet from the data _packet = {'dateTime': int(time.time()), 'usUnits': weewx.METRICWX, 'windDir': interp_data[4], 'inHumidity': interp_data[5], 'outHumidity': interp_data[6]} # For units that are set by the console, convert them to metricwx if necessary sourceUnit = '' if self.temperature_unit == 0: sourceUnit = 'degree_C' else: sourceUnit = 'degree_F' print "temp source unit is %s" % sourceUnit _packet['inTemp'] = self.convert_units((interp_data[7], sourceUnit, 'group_temperature'), weewx.METRICWX)[0] _packet['outTemp'] = self.convert_units((interp_data[10], sourceUnit, 'group_temperature'), weewx.METRICWX)[0] _packet['dewPoint'] = self.convert_units((interp_data[11], sourceUnit, 'group_temperature'), weewx.METRICWX)[0] _packet['windChill'] = self.convert_units((interp_data[12], sourceUnit, 'group_temperature'), weewx.METRICWX)[0] if self.pressure_unit == 0: sourceUnit = 'hPa' elif self.pressure_unit == 1: sourceUnit = 'inHg' else: sourceUnit = 'mmHg' _packet['pressure'] = self.convert_units((interp_data[8], sourceUnit, 'group_pressure'), weewx.METRICWX)[0] _packet['barometer'] = self.convert_units((interp_data[9], sourceUnit, 'group_pressure'), weewx.METRICWX)[0] if self.wind_unit == 0: sourceUnit = 'meter_per_second' elif self.wind_unit == 1: sourceUnit = 'km_per_hour' elif self.wind_unit == 2: sourceUnit = 'knot' elif self.wind_unit == 3: sourceUnit = 'mile_per_hour' elif self.wind_unit == 4: loginf('Beaufort Wind Scale Used - Using Approximation') sourceUnit = 'meter_per_second' # Used so there will be no scale factor applied # Formula taken from https://en.wikipedia.org/wiki/Beaufort_scale interp_data[13] = 0.836 * math.pow(interp_data[13], 1.5) interp_data[14] = 0.836 * math.pow(interp_data[14], 1.5) else: sourceUnit = 'meter_per_second' # Used so no scale factor will be applied interp_data[13] *= 0.3048 # ft/s to m/s interp_data[14] *= 0.3048 _packet['windSpeed'] = self.convert_units((interp_data[13], sourceUnit, 'group_speed'), weewx.METRICWX)[0] _packet['windGust'] = self.convert_units((interp_data[14], sourceUnit, 'group_speed'), weewx.METRICWX)[0] # Weewx does not have a standard for Lux of foot-candles if self.solar_unit == 0: _packet['radiation'] = interp_data[20] * 4.02 # Lux to w/m2 for sunlight elif self.solar_unit == 1: _packet['radiation'] = interp_data[20] * 0.04358 # fc to photons *0.199) to w/m2 (0.219) else: _packet['radiation'] = interp_data[20] _packet['UV'] = interp_data[21] # Actually the UVI current_time = datetime.datetime.now() if self.last_rain_value is None: # Should be the first time through _packet['rain'] = None else: # Regular path if current_time.time() > self.last_rain_time.time() and \ interp_data[16] >= self.last_rain_value: # Still in the same day as the previous loop and # the rain value is not lower now than the last reading if self.rain_unit == 0: _packet['rain'] = interp_data[16] - self.last_rain_value else: _packet['rain'] = (interp_data[16] - self.last_rain_value) * 25.4 # in to mm else: # We have started a new day or the rain value has (somehow) gone down # without a 'new day reset' in the weather station _packet['rain'] = 0.0 self.last_rain_value = interp_data[16] self.last_rain_time = current_time # Leave 'rainRate' to be calculated by wxservice if self.internal_test_mode: # Increment the various units, wrapping around as necessary self.temperature_unit += 1 if self.temperature_unit > 1: self.temperature_unit = 0 self.pressure_unit += 1 if self.pressure_unit > 2: self.pressure_unit = 0 self.wind_unit += 1 if self.wind_unit > 5: self.wind_unit = 0 self.solar_unit += 1 if self.solar_unit > 2: self.solar_unit = 0 self.rain_unit += 1 if self.rain_unit > 1: self.rain_unit = 0 print(weeutil.weeutil.timestamp_to_string(_packet['dateTime']), _packet) self.iteration_count += 1 if self.iteration_count > self.max_iterations: sys.exit() yield _packet @property def hardware_name(self): return self.ws_name def internal_testing(self, new_state=False): self.internal_test_mode = new_state def confeditor_loader(): return HP1000ConfEditor() class HP1000ConfEditor(weewx.drivers.AbstractConfEditor): @property def default_stanza(self): return """ [HP1000] # This section is for the weewx HP1000 weather station driver # The IP address mask to search for a weather station ip_address_mask = '192.168.255.255' # The retry count for getting a response from the weather station retry_count = 5 # Socket timeout value (seconds) socket_timeout = 15 # Loop delay time (seconds) # None or not specified means loop packets are generated as fast as possible # (approx every few seconds) and will increase network traffic volume loop_delay = 15 # Number of times to try to access the network max_retry = 5 # Number of seconds to wait between attempts to access the network retry_wait = 5 # The driver to use: driver = user.HP1000 """ if __name__ == "__main__": station = HP1000Driver(ip_address_mask="10.0.0.205", retry_count="5", socket_timeout="15", loop_delay="5") station.internal_testing(True) print('All of the following should return (approx.) the same values') for packet in station.genLoopPackets(): pass