mirror of
https://github.com/Crocmagnon/display-epaper.git
synced 2024-12-22 03:51:47 +01:00
improve logging & add cache to tcl & velov
This commit is contained in:
parent
afc766f45d
commit
f9c3153db1
8 changed files with 120 additions and 63 deletions
33
epd/epd.go
33
epd/epd.go
|
@ -3,7 +3,8 @@ package epd
|
|||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"periph.io/x/conn/v3/gpio"
|
||||
"periph.io/x/conn/v3/physic"
|
||||
"periph.io/x/conn/v3/spi"
|
||||
|
@ -61,7 +62,8 @@ func (e *EPD) sendCommand(cmd byte) {
|
|||
e.dcPin.Out(gpio.Low)
|
||||
e.csPin.Out(gpio.Low)
|
||||
if _, err := e.spiWrite([]byte{cmd}); err != nil {
|
||||
log.Fatalf("writing to spi: %v", err)
|
||||
slog.Error("writing to spi", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
e.csPin.Out(gpio.High)
|
||||
}
|
||||
|
@ -70,7 +72,8 @@ func (e *EPD) sendData(data byte) {
|
|||
e.dcPin.Out(gpio.High)
|
||||
e.csPin.Out(gpio.Low)
|
||||
if _, err := e.spiWrite([]byte{data}); err != nil {
|
||||
log.Fatalf("writing to spi: %v", err)
|
||||
slog.Error("writing to spi", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
e.csPin.Out(gpio.High)
|
||||
}
|
||||
|
@ -82,7 +85,8 @@ func (e *EPD) sendDataSlice(data []byte) {
|
|||
if toSend <= maxSize {
|
||||
e.csPin.Out(gpio.Low)
|
||||
if _, err := e.spiWrite(data); err != nil {
|
||||
log.Fatalf("writing to spi: %v", err)
|
||||
slog.Error("writing to spi", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
e.csPin.Out(gpio.High)
|
||||
return
|
||||
|
@ -93,7 +97,8 @@ func (e *EPD) sendDataSlice(data []byte) {
|
|||
chunk := data[cursor:min(cursor+maxSize, toSend)]
|
||||
e.csPin.Out(gpio.Low)
|
||||
if _, err := e.spiWrite(chunk); err != nil {
|
||||
log.Fatalf("writing to spi: %v", err)
|
||||
slog.Error("writing to spi", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
e.csPin.Out(gpio.High)
|
||||
cursor = min(cursor+maxSize, toSend)
|
||||
|
@ -120,7 +125,7 @@ func (e *EPD) readBusy() {
|
|||
}
|
||||
|
||||
func (e *EPD) turnOn() error {
|
||||
log.Println("turning on")
|
||||
slog.Info("turning on")
|
||||
if err := e.resetPin.Out(gpio.Low); err != nil {
|
||||
return fmt.Errorf("setting reset pin to low: %w", err)
|
||||
}
|
||||
|
@ -151,7 +156,7 @@ func (e *EPD) turnOn() error {
|
|||
}
|
||||
|
||||
func (e *EPD) Init() error {
|
||||
log.Println("initializing EPD")
|
||||
slog.Info("initializing EPD")
|
||||
|
||||
if err := e.turnOn(); err != nil {
|
||||
return fmt.Errorf("turning on: %w", err)
|
||||
|
@ -188,7 +193,7 @@ func (e *EPD) Init() error {
|
|||
}
|
||||
|
||||
func (e *EPD) InitFast() error {
|
||||
log.Println("initializing Fast EPD")
|
||||
slog.Info("initializing Fast EPD")
|
||||
|
||||
if err := e.turnOn(); err != nil {
|
||||
return fmt.Errorf("turning on: %w", err)
|
||||
|
@ -219,19 +224,19 @@ func (e *EPD) InitFast() error {
|
|||
}
|
||||
|
||||
func (e *EPD) Clear() {
|
||||
log.Println("clearing epd")
|
||||
slog.Info("clearing epd")
|
||||
e.Send(image.White)
|
||||
}
|
||||
|
||||
func (e *EPD) Refresh() {
|
||||
log.Println("refreshing...")
|
||||
slog.Info("refreshing...")
|
||||
e.sendCommand(0x12)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
e.readBusy()
|
||||
}
|
||||
|
||||
func (e *EPD) Sleep() error {
|
||||
log.Println("sleeping display...")
|
||||
slog.Info("sleeping display...")
|
||||
e.sendCommand(0x02)
|
||||
e.readBusy()
|
||||
|
||||
|
@ -247,7 +252,7 @@ func (e *EPD) Sleep() error {
|
|||
}
|
||||
|
||||
func (e *EPD) turnOff() error {
|
||||
log.Println("turning off...")
|
||||
slog.Info("turning off...")
|
||||
if err := e.spiReg.Close(); err != nil {
|
||||
return fmt.Errorf("closing SPI: %w", err)
|
||||
}
|
||||
|
@ -261,11 +266,11 @@ func (e *EPD) turnOff() error {
|
|||
|
||||
func (e *EPD) Send(img image.Image) {
|
||||
if img == nil {
|
||||
log.Println("empty img")
|
||||
slog.Info("empty img")
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("sending img...")
|
||||
slog.Info("sending img...")
|
||||
toSend := make([]byte, 0, e.height*e.width/8)
|
||||
toSend2 := make([]byte, 0, e.height*e.width/8)
|
||||
for row := 0; row < e.height; row++ {
|
||||
|
|
12
fete/fete.go
12
fete/fete.go
|
@ -5,7 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/carlmjohnson/requests"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
@ -70,15 +70,15 @@ func (f Fete) dumpToDisk(location string) error {
|
|||
|
||||
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")
|
||||
slog.InfoContext(ctx, "found fete in cache")
|
||||
if val.Day == date.Day() && val.Month == int(date.Month()) {
|
||||
log.Println("fete cache is up to date")
|
||||
slog.InfoContext(ctx, "fete cache is up to date")
|
||||
return &val, nil
|
||||
}
|
||||
log.Println("fete cache is old, fetching...")
|
||||
slog.InfoContext(ctx, "fete cache is old, fetching...")
|
||||
}
|
||||
|
||||
log.Println("querying fete")
|
||||
slog.InfoContext(ctx, "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").
|
||||
|
@ -90,7 +90,7 @@ func (c *Client) GetFete(ctx context.Context, date time.Time) (res *Fete, err er
|
|||
}
|
||||
|
||||
if err := res.dumpToDisk(c.config.CacheLocation); err != nil {
|
||||
log.Printf("error dumping files to disk: %v\n", err)
|
||||
slog.ErrorContext(ctx, "error dumping files to disk", "err", err)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
|
|
26
img.go
26
img.go
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/llgcode/draw2d/draw2dimg"
|
||||
"image"
|
||||
"image/color"
|
||||
"log"
|
||||
"log/slog"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -53,7 +53,7 @@ func getImg(ctx context.Context, nowFunc func() time.Time, transportsClient *tra
|
|||
|
||||
bus, err = transportsClient.GetTCLPassages(ctx, 290)
|
||||
if err != nil {
|
||||
log.Println("error getting bus:", err)
|
||||
slog.ErrorContext(ctx, "error getting bus", "err", err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
|
@ -66,7 +66,7 @@ func getImg(ctx context.Context, nowFunc func() time.Time, transportsClient *tra
|
|||
|
||||
tram, err = transportsClient.GetTCLPassages(ctx, 34068)
|
||||
if err != nil {
|
||||
log.Println("error getting tram:", err)
|
||||
slog.ErrorContext(ctx, "error getting tram", "err", err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
|
@ -79,7 +79,7 @@ func getImg(ctx context.Context, nowFunc func() time.Time, transportsClient *tra
|
|||
|
||||
velovRoc, err = transportsClient.GetVelovStation(ctx, 10044)
|
||||
if err != nil {
|
||||
log.Println("error getting velov:", err)
|
||||
slog.ErrorContext(ctx, "error getting velov", "err", err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
|
@ -92,7 +92,7 @@ func getImg(ctx context.Context, nowFunc func() time.Time, transportsClient *tra
|
|||
|
||||
fetes, err = feteClient.GetFete(ctx, nowFunc())
|
||||
if err != nil {
|
||||
log.Println("error getting fetes:", err)
|
||||
slog.ErrorContext(ctx, "error getting fetes", "err", err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
|
@ -105,7 +105,7 @@ func getImg(ctx context.Context, nowFunc func() time.Time, transportsClient *tra
|
|||
|
||||
wthr, err = weatherClient.GetWeather(ctx)
|
||||
if err != nil {
|
||||
log.Println("error getting weather:", err)
|
||||
slog.ErrorContext(ctx, "error getting weather", "err", err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
|
@ -118,7 +118,7 @@ func getImg(ctx context.Context, nowFunc func() time.Time, transportsClient *tra
|
|||
|
||||
msg, err = hassClient.GetState(ctx, "input_text.e_paper_message")
|
||||
if err != nil {
|
||||
log.Println("error getting hass message:", err)
|
||||
slog.ErrorContext(ctx, "error getting hass message", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -141,7 +141,7 @@ func getImg(ctx context.Context, nowFunc func() time.Time, transportsClient *tra
|
|||
drawVelov(gc, velovRoc, 365)
|
||||
drawDate(gc, nowFunc())
|
||||
drawFete(gc, fetes)
|
||||
drawWeather(gc, wthr)
|
||||
drawWeather(ctx, gc, wthr)
|
||||
drawMsg(gc, msg)
|
||||
|
||||
return img, nil
|
||||
|
@ -152,13 +152,15 @@ func drawMsg(gc *draw2dimg.GraphicContext, quote string) {
|
|||
|
||||
}
|
||||
|
||||
func drawWeather(gc *draw2dimg.GraphicContext, wthr *weather.Prevision) {
|
||||
func drawWeather(ctx context.Context, gc *draw2dimg.GraphicContext, wthr *weather.Prevision) {
|
||||
if wthr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(wthr.Daily) == 0 || len(wthr.Daily[0].Weather) == 0 {
|
||||
log.Println("missing daily or daily weather")
|
||||
dailyLen := len(wthr.Daily)
|
||||
dailyWeatherLen := len(wthr.Daily[0].Weather)
|
||||
if dailyLen == 0 || dailyWeatherLen == 0 {
|
||||
slog.ErrorContext(ctx, "missing daily or daily weather", "daily_len", dailyLen, "daily_weather_len", dailyWeatherLen)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -166,7 +168,7 @@ func drawWeather(gc *draw2dimg.GraphicContext, wthr *weather.Prevision) {
|
|||
dailyWeather := daily.Weather[0]
|
||||
err := drawWeatherIcon(gc, dailyWeather)
|
||||
if err != nil {
|
||||
log.Println("Failed to draw weather icon:", err)
|
||||
slog.ErrorContext(ctx, "Failed to draw weather icon", "err", err)
|
||||
}
|
||||
|
||||
text(gc, formatTemp(wthr.Current.Temp), 23, leftX, 120)
|
||||
|
|
19
main.go
19
main.go
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/llgcode/draw2d"
|
||||
_ "golang.org/x/image/bmp"
|
||||
"golang.org/x/image/font/gofont/goregular"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
@ -18,14 +18,16 @@ import (
|
|||
const fontName = "default"
|
||||
|
||||
func main() {
|
||||
log.Println("starting...")
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
slog.InfoContext(ctx, "starting...")
|
||||
|
||||
font, err := truetype.Parse(goregular.TTF)
|
||||
if err != nil {
|
||||
log.Fatalf("loading font: %v\n", err)
|
||||
slog.ErrorContext(ctx, "error loading font", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fontCache := MyFontCache{}
|
||||
fontCache.Store(draw2d.FontData{Name: fontName}, font)
|
||||
draw2d.SetFontCache(fontCache)
|
||||
|
@ -56,7 +58,9 @@ func main() {
|
|||
initFastThreshold = minInitFastThreshold
|
||||
}
|
||||
|
||||
log.Printf("sleep duration: %v\n", sleep)
|
||||
slog.InfoContext(ctx, "config",
|
||||
"sleep_duration", sleep,
|
||||
"init_fast_threshold", initFastThreshold)
|
||||
|
||||
hassClient := home_assistant.New(nil, home_assistant.Config{
|
||||
Token: os.Getenv("HOME_ASSISTANT_TOKEN"),
|
||||
|
@ -72,8 +76,9 @@ func main() {
|
|||
weatherClient,
|
||||
hassClient,
|
||||
); err != nil {
|
||||
log.Fatal("error: ", err)
|
||||
slog.ErrorContext(ctx, "error", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
log.Println("done")
|
||||
slog.InfoContext(ctx, "done")
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/Crocmagnon/display-epaper/fete"
|
||||
"github.com/Crocmagnon/display-epaper/home_assistant"
|
||||
"github.com/Crocmagnon/display-epaper/transports"
|
||||
"github.com/Crocmagnon/display-epaper/weather"
|
||||
"github.com/llgcode/draw2d/draw2dimg"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -35,10 +35,11 @@ func run(
|
|||
hassClient,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := draw2dimg.SaveToPngFile("out/black.png", img); err != nil {
|
||||
log.Fatalf("error saving image: %v", err)
|
||||
return fmt.Errorf("saving img: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/Crocmagnon/display-epaper/weather"
|
||||
"image"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"periph.io/x/host/v3"
|
||||
"time"
|
||||
|
@ -39,12 +40,12 @@ func run(
|
|||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Println("stopping because of context")
|
||||
slog.InfoContext(ctx, "stopping because of context")
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
log.Println("running loop")
|
||||
slog.InfoContext(ctx, "running loop")
|
||||
|
||||
img, err := loop(
|
||||
ctx,
|
||||
|
@ -57,12 +58,12 @@ func run(
|
|||
hassClient,
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("error looping: %v\n", err)
|
||||
slog.ErrorContext(ctx, "error looping", "err", err)
|
||||
}
|
||||
|
||||
currentImg = img
|
||||
|
||||
log.Printf("time.Sleep(%v)\n", sleep)
|
||||
slog.InfoContext(ctx, "sleep", "duration", sleep)
|
||||
time.Sleep(sleep)
|
||||
}
|
||||
}
|
||||
|
@ -95,17 +96,17 @@ func loop(
|
|||
}
|
||||
|
||||
if imgEqual(currentImg, img, epd.Width, epd.Height) {
|
||||
log.Println("Images are equal, doing nothing.")
|
||||
slog.InfoContext(ctx, "Images are equal, doing nothing.")
|
||||
return img, nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := display.Sleep(); err != nil {
|
||||
log.Printf("error sleeping: %v\n", err)
|
||||
slog.ErrorContext(ctx, "error sleeping", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
err := initDisplay(display, initFastThreshold)
|
||||
err := initDisplay(ctx, display, initFastThreshold)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("initializing display: %w", err)
|
||||
}
|
||||
|
@ -125,32 +126,31 @@ func shouldDisplay(ctx context.Context, hassClient *home_assistant.Client) bool
|
|||
return true
|
||||
}
|
||||
|
||||
log.Printf("found dayNight=%v\n", dayNight)
|
||||
|
||||
hallLights, err := hassClient.GetState(ctx, "light.couloir")
|
||||
if err != nil {
|
||||
log.Printf("error getting hall lights: %v ; displaying anyway\n", err)
|
||||
return true
|
||||
}
|
||||
|
||||
log.Printf("found hallLights=%v\n", hallLights)
|
||||
|
||||
presentAway, err := hassClient.GetState(ctx, "input_select.house_present_away")
|
||||
if err != nil {
|
||||
log.Printf("error getting day night: %v ; displaying anyway\n", err)
|
||||
return true
|
||||
}
|
||||
|
||||
log.Printf("found presentAway=%v\n", presentAway)
|
||||
slog.InfoContext(ctx, "home assistant states",
|
||||
"hall_lights", hallLights,
|
||||
"day_night", dayNight,
|
||||
"present_away", presentAway)
|
||||
|
||||
res := (hallLights == "on" || dayNight == "day") && presentAway == "present"
|
||||
log.Printf("shouldDisplay: %v\n", res)
|
||||
slog.InfoContext(ctx, "computed should display", "should_display", res)
|
||||
return res
|
||||
}
|
||||
|
||||
const filename = "/perm/display-epaper-lastFullRefresh"
|
||||
|
||||
func initDisplay(display *epd.EPD, threshold time.Duration) error {
|
||||
func initDisplay(ctx context.Context, display *epd.EPD, threshold time.Duration) error {
|
||||
if canInitFast(threshold) {
|
||||
err := display.InitFast()
|
||||
if err != nil {
|
||||
|
@ -164,7 +164,7 @@ func initDisplay(display *epd.EPD, threshold time.Duration) error {
|
|||
return fmt.Errorf("running full init: %w", err)
|
||||
}
|
||||
|
||||
markInitFull()
|
||||
markInitFull(ctx)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -178,10 +178,10 @@ func canInitFast(threshold time.Duration) bool {
|
|||
return stat.ModTime().Add(threshold).After(time.Now())
|
||||
}
|
||||
|
||||
func markInitFull() {
|
||||
func markInitFull(ctx context.Context) {
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
log.Printf("error marking full refresh: %v\n", err)
|
||||
slog.ErrorContext(ctx, "error marking full refresh", "err", err)
|
||||
}
|
||||
|
||||
f.Close()
|
||||
|
|
|
@ -4,7 +4,9 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"github.com/carlmjohnson/requests"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Stop struct {
|
||||
|
@ -26,9 +28,17 @@ type Passages struct {
|
|||
type Config struct {
|
||||
}
|
||||
|
||||
const cacheTimeout = 2 * time.Minute
|
||||
|
||||
type Client struct {
|
||||
client *http.Client
|
||||
config Config
|
||||
|
||||
passagesCache *Passages
|
||||
passagesCacheTime time.Time
|
||||
|
||||
stationCache *Station
|
||||
stationCacheTime time.Time
|
||||
}
|
||||
|
||||
func New(httpClient *http.Client, config Config) *Client {
|
||||
|
@ -48,11 +58,28 @@ func (c *Client) GetTCLPassages(ctx context.Context, stop int) (res *Passages, e
|
|||
ToJSON(&res).
|
||||
Fetch(ctx)
|
||||
if err != nil {
|
||||
if res = c.getPassagesCache(); res != nil {
|
||||
slog.WarnContext(ctx, "retrieving passages from cache")
|
||||
return res, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("calling api: %w", err)
|
||||
}
|
||||
|
||||
c.passagesCache = res
|
||||
c.passagesCacheTime = time.Now()
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *Client) getPassagesCache() *Passages {
|
||||
if c.passagesCache != nil && time.Since(c.passagesCacheTime) < cacheTimeout {
|
||||
return c.passagesCache
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Station struct {
|
||||
Name string `json:"name"`
|
||||
BikesAvailable int `json:"bikes_available"`
|
||||
|
@ -67,7 +94,24 @@ func (c *Client) GetVelovStation(ctx context.Context, station int) (res *Station
|
|||
ToJSON(&res).
|
||||
Fetch(ctx)
|
||||
if err != nil {
|
||||
if res = c.getStationCache(); res != nil {
|
||||
slog.WarnContext(ctx, "retrieving station from cache")
|
||||
return res, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("calling api: %w", err)
|
||||
}
|
||||
|
||||
c.stationCache = res
|
||||
c.stationCacheTime = time.Now()
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *Client) getStationCache() *Station {
|
||||
if c.stationCache != nil && time.Since(c.stationCacheTime) < cacheTimeout {
|
||||
return c.stationCache
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"github.com/carlmjohnson/requests"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
@ -152,11 +152,11 @@ type Weather struct {
|
|||
|
||||
func (c *Client) GetWeather(ctx context.Context) (res *Prevision, err error) {
|
||||
if val, err := loadFromDisk(c.config.CacheLocation); nil == err {
|
||||
log.Println("found weather in cache")
|
||||
slog.InfoContext(ctx, "found weather in cache")
|
||||
return &val, nil
|
||||
}
|
||||
|
||||
log.Println("querying weather")
|
||||
slog.InfoContext(ctx, "querying weather")
|
||||
|
||||
err = requests.URL("https://api.openweathermap.org/data/3.0/onecall").
|
||||
Client(c.client).
|
||||
|
@ -173,7 +173,7 @@ func (c *Client) GetWeather(ctx context.Context) (res *Prevision, err error) {
|
|||
}
|
||||
|
||||
if err := res.dumpToDisk(c.config.CacheLocation); err != nil {
|
||||
log.Printf("error dumping files to disk: %v\n", err)
|
||||
slog.ErrorContext(ctx, "error dumping files to disk", "err", err)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
|
|
Loading…
Reference in a new issue