A Go SDK for building Elgato StreamDeck plugins.
- Full StreamDeck plugin API support
- Type-safe event handling with generics
- Automatic payload unmarshaling
- Context management
- Image and title manipulation
- Settings persistence
package main
import (
"context""fmt""log""os""github.com/FlowingSPDG/streamdeck"
)
typeSettingsstruct {
Counterint`json:"counter"`ButtonTextstring`json:"buttonText"`
}
funcmain() {
ctx:=context.Background()
params, err:=streamdeck.ParseRegistrationParams(os.Args)
iferr!=nil {
log.Fatal(err)
}
client:=streamdeck.NewClient(ctx, params)
setup(client)
iferr:=client.Run(ctx); err!=nil {
log.Fatal(err)
}
}
funcsetup(client*streamdeck.Client) {
action:=client.Action("com.example.myaction")
// Traditional event handlingaction.RegisterHandler(streamdeck.WillAppear, func(ctx context.Context, client*streamdeck.Client, event streamdeck.Event) error {
varp streamdeck.WillAppearPayload[Settings]
iferr:=event.UnmarshalPayload(&p); err!=nil {
returnfmt.Errorf("failed to unmarshal WillAppear payload: %w", err)
}
// Handle the payloadreturnclient.SetTitle(ctx, p.Settings.ButtonText, streamdeck.HardwareAndSoftware)
})
// Type-safe event handling with automatic unmarshalingstreamdeck.RegisterTypedHandler(action, streamdeck.KeyDown, func(ctx context.Context, client*streamdeck.Client, p streamdeck.KeyDownPayload[Settings]) error {
// Payload is automatically unmarshaled and typedp.Settings.Counter++returnclient.SetSettings(ctx, p.Settings)
})
}The SDK provides intuitive event-specific functions that automatically unmarshal event payloads into the specified type:
// Define your settings typetypeMySettingsstruct {
Counterint`json:"counter"`Textstring`json:"text"`
}
// Define your message types for Property Inspector communicationtypePropertyInspectorMessagestruct {
Actionstring`json:"action"`
}
typeResetCompleteResponsestruct {
Actionstring`json:"action"`
}
// Register typed handlers for different events using intuitive method namesstreamdeck.OnWillAppear(action, func(ctx context.Context, client*streamdeck.Client, p streamdeck.WillAppearPayload[MySettings]) error {
// p.Settings is automatically typed as MySettingsreturnclient.SetTitle(ctx, p.Settings.Text, streamdeck.HardwareAndSoftware)
})
streamdeck.OnKeyDown(action, func(ctx context.Context, client*streamdeck.Client, p streamdeck.KeyDownPayload[MySettings]) error {
// p.Settings is automatically typed as MySettingsp.Settings.Counter++returnclient.SetSettings(ctx, p.Settings)
})
streamdeck.OnDidReceiveSettings(action, func(ctx context.Context, client*streamdeck.Client, p streamdeck.DidReceiveSettingsPayload[MySettings]) error {
// p.Settings is automatically typed as MySettingsreturnnil
})
// Fully typed Property Inspector communicationstreamdeck.OnSendToPlugin(action, func(ctx context.Context, client*streamdeck.Client, payloadPropertyInspectorMessage) error {
switchpayload.Action {
case"reset":
returnclient.SendToPropertyInspector(ctx, ResetCompleteResponse{
Action: "resetComplete",
})
}
returnnil
})- Automatic Unmarshaling: No need to manually call
event.UnmarshalPayload() - Complete Type Safety: Zero
anytypes or type assertions - fully typed from input to output - Intuitive API: Event-specific function names (e.g.,
OnKeyDown,OnWillAppear) - Cleaner Code: Reduced boilerplate and error handling
- Better IDE Support: Full autocomplete and type inference
- Compile-time Safety: Catch type mismatches at build time, not runtime
OnWillAppear[T]- Button appears on Stream DeckOnWillDisappear[T]- Button disappears from Stream DeckOnKeyDown[T]- Button pressedOnKeyUp[T]- Button releasedOnDidReceiveSettings[T]- Settings receivedOnTouchTap[T]- Touch screen tapped (Stream Deck +)OnDialDown[T]- Dial pressed (Stream Deck +)OnDialUp[T]- Dial released (Stream Deck +)OnDialRotate[T]- Dial rotated (Stream Deck +)OnPropertyInspectorDidAppear[T]- Property Inspector openedOnPropertyInspectorDidDisappear[T]- Property Inspector closedOnDidReceivePropertyInspectorMessage[T]- Message from Property InspectorOnSendToPlugin[T]- Plugin message received
For cases where you need more control or want to handle raw events, you can still use the traditional RegisterHandler method:
action.RegisterHandler(streamdeck.WillAppear, func(ctx context.Context, client*streamdeck.Client, event streamdeck.Event) error {
varp streamdeck.WillAppearPayload[Settings]
iferr:=event.UnmarshalPayload(&p); err!=nil {
returnfmt.Errorf("failed to unmarshal WillAppear payload: %w", err)
}
// Handle the payloadreturnnil
})See the examples/ directory for complete working examples:
counter/- Simple counter pluginsettings_manager/- Advanced settings management with type-safe handlers
MIT License