add support for barometric pressure
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
@@ -70,59 +71,113 @@ func main() {
|
||||
go runWundergroundUploader(ctx, latest, cfg.Wunderground.StationID, cfg.Wunderground.StationKey, cfg.Wunderground.Interval)
|
||||
}
|
||||
|
||||
bindings := make([]mqttTopicBinding, 0, len(cfg.MQTT.Topics))
|
||||
subscriptions := make([]mqttingest.Subscription, 0, len(cfg.MQTT.Topics))
|
||||
for _, t := range cfg.MQTT.Topics {
|
||||
qos := cfg.MQTT.QoS
|
||||
if t.QoS != nil {
|
||||
qos = *t.QoS
|
||||
}
|
||||
topicType := strings.ToLower(t.Type)
|
||||
bindings = append(bindings, mqttTopicBinding{
|
||||
Name: t.Name,
|
||||
Topic: t.Topic,
|
||||
Type: topicType,
|
||||
QoS: qos,
|
||||
})
|
||||
subscriptions = append(subscriptions, mqttingest.Subscription{
|
||||
Topic: t.Topic,
|
||||
QoS: qos,
|
||||
})
|
||||
}
|
||||
|
||||
// MQTT subscriber (blocks until ctx done)
|
||||
err = mqttingest.RunSubscriber(ctx, mqttingest.MQTTConfig{
|
||||
Broker: cfg.MQTT.Broker,
|
||||
ClientID: cfg.MQTT.ClientID,
|
||||
Username: cfg.MQTT.Username,
|
||||
Password: cfg.MQTT.Password,
|
||||
Topic: cfg.MQTT.Topic,
|
||||
QoS: cfg.MQTT.QoS,
|
||||
Topics: subscriptions,
|
||||
}, func(ctx context.Context, topic string, payload []byte) error {
|
||||
log.Printf("mqtt message topic=%s bytes=%d payload=%s", topic, len(payload), string(payload))
|
||||
|
||||
p, raw, err := mqttingest.ParseWS90(payload)
|
||||
if err != nil {
|
||||
log.Printf("ws90 parse error topic=%s err=%v payload=%s", topic, err, string(payload))
|
||||
binding := matchMQTTBinding(bindings, topic)
|
||||
if binding == nil {
|
||||
log.Printf("mqtt message ignored topic=%s reason=unmatched", topic)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Use receive time as observation ts (WS90 payload doesn't include a timestamp).
|
||||
ts := time.Now().UTC()
|
||||
switch binding.Type {
|
||||
case "ws90":
|
||||
p, raw, err := mqttingest.ParseWS90(payload)
|
||||
if err != nil {
|
||||
log.Printf("ws90 parse error topic=%s err=%v payload=%s", topic, err, string(payload))
|
||||
return nil
|
||||
}
|
||||
|
||||
latest.Update(ts, p)
|
||||
// Use receive time as observation ts (WS90 payload doesn't include a timestamp).
|
||||
ts := time.Now().UTC()
|
||||
|
||||
if snap, ok := latest.Snapshot(); ok {
|
||||
logForecastDeviation(forecastCache, snap)
|
||||
}
|
||||
latest.Update(ts, p)
|
||||
|
||||
if err := d.InsertWS90(ctx, db.InsertWS90Params{
|
||||
TS: ts,
|
||||
Site: cfg.Site.Name,
|
||||
StationID: p.ID,
|
||||
Model: p.Model,
|
||||
BatteryOK: p.BatteryOK,
|
||||
BatteryMV: p.BatteryMV,
|
||||
TempC: p.TemperatureC,
|
||||
Humidity: p.Humidity,
|
||||
WindDirDeg: p.WindDirDeg,
|
||||
WindAvgMS: p.WindAvgMS,
|
||||
WindMaxMS: p.WindMaxMS,
|
||||
UVI: p.UVI,
|
||||
LightLux: p.LightLux,
|
||||
Flags: p.Flags,
|
||||
RainMM: p.RainMM,
|
||||
RainStart: p.RainStart,
|
||||
SupercapV: p.SupercapV,
|
||||
Firmware: p.Firmware,
|
||||
RawData: p.Data,
|
||||
MIC: p.MIC,
|
||||
Protocol: p.Protocol,
|
||||
RSSI: p.RSSI,
|
||||
Duration: p.Duration,
|
||||
Payload: raw,
|
||||
}); err != nil {
|
||||
log.Printf("db insert ws90 error: %v", err)
|
||||
if snap, ok := latest.Snapshot(); ok {
|
||||
logForecastDeviation(forecastCache, snap)
|
||||
}
|
||||
|
||||
if err := d.InsertWS90(ctx, db.InsertWS90Params{
|
||||
TS: ts,
|
||||
Site: cfg.Site.Name,
|
||||
StationID: p.ID,
|
||||
Model: p.Model,
|
||||
BatteryOK: p.BatteryOK,
|
||||
BatteryMV: p.BatteryMV,
|
||||
TempC: p.TemperatureC,
|
||||
Humidity: p.Humidity,
|
||||
WindDirDeg: p.WindDirDeg,
|
||||
WindAvgMS: p.WindAvgMS,
|
||||
WindMaxMS: p.WindMaxMS,
|
||||
UVI: p.UVI,
|
||||
LightLux: p.LightLux,
|
||||
Flags: p.Flags,
|
||||
RainMM: p.RainMM,
|
||||
RainStart: p.RainStart,
|
||||
SupercapV: p.SupercapV,
|
||||
Firmware: p.Firmware,
|
||||
RawData: p.Data,
|
||||
MIC: p.MIC,
|
||||
Protocol: p.Protocol,
|
||||
RSSI: p.RSSI,
|
||||
Duration: p.Duration,
|
||||
Payload: raw,
|
||||
}); err != nil {
|
||||
log.Printf("db insert ws90 error: %v", err)
|
||||
}
|
||||
case "baro", "barometer", "pressure":
|
||||
p, raw, err := mqttingest.ParseBarometer(payload)
|
||||
if err != nil {
|
||||
log.Printf("barometer parse error topic=%s err=%v payload=%s", topic, err, string(payload))
|
||||
return nil
|
||||
}
|
||||
|
||||
ts := time.Now().UTC()
|
||||
source := binding.Name
|
||||
if source == "" {
|
||||
source = binding.Topic
|
||||
}
|
||||
|
||||
if err := d.InsertBarometer(ctx, db.InsertBarometerParams{
|
||||
TS: ts,
|
||||
Site: cfg.Site.Name,
|
||||
Source: source,
|
||||
PressureHPA: p.PressureHPA,
|
||||
Payload: raw,
|
||||
}); err != nil {
|
||||
log.Printf("db insert barometer error: %v", err)
|
||||
} else {
|
||||
log.Printf("barometer stored source=%s pressure_hpa=%.2f", source, p.PressureHPA)
|
||||
}
|
||||
default:
|
||||
log.Printf("mqtt message ignored topic=%s reason=unknown_type type=%s", topic, binding.Type)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -132,6 +187,22 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
type mqttTopicBinding struct {
|
||||
Name string
|
||||
Topic string
|
||||
Type string
|
||||
QoS byte
|
||||
}
|
||||
|
||||
func matchMQTTBinding(bindings []mqttTopicBinding, topic string) *mqttTopicBinding {
|
||||
for i := range bindings {
|
||||
if mqttingest.TopicMatches(bindings[i].Topic, topic) {
|
||||
return &bindings[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runOpenMeteoPoller(ctx context.Context, d *db.DB, cache *ForecastCache, site providers.Site, model string, interval time.Duration) {
|
||||
p := &providers.OpenMeteo{}
|
||||
t := time.NewTicker(interval)
|
||||
|
||||
Reference in New Issue
Block a user