Skip to content

Commit

Permalink
Support multi-file app directories
Browse files Browse the repository at this point in the history
Pixlet supports apps consisting of multiple Starlark files since v0.32.5
(tidbyt/pixlet#1061).

Move each built-in app into a separate directory so that `pixlet render`
can be used to render them.

This could be used to clone
https://github.com/tidbyt/community/tree/main/apps into
/homeassistant/tidbyt and use most community apps.
  • Loading branch information
IngmarStein committed Oct 26, 2024
1 parent 406fea4 commit 09120a4
Show file tree
Hide file tree
Showing 21 changed files with 62 additions and 66 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
working-directory: ./pixlet

- name: Run pixlet lint
run: ./pixlet/pixlet lint --recursive ./TidbytAssistant
run: ./pixlet/pixlet lint --recursive ./TidbytAssistant/display

- name: Run pixlet format
run: ./pixlet/pixlet format --recursive --dry-run ./TidbytAssistant
run: ./pixlet/pixlet format --recursive --dry-run ./TidbytAssistant/display
6 changes: 4 additions & 2 deletions TidbytAssistant/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Changelog

## 1.0.13
- Support pblishing built-in content. Added new content for publishing.
- **NOTE: Be sure to update the integration to v1.0.11**

- Support publishing built-in content. Added new content for publishing.
- **NOTE: Be sure to update the integration to v1.0.11**

## 1.0.12

- Added config to main in built-in files to configure language from Push service.

## 1.0.11
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
118 changes: 56 additions & 62 deletions TidbytAssistant/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
Expand All @@ -25,6 +26,9 @@ var (
cache = runtime.NewInMemoryCache()
healthURL = flag.String("health", "", "perform health check for the given URL and exit")
appCache = map[string]*runtime.Applet{}

errUnknownContentType = errors.New("unknown content type")
errInvalidFileName = errors.New("invalid file name")
)

const (
Expand Down Expand Up @@ -87,28 +91,8 @@ func pushHandler(w http.ResponseWriter, req *http.Request) {

slog.Debug(fmt.Sprintf("Received push request %+v", r))

var rootDir string
cache := false
switch r.ContentType {
case "builtin":
rootDir = "/display"
cache = true
case "custom":
rootDir = "/homeassistant/tidbyt"
default:
http.Error(w, fmt.Sprintf("unknown content type %q", r.ContentType), http.StatusBadRequest)
}

if !validatePath(r.Content) {
http.Error(w, "Invalid file name", http.StatusBadRequest)
return
}

path := filepath.Join(rootDir, r.Content+".star")
if err := renderAndPush(path, r.Arguments, cache, r.DeviceID, "", r.Token, false); err != nil {
slog.Error(err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
if err := pushApp(r.ContentType, r.Content, r.Arguments, r.DeviceID, "", r.Token, false); err != nil {
handleHTTPError(w, err)
}
}

Expand All @@ -121,30 +105,10 @@ func publishHandler(w http.ResponseWriter, req *http.Request) {
}

slog.Debug(fmt.Sprintf("Received publish request %+v", r))

var rootDir string
cache := false
switch r.ContentType {
case "builtin":
rootDir = "/display"
cache = true
case "custom":
rootDir = "/homeassistant/tidbyt"
default:
http.Error(w, fmt.Sprintf("unknown content type %q", r.ContentType), http.StatusBadRequest)
}

if !validatePath(r.Content) {
http.Error(w, "Invalid file name", http.StatusBadRequest)
return
}

path := filepath.Join(rootDir, r.Content+".star")
background := r.PublishType == "background"

if err := renderAndPush(path, r.Arguments, cache, r.DeviceID, r.InstallationID, r.Token, background); err != nil {
slog.Error(err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
if err := pushApp(r.ContentType, r.Content, r.Arguments, r.DeviceID, r.InstallationID, r.Token, background); err != nil {
handleHTTPError(w, err)
}
}

Expand All @@ -163,12 +127,7 @@ func textHandler(w http.ResponseWriter, req *http.Request) {
return
}

if !validatePath(r.TextType) {
http.Error(w, "Invalid file name", http.StatusBadRequest)
return
}

path := filepath.Join("/display", fmt.Sprintf("text-%s.star", r.TextType))
contentName := fmt.Sprintf("text-%s", r.TextType)
config := map[string]string{
"content": r.Content,
"font": r.Font,
Expand All @@ -180,17 +139,42 @@ func textHandler(w http.ResponseWriter, req *http.Request) {
config["titlefont"] = r.TitleFont
}

if err := renderAndPush(path, config, true, r.DeviceID, "", r.Token, false); err != nil {
slog.Error(err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
if err := pushApp("builtin", contentName, config, r.DeviceID, "", r.Token, false); err != nil {
handleHTTPError(w, err)
}
}

func healthHandler(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
}

func renderAndPush(path string, arguments map[string]string, cache bool, deviceID, installationID, token string, background bool) error {
func handleHTTPError(w http.ResponseWriter, err error) {
status := http.StatusInternalServerError
if errors.Is(err, errInvalidFileName) || errors.Is(err, errUnknownContentType) {
status = http.StatusBadRequest
}
slog.Error(err.Error())
http.Error(w, err.Error(), status)
}

func pushApp(contentType, contentName string, arguments map[string]string, deviceID, installationID, token string, background bool) error {
var rootDir string
cache := false
switch contentType {
case "builtin":
rootDir = "/display"
cache = true
case "custom":
rootDir = "/homeassistant/tidbyt"
default:
return fmt.Errorf("%w: %q", errUnknownContentType, contentType)
}

if !validatePath(contentName) {
return errInvalidFileName
}

path := filepath.Join(rootDir, contentName)
image, err := renderApp(path, arguments, cache)
if err != nil {
return fmt.Errorf("failed to render app: %v", err)
Expand Down Expand Up @@ -240,7 +224,7 @@ func tidbytAPI(u, method string, payload []byte, apiToken string) error {
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 fmt.Errorf("tidbyt API returned status: %s", resp.Status)
}

return nil
Expand All @@ -251,9 +235,13 @@ func renderApp(path string, config map[string]string, cache bool) ([]byte, error

if applet == nil {
// check if path exists
_, err := os.Stat(path)
info, err := os.Stat(path)
if err != nil {
return nil, fmt.Errorf("failed to stat %s: %w", path, err)
// legacy: try a single file with ".star" appended
info, err = os.Stat(path + ".star")
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
Expand All @@ -263,12 +251,18 @@ func renderApp(path string, config map[string]string, cache bool) ([]byte, error
opts = append(opts, runtime.WithPrintDisabled())
}

srcBytes, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read %s: %w", path, err)
if info.IsDir() {
fs := os.DirFS(path)
applet, err = runtime.NewAppletFromFS(filepath.Base(path), fs, opts...)
} else {
var srcBytes []byte
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...)
}

applet, err = runtime.NewApplet(filepath.Base(path), srcBytes, opts...)
if err != nil {
return nil, fmt.Errorf("failed to load applet: %w", err)
}
Expand Down

0 comments on commit 09120a4

Please sign in to comment.