Skip to content

Commit

Permalink
Upgrade to 0.7.0 of trafikinfo (#3)
Browse files Browse the repository at this point in the history
This provides a fluent-like API to access nested structs, with the bonus
that nothing throws nil errors even for missing elements.

Also, use all of the aggregate observation values available as some
weather station do or don't publish some of them.
  • Loading branch information
daenney authored Jan 17, 2024
1 parent 698551c commit 909fee0
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 58 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module hemtjan.st/trafikvader
go 1.18

require (
code.dny.dev/trafikinfo v0.6.0
code.dny.dev/trafikinfo v0.7.0
lib.hemtjan.st v0.7.2
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
code.dny.dev/trafikinfo v0.6.0 h1:8R4JILsMJ8dtPgwWsgL/v5sYl+TGBWfkx5Azl5k0ci8=
code.dny.dev/trafikinfo v0.6.0/go.mod h1:vSLjxprxL9sILsFuPFpJicw8LenBsOS1tsMa4oVCIy4=
code.dny.dev/trafikinfo v0.7.0 h1:yQW0KxCI/Yig4K1ivQzHgjr6zdiL02+3j5zk3oR7ifY=
code.dny.dev/trafikinfo v0.7.0/go.mod h1:vSLjxprxL9sILsFuPFpJicw8LenBsOS1tsMa4oVCIy4=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
Expand Down
110 changes: 55 additions & 55 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package main
import (
"bytes"
"context"
"encoding/json"
"encoding/xml"
"flag"
"fmt"
"io"
Expand All @@ -16,6 +16,7 @@ import (
"time"

"code.dny.dev/trafikinfo"
wmp "code.dny.dev/trafikinfo/trv/weathermeasurepoint/v2"
"lib.hemtjan.st/client"
"lib.hemtjan.st/transport/mqtt"
)
Expand Down Expand Up @@ -69,15 +70,20 @@ func main() {
req, err := trafikinfo.NewRequest().
APIKey(*apiToken).
Query(
trafikinfo.NewQuery(
trafikinfo.WeatherMeasurepoint,
2.0,
).Filter(
trafikinfo.NewQuery(wmp.ObjectType()).Filter(
trafikinfo.Or(stationFilters...),
).Include(
"Id", "Name",
"Observation.Air.Temperature.Value",
"Observation.Air.RelativeHumidity.Value",
"Observation.Aggregated5minutes.Precipitation.TotalWaterEquivalent.Value",
"Observation.Aggregated10minutes.Precipitation.TotalWaterEquivalent.Value",
"Observation.Aggregated30minutes.Precipitation.TotalWaterEquivalent.Value",
"Observation.Sample",
),
).Build()
if err != nil {
log.Fatalln(err)
log.Fatalf("invalid query: %v\n", err)
}

ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
Expand Down Expand Up @@ -148,8 +154,8 @@ loop:
os.Exit(0)
}

func update(data []data, stations map[string]client.Device) {
for _, item := range data {
func update(sensors []sensor, stations map[string]client.Device) {
for _, item := range sensors {
station, ok := stations[item.id]
if !ok {
continue
Expand Down Expand Up @@ -178,15 +184,15 @@ func update(data []data, stations map[string]client.Device) {
}
}

type data struct {
type sensor struct {
id string
name string
tempC float64
rhPct float64
precip float64
}

func retrieve(ctx context.Context, client *http.Client, body []byte) ([]data, error) {
func retrieve(ctx context.Context, client *http.Client, body []byte) ([]sensor, error) {
httpReq, err := http.NewRequest(http.MethodPost, trafikinfo.Endpoint, bytes.NewBuffer(body))
if err != nil {
return nil, err
Expand All @@ -204,66 +210,60 @@ func retrieve(ctx context.Context, client *http.Client, body []byte) ([]data, er
resp.Body.Close()
}()

if resp.StatusCode == http.StatusUnauthorized {
return nil, fmt.Errorf("invalid credentials")
}

if resp.StatusCode == http.StatusBadRequest {
var e trafikinfo.APIError
d := json.NewDecoder(resp.Body)
err := d.Decode(&e)
if err != nil {
return nil, fmt.Errorf("failed to decode API error response: %w", err)
}
return nil, fmt.Errorf("invalid request: %s", e.Response.Result[0].Error.Message)
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}

if resp.StatusCode != 200 {
return nil, fmt.Errorf("got status code: %d %s", resp.StatusCode, http.StatusText(resp.StatusCode))
var wr wmp.Response
if err := xml.Unmarshal(data, &wr); err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}

type mpResp struct {
Response struct {
Result []struct {
Measurepoints []trafikinfo.WeatherMeasurepoint2Dot0 `json:"WeatherMeasurepoint"`
} `json:"RESULT"`
} `json:"RESPONSE"`
if resp.StatusCode != http.StatusOK || wr.HasErrors() {
return nil, fmt.Errorf("http code: %d, error: %s", resp.StatusCode, wr.ErrorMsg())
}

d := json.NewDecoder(resp.Body)
var wr mpResp
err = d.Decode(&wr)
if err != nil {
return nil, fmt.Errorf("failed to decode response: %w", err)
}

if numRes := len(wr.Response.Result); numRes != 1 {
if numRes := len(wr.Results); numRes != 1 {
return nil, fmt.Errorf("expected 1 query result, got %d", numRes)
}

res := []data{}
for _, mp := range wr.Response.Result[0].Measurepoints {
sensors := []sensor{}
for _, mp := range wr.Results[0].Data {
// Don't bother updating if samples are old. This usually indicates the station is
// malfunctioning or offline for maintenance
if mp.Observation.Sample.Before(time.Now().Add(-1 * time.Hour)) {
if mp.Observation().Sample().Before(time.Now().Add(-1 * time.Hour)) {
continue
}
precip := 0.0
if mp.Observation.Aggregated30Minutes != nil &&
mp.Observation.Aggregated30Minutes.Precipitation != nil &&
mp.Observation.Aggregated30Minutes.Precipitation.TotalWaterEquivalent != nil &&
mp.Observation.Aggregated30Minutes.Precipitation.TotalWaterEquivalent.Value != nil {
precip = *mp.Observation.Aggregated30Minutes.Precipitation.TotalWaterEquivalent.Value * 2
}

res = append(res, data{
id: *mp.ID,
name: *mp.Name,
tempC: *mp.Observation.Air.Temperature.Value,
rhPct: *mp.Observation.Air.RelativeHumidity.Value,
precip: precip,
sensors = append(sensors, sensor{
id: *mp.ID(),
name: *mp.Name(),
tempC: *mp.Observation().Air().Temperature().Value(),
rhPct: *mp.Observation().Air().RelativeHumidity().Value(),
precip: pick(
[]precip{
{value: mp.Observation().Aggregated5minutes().Precipitation().TotalWaterEquivalent().Value(), multiplier: 12},
{value: mp.Observation().Aggregated10minutes().Precipitation().TotalWaterEquivalent().Value(), multiplier: 6},
{value: mp.Observation().Aggregated30minutes().Precipitation().TotalWaterEquivalent().Value(), multiplier: 2},
},
),
})
}

return res, nil
return sensors, nil
}

type precip struct {
value *float64
multiplier float64
}

func pick(data []precip) float64 {
for _, p := range data {
if p.value != nil {
return *p.value * p.multiplier
}
}
return 0.0
}

0 comments on commit 909fee0

Please sign in to comment.