diff --git a/fete/fete.go b/fete/fete.go new file mode 100644 index 0000000..5918cb7 --- /dev/null +++ b/fete/fete.go @@ -0,0 +1,108 @@ +package fete + +import ( + "context" + "encoding/json" + "fmt" + "github.com/carlmjohnson/requests" + "io" + "log" + "net/http" + "os" + "time" +) + +type Config struct { + APIKey string + CacheLocation string +} + +type Client struct { + client *http.Client + config Config +} + +func New(httpClient *http.Client, config Config) *Client { + if httpClient == nil { + httpClient = &http.Client{} + } + + return &Client{ + client: httpClient, + config: config, + } +} + +type Fete struct { + Day int `json:"day"` + Month int `json:"month"` + Name string `json:"name"` +} + +func loadFromDisk(location string) (Fete, error) { + file, err := os.Open(location) + if err != nil { + return Fete{}, fmt.Errorf("opening fetes: %w", err) + } + + defer file.Close() + + var res Fete + if err = json.NewDecoder(file).Decode(&res); err != nil { + return Fete{}, fmt.Errorf("decoding fetes: %w", err) + } + + return res, nil +} + +func (f Fete) dumpToDisk(location string) error { + file, err := os.Create(location) + if err != nil { + return fmt.Errorf("creating fetes: %w", err) + } + + defer file.Close() + + if err = json.NewEncoder(file).Encode(f); err != nil { + return fmt.Errorf("dumping fetes: %w", err) + } + return nil +} + +func (c *Client) GetFete(ctx context.Context, date time.Time) (res *Fete, err error) { + if val, err := loadFromDisk(c.config.CacheLocation); nil == err { + log.Println("found fete in cache") + if val.Day == date.Day() && val.Month == int(date.Month()) { + log.Println("fete cache is up to date") + return &val, nil + } + log.Println("fete cache is old, fetching...") + } + + log.Println("querying fete") + err = requests.URL("https://fetedujour.fr"). + Pathf("/api/v2/%v/json-normal-%d-%d", c.config.APIKey, date.Day(), date.Month()). + UserAgent("e-paper-display"). + AddValidator(func(resp *http.Response) error { + if resp.StatusCode >= 400 { + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + return fmt.Errorf("Fete API error: %s, %s", resp.Header, string(body)) + } + return nil + }). + Client(c.client). + ToJSON(&res). + Fetch(ctx) + if err != nil { + return nil, fmt.Errorf("calling API: %w", err) + } + + if err := res.dumpToDisk(c.config.CacheLocation); err != nil { + log.Printf("error dumping files to disk: %v\n", err) + } + + return res, nil +} diff --git a/img.go b/img.go index fbf50f0..7cf1b70 100644 --- a/img.go +++ b/img.go @@ -4,19 +4,34 @@ import ( "context" "fmt" "github.com/Crocmagnon/display-epaper/epd" + "github.com/Crocmagnon/display-epaper/fete" "github.com/Crocmagnon/display-epaper/transports" "github.com/llgcode/draw2d" "github.com/llgcode/draw2d/draw2dimg" "image" "image/color" + "strconv" + "time" ) -func getBlack(ctx context.Context, transportsClient *transports.Client) (*image.RGBA, error) { +func getBlack( + ctx context.Context, + nowFunc func() time.Time, + transportsClient *transports.Client, + feteClient *fete.Client, +) (*image.RGBA, error) { passages, err := transportsClient.GetTCLPassages(ctx, 290) if err != nil { return nil, fmt.Errorf("getting passages: %w", err) } + fetes, err := feteClient.GetFete(ctx, nowFunc()) + if err != nil { + return nil, fmt.Errorf("getting fetes: %w", err) + } + + _ = fetes + img := newWhite() gc := draw2dimg.NewGraphicContext(img) @@ -25,16 +40,27 @@ func getBlack(ctx context.Context, transportsClient *transports.Client) (*image. gc.SetFillColor(color.RGBA{0, 0, 0, 255}) gc.SetStrokeColor(color.RGBA{0, 0, 0, 255}) + drawPassages(gc, passages) + + drawFete(gc, fetes, nowFunc()) + + return img, nil +} + +func drawFete(gc *draw2dimg.GraphicContext, fetes *fete.Fete, now time.Time) { + text(gc, getDate(now), 50, 20, 235) + text(gc, fetes.Name, 18, 20, 270) +} + +func drawPassages(gc *draw2dimg.GraphicContext, passages *transports.Passages) { for i, passage := range passages.Passages { - x := float64(10 + i*100) + x := float64(600 + i*100) text(gc, passage.Ligne, 15, x, 20) for j, delay := range passage.Delays { y := float64(20 + (j+1)*30) text(gc, delay, 15, x, y) } } - - return img, nil } func text(gc *draw2dimg.GraphicContext, s string, size, x, y float64) { @@ -55,16 +81,6 @@ func newWhite() *image.RGBA { return img } -func newBlack() *image.RGBA { - img := image.NewRGBA(image.Rect(0, 0, epd.Width, epd.Height)) - for y := 0; y < epd.Height; y++ { - for x := 0; x < epd.Width; x++ { - img.Set(x, y, color.Black) - } - } - return img -} - func rect(gc *draw2dimg.GraphicContext, x1, y1, x2, y2 float64) { gc.BeginPath() gc.MoveTo(x1, y1) @@ -74,3 +90,45 @@ func rect(gc *draw2dimg.GraphicContext, x1, y1, x2, y2 float64) { gc.Close() gc.FillStroke() } + +func getDate(now time.Time) string { + return fmt.Sprintf("%v %v", getDay(now), getMonth(now)) +} + +func getDay(now time.Time) string { + if now.Day() == 1 { + return "1er" + } + + return strconv.Itoa(now.Day()) +} + +func getMonth(t time.Time) string { + switch t.Month() { + case time.January: + return "jan." + case time.February: + return "fev." + case time.March: + return "mars" + case time.April: + return "avr." + case time.May: + return "mai" + case time.June: + return "juin" + case time.July: + return "juil." + case time.August: + return "août" + case time.September: + return "sept." + case time.October: + return "oct." + case time.November: + return "nov." + case time.December: + return "dec." + } + return "?" +} diff --git a/main.go b/main.go index 54fcc34..e145385 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "github.com/Crocmagnon/display-epaper/fete" "github.com/Crocmagnon/display-epaper/transports" "github.com/golang/freetype/truetype" "github.com/llgcode/draw2d" @@ -30,7 +31,12 @@ func main() { Authorization: os.Getenv("TRANSPORTS_AUTHORIZATION"), }) - if err := run(ctx, transportsClient); err != nil { + feteClient := fete.New(nil, fete.Config{ + APIKey: os.Getenv("FETE_API_KEY"), + CacheLocation: os.Getenv("FETE_CACHE_LOCATION"), + }) + + if err := run(ctx, transportsClient, feteClient); err != nil { log.Fatal("error: ", err) } diff --git a/run_darwin_arm64.go b/run_darwin_arm64.go index dad8a65..475d18a 100644 --- a/run_darwin_arm64.go +++ b/run_darwin_arm64.go @@ -2,13 +2,21 @@ package main import ( "context" + "github.com/Crocmagnon/display-epaper/fete" "github.com/Crocmagnon/display-epaper/transports" "github.com/llgcode/draw2d/draw2dimg" "log" + "time" ) -func run(ctx context.Context, transportsClient *transports.Client) error { - img, err := getBlack(ctx, transportsClient) +func run(ctx context.Context, transportsClient *transports.Client, feteClient *fete.Client) error { + img, err := getBlack(ctx, func() time.Time { + t, err := time.Parse(time.DateOnly, "2024-08-01zzz") + if err != nil { + return time.Now() + } + return t + }, transportsClient, feteClient) if err != nil { log.Fatal(err) } diff --git a/run_linux_arm64.go b/run_linux_arm64.go index 61c4976..a0e7e9b 100644 --- a/run_linux_arm64.go +++ b/run_linux_arm64.go @@ -4,13 +4,14 @@ import ( "context" "fmt" "github.com/Crocmagnon/display-epaper/epd" + "github.com/Crocmagnon/display-epaper/fete" "github.com/Crocmagnon/display-epaper/transports" "log" "periph.io/x/host/v3" "time" ) -func run(ctx context.Context, transportsClient *transports.Client) error { +func run(ctx context.Context, transportsClient *transports.Client, feteClient *fete.Client) error { _, err := host.Init() if err != nil { return fmt.Errorf("initializing host: %w", err) @@ -29,7 +30,7 @@ func run(ctx context.Context, transportsClient *transports.Client) error { default: } - err = loop(ctx, display, transportsClient) + err = loop(ctx, display, transportsClient, feteClient) if err != nil { log.Printf("error looping: %v\n", err) } @@ -39,7 +40,12 @@ func run(ctx context.Context, transportsClient *transports.Client) error { } } -func loop(ctx context.Context, display *epd.EPD, transportsClient *transports.Client) error { +func loop( + ctx context.Context, + display *epd.EPD, + transportsClient *transports.Client, + feteClient *fete.Client, +) error { defer func() { if err := display.Sleep(); err != nil { log.Printf("error sleeping: %v\n", err) @@ -53,7 +59,7 @@ func loop(ctx context.Context, display *epd.EPD, transportsClient *transports.Cl display.Clear() - black, err := getBlack(ctx, transportsClient) + black, err := getBlack(ctx, time.Now, transportsClient, feteClient) if err != nil { return fmt.Errorf("getting black: %w", err) }