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)
