diff --git a/streamdeck/models/events/common.py b/streamdeck/models/events/common.py index 0295bf9..292615e 100644 --- a/streamdeck/models/events/common.py +++ b/streamdeck/models/events/common.py @@ -1,7 +1,6 @@ from __future__ import annotations -from abc import ABC -from typing import Annotated, Generic, Literal, NamedTuple, Union +from typing import Annotated, Generic, Literal, NamedTuple, TypeAlias, Union from pydantic import Field from typing_extensions import TypedDict, TypeVar @@ -33,30 +32,37 @@ class DeviceSpecificEventMixin: ## Payload models and metadata used by multiple event models. - - - - EncoderControllerType = Literal["Encoder"] """The 'Encoder' controller type refers to a dial or touchscreen on a 'Stream Deck +' device.""" + + KeypadControllerType = Literal["Keypad"] """The 'Keypad' controller type refers to a standard action on a Stream Deck device, e.g. buttons or a pedal.""" + + ControllerType = Literal[EncoderControllerType, KeypadControllerType] """Defines the controller type the action is applicable to.""" + CT = TypeVar("CT", bound=ControllerType, default=ControllerType) -class BasePayload(ConfiguredBaseModel, Generic[CT], ABC): - """Base class for all complex payload models.""" +class SettingsPayloadMixin: + """Mixin for payload models that carry plugin-defined settings.""" + + settings: PluginDefinedData + """Settings associated with a plugin or action instance.""" + + +class BasePayload(ConfiguredBaseModel, SettingsPayloadMixin, Generic[CT]): + """Base class for action payload models with controller and settings data.""" + controller: CT """Defines the controller type the action is applicable to. 'Keypad' refers to a standard action on a Stream Deck device, e.g. buttons or a pedal. 'Encoder' refers to a dial / touchscreen on a 'Stream Deck +' device. """ - settings: PluginDefinedData - """Settings associated with the action instance.""" class CoordinatesDict(TypedDict): @@ -107,6 +113,26 @@ class StatefulActionPayloadMixin: """ +ContextualPayload: TypeAlias = ( + SettingsPayloadMixin | StatefulActionPayloadMixin | CoordinatesPayloadMixin +) +"""Payload mixins that provide values copied into action-instance contexts.""" + + +ContextualPayloadT = TypeVar( + "ContextualPayloadT", + bound=ContextualPayload, + default=ContextualPayload, +) + + +class ContextualPayloadEventMixin(ContextualEventMixin, Generic[ContextualPayloadT]): + """Mixin for contextual event models with payloads that update action-instance context.""" + + payload: ContextualPayloadT + """Payload data associated with the contextual event.""" + + class SingleActionPayloadMixin: """Mixin class for event models that have a single action payload.""" diff --git a/streamdeck/models/events/dials.py b/streamdeck/models/events/dials.py index bc7bcff..581747e 100644 --- a/streamdeck/models/events/dials.py +++ b/streamdeck/models/events/dials.py @@ -5,7 +5,7 @@ from streamdeck.models.events.base import EventBase from streamdeck.models.events.common import ( BasePayload, - ContextualEventMixin, + ContextualPayloadEventMixin, CoordinatesPayloadMixin, DeviceSpecificEventMixin, EncoderControllerType, @@ -31,21 +31,21 @@ class DialRotatePayload(EncoderPayload): ## Event models for DialDown, DialRotate, and DialUp events -class DialDown(EventBase[Literal["dialDown"]], ContextualEventMixin, DeviceSpecificEventMixin): +class DialDown(EventBase[Literal["dialDown"]], ContextualPayloadEventMixin[EncoderPayload], DeviceSpecificEventMixin): # fmt: skip """Occurs when the user presses a dial (Stream Deck +).""" payload: EncoderPayload """Contextualized information for this event.""" -class DialRotate(EventBase[Literal["dialRotate"]], ContextualEventMixin, DeviceSpecificEventMixin): +class DialRotate(EventBase[Literal["dialRotate"]], ContextualPayloadEventMixin[DialRotatePayload], DeviceSpecificEventMixin): # fmt: skip """Occurs when the user rotates a dial (Stream Deck +).""" payload: DialRotatePayload """Contextualized information for this event.""" -class DialUp(EventBase[Literal["dialUp"]], ContextualEventMixin, DeviceSpecificEventMixin): +class DialUp(EventBase[Literal["dialUp"]], ContextualPayloadEventMixin[EncoderPayload], DeviceSpecificEventMixin): # fmt: skip """Occurs when the user releases a pressed dial (Stream Deck +).""" payload: EncoderPayload diff --git a/streamdeck/models/events/keys.py b/streamdeck/models/events/keys.py index c874d95..be3440a 100644 --- a/streamdeck/models/events/keys.py +++ b/streamdeck/models/events/keys.py @@ -8,7 +8,7 @@ from streamdeck.models.events.common import ( BasePayload, CardinalityDiscriminated, - ContextualEventMixin, + ContextualPayloadEventMixin, CoordinatesPayloadMixin, DeviceSpecificEventMixin, KeypadControllerType, @@ -50,14 +50,14 @@ class MultiActionKeyGesturePayload( ## Event models for KeyDown and KeyUp events -class KeyDown(EventBase[Literal["keyDown"]], ContextualEventMixin, DeviceSpecificEventMixin): +class KeyDown(EventBase[Literal["keyDown"]], ContextualPayloadEventMixin[CardinalityDiscriminated[SingleActionKeyGesturePayload, MultiActionKeyGesturePayload]], DeviceSpecificEventMixin): # fmt: skip """Occurs when the user presses a action down.""" payload: CardinalityDiscriminated[SingleActionKeyGesturePayload, MultiActionKeyGesturePayload] """Contextualized information for this event.""" -class KeyUp(EventBase[Literal["keyUp"]], ContextualEventMixin, DeviceSpecificEventMixin): +class KeyUp(EventBase[Literal["keyUp"]], ContextualPayloadEventMixin[CardinalityDiscriminated[SingleActionKeyGesturePayload, MultiActionKeyGesturePayload]], DeviceSpecificEventMixin): # fmt: skip """Occurs when the user releases a pressed action.""" payload: CardinalityDiscriminated[SingleActionKeyGesturePayload, MultiActionKeyGesturePayload] diff --git a/streamdeck/models/events/settings.py b/streamdeck/models/events/settings.py index f066a4d..2f1ace9 100644 --- a/streamdeck/models/events/settings.py +++ b/streamdeck/models/events/settings.py @@ -6,15 +6,15 @@ from streamdeck.models.events.common import ( BasePayload, CardinalityDiscriminated, - ContextualEventMixin, + ContextualPayloadEventMixin, CoordinatesPayloadMixin, DeviceSpecificEventMixin, KeypadControllerType, MultiActionPayloadMixin, + SettingsPayloadMixin, SingleActionPayloadMixin, StatefulActionPayloadMixin, ) -from streamdeck.types import PluginDefinedData # noqa: TC001 ## Models for didReceiveSettings event and its specific payloads. @@ -40,9 +40,7 @@ class MultiActionSettingsPayload( """ -class DidReceiveSettings( - EventBase[Literal["didReceiveSettings"]], ContextualEventMixin, DeviceSpecificEventMixin -): +class DidReceiveSettings(EventBase[Literal["didReceiveSettings"]], ContextualPayloadEventMixin[CardinalityDiscriminated[SingleActionSettingsPayload, MultiActionSettingsPayload]], DeviceSpecificEventMixin): # fmt: skip """Occurs when the settings associated with an action instance are requested, or when the the settings were updated by the property inspector.""" payload: CardinalityDiscriminated[SingleActionSettingsPayload, MultiActionSettingsPayload] @@ -52,12 +50,9 @@ class DidReceiveSettings( ## Models for didReceiveGlobalSettings event and its specific payload. -class GlobalSettingsPayload(ConfiguredBaseModel): +class GlobalSettingsPayload(ConfiguredBaseModel, SettingsPayloadMixin): """Additional information about the didReceiveGlobalSettings event that occurred.""" - settings: PluginDefinedData - """The global settings received from the Stream Deck.""" - class DidReceiveGlobalSettings(EventBase[Literal["didReceiveGlobalSettings"]]): """Occurs when the plugin receives the global settings from the Stream Deck.""" diff --git a/streamdeck/models/events/title_parameters.py b/streamdeck/models/events/title_parameters.py index b7c9c7f..4480c6c 100644 --- a/streamdeck/models/events/title_parameters.py +++ b/streamdeck/models/events/title_parameters.py @@ -7,7 +7,7 @@ from streamdeck.models.events.base import ConfiguredBaseModel, EventBase from streamdeck.models.events.common import ( BasePayload, - ContextualEventMixin, + ContextualPayloadEventMixin, CoordinatesPayloadMixin, DeviceSpecificEventMixin, StatefulActionPayloadMixin, @@ -50,7 +50,7 @@ class TitleParametersDidChangePayload( """Defines aesthetic properties that determine how the title should be rendered.""" -class TitleParametersDidChange(EventBase[Literal["titleParametersDidChange"]], ContextualEventMixin, DeviceSpecificEventMixin): # fmt: skip +class TitleParametersDidChange(EventBase[Literal["titleParametersDidChange"]], ContextualPayloadEventMixin[TitleParametersDidChangePayload], DeviceSpecificEventMixin): # fmt: skip """Occurs when the user updates an action's title settings in the Stream Deck application.""" payload: TitleParametersDidChangePayload diff --git a/streamdeck/models/events/touch_tap.py b/streamdeck/models/events/touch_tap.py index b3717fa..82526d0 100644 --- a/streamdeck/models/events/touch_tap.py +++ b/streamdeck/models/events/touch_tap.py @@ -7,7 +7,7 @@ from streamdeck.models.events.base import EventBase from streamdeck.models.events.common import ( BasePayload, - ContextualEventMixin, + ContextualPayloadEventMixin, CoordinatesPayloadMixin, DeviceSpecificEventMixin, EncoderControllerType, @@ -23,7 +23,7 @@ class TouchTapPayload(BasePayload[EncoderControllerType], CoordinatesPayloadMixi """Coordinates of where the touchscreen tap occurred, relative to the canvas of the action.""" -class TouchTap(EventBase[Literal["touchTap"]], ContextualEventMixin, DeviceSpecificEventMixin): +class TouchTap(EventBase[Literal["touchTap"]], ContextualPayloadEventMixin[TouchTapPayload], DeviceSpecificEventMixin): # fmt: skip """Occurs when the user taps the touchscreen (Stream Deck +).""" payload: TouchTapPayload diff --git a/streamdeck/models/events/visibility.py b/streamdeck/models/events/visibility.py index d0396f7..8c107f6 100644 --- a/streamdeck/models/events/visibility.py +++ b/streamdeck/models/events/visibility.py @@ -6,7 +6,7 @@ from streamdeck.models.events.common import ( BasePayload, CardinalityDiscriminated, - ContextualEventMixin, + ContextualPayloadEventMixin, CoordinatesPayloadMixin, DeviceSpecificEventMixin, KeypadControllerType, @@ -36,7 +36,7 @@ class MultiActionVisibilityPayload( ## Event models for WillAppear and WillDisappear events -class WillAppear(EventBase[Literal["willAppear"]], ContextualEventMixin, DeviceSpecificEventMixin): +class WillAppear(EventBase[Literal["willAppear"]], ContextualPayloadEventMixin[CardinalityDiscriminated[SingleActionVisibilityPayload, MultiActionVisibilityPayload]], DeviceSpecificEventMixin): # fmt: skip """Occurs when an action appears on the Stream Deck due to the user navigating to another page, profile, folder, etc. This also occurs during startup if the action is on the "front page". @@ -46,7 +46,7 @@ class WillAppear(EventBase[Literal["willAppear"]], ContextualEventMixin, DeviceS payload: CardinalityDiscriminated[SingleActionVisibilityPayload, MultiActionVisibilityPayload] -class WillDisappear(EventBase[Literal["willDisappear"]], ContextualEventMixin, DeviceSpecificEventMixin): # fmt: skip +class WillDisappear(EventBase[Literal["willDisappear"]], ContextualPayloadEventMixin[CardinalityDiscriminated[SingleActionVisibilityPayload, MultiActionVisibilityPayload]], DeviceSpecificEventMixin): # fmt: skip """Occurs when an action disappears from the Stream Deck due to the user navigating to another page, profile, folder, etc. An action refers to all types of actions, e.g. keys, dials, touchscreens, pedals, etc.