diff --git a/README.md b/README.md index 8ce6546..ac5a864 100644 --- a/README.md +++ b/README.md @@ -6,5 +6,7 @@ - [x] Add basic auth - [x] Create Nix package - [x] Create container -- [ ] Write ESP8266 client +- [x] Write ESP8266 client - [ ] Make port configurable +- [ ] Transfer using JSON +- [ ] Correctly implement basic auth diff --git a/main.go b/main.go index b566d11..a005775 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import "encoding/json" +import "errors" import "fmt" import "log" import "net/http" @@ -10,8 +11,10 @@ import "slices" import "strconv" import "time" +// JSON unmarshaling types for departure monitor API + type DMResponse struct { - Departures []DMDeparture `json:"departureList"` + DepartureList []DMDeparture `json:"departureList"` } type DMDeparture struct { @@ -40,11 +43,11 @@ type DMDateTime struct { Minute string `json:"minute"` } -func FetchDepartures(stopId string) (*DMResponse, error) { +func FetchDepartures(stopId string) (DMResponse, error) { // Create request object req, err := http.NewRequest("GET", "https://www.vrn.de/mngvrn/XML_DM_REQUEST", nil) if err != nil { - return nil, err + return DMResponse{}, err } // Configure our request @@ -68,17 +71,72 @@ func FetchDepartures(stopId string) (*DMResponse, error) { } res, err := client.Do(req) if err != nil { - return nil, err + return DMResponse{}, err } defer res.Body.Close() var dmResponse DMResponse err = json.NewDecoder(res.Body).Decode(&dmResponse) if err != nil { - return nil, err + return DMResponse{}, err } - return &dmResponse, nil + return dmResponse, nil +} + +// Types for JSON marshaling + +type Departures struct { + Departures []Departure `json:"departures"` +} + +type Departure struct { + Symbol string `json:"symbol"` + Direction string `json:"direction"` + Leaving string `json:"leaving"` +} + +func ParseDepartures(response DMResponse, allowedPlatform *string) (Departures, error) { + var ds []Departure + + for _, d := range response.DepartureList { + direction := d.ServingLine.Direction + if len(d.OnwardStopSeq) != 0 { + last := d.OnwardStopSeq[len(d.OnwardStopSeq)-1] + if slices.Contains([]string{"5", "6"}, last.PlaceID) { + direction = last.NameWO + } + } + + leaving := fmt.Sprintf("%s min", d.Countdown) + countdown, err := strconv.Atoi(d.Countdown) + if err != nil { + return Departures{}, errors.New("could not parse countdown") + } + + if countdown == 0 { + leaving = "sofort" + } + if countdown > 20 { + dt := d.DateTime + if d.RealDateTime != nil { + dt = *d.RealDateTime + } + leaving = fmt.Sprintf("%02s:%02s", dt.Hour, dt.Minute) + } + + if allowedPlatform != nil && d.Platform != *allowedPlatform { + continue + } + + ds = append(ds, Departure{ + d.ServingLine.Symbol, + direction, + leaving, + }) + } + + return Departures{ds}, nil } func main() { @@ -113,40 +171,17 @@ func main() { return } - for _, d := range ds.Departures { - if platform != nil { - if d.Platform != *platform { - continue - } - } - - direction := d.ServingLine.Direction - if len(d.OnwardStopSeq) != 0 { - last := d.OnwardStopSeq[len(d.OnwardStopSeq)-1] - if slices.Contains([]string{"5", "6"}, last.PlaceID) { - direction = last.NameWO - } - } - - leaving := fmt.Sprintf("%s min", d.Countdown) - countdown, err := strconv.Atoi(d.Countdown) - if err == nil { - if countdown == 0 { - leaving = "sofort" - } - if countdown > 20 { - dt := d.DateTime - if d.RealDateTime != nil { - dt = *d.RealDateTime - } - leaving = fmt.Sprintf("%02s:%02s", dt.Hour, dt.Minute) - } - } + departures, err := ParseDepartures(ds, platform) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + for _, d := range departures.Departures { fmt.Fprintf(w, "%2s %-11s %6s\n", - d.ServingLine.Symbol, - direction[:min(len(direction), 11)], - leaving, + d.Symbol, + d.Direction[:min(11, len(d.Direction))], + d.Leaving, ) } })