From 55e8aa6dc18677f0c0a0bfb58837d1dccd160922 Mon Sep 17 00:00:00 2001 From: Justin Hawkins Date: Sat, 29 Jun 2024 15:50:28 +0930 Subject: [PATCH] First worked example --- spinclock/.gitignore | 3 + spinclock/README.md | 33 +++++ .../manifest.json | 43 +++++++ .../spinclock@1x.png | Bin 0 -> 8987 bytes .../spinclock@2x.png | Bin 0 -> 21881 bytes spinclock/build.sh | 3 + spinclock/go.mod | 7 ++ spinclock/go.sum | 6 + spinclock/main.go | 116 ++++++++++++++++++ 9 files changed, 211 insertions(+) create mode 100644 spinclock/.gitignore create mode 100644 spinclock/README.md create mode 100644 spinclock/au.id.hawkins.sd.spinclock.sdPlugin/manifest.json create mode 100644 spinclock/au.id.hawkins.sd.spinclock.sdPlugin/spinclock@1x.png create mode 100644 spinclock/au.id.hawkins.sd.spinclock.sdPlugin/spinclock@2x.png create mode 100755 spinclock/build.sh create mode 100644 spinclock/go.mod create mode 100644 spinclock/go.sum create mode 100644 spinclock/main.go diff --git a/spinclock/.gitignore b/spinclock/.gitignore new file mode 100644 index 0000000..08ac015 --- /dev/null +++ b/spinclock/.gitignore @@ -0,0 +1,3 @@ +au.id.hawkins.sd.spinclock.sdPlugin/spinclock +au.id.hawkins.sd.spinclock.sdPlugin/spinclock.exe + diff --git a/spinclock/README.md b/spinclock/README.md new file mode 100644 index 0000000..875e40b --- /dev/null +++ b/spinclock/README.md @@ -0,0 +1,33 @@ +# Example Stream Deck plugin - "spinclock" + +This plugin displays a minimalist clock - the number on the clock is the hour (24h time) +and the rotation indicates the minute. With a little practice it should become easy to +tell the time with some accuracy. + +Tapping on a clock changes its colour to a random colour. + +# Trying it out + +Check this code out somewhere. + +Symlink `the au.id.hawkins.sd.spinclock.sdPlugin` directory into your +plugin directory. See [Elgato's documentation](https://docs.elgato.com/sdk/plugins/getting-started#id-4.-add-the-plugin-to-stream-deck). + +Compile the code, using the `./build.sh` script (sorry Windows users, no `.bat` file, patches welcome). + +Restart the Stream Deck software, your plugin should now be available in the list on the right hand side. When you drag it +onto your profile, the plugin will start. + +Stdout/stderr logs are available, on Mac they are at: + + /Users//Library/Logs/ElgatoStreamDeck + +# Making changes + +After modifying the code, rebuild using the script, and simply kill the running process to make Stream Deck restart it for you: + + killall spinclock + +Note that if your plugin restarts too many times in short succession, +Stream Deck will disable it completely (see the logs above) - the +only way I know of to recover is to restart the Stream Deck software. diff --git a/spinclock/au.id.hawkins.sd.spinclock.sdPlugin/manifest.json b/spinclock/au.id.hawkins.sd.spinclock.sdPlugin/manifest.json new file mode 100644 index 0000000..34d9891 --- /dev/null +++ b/spinclock/au.id.hawkins.sd.spinclock.sdPlugin/manifest.json @@ -0,0 +1,43 @@ +{ + "UUID": "au.id.hawkins.sd.spinclock", + "SDKVersion": 2, + "Author": "Justin Hawkins", + "CodePath": "spinclock", + "CodePathWin": "spinclock.exe", + "Description": "A spinning clock", + "Name": "SpinClock", + "Icon": "spinclock", + "DisableAutomaticStates": false, + "URL": "https://github.com/tardisx/streamdeck-plugin-examples/spinclock", + "Version": "1.0.0", + "OS": [ + { + "Platform": "mac", + "MinimumVersion": "10.11" + }, + { + "Platform": "windows", + "MinimumVersion": "10" + } + ], + "Software": { + "MinimumVersion": "6.5" + }, + "Category": "Clocks", + "CategoryIcon": "spinclock", + "Actions": [ + { + "Icon": "spinclock", + "Name": "SpinClock!", + "States": [ + { + "Image": "" + } + ], + "Controllers": ["Keypad"], + + "Tooltip": "A clock that tells time by rotation", + "UUID": "au.id.hawkins.sd.spinclock.clock" + } + ] +} diff --git a/spinclock/au.id.hawkins.sd.spinclock.sdPlugin/spinclock@1x.png b/spinclock/au.id.hawkins.sd.spinclock.sdPlugin/spinclock@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..cc3dd2aafda523f9d4e5d86da41d0921b12c01ea GIT binary patch literal 8987 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 F{SOe0H|GEV literal 0 HcmV?d00001 diff --git a/spinclock/build.sh b/spinclock/build.sh new file mode 100755 index 0000000..6743c5e --- /dev/null +++ b/spinclock/build.sh @@ -0,0 +1,3 @@ +#!/bin/sh +GOOS=darwin GOOARCH=arm64 go build -o au.id.hawkins.sd.spinclock.sdPlugin/spinclock . +GOOS=windows GOOARCH=amd64 go build -o au.id.hawkins.sd.spinclock.sdPlugin/spinclock.exe . diff --git a/spinclock/go.mod b/spinclock/go.mod new file mode 100644 index 0000000..d1e636f --- /dev/null +++ b/spinclock/go.mod @@ -0,0 +1,7 @@ +module spinclock + +go 1.22.1 + +require github.com/tardisx/streamdeck-plugin v0.0.7 + +require github.com/gorilla/websocket v1.5.3 // indirect diff --git a/spinclock/go.sum b/spinclock/go.sum new file mode 100644 index 0000000..2ba85dc --- /dev/null +++ b/spinclock/go.sum @@ -0,0 +1,6 @@ +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/tardisx/streamdeck-plugin v0.0.6 h1:vtaRHiKtcW09jMMIqcg0m+wBMqOq3Cl2PX3ktr7PTAM= +github.com/tardisx/streamdeck-plugin v0.0.6/go.mod h1:ysAJDI3Pi6zIbR3/jX7Pvduy6XGu68caZtSxn5t16V4= +github.com/tardisx/streamdeck-plugin v0.0.7 h1:YpII9PYlIXExe8+DPdffJxCM/2uftCpZGRpNq2rMiZw= +github.com/tardisx/streamdeck-plugin v0.0.7/go.mod h1:ysAJDI3Pi6zIbR3/jX7Pvduy6XGu68caZtSxn5t16V4= diff --git a/spinclock/main.go b/spinclock/main.go new file mode 100644 index 0000000..8bcffcd --- /dev/null +++ b/spinclock/main.go @@ -0,0 +1,116 @@ +package main + +import ( + "fmt" + "log/slog" + "math/rand" + "sync" + "time" + + "github.com/tardisx/streamdeck-plugin" + "github.com/tardisx/streamdeck-plugin/events" + "github.com/tardisx/streamdeck-plugin/tools" +) + +// keep track of instances we've seen, each one has a different +// colour +type clocks struct { + contextColour map[string]string + lock sync.Mutex +} + +// template for making a clock image in SVG +const svgClock = ` + + + + %02d + +` + +func main() { + clocks := clocks{ + contextColour: map[string]string{}, + lock: sync.Mutex{}, + } + slog.Info("Starting up") + c := streamdeck.NewWithLogger(slog.Default()) + + slog.Info("Registering handlers") + c.RegisterHandler(func(e events.ERWillAppear) { + // clock appeared, give it a random colour + slog.Info("appearing " + e.Context) + clocks.lock.Lock() + defer clocks.lock.Unlock() + clocks.contextColour[e.Context] = randRGB() + }) + c.RegisterHandler(func(e events.ERWillDisappear) { + // Stop updating this clock by simply removing it from our struct. + // Note that this is not required, and in this case it means that + // when it gets re-instantiated it will get a new colour. + // But it is good practice to not spend CPU on updating things that + // are not currently being displayed. + slog.Info("disappearing " + e.Context) + clocks.lock.Lock() + defer clocks.lock.Unlock() + delete(clocks.contextColour, e.Context) + }) + c.RegisterHandler(func(e events.ERKeyDown) { + // button pressed, change its colour + slog.Info("keyDown " + e.Context) + clocks.lock.Lock() + defer clocks.lock.Unlock() + clocks.contextColour[e.Context] = randRGB() + drawClock(c, e.Context, clocks.contextColour[e.Context]) + }) + + slog.Info("Connecting web socket") + err := c.Connect() + if err != nil { + panic(err) + } + + // update all clocks, continuously + go func() { + for { + clocks.lock.Lock() + for context, colour := range clocks.contextColour { + drawClock(c, context, colour) + } + clocks.lock.Unlock() + time.Sleep(time.Second) + } + }() + + slog.Info("waiting for the end") + c.WaitForPluginExit() +} + +func drawClock(c streamdeck.Connection, context string, colour string) { + // rotation for this minute of the hour + rot := int(360.0 * (float64(time.Now().Minute()) / 60.0)) + // generate the SVG + svg := fmt.Sprintf(svgClock, colour, rot, time.Now().Hour()) + + // create the event + newImage := events.NewESSetImage( + context, + tools.SVGToPayload(svg), + events.EventTargetBoth, + nil) + + // send it + c.Send(newImage) +} + +// randRGB creates a random colour +func randRGB() string { + return fmt.Sprintf("#%02x%02x%02x", rand.Intn(256), rand.Intn(256), rand.Intn(256)) +}