From 36bdca33088d9e68a1fb6055cffd13cc6b48b25b Mon Sep 17 00:00:00 2001 From: Krzysztof Romanowski Date: Fri, 20 Oct 2023 21:16:20 +0200 Subject: [PATCH] Initial commit --- .github/workflows/distribute.yml | 41 ++++++++++++++++++++++++++++ Dockerfile | 13 +++++++++ README.md | 29 ++++++++++++++++++++ api/api.go | 32 ++++++++++++++++++++++ api/api_test.go | 42 +++++++++++++++++++++++++++++ go.mod | 8 ++++++ go.sum | 4 +++ main.go | 46 ++++++++++++++++++++++++++++++++ 8 files changed, 215 insertions(+) create mode 100644 .github/workflows/distribute.yml create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 api/api.go create mode 100644 api/api_test.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/.github/workflows/distribute.yml b/.github/workflows/distribute.yml new file mode 100644 index 0000000..ebbba9e --- /dev/null +++ b/.github/workflows/distribute.yml @@ -0,0 +1,41 @@ +on: + workflow_dispatch: + push: + branches: + - main + +name: Build and deploy to Docker Hub + +jobs: + deploy: + name: Build + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: setup-docker + uses: docker-practice/actions-setup-docker@1.0.11 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + with: + platforms: linux/amd64 + + - name: Docker Setup Buildx + uses: docker/setup-buildx-action@v2.2.1 + + - name: Docker Login + uses: docker/login-action@v2.1.0 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_PASSWORD }} + + - name: Build and push Docker image + uses: docker/build-push-action@v3.2.0 + with: + platforms: | + linux/amd64 + push: true + tags: c4stus/lights-api:${{ env.GITHUB_SHA }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f9f1b70 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM --platform=$BUILDPLATFORM golang:1.20 AS builder +ARG TARGETPLATFORM +ARG BUILDPLATFORM +ARG TARGETARCH +RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM" + +WORKDIR /data +COPY . /data +RUN GOOS=linux GOARCH=$TARGETARCH go build -o lights-api + +FROM scratch +COPY --from=builder /data/lights-api ./ +CMD ["./lights-api"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..58f2d85 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +This repo is an API to serve values based on time. These values are then used by LED Controllers to dim lights and set a specific color. + +# How it works? + +Brightness and color are values from 0 to 1000. This repo uses simple linear function that increase that number from midnight to noon and then decrease it until midnight. + +``` + ▲ + 1000 │ ┌─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + │ ┌─┘ └─┐ + 0 │┌─┘ └─┐ + └┴─────────────────────────────────────────────────────────────────┴─▶ + midnight noon midnight + +``` diff --git a/api/api.go b/api/api.go new file mode 100644 index 0000000..223dfb2 --- /dev/null +++ b/api/api.go @@ -0,0 +1,32 @@ +package api + +import ( + "fmt" + "math" + "strconv" + "time" +) + +func Get(unixTimestamp time.Time) string { + now := time.Now() + var secondsDay float64 = 86400 + startOfDay := unixTimestamp.Truncate(24 * time.Hour) + noon := startOfDay.Add(12 * time.Hour) + endOfDay := startOfDay.Add(24 * time.Hour) + + fmt.Println("Start of day:", startOfDay) + fmt.Println("End of day:", endOfDay) + fmt.Println(now, secondsDay) + + var value float64 + if unixTimestamp.Before(noon) { + subTime := unixTimestamp.Sub(startOfDay).Seconds() + value = math.Ceil(subTime * 1000 / (secondsDay / 2)) + } else { + subTime := endOfDay.Sub(unixTimestamp).Seconds() + secs := (secondsDay / 2) - subTime + value = 1000 - math.Ceil(secs*1000/(secondsDay/2)) + } + + return strconv.FormatFloat(value, 'f', 0, 64) +} diff --git a/api/api_test.go b/api/api_test.go new file mode 100644 index 0000000..2404697 --- /dev/null +++ b/api/api_test.go @@ -0,0 +1,42 @@ +package api_test + +import ( + "fmt" + "testing" + "time" + + "github.com/castus/lights-api/api" +) + +func TestAfterNoon(t *testing.T) { + ti := GetUnixTimestampForTime(21, 00) + if api.Get(ti) != "250" { + t.Fail() + } +} + +func TestBeforeNoon(t *testing.T) { + ti := GetUnixTimestampForTime(11, 00) + if api.Get(ti) != "917" { + t.Fail() + } +} + +func TestNoon(t *testing.T) { + ti := GetUnixTimestampForTime(12, 00) + if api.Get(ti) != "1000" { + t.Fail() + } +} + +func TestMidnight(t *testing.T) { + ti := GetUnixTimestampForTime(00, 00) + fmt.Println(api.Get(ti)) + if api.Get(ti) != "0" { + t.Fail() + } +} + +func GetUnixTimestampForTime(hour int, minute int) time.Time { + return time.Date(2023, time.October, 19, hour, minute, 0, 0, time.UTC) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..15ddaca --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/castus/lights-api + +go 1.21.3 + +require ( + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9baf97c --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= diff --git a/main.go b/main.go new file mode 100644 index 0000000..70b92e5 --- /dev/null +++ b/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "fmt" + "net/http" + "strconv" + "time" + + "go.uber.org/zap" + + "github.com/castus/lights-api/api" +) + +func main() { + logger, _ := zap.NewProduction() + log := logger.Sugar() + defer logger.Sync() + + http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) { + queryParameters := r.URL.Query() + timestamp := queryParameters.Get("timestamp") + parsedTime, err := strconv.ParseInt(timestamp, 10, 64) + fmt.Println(parsedTime) + if err != nil { + panic(err) + } + unixTime := time.Unix(parsedTime, 0) + lightValue := api.Get(unixTime) + + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + log.Fatal(err) + } else { + w.WriteHeader(http.StatusOK) + } + w.Header().Set("Content-Type", "plain/text") + _, _ = fmt.Fprintf(w, lightValue) + }) + + port := "8080" + log.Infow("API server is running", "port", port) + log.Fatal(http.ListenAndServe(":"+port, nil)) +}