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

Reply via email to