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
}