mirror of
https://github.com/Crocmagnon/display-epaper.git
synced 2024-12-22 12:01:46 +01:00
query everything through home assistant
This commit is contained in:
parent
4c23ecb1ff
commit
01a49beb7e
9 changed files with 129 additions and 783 deletions
97
fete/fete.go
97
fete/fete.go
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
153
img.go
153
img.go
|
@ -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
|
||||||
wthr *weather.Prevision
|
busC17_2 time.Time
|
||||||
msg string
|
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
|
||||||
|
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 {
|
text(gc, "\uE106", 23, x, yoffset+fonts.IconYOffset, fonts.Icons)
|
||||||
return
|
text(gc, title, 23, x+fonts.IconXOffset, yoffset, fonts.Bold)
|
||||||
}
|
for j, t := range times {
|
||||||
|
if t == (time.Time{}) {
|
||||||
for i, passage := range passages.Passages {
|
continue
|
||||||
x := float64(rightX + i*120)
|
|
||||||
text(gc, "\uE106", 23, x, yoffset+fonts.IconYOffset, fonts.Icons)
|
|
||||||
text(gc, passage.Ligne, 23, x+fonts.IconXOffset, yoffset, fonts.Bold)
|
|
||||||
for j, delay := range passage.Delays {
|
|
||||||
y := yoffset + float64(j+1)*35
|
|
||||||
text(gc, delay, 22, x, y, fonts.Regular)
|
|
||||||
if j >= 2 { // limit number of delays displayed
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
delay := t.Sub(now).Truncate(time.Minute)
|
||||||
|
y := yoffset + float64(j+1)*35
|
||||||
|
text(gc, fmt.Sprintf("%v min", delay.Minutes()), 22, x, y, fonts.Regular)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
19
main.go
19
main.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
405
quotes/data.go
405
quotes/data.go
|
@ -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 n’a 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 d’un 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 s’effrite.",
|
|
||||||
"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é d’hier fait des merveilles aujourd’hui.",
|
|
||||||
"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 qu’elle donne le meilleur d’elle-même.",
|
|
||||||
"Une miche bien dorée n’a pas besoin de discours.",
|
|
||||||
"Pain du matin, chaleur dans les mains.",
|
|
||||||
"Le pain du pauvre vaut mieux que l’or 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 s’improvise pas, elle se cultive.",
|
|
||||||
"Qui fait lever son pain avec soin n’a jamais faim.",
|
|
||||||
"Mie tendre, journée plus douce.",
|
|
||||||
"Le pain que l’on 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 n’a 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, c’est 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 n’a 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 d’un jour radieux.",
|
|
||||||
"Qui pétrit en mai prépare l’abondance 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 l’histoire 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 qu’un 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, c’est 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 l’ami du boulanger comme le soleil l’est 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 qu’un 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 n’a besoin que de farine et d’un 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 l’art 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 s’habille d’or.",
|
|
||||||
"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 l’hiver à 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 l’automne.",
|
|
||||||
"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 d’automne 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 d’automne.",
|
|
||||||
"Un bon pain en septembre est la promesse d’un 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 s’allongent.",
|
|
||||||
"À 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 d’automne.",
|
|
||||||
"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 d’automne.",
|
|
||||||
}
|
|
||||||
|
|
||||||
var october = [31]string{
|
|
||||||
"En octobre, le pain se dore sous les premières brises d’automne.",
|
|
||||||
"À pain bien levé, octobre en beauté.",
|
|
||||||
"Le pain d’octobre se nourrit des vents frais et des jours raccourcis.",
|
|
||||||
"Chaque miche d’octobre porte la richesse des champs dorés.",
|
|
||||||
"Pain cuit avec soin en octobre, réchauffe les soirées d’automne.",
|
|
||||||
"En octobre, la pâte se prépare pour les jours plus frais.",
|
|
||||||
"Le four d’octobre est le gardien des secrets du pain parfait.",
|
|
||||||
"Mie légère en octobre, douceur assurée malgré le froid.",
|
|
||||||
"Le pain d’octobre est un régal pour les jours qui se refroidissent.",
|
|
||||||
"Sous les feuilles d’octobre, le pain se dore à souhait.",
|
|
||||||
"Chaque tranche de pain en octobre est un réconfort pour les soirées longues.",
|
|
||||||
"Le boulanger d’octobre 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 d’un peu de chaleur.",
|
|
||||||
"Le pain d’octobre est l’accompagnement idéal pour les repas d’automne.",
|
|
||||||
"À four chaud en octobre, chaque miche est une promesse de confort.",
|
|
||||||
"Le levain d’octobre prépare les pâtes pour l’hiver.",
|
|
||||||
"Pain doré sous le ciel d’octobre, 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 s’achève.",
|
|
||||||
"Pain bien levé en octobre, chaleur dans chaque bouchée.",
|
|
||||||
"Sous la fraîcheur d’octobre, le pain est une source de réconfort.",
|
|
||||||
"Le four d’octobre transforme chaque pâte en délice automnal.",
|
|
||||||
"Pain pétri avec soin en octobre, festin pour les soirées fraîches.",
|
|
||||||
"Le pain d’octobre est un préambule aux douceurs de l’hiver.",
|
|
||||||
"En octobre, chaque miche est un hommage aux récoltes passées.",
|
|
||||||
"Le boulanger d’octobre prépare le pain pour les jours froids à venir.",
|
|
||||||
"Chaque croûte en octobre raconte l’histoire des saisons changeantes.",
|
|
||||||
"Pain bien fait en octobre, bonheur pour chaque saison.",
|
|
||||||
"Chaque miche d’octobre est un délice pour les repas d’automne.",
|
|
||||||
"Sous les feuilles d’octobre, 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 d’automne.",
|
|
||||||
"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 l’hiver.",
|
|
||||||
"Le pain d’octobre 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 d’hiver.",
|
|
||||||
"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 d’en 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 d’hiver.",
|
|
||||||
"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 d’hiver 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 d’hiver.",
|
|
||||||
"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 l’accompagnement parfait des repas de fin d’anné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 d’hiver 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 d’anné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 d’anné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 d’année.",
|
|
||||||
}
|
|
|
@ -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 ""
|
|
||||||
}
|
|
|
@ -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,
|
t, err := time.Parse(time.DateOnly, "2024-08-01zzz")
|
||||||
func() time.Time {
|
if err != nil {
|
||||||
t, err := time.Parse(time.DateOnly, "2024-08-01zzz")
|
return time.Now()
|
||||||
if err != nil {
|
}
|
||||||
return time.Now()
|
return t
|
||||||
}
|
}, weatherClient, hassClient)
|
||||||
return t
|
|
||||||
},
|
|
||||||
transportsClient,
|
|
||||||
feteClient,
|
|
||||||
weatherClient,
|
|
||||||
hassClient,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
Loading…
Reference in a new issue