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