Working Server

main v0.0.1
DustyP 4 years ago
parent 4b3f6c0d72
commit 41d558488e

@ -0,0 +1,60 @@
name: CI
# Controls when the action will run. Triggers the workflow on push to master or development
# with a tag like v1.0.0 or v1.0.0-dev
on:
push:
tags:
- v[0-9]+.[0-9]+.[0-9]+
- v[0-9]+.[0-9]+.[0-9]+-[a-zA-Z]+
jobs:
build:
runs-on: ubuntu-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- name: Get Version
id: get_version
uses: battila7/get-version-action@v2.0.0
- name: install buildx
id: buildx
uses: crazy-max/ghaction-docker-buildx@v1
with:
version: latest
- name: Docker Login
# You may pin to the exact commit or the version.
# uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
uses: docker/login-action@v1.10.0
with:
registry: ${{ secrets.DR_URL }}
# Username used to log against the Docker registry
username: ${{ secrets.DH_USERNAME }}
# Password or personal access token used to log against the Docker registry
password: ${{ secrets.DH_PASSWORD }}
# Log out from the Docker registry at the end of a job
logout: true
- name: Docker Build & Push
env:
IMAGE_TAG: ${{ steps.get_version.outputs.version-without-v }}
run: |
docker buildx build --push \
--tag ${{ secrets.DR_URL }}/weather:$IMAGE_TAG \
--platform linux/amd64,linux/arm/v7,linux/arm64 .
- name: Update deployment file
run: TAG=${{ steps.get_version.outputs.version-without-v }} && sed -i 's|<IMAGE>|${{ secrets.DR_URL }}/weather:'${TAG}'|' $GITHUB_WORKSPACE/deployment.yml
- uses: azure/k8s-set-context@v1
with:
method: kubeconfig
kubeconfig: ${{ secrets.KUBE_CONFIG }}
id: setcontext
- name: Deploy to Kubernetes
run: kubectl apply -f $GITHUB_WORKSPACE/deployment.yml

@ -0,0 +1,20 @@
FROM golang:1.17-alpine as dev
WORKDIR /go/src/weather
COPY ./go.mod .
COPY ./go.sum .
RUN go mod download
COPY . .
RUN go install ./...
CMD [ "go", "run", "cmd/weather/main.go"]
from alpine
WORKDIR /bin
COPY --from=dev /go/bin/weather ./weather
CMD [ "weather" ]

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2021 Dusty.P Copyright (c) 2022 Dusty.P
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

@ -1 +1,2 @@
# weather # weather
Golang server to bridge common Weather stations to MQTT and a database

@ -0,0 +1,147 @@
package weather
import "time"
type AmbientService interface {
Event(uint64) (*AmbientEntry, error)
AddEvent(*AmbientEntry) (*AmbientEntry, error)
UpdateEvent(*AmbientEntry) error
DeleteEvent(*AmbientEntry) error
}
type AmbientEntry struct {
ID uint64
MAC string `schema:"MAC"`
DateUTC time.Time `schema:"dateutc"`
WindDir int `schema:"winddir"`
WindSpeedMPH float32 `schema:"windspeedmph"`
WindGustMPH float32 `schema:"windgustmph"`
WindGustDir int `schema:"windgustdir"`
MaxDailyGust float32 `schema:"maxdailygust"`
WindSpdMPH_Avg2m float32 `schema:"windspdmph_avg2m"`
WindDir_Avg2m int `schema:"winddir_avg2m"`
WindSpdMPH_Avg10m float32 `schema:"windspdmph_avg10m"`
WindDir_Avg10m int `schema:"winddir_avg10m"`
WindGustMPH_Interval int `schema:"windgustmph_interval"`
Humidity int `schema:"humidity"`
Humidity1 int `schema:"humidity1"`
Humidity2 int `schema:"humidity2"`
Humidity3 int `schema:"humidity3"`
Humidity4 int `schema:"humidity4"`
Humidity5 int `schema:"humidity5"`
Humidity6 int `schema:"humidity6"`
Humidity7 int `schema:"humidity7"`
Humidity8 int `schema:"humidity8"`
Humidity9 int `schema:"humidity9"`
Humidity10 int `schema:"humidity10"`
HumidityIn int `schema:"humidityin"`
TempF float32 `schema:"tempf"`
Temp1F float32 `schema:"temp1f"`
Temp2F float32 `schema:"temp2f"`
Temp3F float32 `schema:"temp3f"`
Temp4F float32 `schema:"temp4f"`
Temp5F float32 `schema:"temp5f"`
Temp6F float32 `schema:"temp6f"`
Temp7F float32 `schema:"temp7f"`
Temp8F float32 `schema:"temp8f"`
Temp9F float32 `schema:"temp9f"`
Temp10F float32 `schema:"temp10f"`
TempInF float32 `schema:"tempinf"`
HourlyRainIn float32 `schema:"hourlyrainin"`
DailyRainIn float32 `schema:"dailyrainin"`
Last24HourRainIn float32 `schema:"24hourrainin"`
WeeklyRainIn float32 `schema:"weeklyrainin"`
MonthlyRainIn float32 `schema:"monthlyrainin"`
YearlyRainIn float32 `schema:"yearlyrainin"`
EventRainIn float32 `schema:"eventrainin"`
TotalRainIn float32 `schema:"totalrainin"`
BaromRelIn float32 `schema:"baromrelin"`
BaromAbsIn float32 `schema:"baromabsin"`
UV int `schema:"uv"`
SolarRadiation float32 `schema:"solarradiation"`
CO2 int `schema:"co2"`
PM25 int `schema:"pm25"`
PM25_24H float32 `schema:"pm25_24h"`
PM25_In int `schema:"pm25_in"`
PM25_In_24H float32 `schema:"pm25_in_24h"`
PM10_In int `schema:"pm10_in"`
PM10_In_24H float32 `schema:"pm10_in_24h"`
CO2_In int `schema:"co2_in"`
CO2_In_24H float32 `schema:"co2_in_24h"`
PM_In_Temp float32 `schema:"pm_in_temp"`
PM_in_Humidity int `schema:"pm_in_humidity"`
Relay1 bool `schema:"relay1"`
Relay2 bool `schema:"relay2"`
Relay3 bool `schema:"relay3"`
Relay4 bool `schema:"relay4"`
Relay5 bool `schema:"relay5"`
Relay6 bool `schema:"relay6"`
Relay7 bool `schema:"relay7"`
Relay8 bool `schema:"relay8"`
Relay9 bool `schema:"relay9"`
Relay10 bool `schema:"relay10"`
SoilTemp1 float32 `schema:"soiltemp1"`
SoilTemp2 float32 `schema:"soiltemp2"`
SoilTemp3 float32 `schema:"soiltemp3"`
SoilTemp4 float32 `schema:"soiltemp4"`
SoilTemp5 float32 `schema:"soiltemp5"`
SoilTemp6 float32 `schema:"soiltemp6"`
SoilTemp7 float32 `schema:"soiltemp7"`
SoilTemp8 float32 `schema:"soiltemp8"`
SoilTemp9 float32 `schema:"soiltemp9"`
SoilTemp10 float32 `schema:"soiltemp10"`
SoilHum1 int `schema:"soilhum1"`
SoilHum2 int `schema:"soilhum2"`
SoilHum3 int `schema:"soilhum3"`
SoilHum4 int `schema:"soilhum4"`
SoilHum5 int `schema:"soilhum5"`
SoilHum6 int `schema:"soilhum6"`
SoilHum7 int `schema:"soilhum7"`
SoilHum8 int `schema:"soilhum8"`
SoilHum9 int `schema:"soilhum9"`
SoilHum10 int `schema:"soilhum10"`
Leak1 bool `schema:"leak1"`
Leak2 bool `schema:"leak2"`
Leak3 bool `schema:"leak3"`
Leak4 bool `schema:"leak4"`
Lightning_Time time.Time `schema:"lightning_time"`
Lightning_Day int `schema:"lightning_day"`
Lightning_Distance float32 `schema:"lightning_distance"`
BattOut bool `schema:"battout"`
BattIn bool `schema:"battin"`
Batt1 bool `schema:"batt1"`
Batt2 bool `schema:"batt2"`
Batt3 bool `schema:"batt3"`
Batt4 bool `schema:"batt4"`
Batt5 bool `schema:"batt5"`
Batt6 bool `schema:"batt6"`
Batt7 bool `schema:"batt7"`
Batt8 bool `schema:"batt8"`
Batt9 bool `schema:"batt9"`
Batt10 bool `schema:"batt10"`
BattR1 bool `schema:"battr1"`
BattR2 bool `schema:"battr2"`
BattR3 bool `schema:"battr3"`
BattR4 bool `schema:"battr4"`
BattR5 bool `schema:"battr5"`
BattR6 bool `schema:"battr6"`
BattR7 bool `schema:"battr7"`
BattR8 bool `schema:"battr8"`
BattR9 bool `schema:"battr9"`
BattR10 bool `schema:"battr10"`
Batt_25 bool `schema:"batt_25"`
Batt_25In bool `schema:"batt_25in"`
BatLeak1 bool `schema:"batleak1"`
BatLeak2 bool `schema:"batleak2"`
BatLeak3 bool `schema:"batleak3"`
BatLeak4 bool `schema:"batleak4"`
Batt_Lightning bool `schema:"batt_lightning"`
BattSM1 bool `schema:"battsm1"`
BattSM2 bool `schema:"battsm2"`
BattSM3 bool `schema:"battsm3"`
BattSM4 bool `schema:"battsm4"`
BattRain bool `schema:"battrain"`
BattCO2 bool `schema:"batt_co2"`
StationType string `schema:"stationtype"`
PASSKEY string `schema:"passkey"`
}

@ -0,0 +1,36 @@
package main
import (
"log"
"net/http"
"os"
"strconv"
"github.com/gorilla/mux"
"github.com/dustinpianalto/weather/internal/mqtt"
"github.com/dustinpianalto/weather/internal/postgres"
"github.com/dustinpianalto/weather/pkg/endpoints"
"github.com/dustinpianalto/weather/pkg/services"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/data/report", endpoints.AmbientHandler)
router.HandleFunc("/data/report/", endpoints.AmbientHandler)
router.HandleFunc("/", endpoints.AmbientHandler)
dbConnString := os.Getenv("DATABASE_URL")
postgres.ConnectDatabase(dbConnString)
host := os.Getenv("MQTT_HOST")
port, err := strconv.Atoi(os.Getenv("MQTT_PORT"))
if err != nil {
log.Fatal(err)
}
user := os.Getenv("MQTT_USER")
pass := os.Getenv("MQTT_PASSWORD")
mqtt.CreateMQTTClient(host, user, pass, port)
services.InitServices()
log.Fatal(http.ListenAndServe(":8080", router))
}

@ -0,0 +1,125 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: weather
namespace: weather
labels:
app: weather
spec:
replicas: 1
selector:
matchLabels:
app: weather
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
minReadySeconds: 120
template:
metadata:
labels:
app: weather
spec:
containers:
- name: pgbouncer
image: timoha/pgbouncer:1.15.0
resources:
requests:
memory: "256Mi"
cpu: "0.5"
limits:
memory: "512Mi"
cpu: "1"
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: weather
key: pgbouncer_url
- name: SERVER_TLS_SSLMODE
valueFrom:
secretKeyRef:
name: weather
key: pgbouncer_ssl
- name: AUTH_TYPE
valueFrom:
secretKeyRef:
name: weather
key: pgbouncer_auth
ports:
- containerPort: 5432
- name: weather
image: <IMAGE>
resources:
requests:
memory: "256Mi"
cpu: "0.5"
limits:
memory: "512Mi"
cpu: "1"
ports:
- containerPort: 8080
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: weather
key: database_url
- name: MQTT_HOST
valueFrom:
secretKeyRef:
name: weather
key: mqtt_host
- name: MQTT_PASSWORD
valueFrom:
secretKeyRef:
name: weather
key: mqtt_password
- name: MQTT_USER
valueFrom:
secretKeyRef:
name: weather
key: mqtt_user
- name: MQTT_PORT
valueFrom:
secretKeyRef:
name: weather
key: mqtt_port
imagePullSecrets:
- name: registry-1
---
apiVersion: v1
kind: Service
metadata:
name: weather-service
namespace: weather
labels:
app: weather
spec:
selector:
app: weather
ports:
- protocol: TCP
name: http
port: 8080
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: weather
namespace: weather
spec:
rules:
- host: weather.djpianalto.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: weather-service
port:
number: 8080

@ -0,0 +1,16 @@
module github.com/dustinpianalto/weather
go 1.18
require (
github.com/dustinpianalto/errors v0.1.0
github.com/eclipse/paho.mqtt.golang v1.3.5
github.com/gorilla/mux v1.8.0
github.com/gorilla/schema v1.2.0
github.com/lib/pq v1.10.4
)
require (
github.com/gorilla/websocket v1.4.2 // indirect
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 // indirect
)

@ -0,0 +1,18 @@
github.com/dustinpianalto/errors v0.1.0 h1:1wG0dhtE1w0u3+QRrQnR6vRO3QrVGpqIEP/0X83i4f4=
github.com/dustinpianalto/errors v0.1.0/go.mod h1:Fo865gGhrM1eyVIp5H5U8kQkZtFc/daiU3QBpUCd+B4=
github.com/eclipse/paho.mqtt.golang v1.3.5 h1:sWtmgNxYM9P2sP+xEItMozsR3w0cqZFlqnNN1bdl41Y=
github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

@ -0,0 +1,46 @@
package mqtt
import (
"fmt"
"log"
"github.com/dustinpianalto/errors"
"github.com/dustinpianalto/weather"
mqtt "github.com/eclipse/paho.mqtt.golang"
)
var (
AmbientService weather.AmbientService
)
func CreateMQTTClient(host, username, password string, port int) {
opts := mqtt.NewClientOptions()
opts.AddBroker(fmt.Sprintf("tcp://%s:%d", host, port))
opts.SetClientID("weather_bridge")
opts.SetUsername(username)
opts.SetPassword(password)
opts.OnConnect = connectHandler
opts.OnConnectionLost = connectionLostHandler
client := mqtt.NewClient(opts)
log.Println("mqtt client created")
token := client.Connect()
if token.Wait() && token.Error() != nil {
log.Println(token.Error())
return
}
initServices(&client)
log.Println("mqtt client initalized")
}
func connectHandler(client mqtt.Client) {
log.Println("MQTT Connected")
}
func connectionLostHandler(client mqtt.Client, err error) {
const method string = "mqtt/connectionLostHandler"
log.Println(errors.E(method, errors.Internal, "MQTT Connection Lost", err))
}
func initServices(client *mqtt.Client) {
AmbientService = eventService{client: client}
}

@ -0,0 +1,70 @@
package mqtt
import (
"fmt"
"reflect"
"strconv"
"time"
"github.com/dustinpianalto/weather"
mqtt "github.com/eclipse/paho.mqtt.golang"
)
type eventService struct {
client *mqtt.Client
}
func (e eventService) Event(i uint64) (*weather.AmbientEntry, error) {
return nil, nil
}
func (e eventService) AddEvent(event *weather.AmbientEntry) (*weather.AmbientEntry, error) {
m := eventToMap(event)
for k, v := range m {
(*e.client).Publish(fmt.Sprintf("weather/WS-2920C/%s", k), 0, false, v)
}
return event, nil
}
func (e eventService) UpdateEvent(event *weather.AmbientEntry) error {
return nil
}
func (e eventService) DeleteEvent(event *weather.AmbientEntry) error {
return nil
}
func eventToMap(event *weather.AmbientEntry) map[string]string {
m := make(map[string]string)
iVal := reflect.ValueOf(event).Elem()
typ := iVal.Type()
for i := 0; i < iVal.NumField(); i++ {
f := iVal.Field(i)
var v string
switch f.Interface().(type) {
case int, int8, int16, int32, int64:
v = strconv.FormatInt(f.Int(), 10)
case uint, uint8, uint16, uint32, uint64:
v = strconv.FormatUint(f.Uint(), 10)
case float32:
v = strconv.FormatFloat(f.Float(), 'f', 4, 32)
case float64:
v = strconv.FormatFloat(f.Float(), 'f', 4, 64)
case []byte:
v = string(f.Bytes())
case string:
v = f.String()
case bool:
if f.Bool() {
v = "true"
} else {
v = "false"
}
case time.Time:
v = strconv.FormatInt(f.Interface().(time.Time).Unix(), 10)
}
m[typ.Field(i).Name] = v
}
return m
}

@ -0,0 +1,31 @@
package postgres
import (
"database/sql"
"fmt"
"log"
"github.com/dustinpianalto/weather"
_ "github.com/lib/pq"
)
var (
EventService weather.AmbientService
)
func ConnectDatabase(dbConnString string) {
db, err := sql.Open("postgres", dbConnString)
if err != nil {
panic(fmt.Sprintf("Can't connect to the database. %v", err))
}
db.SetMaxOpenConns(75) // The RDS instance has a max of 75 open connections
db.SetMaxIdleConns(5)
db.SetConnMaxLifetime(300)
log.Println("Database Connected")
initServices(db)
log.Println("Postgres Database Initialized")
}
func initServices(db *sql.DB) {
EventService = eventService{db: db}
}

@ -0,0 +1,173 @@
package postgres
import (
"database/sql"
"github.com/dustinpianalto/weather"
)
type eventService struct {
db *sql.DB
}
func (e eventService) Event(i uint64) (*weather.AmbientEntry, error) {
queryString := `SELECT
mac, dateutc, winddir, windspeedmph, windgustmph, windgustdir, maxdailygust,
windspdmph_avg2m, winddir_avg2m, windspdmph_avg10m, winddir_avg10m, windgustmph_interval,
humidity, humidity1, humidity2, humidity3, humidity4, humidity5, humidity6, humidity7,
humidity8, humidity9, humidity10, humidityin, tempf, temp1f, temp2f, temp3f, temp4f, temp5f,
temp6f, temp7f, temp8f, temp9f, temp10f, tempinf, hourlyrainin, dailyrainin, last24hourrainin,
weeklyrainin, monthlyrainin, yearlyrainin, eventrainin, totalrainin, baromrelin, baromabsin,
uv, solarradiation, co2, pm25, pm25_24h, pm25_in, pm25_in_24h, pm10_in, pm10_in_24h, co2_in,
co2_in_24h, pm_in_temp, pm_in_humidity, relay1, relay2, relay3, relay4, relay5, relay6,
relay7, relay8, relay9, relay10, soiltemp1, soiltemp2, soiltemp3, soiltemp4, soiltemp5,
soiltemp6, soiltemp7, soiltemp8, soiltemp9, soiltemp10, soilhum1, soilhum2, soilhum3, soilhum4,
soilhum5, soilhum6, soilhum7, soilhum8, soilhum9, soilhum10, leak1, leak2, leak3, leak4,
lightning_time, lightning_day, lightning_distance, battout, battin, batt1, batt2, batt3, batt4,
batt5, batt6, batt7, batt8, batt9, batt10, battr1, battr2, battr3, battr4, battr5, battr6,
battr7, battr8, battr9, battr10, batt_25, batt_25in, batleak1, batleak2, batleak3, batleak4,
batt_lightning, battsm1, battsm2, battsm3, battsm4, battrain, batt_co2, stationtype, passkey
FROM events WHERE id = $1`
event := weather.AmbientEntry{
ID: i,
}
err := e.db.QueryRow(queryString, i).Scan(
&event.MAC, &event.DateUTC, &event.WindDir, &event.WindSpeedMPH, &event.WindGustMPH,
&event.WindGustDir, &event.MaxDailyGust, &event.WindSpdMPH_Avg2m, &event.WindDir_Avg2m,
&event.WindSpdMPH_Avg10m, &event.WindDir_Avg10m, &event.WindGustMPH_Interval, &event.Humidity,
&event.Humidity1, &event.Humidity2, &event.Humidity3, &event.Humidity4, &event.Humidity5,
&event.Humidity6, &event.Humidity7, &event.Humidity8, &event.Humidity9, &event.Humidity10,
&event.HumidityIn, &event.TempF, &event.Temp1F, &event.Temp2F, &event.Temp3F, &event.Temp4F,
&event.Temp5F, &event.Temp6F, &event.Temp7F, &event.Temp8F, &event.Temp9F, &event.Temp10F,
&event.TempInF, &event.HourlyRainIn, &event.DailyRainIn, &event.Last24HourRainIn,
&event.WeeklyRainIn, &event.MonthlyRainIn, &event.YearlyRainIn, &event.EventRainIn,
&event.TotalRainIn, &event.BaromRelIn, &event.BaromAbsIn, &event.UV, &event.SolarRadiation,
&event.CO2, &event.PM25, &event.PM25_24H, &event.PM25_In, &event.PM25_In_24H, &event.PM10_In,
&event.PM10_In_24H, &event.CO2_In, &event.CO2_In_24H, &event.PM_In_Temp, &event.PM_in_Humidity,
&event.Relay1, &event.Relay2, &event.Relay3, &event.Relay4, &event.Relay5, &event.Relay6,
&event.Relay7, &event.Relay8, &event.Relay9, &event.Relay10, &event.SoilTemp1, &event.SoilTemp2,
&event.SoilTemp3, &event.SoilTemp4, &event.SoilTemp5, &event.SoilTemp6, &event.SoilTemp7,
&event.SoilTemp8, &event.SoilTemp9, &event.SoilTemp10, &event.SoilHum1, &event.SoilHum2,
&event.SoilHum3, &event.SoilHum4, &event.SoilHum5, &event.SoilHum6, &event.SoilHum7,
&event.SoilHum8, &event.SoilHum9, &event.SoilHum10, &event.Leak1, &event.Leak2, &event.Leak3,
&event.Leak4, &event.Lightning_Time, &event.Lightning_Day, &event.Lightning_Distance,
&event.BattOut, &event.BattIn, &event.Batt1, &event.Batt2, &event.Batt3, &event.Batt4,
&event.Batt5, &event.Batt6, &event.Batt7, &event.Batt8, &event.Batt9, &event.Batt10,
&event.BattR1, &event.BattR2, &event.BattR3, &event.BattR4, &event.BattR5, &event.BattR6,
&event.BattR7, &event.BattR8, &event.BattR9, &event.BattR10, &event.Batt_25, &event.Batt_25In,
&event.BatLeak1, &event.BatLeak2, &event.BatLeak3, &event.BatLeak4, &event.Batt_Lightning,
&event.BattSM1, &event.BattSM2, &event.BattSM3, &event.BattSM4, &event.BattRain, &event.BattCO2,
&event.StationType, &event.PASSKEY)
if err != nil {
return nil, err
}
return &event, nil
}
func (e eventService) AddEvent(event *weather.AmbientEntry) (*weather.AmbientEntry, error) {
queryString := `INSERT INTO events
(mac, dateutc, winddir, windspeedmph, windgustmph, windgustdir, maxdailygust,
windspdmph_avg2m, winddir_avg2m, windspdmph_avg10m, winddir_avg10m, windgustmph_interval,
humidity, humidity1, humidity2, humidity3, humidity4, humidity5, humidity6, humidity7,
humidity8, humidity9, humidity10, humidityin, tempf, temp1f, temp2f, temp3f, temp4f,
temp5f, temp6f, temp7f, temp8f, temp9f, temp10f, tempinf, hourlyrainin, dailyrainin,
last24hourrainin, weeklyrainin, monthlyrainin, yearlyrainin, eventrainin, totalrainin, baromrelin,
baromabsin, uv, solarradiation, co2, pm25, pm25_24h, pm25_in, pm25_in_24h, pm10_in, pm10_in_24h,
co2_in, co2_in_24h, pm_in_temp, pm_in_humidity, relay1, relay2, relay3, relay4, relay5, relay6,
relay7, relay8, relay9, relay10, soiltemp1, soiltemp2, soiltemp3, soiltemp4, soiltemp5, soiltemp6,
soiltemp7, soiltemp8, soiltemp9, soiltemp10, soilhum1, soilhum2, soilhum3, soilhum4, soilhum5,
soilhum6, soilhum7, soilhum8, soilhum9, soilhum10, leak1, leak2, leak3, leak4, lightning_time,
lightning_day, lightning_distance, battout, battin, batt1, batt2, batt3, batt4, batt5, batt6,
batt7, batt8, batt9, batt10, battr1, battr2, battr3, battr4, battr5, battr6, battr7, battr8,
battr9, battr10, batt_25, batt_25in, batleak1, batleak2, batleak3, batleak4, batt_lightning,
battsm1, battsm2, battsm3, battsm4, battrain, batt_co2, stationtype, passkey)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21,
$22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $38, $39, $40,
$41, $42, $43, $44, $45, $46, $47, $48, $49, $50, $51, $52, $53, $54, $55, $56, $57, $58, $59,
$60, $61, $62, $63, $64, $65, $66, $67, $68, $69, $70, $71, $72, $73, $74, $75, $76, $77, $78,
$79, $80, $81, $82, $83, $84, $85, $86, $87, $88, $89, $90, $91, $92, $93, $94, $95, $96, $97,
$98, $99, $100, $101, $102, $103, $104, $105, $106, $107, $108, $109, $110, $111, $112, $113,
$114, $115, $116, $117, $118, $119, $120, $121, $122, $123, $124, $125, $126, $127, $128, $129,
$130, $131, $132, $133)
RETURNING id`
err := e.db.QueryRow(queryString, event.MAC, event.DateUTC, event.WindDir, event.WindSpeedMPH,
event.WindGustMPH, event.WindGustDir, event.MaxDailyGust, event.WindSpdMPH_Avg2m, event.WindDir_Avg2m,
event.WindSpdMPH_Avg10m, event.WindDir_Avg10m, event.WindGustMPH_Interval, event.Humidity,
event.Humidity1, event.Humidity2, event.Humidity3, event.Humidity4, event.Humidity5, event.Humidity6,
event.Humidity7, event.Humidity8, event.Humidity9, event.Humidity10, event.HumidityIn, event.TempF,
event.Temp1F, event.Temp2F, event.Temp3F, event.Temp4F, event.Temp5F, event.Temp6F, event.Temp7F,
event.Temp8F, event.Temp9F, event.Temp10F, event.TempInF, event.HourlyRainIn, event.DailyRainIn,
event.Last24HourRainIn, event.WeeklyRainIn, event.MonthlyRainIn, event.YearlyRainIn, event.EventRainIn,
event.TotalRainIn, event.BaromRelIn, event.BaromAbsIn, event.UV, event.SolarRadiation, event.CO2,
event.PM25, event.PM25_24H, event.PM25_In, event.PM25_In_24H, event.PM10_In, event.PM10_In_24H,
event.CO2_In, event.CO2_In_24H, event.PM_In_Temp, event.PM_in_Humidity, event.Relay1, event.Relay2,
event.Relay3, event.Relay4, event.Relay5, event.Relay6, event.Relay7, event.Relay8, event.Relay9,
event.Relay10, event.SoilTemp1, event.SoilTemp2, event.SoilTemp3, event.SoilTemp4, event.SoilTemp5,
event.SoilTemp6, event.SoilTemp7, event.SoilTemp8, event.SoilTemp9, event.SoilTemp10, event.SoilHum1,
event.SoilHum2, event.SoilHum3, event.SoilHum4, event.SoilHum5, event.SoilHum6, event.SoilHum7,
event.SoilHum8, event.SoilHum9, event.SoilHum10, event.Leak1, event.Leak2, event.Leak3, event.Leak4,
event.Lightning_Time, event.Lightning_Day, event.Lightning_Distance, event.BattOut, event.BattIn,
event.Batt1, event.Batt2, event.Batt3, event.Batt4, event.Batt5, event.Batt6, event.Batt7, event.Batt8,
event.Batt9, event.Batt10, event.BattR1, event.BattR2, event.BattR3, event.BattR4, event.BattR5,
event.BattR6, event.BattR7, event.BattR8, event.BattR9, event.BattR10, event.Batt_25, event.Batt_25In,
event.BatLeak1, event.BatLeak2, event.BatLeak3, event.BatLeak4, event.Batt_Lightning, event.BattSM1,
event.BattSM2, event.BattSM3, event.BattSM4, event.BattRain, event.BattCO2, event.StationType,
event.PASSKEY).Scan(&event.ID)
return event, err
}
func (e eventService) UpdateEvent(event *weather.AmbientEntry) error {
queryString := `UPDATE events SET
mac = $2, dateutc = $3, winddir = $4, windspeedmph = $5, windgustmph = $6, windgustdir = $7,
maxdailygust = $8, windspdmph_avg2m = $9, winddir_avg2m = $10, windspdmph_avg10m = $11, winddir_avg10m = $12,
windgustmph_interval = $13, humidity = $14, humidity1 = $15, humidity2 = $16, humidity3 = $17, humidity4 = $18,
humidity5 = $19, humidity6 = $20, humidity7 = $21, humidity8 = $22, humidity9 = $23, humidity10 = $24,
humidityin = $25, tempf = $26, temp1f = $27, temp2f = $28, temp3f = $29, temp4f = $30, temp5f = $31,
temp6f = $32, temp7f = $33, temp8f = $34, temp9f = $35, temp10f = $36, tempinf = $37, hourlyrainin = $38,
dailyrainin = $39, last24hourrainin = $40, weeklyrainin = $41, monthlyrainin = $42, yearlyrainin = $43,
eventrainin = $44, totalrainin = $45, baromrelin = $46, baromabsin = $47, uv = $48, solarradiation = $49,
co2 = $50, pm25 = $51, pm25_24h = $52, pm25_in = $53, pm25_in_24h = $54, pm10_in = $55, pm10_in_24h = $56,
co2_in = $57, co2_in_24h = $58, pm_in_temp = $59, pm_in_humidity = $60, relay1 = $61, relay2 = $62,
relay3 = $63, relay4 = $64, relay5 = $65, relay6 = $66, relay7 = $67, relay8 = $68, relay9 = $69,
relay10 = $70, soiltemp1 = $71, soiltemp2 = $72, soiltemp3 = $73, soiltemp4 = $74, soiltemp5 = $75,
soiltemp6 = $76, soiltemp7 = $77, soiltemp8 = $78, soiltemp9 = $79, soiltemp10 = $80, soilhum1 = $81,
soilhum2 = $82, soilhum3 = $83, soilhum4 = $84, soilhum5 = $85, soilhum6 = $86, soilhum7 = $87,
soilhum8 = $88, soilhum9 = $89, soilhum10 = $90, leak1 = $91, leak2 = $92, leak3 = $93, leak4 = $94,
lightning_time = $95, lightning_day = $96, lightning_distance = $97, battout = $98, battin = $99,
batt1 = $100, batt2 = $101, batt3 = $102, batt4 = $103, batt5 = $104, batt6 = $105, batt7 = $106,
batt8 = $107, batt9 = $108, batt10 = $109, battr1 = $110, battr2 = $111, battr3 = $112, battr4 = $113,
battr5 = $114, battr6 = $115, battr7 = $116, battr8 = $117, battr9 = $118, battr10 = $119, batt_25 = $120,
batt_25in = $121, batleak1 = $122, batleak2 = $123, batleak3 = $124, batleak4 = $125, batt_lightning = $126,
battsm1 = $127, battsm2 = $128, battsm3 = $129, battsm4 = $130, battrain = $131, batt_co2 = $132,
stationtype = $133, passkey = $134, WHERE id = $1`
_, err := e.db.Exec(queryString, event.ID, event.MAC, event.DateUTC, event.WindDir, event.WindSpeedMPH,
event.WindGustMPH, event.WindGustDir, event.MaxDailyGust, event.WindSpdMPH_Avg2m, event.WindDir_Avg2m,
event.WindSpdMPH_Avg10m, event.WindDir_Avg10m, event.WindGustMPH_Interval, event.Humidity, event.Humidity1,
event.Humidity2, event.Humidity3, event.Humidity4, event.Humidity5, event.Humidity6, event.Humidity7,
event.Humidity8, event.Humidity9, event.Humidity10, event.HumidityIn, event.TempF, event.Temp1F,
event.Temp2F, event.Temp3F, event.Temp4F, event.Temp5F, event.Temp6F, event.Temp7F, event.Temp8F,
event.Temp9F, event.Temp10F, event.TempInF, event.HourlyRainIn, event.DailyRainIn, event.Last24HourRainIn,
event.WeeklyRainIn, event.MonthlyRainIn, event.YearlyRainIn, event.EventRainIn, event.TotalRainIn,
event.BaromRelIn, event.BaromAbsIn, event.UV, event.SolarRadiation, event.CO2, event.PM25, event.PM25_24H,
event.PM25_In, event.PM25_In_24H, event.PM10_In, event.PM10_In_24H, event.CO2_In, event.CO2_In_24H,
event.PM_In_Temp, event.PM_in_Humidity, event.Relay1, event.Relay2, event.Relay3, event.Relay4, event.Relay5,
event.Relay6, event.Relay7, event.Relay8, event.Relay9, event.Relay10, event.SoilTemp1, event.SoilTemp2,
event.SoilTemp3, event.SoilTemp4, event.SoilTemp5, event.SoilTemp6, event.SoilTemp7, event.SoilTemp8,
event.SoilTemp9, event.SoilTemp10, event.SoilHum1, event.SoilHum2, event.SoilHum3, event.SoilHum4,
event.SoilHum5, event.SoilHum6, event.SoilHum7, event.SoilHum8, event.SoilHum9, event.SoilHum10,
event.Leak1, event.Leak2, event.Leak3, event.Leak4, event.Lightning_Time, event.Lightning_Day,
event.Lightning_Distance, event.BattOut, event.BattIn, event.Batt1, event.Batt2, event.Batt3, event.Batt4,
event.Batt5, event.Batt6, event.Batt7, event.Batt8, event.Batt9, event.Batt10, event.BattR1, event.BattR2,
event.BattR3, event.BattR4, event.BattR5, event.BattR6, event.BattR7, event.BattR8, event.BattR9,
event.BattR10, event.Batt_25, event.Batt_25In, event.BatLeak1, event.BatLeak2, event.BatLeak3, event.BatLeak4,
event.Batt_Lightning, event.BattSM1, event.BattSM2, event.BattSM3, event.BattSM4, event.BattRain,
event.BattCO2, event.StationType, event.PASSKEY)
return err
}
func (e eventService) DeleteEvent(event *weather.AmbientEntry) error {
queryString := "DELETE FROM events WHERE id = $1"
_, err := e.db.Exec(queryString, event.ID)
return err
}

@ -0,0 +1,136 @@
CREATE TABLE IF NOT EXISTS events (
id BIGSERIAL PRIMARY KEY,
mac char(17) not null,
dateutc timestamp not null,
winddir integer,
windspeedmph float,
windgustmph float,
windgustdir integer,
maxdailygust float,
windspdmph_avg2m float,
winddir_avg2m integer,
windspdmph_avg10m float,
winddir_avg10m integer,
windgustmph_interval integer,
humidity integer,
humidity1 integer,
humidity2 integer,
humidity3 integer,
humidity4 integer,
humidity5 integer,
humidity6 integer,
humidity7 integer,
humidity8 integer,
humidity9 integer,
humidity10 integer,
humidityin integer,
tempf float,
temp1f float,
temp2f float,
temp3f float,
temp4f float,
temp5f float,
temp6f float,
temp7f float,
temp8f float,
temp9f float,
temp10f float,
tempinf float,
hourlyrainin float,
dailyrainin float,
last24hourrainin float,
weeklyrainin float,
monthlyrainin float,
yearlyrainin float,
eventrainin float,
totalrainin float,
baromrelin float,
baromabsin float,
uv integer,
solarradiation float,
co2 integer,
pm25 integer,
pm25_24h float,
pm25_in integer,
pm25_in_24h float,
pm10_in integer,
pm10_in_24h float,
co2_in integer,
co2_in_24h float,
pm_in_temp float,
pm_in_humidity integer,
relay1 boolean,
relay2 boolean,
relay3 boolean,
relay4 boolean,
relay5 boolean,
relay6 boolean,
relay7 boolean,
relay8 boolean,
relay9 boolean,
relay10 boolean,
soiltemp1 float,
soiltemp2 float,
soiltemp3 float,
soiltemp4 float,
soiltemp5 float,
soiltemp6 float,
soiltemp7 float,
soiltemp8 float,
soiltemp9 float,
soiltemp10 float,
soilhum1 integer,
soilhum2 integer,
soilhum3 integer,
soilhum4 integer,
soilhum5 integer,
soilhum6 integer,
soilhum7 integer,
soilhum8 integer,
soilhum9 integer,
soilhum10 integer,
leak1 boolean,
leak2 boolean,
leak3 boolean,
leak4 boolean,
lightning_time timestamp,
lightning_day integer,
lightning_distance float,
battout boolean,
battin boolean,
batt1 boolean,
batt2 boolean,
batt3 boolean,
batt4 boolean,
batt5 boolean,
batt6 boolean,
batt7 boolean,
batt8 boolean,
batt9 boolean,
batt10 boolean,
battr1 boolean,
battr2 boolean,
battr3 boolean,
battr4 boolean,
battr5 boolean,
battr6 boolean,
battr7 boolean,
battr8 boolean,
battr9 boolean,
battr10 boolean,
batt_25 boolean,
batt_25in boolean,
batleak1 boolean,
batleak2 boolean,
batleak3 boolean,
batleak4 boolean,
batt_lightning boolean,
battsm1 boolean,
battsm2 boolean,
battsm3 boolean,
battsm4 boolean,
battrain boolean,
batt_co2 boolean,
stationtype varchar(30),
passkey char(17)
);

@ -0,0 +1,46 @@
package endpoints
import (
"log"
"net/http"
"reflect"
"strconv"
"time"
"github.com/dustinpianalto/errors"
"github.com/dustinpianalto/weather"
"github.com/dustinpianalto/weather/pkg/services"
"github.com/gorilla/schema"
)
func AmbientHandler(w http.ResponseWriter, r *http.Request) {
const method errors.Method = "endpoints/AmbientHandler"
entry := &weather.AmbientEntry{}
decoder := schema.NewDecoder()
decoder.RegisterConverter(time.Time{}, timeConverter)
if err := decoder.Decode(entry, r.URL.Query()); err != nil {
log.Println(errors.E(method, errors.Malformed, "error decoding AmbientEntry", err))
return
}
if entry.MAC == "" && entry.PASSKEY != "" {
entry.MAC = entry.PASSKEY
}
entry, err := services.EventService.AddEvent(entry)
if err != nil {
log.Println(errors.E(method, errors.Internal, "error adding entry to database", err))
return
}
log.Printf("%#v\n\n", entry)
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
w.Write([]byte{})
}
func timeConverter(value string) reflect.Value {
if v, err := time.Parse("2006-01-02 15:04:05", value); err == nil {
return reflect.ValueOf(v)
} else if i, err := strconv.ParseInt(value, 10, 64); err == nil {
return reflect.ValueOf(time.Unix(i, 0))
}
return reflect.Value{}
}

@ -0,0 +1,48 @@
package services
import (
"log"
"github.com/dustinpianalto/weather"
"github.com/dustinpianalto/weather/internal/mqtt"
"github.com/dustinpianalto/weather/internal/postgres"
)
var EventService weather.AmbientService
func InitServices() {
EventService = eventService{
postgesService: postgres.EventService,
mqttService: mqtt.AmbientService,
}
log.Println("Services Initialized")
}
type eventService struct {
postgesService weather.AmbientService
mqttService weather.AmbientService
}
func (e eventService) Event(i uint64) (*weather.AmbientEntry, error) {
return e.postgesService.Event(i)
}
func (e eventService) AddEvent(event *weather.AmbientEntry) (*weather.AmbientEntry, error) {
event, err := e.postgesService.AddEvent(event)
if err != nil {
log.Println(err)
}
event, err = e.mqttService.AddEvent(event)
if err != nil {
log.Println(err)
}
return event, nil
}
func (e eventService) UpdateEvent(event *weather.AmbientEntry) error {
return e.postgesService.UpdateEvent(event)
}
func (e eventService) DeleteEvent(event *weather.AmbientEntry) error {
return e.postgesService.UpdateEvent(event)
}
Loading…
Cancel
Save