Skip to content

Commit

Permalink
devel:
Browse files Browse the repository at this point in the history
* Add flags
* Setup htmx
* Rename files and functions
  • Loading branch information
zaggash committed Nov 22, 2023
1 parent a29211a commit 668c1d6
Show file tree
Hide file tree
Showing 20 changed files with 345 additions and 126 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## Led-Matrix-UI

WebUI to manage HUB75 LED Matrix Panels with the hzeller/rpi-rgb-led-matrix library

Build with:
* Go-Gin
* Bootstrap
* htmx

## Usage

Run as root to get access to the /dev GPIO
Configure it through the app flags

## Build

TODO


__Please feel free to report any issues or suggestions__
44 changes: 22 additions & 22 deletions routes/run.go → api/api.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,40 @@
package routes
package api

import (
"io/fs"
"net/http"

"github.com/gin-gonic/gin"
"github.com/go-errors/errors"
"github.com/zaggash/led-matrix-ui/handlers"
"github.com/zaggash/led-matrix-ui/webui"
)

type HttpResponse struct {
type httpResponse struct {
Message string
Status int
Description string
}

func ErrorHandler(c *gin.Context, err any) {
goErr := errors.Wrap(err, 2)
httpResponse := HttpResponse{Message: "Internal server error", Status: 500, Description: goErr.Error()}
c.AbortWithStatusJSON(500, httpResponse)
}

var pixelFolder = "./PixelImages"

func Run() {
func (d *Display) Run(pixelFolder string) {
// Set the router as the default one shipped with Gin
router := gin.Default()
//gin.SetMode(gin.ReleaseMode) // Set to Production Mode !
//gin.SetMode(gin.ReleaseMode) // TODO : Set to Production Mode !

// Set Middleware
router.Use(gin.CustomRecovery(ErrorHandler))
router.Use(gin.CustomRecovery(errorHandler))

// Set MaxFile size to 8Mb
router.MaxMultipartMemory = 8 << 20

// Set Webui
var webStatic = "/public/"
var webPixels = pixelFolder
//// Enable subdir from embedFS assets to /public
subAssets, _ := fs.Sub(webui.EmbedAssets, "assets")
router.StaticFS("/public/", http.FS(subAssets))
router.StaticFS(webStatic, http.FS(subAssets))

//// Enable Pixel Images folder to /pixels
router.Static(pixelFolder, pixelFolder)
router.Static(webPixels, pixelFolder)
router.HTMLRender = webui.LoadTemplates(webui.EmbedTemplates)

// Define favicon as favicon.ico and use it in html templates
Expand All @@ -59,13 +53,19 @@ func Run() {
// Setup route group for the API
api := router.Group("/api")
{
api.GET("/ping", handlers.Healthcheck)
//api.POST("/upload", handlers.UploadFile)
//api.POST("/display", handlers.DisplayImage)
//api.POST("/settings", handlers.GetSettings)
api.GET("/images", handlers.ListImages)
api.GET("/images/:mime", handlers.ListImages)
api.GET("/ping", healthcheck)
//api.POST("/upload", UploadFile)
api.POST("/draw", drawImage(d, pixelFolder))
//api.POST("/settings", GetSettings)
api.GET("/images", listImages(pixelFolder))
api.GET("/images/:mime", listImages(pixelFolder))
}
// Start and run the server
router.Run(":3000")
}

func errorHandler(c *gin.Context, err any) {
goErr := errors.Wrap(err, 2)
httpResponse := httpResponse{Message: "Internal server error", Status: 500, Description: goErr.Error()}
c.AbortWithStatusJSON(500, httpResponse)
}
48 changes: 48 additions & 0 deletions api/draw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package api

import (
"image"
"image/draw"
"log"
"os"

"github.com/disintegration/imaging"
"github.com/gin-gonic/gin"
"github.com/zaggash/led-matrix-ui/display"
)

type Display struct {
*display.Config
}

func New(c *display.Config) *Display {
return &Display{c}
}

func drawImage(d *Display, p string) gin.HandlerFunc {
return gin.HandlerFunc(func(ctx *gin.Context) {

imgPath := ctx.PostForm("Path")

f, err := os.Open(imgPath)
if err != nil {
log.Fatalln(err)
}
defer f.Close()

img, _, err := image.Decode(f)
if err != nil {
log.Fatalln(err)
}

w, h := d.Matrix.Geometry()
d.Toolkit.Canvas.Clear() // Clear current display
img = imaging.Fill(img, w, h, imaging.Center, imaging.Lanczos)
draw.Draw(d.Toolkit.Canvas, d.Toolkit.Canvas.Bounds(), img, image.ZP, draw.Over)
d.Matrix.Render()

// DEBUG: Print POST body request
// body, _ := io.ReadAll(ctx.Request.Body)
// println(string(body))
})
}
4 changes: 2 additions & 2 deletions handlers/ping.go → api/healthcheck.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package handlers
package api

import (
"net/http"

"github.com/gin-gonic/gin"
)

func Healthcheck(ctx *gin.Context) {
func healthcheck(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"message": "pong",
})
Expand Down
66 changes: 66 additions & 0 deletions api/images.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package api

import (
"io/fs"
"log"
"net/http"
"os"
"path/filepath"
"strings"

"github.com/gabriel-vasile/mimetype"
"github.com/gin-gonic/gin"
)

type localImage struct {
Name string
Path string
Size int64 // Size in bytes
MimeType string
}

func listImages(rootFolder string) gin.HandlerFunc {
return gin.HandlerFunc(func(ctx *gin.Context) {
whitelistedMime := []string{""}
requestedMime := strings.ToLower(ctx.Param("mime"))
var images []localImage

err := filepath.WalkDir(rootFolder, func(filename string, file fs.DirEntry, err error) error {
if !file.IsDir() {
switch requestedMime {
case "":
whitelistedMime = []string{"image/png", "image/gif", "image/jpeg"}
case "png":
whitelistedMime = []string{"image/png"}
case "gif":
whitelistedMime = []string{"image/gif"}
}

fileMime, err := mimetype.DetectFile(filename)
if err != nil {
log.Println(err)
}

if mimetype.EqualsAny(fileMime.String(), whitelistedMime...) {
info, err := os.Stat(filename)
if err != nil {
log.Println(err)
}
images = append(images, localImage{
Name: info.Name(),
Path: filename,
Size: info.Size(),
MimeType: fileMime.String(),
})
}
}
return nil
})

if err != nil {
log.Println(err)
}
ctx.JSON(http.StatusOK, images)

})
}
9 changes: 9 additions & 0 deletions api/upload.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package api

import (
"github.com/gin-gonic/gin"
)

func uploadFile(ctx *gin.Context) {

}
63 changes: 63 additions & 0 deletions display/display.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package display

import (
"flag"

rgbmatrix "github.com/zaggash/go-rpi-rgb-led-matrix"
)

var (
rows = flag.Int("led-rows", 32, "number of rows supported")
cols = flag.Int("led-cols", 32, "number of columns supported")
parallel = flag.Int("led-parallel", 1, "number of daisy-chained panels")
chainLength = flag.Int("led-chain", 1, "number of displays daisy-chained")
pwm_bits = flag.Int("pwm-bits", 11, "set PWM bits used for output")
pwm_lsb_nanoseconds = flag.Int("pwm-lsb-nanoseconds", 130, "set base time-unit for the on-time in the lowest significant bit")
brightness = flag.Int("brightness", 80, "brightness (0-100)")
pixelMapperConfig = flag.String("pixel-mapper-config", "", "semicolon-separated list of pixel-mappers to arrange pixels")
hardware_mapping = flag.String("led-gpio-mapping", "adafruit-hat-pwm", "name of GPIO mapping used.")
show_refresh = flag.Bool("led-show-refresh", false, "show refresh rate.")
inverse_colors = flag.Bool("led-inverse", false, "switch if your matrix has inverse colors on")
disable_hardware_pulsing = flag.Bool("led-no-hardware-pulse", false, "don't use hardware pin-pulse generation")
)

type Config struct {
Matrix rgbmatrix.Matrix
Toolkit *rgbmatrix.ToolKit
Hardware rgbmatrix.HardwareConfig
}

func New() (*Config, error) {
c := Config{}

hwConfig := &rgbmatrix.DefaultConfig
hwConfig.Rows = *rows
hwConfig.Cols = *cols
hwConfig.Parallel = *parallel
hwConfig.ChainLength = *chainLength
hwConfig.PWMBits = *pwm_bits
hwConfig.PWMLSBNanoseconds = *pwm_lsb_nanoseconds
hwConfig.Brightness = *brightness
hwConfig.PixelMapperConfig = *pixelMapperConfig
hwConfig.HardwareMapping = *hardware_mapping
hwConfig.ShowRefreshRate = *show_refresh
hwConfig.InverseColors = *inverse_colors
hwConfig.DisableHardwarePulsing = *disable_hardware_pulsing

c.Hardware = *hwConfig

// create a new Matrix instance with the DefaultConfig
matrix, err := rgbmatrix.NewRGBLedMatrix(hwConfig)
c.Matrix = matrix
if err != nil {
return &c, err
}

// create a ToolKit instance
toolkit := rgbmatrix.NewToolKit(matrix)
c.Toolkit = toolkit

// clear canvas
err = toolkit.Canvas.Clear()
return &c, err
}
17 changes: 16 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
module github.com/zaggash/led-matrix-ui

go 1.20
go 1.21

toolchain go1.21.4

replace github.com/zaggash/led-matrix-ui => ./

replace github.com/zaggash/go-rpi-rgb-led-matrix => ../go-rpi-rgb-led-matrix/

require (
github.com/disintegration/imaging v1.6.2
github.com/gabriel-vasile/mimetype v1.4.2
github.com/gin-contrib/multitemplate v0.0.0-20230212012517-45920c92c271
github.com/gin-gonic/gin v1.9.1
github.com/go-errors/errors v1.4.2
github.com/zaggash/go-rpi-rgb-led-matrix v0.0.0-00010101000000-000000000000
)

require (
dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037 // indirect
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210311203641-62640a716d48 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
Expand All @@ -23,13 +32,19 @@ require (
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nsf/termbox-go v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/tfk1410/go-rpi-rgb-led-matrix v0.0.0-20210404121211-ed43f29cbccb // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.10.0 // indirect
golang.org/x/exp v0.0.0-20210220032938-85be41e4509f // indirect
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb // indirect
golang.org/x/mobile v0.0.0-20210220033013-bdb1ca9a1e08 // indirect
golang.org/x/net v0.11.0 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
Expand Down
Loading

0 comments on commit 668c1d6

Please sign in to comment.