Time to revisit this old post as I finally spent some time with morrowwm's 
wxMesh driver to make slight modifications required for this use case.  

It has been working well for the last week and I am making it available in 
case it may be useful for someone else.  If there is sufficient interest I 
will clean it up and upload it to weewx-wiki as a separate driver.

The following is a description from the driver comment section describing 
how it works.  

# Original driver created by morrowwm
# This is a specific modification for the use case of a station in a remote 
location
# where bandwidth and data allowance is limited and a lightweight data 
transfer protocol
# such as MQTT is required.
#
# The system requires a local (at the weather station) instance of weewx to 
collect data,
# archive locally and publish complete archive records to MQTT broker on a 
remote server.
# The remote server runs a remote instance of weewx that receives the 
complete archive
# records by subsribing to MQTT topic on the remote server, archives 
remotely and uploads
# generated webpages to the webserver.
#
# The local instance of weewx publishes the data to the MQTT broker using 
mwall's 
# weewx-mqtt uploader with the following settings
# 
#        server_url = mqtt://<username>:<password>@<mqtt_host>:1883/
#        topic = weather
#        unit_system = US
#        binding = archive
#        aggregation = aggregate
#        append_units_label = false
#
# MQTT broker set up as described in this excellent guide by Pat O'Brien
# https://obrienlabs.net/how-to-setup-your-own-mqtt-broker/
#
## The units must be weewx.US:
#   degree_F, inHg, inch, inch_per_hour, mile_per_hour
#
# To use this driver, put this file in the weewx user directory, then make
# the following changes to weewx.conf:
#
# [Station]
#     station_type = wxMesh
# [wxMesh]
#     host = localhost           # MQTT broker hostname
#     username = <username>      # MQTT broker user name. Assumption is 
your broker requires authentication
#     password = "password"
#     topic = weather/+          # topic
#     driver = user.wxMesh
#
# Since the subscribed topic already uses weewx standard labels no labell 
mapping is required.
#
# This driver has not been tested for use with Python 3

-- 
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].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/weewx-user/4cda152e-1ab0-483d-9423-7e4faf2d0f87%40googlegroups.com.
#!/usr/bin/python
#
# weewx driver that reads data from MQTT subscription
#
# Original driver created by morrowwm
# This is a specific modification for the use case of a station in a remote location
# where bandwidth and data allowance is limited and a lightweight data transfer protocol
# such as MQTT is required.
#
# The system requires a local (at the weather station) instance of weewx to collect data,
# archive locally and publish complete archive records to MQTT broker on a remote server.
# The remote server runs a remote instance of weewx that receives the complete archive
# records by subsribing to MQTT topic on the remote server, archives remotely and uploads
# generated webpages to the webserver.
#
# The local instance of weewx publishes the data to the MQTT broker using mwall's 
# weewx-mqtt uploader with the following settings
# 
#        server_url = mqtt://<username>:<password>@<mqtt_host>:1883/
#        topic = weather
#        unit_system = US
#        binding = archive
#        aggregation = aggregate
#        append_units_label = false
#
# MQTT broker set up as described in this excellent guide by Pat O'Brien
# https://obrienlabs.net/how-to-setup-your-own-mqtt-broker/
#
## The units must be weewx.US:
#   degree_F, inHg, inch, inch_per_hour, mile_per_hour
#
# To use this driver, put this file in the weewx user directory, then make
# the following changes to weewx.conf:
#
# [Station]
#     station_type = wxMesh
# [wxMesh]
#     host = localhost           # MQTT broker hostname
#     username = <username>      # MQTT broker user name. Assumption is your broker requires authentication
#     password = "password"
#     topic = weather/+          # topic
#     driver = user.wxMesh
#
# Since the subscribed topic already uses weewx standard labels no labell mapping is required.
#
# This driver has not been tested for use with Python 3

# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.
#
# See http://www.gnu.org/licenses/

from __future__ import with_statement
import syslog
import time
import Queue
import paho.mqtt.client as mqtt
import weewx.drivers
import re

DRIVER_VERSION = "0.1"

def logmsg(dst, msg):
    syslog.syslog(dst, 'wxMesh: %s' % msg)

def logdbg(msg):
    logmsg(syslog.LOG_DEBUG, msg)

def loginf(msg):
    logmsg(syslog.LOG_INFO, msg)

def logerr(msg):
    logmsg(syslog.LOG_ERR, msg)

def _get_as_float(d, s):
    v = None
    if s in d:
        try:
            v = float(d[s])
        except ValueError, e:
            logerr("cannot read value for '%s': %s" % (s, e))
    return v

def loader(config_dict, engine):
    return wxMesh(**config_dict['wxMesh'])

class wxMesh(weewx.drivers.AbstractDevice):
    """weewx driver that reads data from a file"""

    def __init__(self, **stn_dict):
      # where to find the data file
      self.host = stn_dict.get('host', 'localhost')
      self.topic = stn_dict.get('topic', 'weather')
      self.username = stn_dict.get('username', 'no default')
      self.password = stn_dict.get('password', 'no default')
      self.client_id = stn_dict.get('client', 'wxclient') # MQTT client id - adjust as desired
      self.record = {'usUnits':weewx.US, 'dateTime':None}

      # how often to poll the weather data file, seconds
      self.poll_interval = float(stn_dict.get('poll_interval', 5.0))
      # mapping from variable names to weewx names
      self.label_map = stn_dict.get('label_map', {})

      loginf("MQTT host is %s" % self.host)
      loginf("MQTT topic is %s" % self.topic)
      loginf("MQTT client is %s" % self.client_id)
      loginf("polling interval is %s" % self.poll_interval)
      loginf('label map is %s' % self.label_map)
      
      self.payload = Queue.Queue()
      self.connected = False

      self.client = mqtt.Client(client_id=self.client_id, protocol=mqtt.MQTTv31)

      # TODO - need some reconnect on disconnect logic
      #self.client.on_disconnect = self.on_disconnect
      self.client.on_message = self.on_message

      self.client.username_pw_set(self.username, self.password)
      self.client.connect(self.host, 1883, 60)
      logdbg("Connected")

      self.client.loop_start()
      self.client.subscribe(self.topic, qos=1)

    # The callback for when a PUBLISH message is received from the MQTT server.
    def on_message(self, client, userdata, msg):
      self.payload.put(msg.payload,)
      logdbg("Added to queue of %d message %s" % (self.payload.qsize(), msg.payload))

    def on_connect(self, client, userdata, rc):
      if rc == 0:
	self.connected = True
        
    def closePort(self):
      self.client.disconnect()
      self.client.loop_stop()

    def genLoopPackets(self):
      while True:
	# read whatever values we can get from the MQTT broker
	logdbg("Queue of %d entries" % self.payload.qsize())
	logdbg("Waiting for non-empty queue")
        while not self.payload.empty(): 
          msg = str(self.payload.get(block=True, timeout=3)) # block until something gets queued
	  logdbg("Working on queue of size %d with payload : %s" % (self.payload.qsize(), msg))
	  data = {}
	  row = msg.split(",")
	  for datum in row:
	    (key,value)  = datum.split(":")
            key = re.findall(r'\"(.+?)\"',key)[0]
	    data[key] = re.findall(r'\"(.+?)\"',value)[0]
	    logdbg("key: "+key+" value: "+data[key])
            
	  # map the data into a weewx loop packet
	  _packet = {}
	  for vname in data:
	    _packet[self.label_map.get(vname, vname)] = _get_as_float(data, vname)
          self.record = _packet
          logdbg("Archive Record: %s" % self.record)
	  yield _packet

	logdbg("Sleeping for %d" % self.poll_interval)
	time.sleep(self.poll_interval)

      self.client.disconnect()
      self.client.loop_stop()
    
    def genArchiveRecords(self, since_ts):
      if self.record['dateTime'] is not None:
        yield self.record
      else:
        logdbg("No archive records on startup")
    
    @property
    def hardware_name(self):
        return "Vantage Pro2 remote via MQTT"

    @property
    def archive_interval(self):
        return int(300)

Reply via email to