First worked example

This commit is contained in:
Justin Hawkins 2024-06-29 15:50:28 +09:30
parent c4f7e3e73a
commit 55e8aa6dc1
9 changed files with 211 additions and 0 deletions

3
spinclock/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
au.id.hawkins.sd.spinclock.sdPlugin/spinclock
au.id.hawkins.sd.spinclock.sdPlugin/spinclock.exe

33
spinclock/README.md Normal file
View File

@ -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/<username>/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.

View File

@ -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"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

3
spinclock/build.sh Executable file
View File

@ -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 .

7
spinclock/go.mod Normal file
View File

@ -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

6
spinclock/go.sum Normal file
View File

@ -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=

116
spinclock/main.go Normal file
View File

@ -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 = `
<svg width="144" height="144" xmlns="http://www.w3.org/2000/svg">
<rect width="144" height="144" fill="black"/>
<text x="72" y="108"
font-family="Arial, sans-serif"
font-size="96"
font-weight="bold"
fill="%s"
text-anchor="middle"
dominant-baseline="central"
transform="rotate(%d, 72, 72)">
%02d
</text>
</svg>`
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))
}