diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..5911f9e
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,34 @@
+{
+ "name": "devcontainer for add-on repositories",
+ "image": "ghcr.io/home-assistant/devcontainer:addons",
+ "appPort": ["7123:8123", "7357:4357"],
+ "postStartCommand": "bash devcontainer_bootstrap",
+ "postCreateCommand": "sudo apt-get update && sudo apt-get install -y libwebp-dev",
+ "runArgs": ["-e", "GIT_EDITOR=code --wait", "--privileged"],
+ "containerEnv": {
+ "WORKSPACE_DIRECTORY": "${containerWorkspaceFolder}"
+ },
+ "features": {
+ "ghcr.io/devcontainers/features/go:1": {
+ "version": "1.23.2"
+ }
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": ["timonwong.shellcheck", "esbenp.prettier-vscode", "golang.go", "github.vscode-github-actions"],
+ "settings": {
+ "terminal.integrated.profiles.linux": {
+ "zsh": {
+ "path": "/usr/bin/zsh"
+ }
+ },
+ "terminal.integrated.defaultProfile.linux": "zsh",
+ "editor.formatOnPaste": false,
+ "editor.formatOnSave": true,
+ "editor.formatOnType": true,
+ "files.trimTrailingWhitespace": true
+ }
+ }
+ },
+ "mounts": [ "type=volume,target=/var/lib/docker" ]
+ }
\ No newline at end of file
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
new file mode 100644
index 0000000..9eff3ca
--- /dev/null
+++ b/.github/workflows/go.yml
@@ -0,0 +1,34 @@
+name: Go
+
+on:
+ push:
+ branches: ["main"]
+ pull_request:
+ branches: ["main"]
+
+defaults:
+ run:
+ working-directory: ./TidbytAssistant
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Go
+ uses: actions/setup-go@v4
+ with:
+ go-version: "1.23.2"
+
+ - name: Install dependencies
+ run: sudo apt-get update && sudo apt-get -y install libweb-dev
+
+ - name: Lint
+ run: go vet ./...
+
+ - name: Build
+ run: go build -v ./...
+
+ - name: Test
+ run: go test -v ./...
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000..cd52231
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,19 @@
+name: hadolint
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+
+defaults:
+ run:
+ working-directory: ./TidbytAssistant
+
+jobs:
+ hadolint:
+ name: lint
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4.2.1
+ - uses: brpaz/hadolint-action@v1.5.0
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..36e373a
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,20 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Start Home Assistant",
+ "type": "shell",
+ "command": "supervisor_run",
+ "group": {
+ "kind": "test",
+ "isDefault": true
+ },
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
+ },
+ "problemMatcher": []
+ }
+ ]
+ }
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 70440d1..9724207 100644
--- a/README.md
+++ b/README.md
@@ -5,12 +5,14 @@ You can add this repository to your HomeAssistant instance by adding the URL bel
```txt
https://github.com/savdagod/ha-addons
```
+
## Add-ons provided by this repository
-
+
-A docker container that hosts the pixlet app as well as webhook to receive API request to display information on your Tidbyt. After installing this add-on, install the TidbytAssistant integration found here:
+A Docker container that receives API requests to display information on your Tidbyt.
+After installing this add-on, install the TidbytAssistant integration found here:
```txt
https://github.com/savdagod/TidbytAssistant/
diff --git a/TidbytAssistant/CHANGELOG.md b/TidbytAssistant/CHANGELOG.md
index 235f0d0..dba854a 100644
--- a/TidbytAssistant/CHANGELOG.md
+++ b/TidbytAssistant/CHANGELOG.md
@@ -1,10 +1,25 @@
# Changelog
+## 1.0.9
+
+- Reduced size of the addon by running a distroless container with a static Go server.
+
+## 1.0.8
+
+- Use command line arguments for text scripts instead of replacing strings.
+- Add Text & Title type for Text service.
+- Publish service now pushes to the background, preventing the current app from being replaced by the pushed app.
+- Push and Publish service now support key=value pair arguments.
+- Scripts clear tmp folder before rather than after commands.
+- Moved scripts to their own folder.
+
## 1.0.7
+
- Added option to publish from background or foreground.
- **NOTE: Be sure to update the integration to v1.0.5 to be able to use this feature!**
## 1.0.6
+
- Use command line arguments for text scripts instead of replacing strings.
- Add Text & Title type for Text service.
- Publish service now pushes to the background, preventing the current app from being replaced by the pushed app.
@@ -14,28 +29,34 @@
- Point pixlet build to forked repo to keep Pixlet version consistent.
- Add error handling to scripts, webhook responds with error which will be logged in HomeAssistant.
- **NOTE: Be sure to update the integration to v1.0.4 to take advantage of these new features!**
-
+
## 1.0.5
+
- Change Dockerfile to build pixlet binary.
- Edit scripts to move .star files to tmp directory to work around current bug in pixlet.
## 1.0.4
+
- Added libwep to image.
## 1.0.3
+
- Added full path for pixlet app to potentially fix script not finding pixlet app.
## 1.0.2
+
- Fix to Dockerfile for those building on arm64 architecture.
- Added new service TidbytAssistant: Delete, which allows you to delete apps using their content IDs.
- Be sure to download the most up to date TidbytAssistant integration (ver. 1.0.2)
## 1.0.1
+
- Added 2 new services: Publish and Text
- - Publish: Add apps to your rotation of apps
- - Text: Push custom text to your device. Supports the various available Tidbyt fonts and colors.
+ - Publish: Add apps to your rotation of apps
+ - Text: Push custom text to your device. Supports the various available Tidbyt fonts and colors.
- Be sure to download the most up to date TidbytAssistant integration. (ver. 1.0.1)
## 1.0.0
-- Initial release.
+
+- Initial release.
- Be sure to download the most up to date TidbytAssistant integration. (ver. 1.0.0)
diff --git a/TidbytAssistant/Dockerfile b/TidbytAssistant/Dockerfile
index 7b2fe5f..3cc6f9c 100644
--- a/TidbytAssistant/Dockerfile
+++ b/TidbytAssistant/Dockerfile
@@ -1,51 +1,35 @@
-ARG BUILD_FROM
+ARG BUILD_FROM=homeassistant/amd64-base:latest
-FROM $BUILD_FROM
+FROM ${BUILD_FROM} AS builder
# Args from build.yaml
-ARG GO_VERSION
+ARG GO_VERSION=1.23.2
-# Copy data for add-on
-COPY hooks.yaml /
-ADD scripts /opt/scripts
-RUN chmod a+x /opt/scripts/*
-ADD display /opt/display
+SHELL ["/bin/ash", "-eo", "pipefail", "-c"]
# Download Go and add to PATH
RUN arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) && \
- wget "https://go.dev/dl/go${GO_VERSION}.linux-${arch}.tar.gz"
+ wget -q "https://go.dev/dl/go${GO_VERSION}.linux-${arch}.tar.gz"
RUN arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) && \
- tar -xzf go${GO_VERSION}.linux-${arch}.tar.gz -C /usr/local && \
- rm go${GO_VERSION}.linux-${arch}.tar.gz
-ENV PATH=${PATH}:/usr/local/go/bin
-
-# Build Webhook binary using Go and move to PATH
-RUN go install github.com/adnanh/webhook@latest
-RUN mv /root/go/bin/webhook /usr/local/bin/webhook
+ tar -xzf "go${GO_VERSION}.linux-${arch}.tar.gz" -C /usr/local && \
+ rm "go${GO_VERSION}.linux-${arch}.tar.gz"
+ENV PATH=/usr/local/go/bin:${PATH}
+ENV GOROOT=/usr/local/go
# Download dependencies
-ENV GOPATH /usr/local/go
-ENV REPO $GOPATH/pixlet
-RUN apk update && \
- apk upgrade -U && \
- apk add curl wget git make libc-dev gcc ca-certificates npm libwebp-dev libwebp-tools patchelf gcompat && \
- rm -rf /var/cache/*
-
-# Download Pixlet
-RUN git clone https://github.com/savdagod/pixlet.git $REPO
-WORKDIR $REPO
-
-#Build Pixlet
-RUN npm install && npm run build
-RUN make build
+RUN apk --no-cache add libc-dev gcc ca-certificates libwebp-dev libwebp-static
-# Move pixlet binary to path
-RUN mv $GOPATH/pixlet/pixlet /usr/local/bin/pixlet
+# Build binary
+COPY *.go go.mod go.sum /src/
+WORKDIR /src
+RUN CGO_ENABLED=1 go build -ldflags "-s -w -linkmode=external '-extldflags=-static -lsharpyuv'" -o /tidbyt-assistant ./...
-# Clean up build prereqs
-RUN apk del -r curl wget git make gcc patchelf libc-dev
-RUN rm -r $GOPATH
-RUN rm -r /root/go
+FROM scratch
-CMD [ "webhook", "-hooks", "/hooks.yaml", "-verbose" ]
+COPY --from=builder /tidbyt-assistant /tidbyt-assistant
+#COPY --from=builder /usr/lib/libwebp*.so* /usr/lib/libsharpyuv.so* /usr/lib/
+COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
+COPY display /display
+ENTRYPOINT [ "/tidbyt-assistant" ]
+HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ "/tidbyt-assistant", "-health", "http://localhost:9000/health" ]
diff --git a/TidbytAssistant/build.yaml b/TidbytAssistant/build.yaml
index 0cb1fea..8b0ba01 100644
--- a/TidbytAssistant/build.yaml
+++ b/TidbytAssistant/build.yaml
@@ -1,2 +1,2 @@
args:
- GO_VERSION: "1.23.1"
+ GO_VERSION: "1.23.2"
diff --git a/TidbytAssistant/config.yaml b/TidbytAssistant/config.yaml
index ad88c4a..bb9e645 100644
--- a/TidbytAssistant/config.yaml
+++ b/TidbytAssistant/config.yaml
@@ -1,6 +1,6 @@
name: "TidbytAssistant"
description: "Add-on with Pixlet application. Allows you to push custom apps to your Tidbyt. Install with integration v1.0.5"
-version: "1.0.7"
+version: "1.0.9"
slug: "tidbytassistant"
url: "https://github.com/savdagod/ha-addons/tree/main/TidbytAssistant"
init: False
diff --git a/TidbytAssistant/hooks.yaml b/TidbytAssistant/hooks.yaml
deleted file mode 100644
index c9fb6ef..0000000
--- a/TidbytAssistant/hooks.yaml
+++ /dev/null
@@ -1,70 +0,0 @@
-- id: tidbyt-push
- execute-command: "/opt/scripts/TidbytPush.sh"
- command-working-directory: "/opt"
- include-command-output-in-response: true
- include-command-output-in-response-on-error: true
- pass-arguments-to-command:
- - source: "payload"
- name: "content"
- - source: "payload"
- name: "deviceid"
- - source: "payload"
- name: "token"
- - source: "payload"
- name: "contenttype"
- - source: "payload"
- name: "starargs"
-- id: tidbyt-publish
- execute-command: "/opt/scripts/TidbytPublish.sh"
- command-working-directory: "/opt"
- include-command-output-in-response: true
- include-command-output-in-response-on-error: true
- pass-arguments-to-command:
- - source: "payload"
- name: "content"
- - source: "payload"
- name: "deviceid"
- - source: "payload"
- name: "token"
- - source: "payload"
- name: "contentid"
- - source: "payload"
- name: "publishtype"
- - source: "payload"
- name: "starargs"
-- id: tidbyt-text
- execute-command: "/opt/scripts/TidbytText.sh"
- command-working-directory: "/opt"
- include-command-output-in-response: true
- include-command-output-in-response-on-error: true
- pass-arguments-to-command:
- - source: "payload"
- name: "content"
- - source: "payload"
- name: "deviceid"
- - source: "payload"
- name: "token"
- - source: "payload"
- name: "texttype"
- - source: "payload"
- name: "font"
- - source: "payload"
- name: "color"
- - source: "payload"
- name: "title"
- - source: "payload"
- name: "titlecolor"
- - source: "payload"
- name: "titlefont"
-- id: tidbyt-delete
- execute-command: "/opt/scripts/TidbytDelete.sh"
- command-working-directory: "/opt"
- include-command-output-in-response: true
- include-command-output-in-response-on-error: true
- pass-arguments-to-command:
- - source: "payload"
- name: "contentid"
- - source: "payload"
- name: "deviceid"
- - source: "payload"
- name: "token"
diff --git a/TidbytAssistant/main.go b/TidbytAssistant/main.go
new file mode 100644
index 0000000..7691dfc
--- /dev/null
+++ b/TidbytAssistant/main.go
@@ -0,0 +1,367 @@
+package main
+
+import (
+ "bytes"
+ "context"
+ "encoding/base64"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io"
+ "log/slog"
+ "net/http"
+ "net/url"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "tidbyt.dev/pixlet/encode"
+ "tidbyt.dev/pixlet/runtime"
+)
+
+var (
+ cache = runtime.NewInMemoryCache()
+ healthURL = flag.String("health", "", "perform health check for the given URL and exit")
+)
+
+const (
+ tidbytBaseURL = "https://api.tidbyt.com"
+ silenceOutput = false
+ renderGif = false
+ timeout = 30 * time.Second
+ maxDuration = 15 * time.Second
+)
+
+type (
+ pushRequest struct {
+ Content string `json:"content"`
+ DeviceID string `json:"deviceid"`
+ Token string `json:"token"`
+ ContentType string `json:"contenttype"`
+ Arguments []string `json:"starargs"`
+ }
+
+ publishRequest struct {
+ Content string `json:"content"`
+ DeviceID string `json:"deviceid"`
+ Token string `json:"token"`
+ InstallationID string `json:"contentid"`
+ PublishType string `json:"publishtype"`
+ Arguments []string `json:"starargs"`
+ }
+
+ textRequest struct {
+ Content string `json:"content"`
+ DeviceID string `json:"deviceid"`
+ Token string `json:"token"`
+ TextType string `json:"texttype"`
+ Font string `json:"font"`
+ Color string `json:"color"`
+ Title string `json:"title"`
+ TitleColor string `json:"titlecolor"`
+ TitleFont string `json:"titlefont"`
+ }
+
+ deleteRequest struct {
+ InstallationID string `json:"contentid"`
+ DeviceID string `json:"deviceid"`
+ Token string `json:"token"`
+ }
+
+ tidbytPushRequest struct {
+ Image string `json:"image"`
+ InstallationID string `json:"installationID,omitempty"`
+ Background bool `json:"background"`
+ }
+)
+
+func pushHandler(w http.ResponseWriter, req *http.Request) {
+ var r pushRequest
+
+ if err := json.NewDecoder(req.Body).Decode(&r); err != nil {
+ http.Error(w, fmt.Sprintf("failed to decode push request: %v", err), http.StatusBadRequest)
+ return
+ }
+
+ slog.Debug(fmt.Sprintf("Received push request %+v", r))
+
+ var rootDir string
+ switch r.ContentType {
+ case "builtin":
+ rootDir = "/display"
+ case "custom":
+ rootDir = "/homeassistant/tidbyt"
+ default:
+ http.Error(w, fmt.Sprintf("unknown content type %q", r.ContentType), http.StatusBadRequest)
+ }
+
+ config, err := parseArguments(r.Arguments)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ path := filepath.Join(rootDir, r.Content+".star")
+ if err := renderAndPush(path, config, r.DeviceID, "", r.Token, false); err != nil {
+ slog.Error(err.Error())
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+}
+
+func publishHandler(w http.ResponseWriter, req *http.Request) {
+ var r publishRequest
+
+ if err := json.NewDecoder(req.Body).Decode(&r); err != nil {
+ http.Error(w, fmt.Sprintf("failed to decode publish request: %v", err), http.StatusBadRequest)
+ return
+ }
+
+ slog.Debug(fmt.Sprintf("Received publish request %+v", r))
+
+ config, err := parseArguments(r.Arguments)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ path := filepath.Join("/homeassistant/tidbyt", r.Content+".star")
+ background := r.PublishType == "background"
+
+ if err := renderAndPush(path, config, r.DeviceID, r.InstallationID, r.Token, background); err != nil {
+ slog.Error(err.Error())
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+}
+
+func textHandler(w http.ResponseWriter, req *http.Request) {
+ var r textRequest
+
+ if err := json.NewDecoder(req.Body).Decode(&r); err != nil {
+ http.Error(w, fmt.Sprintf("failed to decode text request: %v", err), http.StatusBadRequest)
+ return
+ }
+
+ slog.Debug(fmt.Sprintf("Received text request %+v", r))
+
+ if r.TextType == "" {
+ http.Error(w, "missing \"texttype\"", http.StatusBadRequest)
+ return
+ }
+
+ path := filepath.Join("/display", fmt.Sprintf("text-%s.star", r.TextType))
+ config := map[string]string{
+ "content": r.Content,
+ "font": r.Font,
+ "color": r.Color,
+ }
+ if r.TextType == "title" {
+ config["title"] = r.Title
+ config["titlecolor"] = r.TitleColor
+ config["titlefont"] = r.TitleFont
+ }
+
+ if err := renderAndPush(path, config, r.DeviceID, "", r.Token, false); err != nil {
+ slog.Error(err.Error())
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+}
+
+func deleteHandler(w http.ResponseWriter, req *http.Request) {
+ var r deleteRequest
+
+ if err := json.NewDecoder(req.Body).Decode(&r); err != nil {
+ http.Error(w, fmt.Sprintf("failed to decode delete request: %v", err), http.StatusBadRequest)
+ return
+ }
+
+ slog.Debug(fmt.Sprintf("Received delete request %+v", r))
+
+ u := fmt.Sprintf(
+ "%s/v0/devices/%s/installations/%s",
+ tidbytBaseURL,
+ r.DeviceID,
+ r.InstallationID,
+ )
+ if err := tidbytAPI(u, "DELETE", nil, r.Token); err != nil {
+ slog.Error(err.Error())
+ http.Error(w, fmt.Sprintf("failed to delete: %v", err), http.StatusInternalServerError)
+ return
+ }
+}
+
+func healthHandler(w http.ResponseWriter, req *http.Request) {
+ w.WriteHeader(http.StatusOK)
+}
+
+func renderAndPush(path string, arguments map[string]string, deviceID, installationID, token string, background bool) error {
+ image, err := renderApp(path, arguments)
+ if err != nil {
+ return fmt.Errorf("failed to render app: %v", err)
+ }
+
+ if err := tidbytPush(image, deviceID, installationID, token, background); err != nil {
+ return fmt.Errorf("failed to push image: %v", err)
+ }
+
+ slog.Info(fmt.Sprintf("Pushed %v", path))
+
+ return nil
+}
+
+func tidbytPush(imageData []byte, deviceID, installationID, apiToken string, background bool) error {
+ payload, err := json.Marshal(
+ tidbytPushRequest{
+ Image: base64.StdEncoding.EncodeToString(imageData),
+ InstallationID: installationID,
+ Background: background,
+ },
+ )
+ if err != nil {
+ return fmt.Errorf("failed to marshal json: %w", err)
+ }
+ u := fmt.Sprintf("%s/v0/devices/%s/push", tidbytBaseURL, url.PathEscape(deviceID))
+ if err := tidbytAPI(u, "POST", payload, apiToken); err != nil {
+ return fmt.Errorf("failed to push image: %w", err)
+ }
+ return nil
+}
+
+func tidbytAPI(u, method string, payload []byte, apiToken string) error {
+ req, err := http.NewRequest(method, u, bytes.NewReader(payload))
+ if err != nil {
+ return fmt.Errorf("creating %v request: %w", method, err)
+ }
+
+ req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", apiToken))
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return fmt.Errorf("pushing to API: %w", err)
+ }
+
+ if resp.StatusCode != http.StatusOK {
+ slog.Error(fmt.Sprintf("Tidbyt API returned status %s\n", resp.Status))
+ body, _ := io.ReadAll(resp.Body)
+ slog.Error(string(body))
+ return fmt.Errorf("Tidbyt API returned status: %s", resp.Status)
+ }
+
+ return nil
+}
+
+func parseArguments(args []string) (map[string]string, error) {
+ config := map[string]string{}
+ for _, param := range args {
+ split := strings.Split(param, "=")
+ if len(split) < 2 {
+ return nil, fmt.Errorf("parameters must be in form =, found %s", param)
+ }
+ config[split[0]] = strings.Join(split[1:], "=")
+ }
+
+ return config, nil
+}
+
+func renderApp(path string, config map[string]string) ([]byte, error) {
+ // check if path exists
+ _, err := os.Stat(path)
+ if err != nil {
+ return nil, fmt.Errorf("failed to stat %s: %w", path, err)
+ }
+
+ // Remove the print function from the starlark thread if the silent flag is
+ // passed.
+ var opts []runtime.AppletOption
+ if silenceOutput {
+ opts = append(opts, runtime.WithPrintDisabled())
+ }
+
+ ctx := context.Background()
+ if timeout > 0 {
+ ctx, _ = context.WithTimeoutCause(
+ ctx,
+ timeout,
+ fmt.Errorf("timeout after %v", timeout),
+ )
+ }
+
+ srcBytes, err := os.ReadFile(path)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read %s: %w", path, err)
+ }
+
+ applet, err := runtime.NewApplet(filepath.Base(path), srcBytes, opts...)
+ if err != nil {
+ return nil, fmt.Errorf("failed to load applet: %w", err)
+ }
+
+ roots, err := applet.RunWithConfig(ctx, config)
+ if err != nil {
+ return nil, fmt.Errorf("error running script: %w", err)
+ }
+ screens := encode.ScreensFromRoots(roots)
+
+ var buf []byte
+
+ duration := maxDuration
+ if screens.ShowFullAnimation {
+ duration = 0 * time.Millisecond
+ }
+
+ if renderGif {
+ buf, err = screens.EncodeGIF(int(duration.Milliseconds()))
+ } else {
+ buf, err = screens.EncodeWebP(int(duration.Milliseconds()))
+ }
+ if err != nil {
+ return nil, fmt.Errorf("error rendering: %w", err)
+ }
+
+ return buf, nil
+}
+
+func checkHealth(url string) error {
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ return fmt.Errorf("creating GET request: %w", err)
+ }
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return err
+ }
+
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("unhealthy status: %d", resp.StatusCode)
+ }
+
+ return nil
+}
+
+func main() {
+ flag.Parse()
+
+ if *healthURL != "" {
+ if err := checkHealth(*healthURL); err != nil {
+ slog.Error(fmt.Sprintf("Health check failed: %v", err))
+ os.Exit(1)
+ }
+ os.Exit(0)
+ }
+
+ runtime.InitHTTP(cache)
+ runtime.InitCache(cache)
+
+ http.HandleFunc("POST /tidbyt-push", pushHandler)
+ http.HandleFunc("POST /tidbyt-publish", publishHandler)
+ http.HandleFunc("POST /tidbyt-text", textHandler)
+ http.HandleFunc("POST /tidbyt-delete", deleteHandler)
+ http.HandleFunc("GET /health", healthHandler)
+
+ slog.Info("Starting TidbytAssistant server")
+ if err := http.ListenAndServe(":9000", nil); err != nil {
+ slog.Error(fmt.Sprintf("Failed to start server: %v", err))
+ }
+}
diff --git a/TidbytAssistant/scripts/TidbytDelete.sh b/TidbytAssistant/scripts/TidbytDelete.sh
deleted file mode 100644
index 576fa08..0000000
--- a/TidbytAssistant/scripts/TidbytDelete.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-set -e
-
-# These will be sent directly from HomeAssistant
-CONTENT_ID=${1:?"missing arg 1 for CONTENT_ID"}
-TB_DEVICEID=${2:?"missing arg 2 for DEVICE ID"}
-TB_TOKEN=${3:?"missing arg 3 for TOKEN"}
-
-/usr/local/bin/pixlet delete $TB_DEVICEID $CONTENT_ID --api-token $TB_TOKEN
-
-exit 0
diff --git a/TidbytAssistant/scripts/TidbytPublish.sh b/TidbytAssistant/scripts/TidbytPublish.sh
deleted file mode 100644
index 0f78883..0000000
--- a/TidbytAssistant/scripts/TidbytPublish.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-set -e
-
-# These will be sent directly from HomeAssistant
-CONTENT=${1:?"missing arg 1 for CONTENT"}
-TB_DEVICEID=${2:?"missing arg 2 for DEVICE ID"}
-TB_TOKEN=${3:?"missing arg 3 for TOKEN"}
-CONTENT_ID=${4:?"missing arg 4 for CONTENT_ID"}
-PUBLISH_TYPE=${5:?"missing arg 5 for PUBLISH_TYPE"}
-ARGS=${6:-}
-
-rm -f /tmp/*
-
-ROOT_DIR=/homeassistant/tidbyt
-RENDER_PATH=/tmp/render.webp
-
-cp $ROOT_DIR/$CONTENT.star /tmp/$CONTENT.star -f
-if [[ $ARGS ]]; then
- arg=()
- IFS=';' read -ra pairs <<< "$ARGS"
- for pair in "${pairs[@]}"; do
- IFS='=' read -r key value <<< "$pair"
- arg+=("$key=$value")
- done
- /usr/local/bin/pixlet render /tmp/$CONTENT.star "${arg[@]}" -o $RENDER_PATH
-else
- /usr/local/bin/pixlet render /tmp/$CONTENT.star -o $RENDER_PATH
-fi
-
-if [[ $PUBLISH_TYPE == 'background' ]]; then
- /usr/local/bin/pixlet push --installation-id $CONTENT_ID --background --api-token $TB_TOKEN $TB_DEVICEID $RENDER_PATH
-else
- /usr/local/bin/pixlet push --installation-id $CONTENT_ID --api-token $TB_TOKEN $TB_DEVICEID $RENDER_PATH
-fi
-
-exit 0
diff --git a/TidbytAssistant/scripts/TidbytPush.sh b/TidbytAssistant/scripts/TidbytPush.sh
deleted file mode 100644
index ba0341c..0000000
--- a/TidbytAssistant/scripts/TidbytPush.sh
+++ /dev/null
@@ -1,37 +0,0 @@
-#!/bin/bash
-set -e
-
-# These will be sent directly from HomeAssistant
-CONTENT=${1:?"missing arg 1 for CONTENT"}
-TB_DEVICEID=${2:?"missing arg 2 for DEVICE ID"}
-TB_TOKEN=${3:?"missing arg 3 for TOKEN"}
-CONTENT_TYPE=${4:?"missing arg 4 for CONTENT_TYPE"}
-ARGS=${5:-}
-
-rm -f /tmp/*
-
-case "$CONTENT_TYPE" in
- "builtin")
- ROOT_DIR=/opt/display ;;
- "custom")
- ROOT_DIR=/homeassistant/tidbyt ;;
-esac
-
-RENDER_PATH=/tmp/render.webp
-
-cp $ROOT_DIR/$CONTENT.star /tmp/$CONTENT.star -f
-if [[ $ARGS ]]; then
- arg=()
- IFS=';' read -ra pairs <<< "$ARGS"
- for pair in "${pairs[@]}"; do
- IFS='=' read -r key value <<< "$pair"
- arg+=("$key=$value")
- done
- /usr/local/bin/pixlet render /tmp/$CONTENT.star "${arg[@]}" -o $RENDER_PATH
-else
- /usr/local/bin/pixlet render /tmp/$CONTENT.star -o $RENDER_PATH
-fi
-
-/usr/local/bin/pixlet push --api-token $TB_TOKEN $TB_DEVICEID $RENDER_PATH
-
-exit 0
diff --git a/TidbytAssistant/scripts/TidbytText.sh b/TidbytAssistant/scripts/TidbytText.sh
deleted file mode 100644
index 5704457..0000000
--- a/TidbytAssistant/scripts/TidbytText.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-set -e
-
-CONTENT=${1:?"missing arg 1 for CONTENT"}
-TB_DEVICEID=${2:?"missing arg 2 for DEVICE ID"}
-TB_TOKEN=${3:?"missing arg 3 for TOKEN"}
-TEXT_TYPE=${4:?"missing arg 4 for TEXT_TYPE"}
-FONT=${5:?"missing arg 5 for FONT"}
-COLOR=${6:?"missing arg 6 for COLOR"}
-TITLE=${7:-}
-TITLE_COLOR=${8:-}
-TITLE_FONT=${9:-}
-
-rm -f /tmp/*
-
-ROOT_DIR=/tmp
-FILE=text-$TEXT_TYPE
-
-cp /opt/display/$FILE.star $ROOT_DIR/$FILE.star -f
-
-RENDER_PATH=/tmp/render.webp
-
-if [[ "$TEXT_TYPE" == "title" ]]; then
- /usr/local/bin/pixlet render $ROOT_DIR/$FILE.star content="$CONTENT" font="$FONT" color="$COLOR" title="$TITLE" titlecolor="$TITLE_COLOR" titlefont="$TITLE_FONT" -o $RENDER_PATH
-else
- /usr/local/bin/pixlet render $ROOT_DIR/$FILE.star content="$CONTENT" font="$FONT" color="$COLOR" -o $RENDER_PATH
-fi
-
-/usr/local/bin/pixlet push --api-token $TB_TOKEN $TB_DEVICEID $RENDER_PATH
-
-exit 0