query everything through home assistant

This commit is contained in:
Gabriel Augendre 2024-12-16 19:40:10 +01:00
parent 4c23ecb1ff
commit 01a49beb7e
9 changed files with 129 additions and 783 deletions

View file

@ -1,97 +0,0 @@
package fete
import (
"context"
"encoding/json"
"fmt"
"github.com/carlmjohnson/requests"
"log/slog"
"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 {
slog.InfoContext(ctx, "found fete in cache")
if val.Day == date.Day() && val.Month == int(date.Month()) {
slog.InfoContext(ctx, "fete cache is up to date")
return &val, nil
}
slog.InfoContext(ctx, "fete cache is old, fetching...")
}
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").
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 {
slog.ErrorContext(ctx, "error dumping files to disk", "err", err)
}
return res, nil
}

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/carlmjohnson/requests" "github.com/carlmjohnson/requests"
"net/http" "net/http"
"time"
) )
type Config struct { type Config struct {
@ -21,20 +22,40 @@ func New(client *http.Client, config Config) *Client {
} }
func (c *Client) GetState(ctx context.Context, entityID string) (string, error) { func (c *Client) GetState(ctx context.Context, entityID string) (string, error) {
type stateResponse struct { var resp struct {
State string `json:"state"` State string `json:"state"`
} }
var resp stateResponse err := c.getState(ctx, entityID, &resp)
err := requests.URL(c.config.BaseURL).
Header("Authorization", "Bearer "+c.config.Token).
Pathf("/api/states/%s", entityID).
ToJSON(&resp).
Fetch(ctx)
if err != nil { if err != nil {
return "", err return "", err
} }
return resp.State, nil return resp.State, nil
} }
func (c *Client) GetTimeState(ctx context.Context, entityID string) (time.Time, error) {
var resp struct {
State time.Time `json:"state"`
}
err := c.getState(ctx, entityID, &resp)
if err != nil {
return time.Time{}, err
}
return resp.State, nil
}
func (c *Client) getState(ctx context.Context, entityID string, resp any) error {
err := requests.URL(c.config.BaseURL).
Header("Authorization", "Bearer "+c.config.Token).
Pathf("/api/states/%s", entityID).
ToJSON(&resp).
Fetch(ctx)
if err != nil {
return err
}
return nil
}

147
img.go
View file

@ -5,11 +5,8 @@ import (
"embed" "embed"
"fmt" "fmt"
"github.com/Crocmagnon/display-epaper/epd" "github.com/Crocmagnon/display-epaper/epd"
"github.com/Crocmagnon/display-epaper/fete"
"github.com/Crocmagnon/display-epaper/fonts" "github.com/Crocmagnon/display-epaper/fonts"
"github.com/Crocmagnon/display-epaper/home_assistant" "github.com/Crocmagnon/display-epaper/home_assistant"
"github.com/Crocmagnon/display-epaper/quotes"
"github.com/Crocmagnon/display-epaper/transports"
"github.com/Crocmagnon/display-epaper/weather" "github.com/Crocmagnon/display-epaper/weather"
"github.com/llgcode/draw2d" "github.com/llgcode/draw2d"
"github.com/llgcode/draw2d/draw2dimg" "github.com/llgcode/draw2d/draw2dimg"
@ -31,31 +28,74 @@ const (
rightX = 530 rightX = 530
) )
func getImg(ctx context.Context, nowFunc func() time.Time, transportsClient *transports.Client, feteClient *fete.Client, weatherClient *weather.Client, hassClient *home_assistant.Client) (*image.RGBA, error) { func getImg(ctx context.Context, nowFunc func() time.Time, weatherClient *weather.Client, hassClient *home_assistant.Client) (*image.RGBA, error) {
var ( var (
bus *transports.Passages bus37_1 time.Time
tram *transports.Passages bus37_2 time.Time
velovRoc *transports.Station bus37_3 time.Time
fetes *fete.Fete busC17_1 time.Time
busC17_2 time.Time
busC17_3 time.Time
tramT1_1 time.Time
tramT1_2 time.Time
tramT1_3 time.Time
velovRocBikes string
velovRocStands string
feteName string
wthr *weather.Prevision wthr *weather.Prevision
msg string msg string
) )
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
wg.Add(6) wg.Add(5)
go func() { go func() {
defer wg.Done() defer wg.Done()
ctx, cancel := context.WithTimeout(ctx, 20*time.Second) ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel() defer cancel()
var err error var err error
bus, err = transportsClient.GetTCLPassages(ctx, 290) bus37_1, err = hassClient.GetTimeState(ctx, "sensor.tcl_37_1")
if err != nil { if err != nil {
slog.ErrorContext(ctx, "error getting bus", "err", err) slog.ErrorContext(ctx, "error getting 37_1", "err", err)
} }
bus37_2, err = hassClient.GetTimeState(ctx, "sensor.tcl_37_2")
if err != nil {
slog.ErrorContext(ctx, "error getting 37_2", "err", err)
}
bus37_3, err = hassClient.GetTimeState(ctx, "sensor.tcl_37_3")
if err != nil {
slog.ErrorContext(ctx, "error getting 37_3", "err", err)
}
busC17_1, err = hassClient.GetTimeState(ctx, "sensor.tcl_c17_1")
if err != nil {
slog.ErrorContext(ctx, "error getting C17_1", "err", err)
}
busC17_2, err = hassClient.GetTimeState(ctx, "sensor.tcl_c17_2")
if err != nil {
slog.ErrorContext(ctx, "error getting C17_2", "err", err)
}
busC17_3, err = hassClient.GetTimeState(ctx, "sensor.tcl_c17_3")
if err != nil {
slog.ErrorContext(ctx, "error getting C17_3", "err", err)
}
tramT1_1, err = hassClient.GetTimeState(ctx, "sensor.tcl_t1_1")
if err != nil {
slog.ErrorContext(ctx, "error getting T1_1", "err", err)
}
tramT1_2, err = hassClient.GetTimeState(ctx, "sensor.tcl_t1_2")
if err != nil {
slog.ErrorContext(ctx, "error getting T1_2", "err", err)
}
tramT1_3, err = hassClient.GetTimeState(ctx, "sensor.tcl_t1_3")
if err != nil {
slog.ErrorContext(ctx, "error getting T1_3", "err", err)
}
}() }()
go func() { go func() {
defer wg.Done() defer wg.Done()
@ -65,22 +105,13 @@ func getImg(ctx context.Context, nowFunc func() time.Time, transportsClient *tra
var err error var err error
tram, err = transportsClient.GetTCLPassages(ctx, 34068) velovRocBikes, err = hassClient.GetState(ctx, "sensor.velov_rocard_octavie_bikes")
if err != nil { if err != nil {
slog.ErrorContext(ctx, "error getting tram", "err", err) slog.ErrorContext(ctx, "error getting velov rocard bikes", "err", err)
} }
}() velovRocStands, err = hassClient.GetState(ctx, "sensor.velov_rocard_octavie_stands")
go func() {
defer wg.Done()
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
var err error
velovRoc, err = transportsClient.GetVelovStation(ctx, 10044)
if err != nil { if err != nil {
slog.ErrorContext(ctx, "error getting velov", "err", err) slog.ErrorContext(ctx, "error getting velov rocard stands", "err", err)
} }
}() }()
go func() { go func() {
@ -91,9 +122,9 @@ func getImg(ctx context.Context, nowFunc func() time.Time, transportsClient *tra
var err error var err error
fetes, err = feteClient.GetFete(ctx, nowFunc()) feteName, err = hassClient.GetState(ctx, "sensor.fete_du_jour")
if err != nil { if err != nil {
slog.ErrorContext(ctx, "error getting fetes", "err", err) slog.ErrorContext(ctx, "error getting fete_du_jour", "err", err)
} }
}() }()
go func() { go func() {
@ -121,6 +152,15 @@ func getImg(ctx context.Context, nowFunc func() time.Time, transportsClient *tra
if err != nil { if err != nil {
slog.ErrorContext(ctx, "error getting hass message", "err", err) slog.ErrorContext(ctx, "error getting hass message", "err", err)
} }
if msg != "" {
return
}
msg, err = hassClient.GetState(ctx, "input_text.proverbe_du_jour")
if err != nil {
slog.ErrorContext(ctx, "error getting hass proverbe", "err", err)
}
}() }()
img := newWhite() img := newWhite()
@ -133,15 +173,12 @@ func getImg(ctx context.Context, nowFunc func() time.Time, transportsClient *tra
wg.Wait() wg.Wait()
if msg == "" { drawTCL(gc, "37", []time.Time{bus37_1, bus37_2, bus37_3}, nowFunc(), rightX, 45)
msg = quotes.GetQuote(nowFunc()) drawTCL(gc, "C17", []time.Time{busC17_1, busC17_2, busC17_3}, nowFunc(), rightX+120, 45)
} drawTCL(gc, "T1", []time.Time{tramT1_1, tramT1_2, tramT1_3}, nowFunc(), rightX+120, 205)
drawVelov(gc, "Rocard Octavie", velovRocBikes, velovRocStands, 365)
drawTCL(gc, bus, 45)
drawTCL(gc, tram, 205)
drawVelov(gc, velovRoc, 365)
drawDate(gc, nowFunc()) drawDate(gc, nowFunc())
drawFete(gc, fetes) drawFete(gc, feteName)
drawWeather(ctx, gc, wthr) drawWeather(ctx, gc, wthr)
drawMsg(gc, msg) drawMsg(gc, msg)
@ -269,21 +306,17 @@ func formatTemp(temp float64) string {
return fmt.Sprintf("%v°C", int(math.Round(temp))) return fmt.Sprintf("%v°C", int(math.Round(temp)))
} }
func drawVelov(gc *draw2dimg.GraphicContext, station *transports.Station, yOffset float64) { func drawVelov(gc *draw2dimg.GraphicContext, title, bikes, stands string, yOffset float64) {
if station == nil { text(gc, title, 23, rightX, yOffset, fonts.Bold)
return
}
text(gc, station.Name, 23, rightX, yOffset, fonts.Bold)
yOffset += 30 yOffset += 30
text(gc, "\uE0D6", 22, rightX, yOffset+fonts.IconYOffset, fonts.Icons) // bike icon text(gc, "\uE0D6", 22, rightX, yOffset+fonts.IconYOffset, fonts.Icons) // bike icon
text(gc, strconv.Itoa(station.BikesAvailable), 22, rightX+fonts.IconXOffset, yOffset, fonts.Regular) text(gc, bikes, 22, rightX+fonts.IconXOffset, yOffset, fonts.Regular)
nextCol := rightX + 100.0 nextCol := rightX + 100.0
text(gc, "\uEC08", 22, nextCol, yOffset+fonts.IconYOffset, fonts.Icons) // parking icon text(gc, "\uEC08", 22, nextCol, yOffset+fonts.IconYOffset, fonts.Icons) // parking icon
text(gc, strconv.Itoa(station.DocksAvailable), 22, nextCol+fonts.IconXOffset, yOffset, fonts.Regular) text(gc, stands, 22, nextCol+fonts.IconXOffset, yOffset, fonts.Regular)
} }
func drawDate(gc *draw2dimg.GraphicContext, now time.Time) { func drawDate(gc *draw2dimg.GraphicContext, now time.Time) {
@ -291,30 +324,24 @@ func drawDate(gc *draw2dimg.GraphicContext, now time.Time) {
text(gc, getDate(now), 30, leftX, 345, fonts.Regular) text(gc, getDate(now), 30, leftX, 345, fonts.Regular)
} }
func drawFete(gc *draw2dimg.GraphicContext, fetes *fete.Fete) { func drawFete(gc *draw2dimg.GraphicContext, feteName string) {
if fetes == nil { if feteName == "" {
return return
} }
text(gc, fmt.Sprintf("On fête les %s", fetes.Name), 18, leftX, 380, fonts.Regular) text(gc, fmt.Sprintf("On fête les %s", feteName), 18, leftX, 380, fonts.Regular)
} }
func drawTCL(gc *draw2dimg.GraphicContext, passages *transports.Passages, yoffset float64) { func drawTCL(gc *draw2dimg.GraphicContext, title string, times []time.Time, now time.Time, x, yoffset float64) {
if passages == nil {
return
}
for i, passage := range passages.Passages {
x := float64(rightX + i*120)
text(gc, "\uE106", 23, x, yoffset+fonts.IconYOffset, fonts.Icons) text(gc, "\uE106", 23, x, yoffset+fonts.IconYOffset, fonts.Icons)
text(gc, passage.Ligne, 23, x+fonts.IconXOffset, yoffset, fonts.Bold) text(gc, title, 23, x+fonts.IconXOffset, yoffset, fonts.Bold)
for j, delay := range passage.Delays { for j, t := range times {
if t == (time.Time{}) {
continue
}
delay := t.Sub(now).Truncate(time.Minute)
y := yoffset + float64(j+1)*35 y := yoffset + float64(j+1)*35
text(gc, delay, 22, x, y, fonts.Regular) text(gc, fmt.Sprintf("%v min", delay.Minutes()), 22, x, y, fonts.Regular)
if j >= 2 { // limit number of delays displayed
break
}
}
} }
} }

19
main.go
View file

@ -2,10 +2,8 @@ package main
import ( import (
"context" "context"
"github.com/Crocmagnon/display-epaper/fete"
"github.com/Crocmagnon/display-epaper/fonts" "github.com/Crocmagnon/display-epaper/fonts"
"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/weather" "github.com/Crocmagnon/display-epaper/weather"
"github.com/llgcode/draw2d" "github.com/llgcode/draw2d"
_ "golang.org/x/image/bmp" _ "golang.org/x/image/bmp"
@ -27,13 +25,6 @@ func main() {
draw2d.SetFontCache(fontCache) draw2d.SetFontCache(fontCache)
transportsClient := transports.New(nil, transports.Config{})
feteClient := fete.New(nil, fete.Config{
APIKey: os.Getenv("FETE_API_KEY"),
CacheLocation: os.Getenv("FETE_CACHE_LOCATION"),
})
weatherClient := weather.New(nil, weather.Config{ weatherClient := weather.New(nil, weather.Config{
APIKey: os.Getenv("WEATHER_API_KEY"), APIKey: os.Getenv("WEATHER_API_KEY"),
CacheLocation: os.Getenv("WEATHER_CACHE_LOCATION"), CacheLocation: os.Getenv("WEATHER_CACHE_LOCATION"),
@ -62,15 +53,7 @@ func main() {
BaseURL: os.Getenv("HOME_ASSISTANT_BASE_URL"), BaseURL: os.Getenv("HOME_ASSISTANT_BASE_URL"),
}) })
if err := run( if err := run(ctx, sleep, initFastThreshold, weatherClient, hassClient); err != nil {
ctx,
sleep,
initFastThreshold,
transportsClient,
feteClient,
weatherClient,
hassClient,
); err != nil {
slog.ErrorContext(ctx, "error", "err", err) slog.ErrorContext(ctx, "error", "err", err)
os.Exit(1) os.Exit(1)
} }

View file

@ -1,405 +0,0 @@
package quotes
// Courtesy of ChatGPT.
var january = [31]string{
"La nouvelle année commence toujours mieux avec du pain frais.",
"Un bon pétrissage vaut plus que mille discours.",
"Pour un pain bien cuit, patience et chaleur sont de mise.",
"À four ouvert, pâte brûlée.",
"Le pain partagé est toujours meilleur.",
"On ne fait pas lever la pâte en la pressant.",
"Pain croustillant, matin gagnant.",
"Sans sel dans la pâte, le pain reste fade.",
"Il faut du temps pour faire lever le meilleur des pains.",
"Qui veut une croûte dorée doit savoir attendre.",
"Une farine bien tamisée fait la mie plus fine.",
"Même la mie la plus tendre a besoin de croûte.",
"Le bon boulanger connaît son four comme son âme.",
"Un pain trop vite cuit na jamais de goût.",
"Le pain du matin est un trésor quotidien.",
"Il faut de la levure pour toute grande aventure.",
"Entre pétrir et cuire, tout est dans le timing.",
"Mie aérée, artisan comblé.",
"Qui ne farine pas sa planche colle son pain.",
"Le four mal allumé ne cuit que des regrets.",
"Un pain bien doré attire les gourmands du quartier.",
"Point de croissant sans beurre, point de vie sans saveur.",
"Le levain qui dort ne nourrit personne.",
"Pour un pain parfait, la main doit être légère.",
"Pain trop pétri, croûte trop durcie.",
"Le secret dun bon pain réside dans la chaleur du four et du cœur.",
"Plus on attend, plus le pain grandit.",
"Une miche pour deux fait oublier les soucis.",
"Le boulanger lève tôt, et la pâte avec lui.",
"À pain chaud, miel bienvenu.",
"Un pain pour chaque jour, un bonheur pour toujours.",
}
var february = [29]string{
"Un pain bien pétri ne craint pas l'hiver.",
"Pain cuit trop vite, croûte qui seffrite.",
"Qui sème du blé, récolte des miches dorées.",
"Sans amour, même le meilleur pain reste fade.",
"Un four bien chaud fait lever la pâte comme il faut.",
"À petite miche, grand plaisir.",
"Qui cuit doucement, savoure longuement.",
"Le bon pain se prépare avec soin, pas en chemin.",
"Pas de levain, pas de lendemain.",
"Chaque grain de blé a son destin dans une miche.",
"Pain trop salé, ami délaissé.",
"Qui goûte la croûte, goûte la vérité.",
"Mie trop dense, pain sans danse.",
"Pour un pain parfait, il faut l'eau de la pluie et la chaleur du feu.",
"Le levain est lâme du pain.",
"Dans un four trop froid, rien ne se fait grand.",
"Farine bien choisie, pain qui réjouit.",
"Le bon pain attend toujours son heure.",
"Un pain levé dhier fait des merveilles aujourdhui.",
"Il faut nourrir le levain pour qu'il fasse grandir le pain.",
"Un four bien allumé réchauffe plus que des mots.",
"Sans pétrin, point de festin.",
"Pain bien formé, fête assurée.",
"Il faut respecter la pâte pour quelle donne le meilleur delle-même.",
"Une miche bien dorée na pas besoin de discours.",
"Pain du matin, chaleur dans les mains.",
"Le pain du pauvre vaut mieux que lor du riche.",
"Tant que le four est chaud, le boulanger a du travail.",
"Pain mal pétri, croûte sans vie.",
}
var march = [31]string{
"Mars arrive avec le vent, mais le bon pain reste constant.",
"À pain chaud, sourire éclatant.",
"Une miche oubliée finit toujours par sécher.",
"Qui veut une croûte dorée ne doit pas se presser.",
"Le pain qui lève trop vite finit par retomber.",
"Le four mal fermé laisse échapper le bonheur.",
"Dans chaque grain de blé, il y a un pain qui sommeille.",
"Pain du jour, force de toujours.",
"Mieux vaut une croûte trop dure qu'une pâte trop crue.",
"La farine bien moulue fait la mie douce comme la soie.",
"Le boulanger sage ne se presse jamais.",
"Un pain à partager est un bonheur multiplié.",
"La levure ne simprovise pas, elle se cultive.",
"Qui fait lever son pain avec soin na jamais faim.",
"Mie tendre, journée plus douce.",
"Le pain que lon attend est le plus savoureux.",
"Pas de bon pain sans un brin de patience.",
"Même sous la pluie, la farine trouve son chemin vers la miche.",
"Un bon levain fait durer le festin.",
"Le pain bien cuit nourrit plus que l'estomac.",
"Sous une croûte dorée, tout est douceur.",
"Mie aérée, journée réussie.",
"Le secret d'un pain réussi : du temps et de la flamme.",
"Le pain pétri avec amour est toujours meilleur.",
"Même un petit pain fait un grand festin.",
"Quand le four est chaud, tout est possible.",
"Chaque grain de farine compte dans le pétrin.",
"Le bon pain sait attendre son heure dans le four.",
"Mieux vaut un pain simple bien fait qu'un festin compliqué.",
"En mars, le pain se prépare aux premiers rayons du printemps.",
"Pain cuit en mars, douceur pour les journées qui séclaircissent.",
}
var april = [30]string{
"En avril, le pain se dore sous le soleil timide.",
"Qui rit en avril, pétrit en douceur.",
"Le pain bien levé chasse les doutes du boulanger.",
"À pâte bien travaillée, mie légère assurée.",
"Quand le four parle, le boulanger écoute.",
"Il faut du levain pour affronter les caprices d'avril.",
"Sans farine fine, la miche ne se dessine.",
"Le pain qui chante dans le four annonce une bonne journée.",
"Point de bon pain sans un soupçon de patience.",
"Même en avril, la pâte n'aime pas les courants d'air.",
"À pain du matin, sourires jusqu'au soir.",
"Qui sème en avril, pétrit en mai.",
"Un bon pétrissage vaut bien une journée de travail.",
"Le pain qui ne lève pas est un cœur qui reste fermé.",
"En avril, pain tendre est un bonheur sincère.",
"Le boulanger qui sème ne manque jamais de farine.",
"Pain mal doré, journée ratée.",
"Le pain lève au rythme du cœur de celui qui le fait.",
"Avril pluvieux fait le pain précieux.",
"Qui cuit sans hâte, mange sans regret.",
"La mie légère est fille du temps et de l'effort.",
"Le boulanger patient fait toujours lever la pâte.",
"Pain bien levé, soucis envolés.",
"Chaque miche a sa saison, chaque croûte a sa raison.",
"En avril, le pain se lève malgré la pluie.",
"Le bon pain na pas dâge, mais il a toujours du goût.",
"Même une petite miche a besoin de chaleur.",
"Avril timide, mie solide.",
"Pétrir doucement, cest savourer longtemps.",
"Le four d'avril est plein de promesses pour lété.",
}
var may = [31]string{
"En mai, le pain lève comme les fleurs dans les champs.",
"Mie dorée en mai, gourmandise assurée.",
"Le boulanger de mai travaille avec le soleil en souriant.",
"Pain chaud en mai, bonheur partagé.",
"Avec le blé de mai, le pain na jamais failli.",
"Un bon pain en mai vaut plus que mille festins.",
"Pétrir en mai, c'est préparer l'été.",
"Le levain bien nourri fait des miches comme au paradis.",
"Même le pain a ses saisons, et mai est le plus doux.",
"En mai, le pain se dore sous les rayons de l'aube.",
"Pain levé en mai, saveur de la liberté.",
"Le pain du matin est la promesse dun jour radieux.",
"Qui pétrit en mai prépare labondance de demain.",
"En mai, pain au four, maison en fête.",
"Le secret du pain de mai, c'est la chaleur du four et celle du cœur.",
"Pain fait avec soin est toujours un pain divin.",
"Le levain de mai est la force de lété.",
"Chaque miche de mai raconte lhistoire du blé passé.",
"Mai fleuri, four nourri.",
"Le pain de mai réchauffe même sans four.",
"Avec du bon levain, tout pain est un festin.",
"En mai, qui fait du pain récolte des sourires.",
"La pâte pétrie en mai est plus légère quun nuage.",
"Le boulanger de mai ne manque jamais de blé.",
"En mai, pain cuit vaut mille mots doux.",
"Quand le soleil de mai brille, le pain se dore à merveille.",
"Pain trop pressé en mai ne fait jamais honneur.",
"La farine de mai sent la promesse des moissons.",
"Un bon pain en mai est le prélude de lété.",
"Chaque miche de mai est un régal pour les repas en extérieur.",
"Sous le ciel clair de mai, le pain se transforme en délice.",
}
var june = [30]string{
"En juin, le pain se lève avec les premiers blés.",
"Sous le soleil de juin, la croûte se dore sans fin.",
"Le pain bien fait en juin nourrit jusqu'au lendemain.",
"Le boulanger de juin façonne la mie comme le vent sculpte les champs.",
"À pain bien pétri, mie légère sous un ciel dété.",
"Juin sans pain, cest comme un jour sans soleil.",
"Le pain de juin réchauffe les cœurs même sous la chaleur.",
"Mie de juin, douceur en chemin.",
"En juin, qui ne lève pas sa pâte reste sur la touche.",
"Juin donne aux miches la saveur des jours longs.",
"Avec du pain fait en juin, chaque repas devient festin.",
"Le four de juin est lami du boulanger comme le soleil lest du blé.",
"Qui pétrit en juin prépare les festins dété.",
"Le pain bien doré de juin est le fruit du travail bien fait.",
"En juin, les miches se parent de la couleur des blés.",
"Pain levé en juin, fête assurée pour demain.",
"Chaque miche de juin a le goût du soleil et de la fête.",
"En juin, le blé se prépare, et le pain suit le même chemin.",
"Le pain de juin nourrit plus quun simple festin.",
"Sous le ciel de juin, tout pain est promesse de lété.",
"Le levain de juin porte en lui toute la chaleur des jours à venir.",
"En juin, chaque fournée est un hommage à la terre.",
"Pain pétri en juin, saveurs divines pour le festin.",
"Avec la chaleur de juin, même le plus petit pain grandit.",
"Juin chante avec le pain qui lève doucement.",
"Le boulanger de juin na besoin que de farine et dun ciel sans nuage.",
"En juin, un pain bien cuit annonce un bel été.",
"Le four de juin est un compagnon fidèle pour tout boulanger.",
"Le pain doré sous le soleil de juin reste gravé dans les cœurs.",
"Le pain de juin est le compagnon des repas en plein air.",
}
var july = [31]string{
"En juillet, le pain dore plus vite que les épis.",
"Sous le soleil de juillet, la croûte est toujours parfaite.",
"Le pain de juillet nourrit les longues soirées dété.",
"Juillet, mois du blé, mois du pain bien levé.",
"Pain chaud en juillet, bonheur partagé sous le ciel étoilé.",
"En juillet, qui pétrit récolte des sourires dorés.",
"Le four de juillet ne connaît pas la pause, comme le soleil.",
"À pain bien doré, saveur de lété retrouvé.",
"Juillet chante dans la miche, comme le vent dans les champs.",
"Le pain de juillet porte en lui la chaleur des jours heureux.",
"Mie légère en juillet, journée sans nuages.",
"Juillet offre au pain la douceur des soirées dété.",
"En juillet, la farine danse sous les mains du boulanger.",
"À four chaud en juillet, mie tendre à souhait.",
"Pain pétri en juillet, festin assuré pour lété.",
"Sous le soleil de juillet, chaque miche est une fête.",
"La croûte de juillet est un hommage aux champs dorés.",
"Qui lève sa pâte en juillet ne manque jamais de blé.",
"En juillet, chaque pain est un rayon de soleil.",
"Le pain bien levé de juillet accompagne chaque repas avec fierté.",
"Même les petits pains de juillet ont le goût de la fête.",
"Le four de juillet fait danser la mie comme la brise dans les blés.",
"Pain doré sous le soleil de juillet, plaisir garanti.",
"Le boulanger de juillet connaît lart du pain comme le ciel connaît le soleil.",
"En juillet, un pain bien cuit fait oublier les soucis.",
"Chaque miche de juillet est remplie de la générosité de la terre.",
"Pain levé sous la chaleur de juillet, saveur inégalée.",
"Le pain de juillet est une promesse de jours heureux.",
"Avec un pain bien fait en juillet, tout festin devient mémorable.",
"La chaleur de juillet scelle le destin de chaque miche.",
"En juillet, le pain se fait léger pour les chaudes journées.",
}
var august = [31]string{
"En août, le pain croustille sous la chaleur du four et du ciel.",
"Pain d'août, mie tendre et croûte en fête.",
"Le pain d'août nourrit autant qu'il réjouit.",
"À four chaud en août, mie aérée et bonheur partout.",
"Le pain de lété est un trésor de générosité.",
"Août brûlant fait lever la pâte doucement.",
"Sous le soleil d'août, le pain shabille dor.",
"Pain doré en août, festin sans aucun doute.",
"Le boulanger d'août connaît la patience sous la chaleur.",
"Pain bien levé, août enchanté.",
"Chaque miche d'août porte en elle la richesse des moissons.",
"Août généreux, pain savoureux.",
"Le pain cuit en août a le goût des champs dorés.",
"Mie tendre et croûte dorée sont les fruits du travail d'août.",
"À pain bien pétri, août se rit.",
"En août, chaque miche est un hommage au blé mûr.",
"Le four d'août réchauffe les cœurs autant que le pain nourrit.",
"Pain cuit avec soin en août, festin en vue.",
"En août, un bon pain vaut plus que mille festins.",
"Le pain d'août est la saveur des longues soirées estivales.",
"Chaque grain de blé moissonné en août devient un pain doré.",
"Pain levé sous le ciel d'août, saveur inoubliable.",
"Même sous la chaleur d'août, le boulanger ne faiblit pas.",
"Le pain d'août se cuit avec le cœur et la patience.",
"Mie légère en août, été parfait sans aucun doute.",
"Un pain bien doré en août vaut toutes les douceurs.",
"Le four brûlant d'août fait naître des miches croquantes.",
"Août offre au pain une saveur venue des champs dorés.",
"Chaque miche en août est un rayon de soleil.",
"Pain pétri en août, festin parfait pour un été radieux.",
"Mie fraîche en août, plaisir assuré.",
}
var september = [30]string{
"En septembre, le pain se prépare pour lhiver à venir.",
"Le pain de septembre est le reflet des récoltes de lété.",
"Mie bien aérée en septembre, journées dorées assurées.",
"À pain bien cuit, septembre sourit.",
"En septembre, le four réchauffe plus que le pain.",
"Chaque miche de septembre porte les promesses de lautomne.",
"Le pain de septembre se nourrit des derniers rayons dété.",
"Le boulanger de septembre sait que chaque grain compte.",
"Pain pétri avec soin en septembre, saveur dautomne assurée.",
"Sous la lumière douce de septembre, le pain se dore à merveille.",
"Le pain de septembre est le délice des journées fraîches.",
"En septembre, même le pain a le goût des nouvelles saisons.",
"Mie de septembre, douceur dans chaque bouchée.",
"Le four de septembre est prêt pour les pains dautomne.",
"Un bon pain en septembre est la promesse dun hiver bien nourri.",
"Pain bien levé, septembre apaisé.",
"Chaque miche de septembre est une préparation pour les jours froids.",
"En septembre, le levain se prépare à la fraîcheur des matins.",
"Le pain doré de septembre est le meilleur compagnon des soirées qui sallongent.",
"À four bien chaud en septembre, mie toujours tendre.",
"Le pain de septembre est le fruit du travail des mois chauds.",
"Sous le ciel de septembre, le pain est une douce transition.",
"Le boulanger de septembre voit chaque miche comme une promesse.",
"Pain cuit en septembre, plaisir dautomne.",
"Chaque tranche de pain de septembre a le goût des récoltes.",
"Septembre partage le pain comme il partage les récoltes.",
"Le four de septembre réchauffe le pain comme les cœurs.",
"En septembre, chaque miche est une ode aux saisons passées.",
"Le pain de septembre se prépare pour la saison des fêtes.",
"Le four de septembre est prêt pour les premières miches dautomne.",
}
var october = [31]string{
"En octobre, le pain se dore sous les premières brises dautomne.",
"À pain bien levé, octobre en beauté.",
"Le pain doctobre se nourrit des vents frais et des jours raccourcis.",
"Chaque miche doctobre porte la richesse des champs dorés.",
"Pain cuit avec soin en octobre, réchauffe les soirées dautomne.",
"En octobre, la pâte se prépare pour les jours plus frais.",
"Le four doctobre est le gardien des secrets du pain parfait.",
"Mie légère en octobre, douceur assurée malgré le froid.",
"Le pain doctobre est un régal pour les jours qui se refroidissent.",
"Sous les feuilles doctobre, le pain se dore à souhait.",
"Chaque tranche de pain en octobre est un réconfort pour les soirées longues.",
"Le boulanger doctobre sait que la patience fait la mie.",
"Pain bien pétri en octobre, festin pour les mois froids.",
"En octobre, même le pain a besoin dun peu de chaleur.",
"Le pain doctobre est laccompagnement idéal pour les repas dautomne.",
"À four chaud en octobre, chaque miche est une promesse de confort.",
"Le levain doctobre prépare les pâtes pour lhiver.",
"Pain doré sous le ciel doctobre, douceur pour chaque repas.",
"En octobre, le pain se prépare comme les feuilles tombent.",
"Chaque miche en octobre a le goût de la saison qui sachève.",
"Pain bien levé en octobre, chaleur dans chaque bouchée.",
"Sous la fraîcheur doctobre, le pain est une source de réconfort.",
"Le four doctobre transforme chaque pâte en délice automnal.",
"Pain pétri avec soin en octobre, festin pour les soirées fraîches.",
"Le pain doctobre est un préambule aux douceurs de lhiver.",
"En octobre, chaque miche est un hommage aux récoltes passées.",
"Le boulanger doctobre prépare le pain pour les jours froids à venir.",
"Chaque croûte en octobre raconte lhistoire des saisons changeantes.",
"Pain bien fait en octobre, bonheur pour chaque saison.",
"Chaque miche doctobre est un délice pour les repas dautomne.",
"Sous les feuilles doctobre, le pain se transforme en régal.",
}
var november = [30]string{
"En novembre, le pain est le réconfort des journées fraîches.",
"Le pain de novembre se nourrit de la chaleur des foyers.",
"À chaque miche en novembre, une chaleur pour les cœurs.",
"Le four en novembre est le gardien des saveurs dautomne.",
"Pain bien cuit en novembre, douceur pour les soirées longues.",
"Sous la brume de novembre, le pain réchauffe comme un bon feu.",
"Chaque miche de novembre porte en elle la promesse de lhiver.",
"Le pain doctobre donne son meilleur en novembre.",
"En novembre, le pain se fait compagnon des jours gris.",
"À four chaud en novembre, chaque tranche est un réconfort.",
"Le pain de novembre est la chaleur du foyer dans les jours froids.",
"Pain bien pétri en novembre, bonheur pour les soirées dhiver.",
"Le boulanger de novembre sait transformer la farine en chaleur.",
"Sous le ciel de novembre, le pain est un baume pour lâme.",
"Mie tendre en novembre, douceur dans chaque bouchée.",
"Le pain den novembre est le meilleur ami des repas en famille.",
"En novembre, le pain se cuit lentement pour mieux réchauffer.",
"Chaque miche de novembre se fait festive sous la lueur des chandelles.",
"Le four de novembre est le préparateur des festins dhiver.",
"Pain doré en novembre, chaleur assurée.",
"Le pain bien levé en novembre annonce des repas chaleureux.",
"Sous la douceur de novembre, le pain est un régal réconfortant.",
"Le boulanger de novembre prépare des miches pour les fêtes.",
"Pain pétri avec soin en novembre, réchauffe les soirées hivernales.",
"Chaque croûte en novembre est un hommage à la saison qui change.",
"Le pain de novembre est lélément central des repas de famille.",
"En novembre, le four est un havre de chaleur pour le pain.",
"Le pain dhiver commence à se préparer en novembre.",
"À pain bien cuit en novembre, le réconfort est assuré.",
"Pain de novembre, douceur pour les soirées longues.",
}
var december = [31]string{
"En décembre, le pain est la chaleur des longues soirées dhiver.",
"Le pain de décembre est le réconfort après les journées froides.",
"À chaque miche de décembre, une fête pour les papilles.",
"Le four en décembre est le gardien des saveurs des fêtes.",
"Pain bien cuit en décembre, bonheur sous le sapin.",
"Sous la neige de décembre, le pain réchauffe comme un bon feu.",
"Chaque tranche de pain en décembre est un délice pour les repas festifs.",
"Le pain de décembre porte en lui la magie des fêtes.",
"En décembre, le pain se cuit lentement pour remplir les maisons de chaleur.",
"À four bien chaud en décembre, chaque miche est une célébration.",
"Le pain de décembre est laccompagnement parfait des repas de fin dannée.",
"Pain bien pétri en décembre, douceur pour les soirées en famille.",
"Le boulanger de décembre transforme la farine en festin hivernal.",
"Sous les lumières de décembre, le pain est une douceur réconfortante.",
"Mie tendre en décembre, chaleur dans chaque bouchée.",
"Le pain dhiver commence à se préparer en décembre.",
"En décembre, chaque miche est un hommage aux traditions des fêtes.",
"Le four de décembre est prêt pour les festins de fin dannée.",
"Pain doré en décembre, plaisir des fêtes.",
"Le pain bien levé en décembre rend les repas plus joyeux.",
"Sous le froid de décembre, le pain est un baume pour lâme.",
"Le boulanger de décembre prépare des miches pour les fêtes de fin dannée.",
"Pain pétri avec soin en décembre, réchauffe les soirées les plus froides.",
"Chaque croûte en décembre est un hommage à la saison des fêtes.",
"Le pain de décembre est un symbole de chaleur et de convivialité.",
"En décembre, le four transforme chaque pâte en délice hivernal.",
"Le pain de décembre est le réconfort après les festivités.",
"À pain bien cuit en décembre, les fêtes sont pleines de saveur.",
"Mie tendre en décembre, plaisir des soirées hivernales.",
"À chaque miche de décembre, un cadeau pour les papilles.",
"Pain bien cuit en décembre, régal des repas de fin dannée.",
}

View file

@ -1,33 +0,0 @@
package quotes
import "time"
func GetQuote(date time.Time) string {
switch date.Month() {
case time.January:
return january[date.Day()-1]
case time.February:
return february[date.Day()-1]
case time.March:
return march[date.Day()-1]
case time.April:
return april[date.Day()-1]
case time.May:
return may[date.Day()-1]
case time.June:
return june[date.Day()-1]
case time.July:
return july[date.Day()-1]
case time.August:
return august[date.Day()-1]
case time.September:
return september[date.Day()-1]
case time.October:
return october[date.Day()-1]
case time.November:
return november[date.Day()-1]
case time.December:
return december[date.Day()-1]
}
return ""
}

View file

@ -3,9 +3,7 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"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/weather" "github.com/Crocmagnon/display-epaper/weather"
"github.com/llgcode/draw2d/draw2dimg" "github.com/llgcode/draw2d/draw2dimg"
"time" "time"
@ -15,25 +13,16 @@ func run(
ctx context.Context, ctx context.Context,
_ time.Duration, _ time.Duration,
_ time.Duration, _ time.Duration,
transportsClient *transports.Client,
feteClient *fete.Client,
weatherClient *weather.Client, weatherClient *weather.Client,
hassClient *home_assistant.Client, hassClient *home_assistant.Client,
) error { ) error {
img, err := getImg( img, err := getImg(ctx, func() time.Time {
ctx,
func() time.Time {
t, err := time.Parse(time.DateOnly, "2024-08-01zzz") t, err := time.Parse(time.DateOnly, "2024-08-01zzz")
if err != nil { if err != nil {
return time.Now() return time.Now()
} }
return t return t
}, }, weatherClient, hassClient)
transportsClient,
feteClient,
weatherClient,
hassClient,
)
if err != nil { if err != nil {
return err return err
} }

View file

@ -4,9 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/Crocmagnon/display-epaper/epd" "github.com/Crocmagnon/display-epaper/epd"
"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/weather" "github.com/Crocmagnon/display-epaper/weather"
"image" "image"
"log" "log"
@ -20,8 +18,6 @@ func run(
ctx context.Context, ctx context.Context,
sleep time.Duration, sleep time.Duration,
initFastThreshold time.Duration, initFastThreshold time.Duration,
transportsClient *transports.Client,
feteClient *fete.Client,
weatherClient *weather.Client, weatherClient *weather.Client,
hassClient *home_assistant.Client, hassClient *home_assistant.Client,
) error { ) error {
@ -47,16 +43,7 @@ func run(
slog.InfoContext(ctx, "running loop") slog.InfoContext(ctx, "running loop")
img, err := loop( img, err := loop(ctx, display, initFastThreshold, currentImg, weatherClient, hassClient)
ctx,
display,
initFastThreshold,
currentImg,
transportsClient,
feteClient,
weatherClient,
hassClient,
)
if err != nil { if err != nil {
slog.ErrorContext(ctx, "error looping", "err", err) slog.ErrorContext(ctx, "error looping", "err", err)
} }
@ -73,8 +60,6 @@ func loop(
display *epd.EPD, display *epd.EPD,
initFastThreshold time.Duration, initFastThreshold time.Duration,
currentImg image.Image, currentImg image.Image,
transportsClient *transports.Client,
feteClient *fete.Client,
weatherClient *weather.Client, weatherClient *weather.Client,
hassClient *home_assistant.Client, hassClient *home_assistant.Client,
) (image.Image, error) { ) (image.Image, error) {
@ -82,14 +67,7 @@ func loop(
if shouldDisplay(ctx, hassClient) { if shouldDisplay(ctx, hassClient) {
var err error var err error
img, err = getImg( img, err = getImg(ctx, time.Now, weatherClient, hassClient)
ctx,
time.Now,
transportsClient,
feteClient,
weatherClient,
hassClient,
)
if err != nil { if err != nil {
return nil, fmt.Errorf("getting black: %w", err) return nil, fmt.Errorf("getting black: %w", err)
} }

View file

@ -1,117 +0,0 @@
package transports
import (
"context"
"fmt"
"github.com/carlmjohnson/requests"
"log/slog"
"net/http"
"time"
)
type Stop struct {
ID int `json:"id"`
Name string `json:"name"`
}
type Passage struct {
Ligne string `json:"ligne"`
Delays []string `json:"delais"`
Destination Stop `json:"destination"`
}
type Passages struct {
Passages []Passage `json:"passages"`
Stop Stop `json:"stop"`
}
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 {
if httpClient == nil {
httpClient = &http.Client{}
}
return &Client{
client: httpClient,
config: config,
}
}
func (c *Client) GetTCLPassages(ctx context.Context, stop int) (res *Passages, err error) {
err = requests.URL("https://tcl.augendre.info").
Pathf("/tcl/stop/%v", stop).
Client(c.client).
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"`
DocksAvailable int `json:"docks_available"`
AvailabilityCode int `json:"availability_code"`
}
func (c *Client) GetVelovStation(ctx context.Context, station int) (res *Station, err error) {
err = requests.URL("https://tcl.augendre.info").
Pathf("/velov/station/%v", station).
Client(c.client).
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
}