WeeWx MQTT Listener Framework Driver

I was up late last night cleaning up local access to the MQTT driver of a WeeWx implementation that is hosting my WeatherFlow Tempest weather station. It's very basic, has some error handling to reconnect to MQTT if the connection is dropped. Also just for better understanding there are 3 json payload types that WeeWx puts on the weather/loop topic:

  • 1 Minute Observations
  • 3 Second wind and rain
  • 5 Minute WeeWx archive observation

To keep updates to a minimum I filter for the 1 minute observation and only update events for:

  • Temperature
  • Humidity
  • Barometric Pressure
  • Lux

To make this work you need a lot of patience setting up the weather station, raspberry pi with WeeWx, and Mosquito MQTT and few other things but it was fun to do this. I can now stop polling WeatherFlow for my data or using their Websocket and everything is now local to me.

/*
* WeeWX MQTT WeatherFlow
*
* Description:
* This Hubitat driver listens to the MQTT topic from WeeWx.  Coded to WeatherFlow Tempest values from the "Loop"
* 
*  The hardware/software linkage looks like this:
*
*  Tempest -->  WeatherFlow Wifi Hub --> UDP Broadcasts -->  WeeWx Tempest Driver --> WeeWx MQTT Driver --> MQTT Topic: weather/loop
* 
* Version Control:
* 0.03 - Added a HTML string that can be used on a tile in the Hubitat dashboards
* 0.02 - Cleaned up formatting of attributes
* 0.01 - Initial version
* 
*/

metadata {
	definition(name: "WeeWx MQTT driver for WeatherFlow Tempest", namespace: "RonV42", author: "Ron Vargo") {
		capability "Initialize"
		capability "Sensor"

		attribute "outTemp_F", "Number"
		attribute "outHumidity", "Number"
        attribute "barometer_inHg", "Number"
        attribute "illuminance_lux", "Number"
        attribute "outTempBatteryStatus", "Number"
        attribute "html", "string"
        
	}
		
	preferences {


		input name: "MQTTBroker", type: "text", title: "MQTT Broker Address:", required: true, displayDuringSetup: true
		input name: "username", type: "text", title: "MQTT Username:", description: "(blank if none)", required: false, displayDuringSetup: true
		input name: "password", type: "password", title: "MQTT Password:", description: "(blank if none)", required: false, displayDuringSetup: true
		input name: "topicSub", type: "text", title: "Topic to Subscribe:", description: "Example Topic ( weather/loop)", required: false, displayDuringSetup: true
   
		input("logEnable", "bool", title: "Enable logging", required: true, defaultValue: true)
	}
}

def installed() {
	log.info "installed..."
}

def updated() {
	if (logEnable) log.info "Updated..."
	initialize()
}

def uninstalled() {
	if (logEnable) log.info "Disconnecting from mqtt"
	interfaces.mqtt.disconnect()
}

def initialize() {
	
	
	displayDebugLog("initialize")

	if (logEnable) runIn(900, logsOff)

	state.ThisCharge = 0
	state.ChargeLimit = 0
	state.Relay = -1

	MQTTconnect()
	
	unschedule()
	
	schedule("0/10 * * * * ? *", MQTTconnect)
	

}

// Parse incoming device messages to generate events
def parse(String description) {


	try {

		displayDebugLog("Parse")
		displayDebugLog(description);
		displayDebugLog(did);
		
		did = device.getId()

		state.DeviceID = did
		
		// parse message
		mqtt = interfaces.mqtt.parseMessage(description)

		if (logEnable) log.debug mqtt.topic
		if (logEnable) log.debug mqtt.payload

		state.topic = mqtt.topic

		json = new groovy.json.JsonSlurper().parseText(mqtt.payload)
        
        displayDebugLog("json object")
        if (logEnable) log.debug json
        
        // There are 3 types of observations records, Wind, 1 minute, 5 mintue WeeWx Archive
        // This statement is needed to key the 1 minute observation
        if (json.illuminance_lux && !interval_minute) {
            if (logEnable) log.debug json.outTemp_F
            if (logEnable) log.debug json.outHumidity
            if (logEnable) log.debug json.barometer_inHg
            if (logEnable) log.debug json.illuminance_lux
            if (logEnable) log.debug json.outTempBatteryStatus
            
            float temperature = Float.parseFloat(json.outTemp_F).round(1)
            int humidity = Float.parseFloat(json.outHumidity).round(0)
            float barometer = Float.parseFloat(json.barometer_inHg).round(3)
            int illuminance = Float.parseFloat(json.illuminance_lux).round(0)
            float voltage = Float.parseFloat(json.outTempBatteryStatus).round(2)
            
            if (logEnable) log.debug "Temperature is: ${temperature}"
            if (logEnable) log.debug "Humidity is: ${humidity}"
            if (logEnable) log.debug "Barometer is: ${barometer}"
            if (logEnable) log.debug "Illuminanceis: ${illuminance}"
            if (logEnable) log.debug "Voltage is: ${voltage}"

            sendEvent(name: "outTemp_F", value: temperature)
            sendEvent(name: "outHumidity", value: humidity)
            sendEvent(name: "barometer_inHg", value: barometer)
            sendEvent(name: "illuminance_lux", value: illuminance)
            sendEvent(name: "outTempBatteryStatus", value: voltage)
            sendEvent(name: "html", value: "<p>Outdoor</p><p>Temperature ${temperature}°F</p><p>Humidity ${humidity}%</p><p>Barometer ${barometer} inHg</p>")
            
            
        }
        
                      
                      
        }
    
	catch (Exception e) 
	{
		log.error "Parse error: ${e.message}"
	}

}

def MQTTconnect() {

	try {

		def mqttInt = interfaces.mqtt

		if (mqttInt.isConnected()) {
			displayDebugLog( "Allready Connected to: $MQTTBroker $topicSub")
			return
		}

		def clientID = "hubitat-" + device.deviceNetworkId
		state.clientID = clientID
		
		 displayDebugLog( "Allready Connected to: $MQTTBroker $settings?.MQTTBroker/$settings?.topicSub")

		//open connection
		mqttbroker = "tcp://" + settings?.MQTTBroker + ":1883"
		mqttInt.connect(mqttbroker, clientID, settings?.username, settings?.password)

		//give it a chance to start
		pauseExecution(500)

		mqttInt.subscribe(settings?.topicSub)

		log.info "Connection established: $MQTTBroker $topicSub"
		log.info "clientID: $clientID"
		log.info "Subscribed to: $topicSub"


	} catch (e) {
		log.error "MQTTconnect error: ${e.message}"
	}
} 

def mqttClientStatus(String status) {
	log.error "MQTTStatus- error: ${status}"
}


def logsOff() {
	log.warn "Debug logging disabled."
	device.updateSetting("logEnable", [value: "false", type: "bool"])
}

private def displayDebugLog(message) {
	if (logEnable) log.debug "${device.displayName}: ${message}"
}

private def displayInfoLog(message) {

	log.info "${device.displayName}: ${message}"
}

// Used to convert epoch values to text dates
def String ConvertEpochToDate( Number Epoch ){
    def date = use( groovy.time.TimeCategory ) {
          new Date( 0 ) + Epoch.seconds
    }
    return date
}
2 Likes

I'm sort of obligated to "like" this post, since I developed one of the pieces in the data flow chain..... :blush: