Skip to content

Commit 593bbca

Browse files
committed
Create Controller type TypeAliases, and generic base classes to define which controller type is used by a payload model
1 parent 1005474 commit 593bbca

File tree

7 files changed

+97
-86
lines changed

7 files changed

+97
-86
lines changed

streamdeck/models/events/common.py

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
from __future__ import annotations
22

3-
from typing import Any, Literal, Optional
3+
from abc import ABC
4+
from typing import Annotated, Any, Generic, Literal, NamedTuple, Optional, Union
5+
6+
from pydantic import Field
7+
from typing_extensions import TypedDict, TypeVar
48

59
from streamdeck.models.events.base import ConfiguredBaseModel
610

@@ -23,6 +27,17 @@ class DeviceSpecificEventMixin:
2327

2428
## Payload models and metadata used by multiple event models.
2529

30+
31+
EncoderControllerType = Literal["Encoder"]
32+
"""The 'Encoder' controller type refers to a dial or touchscreen on a 'Stream Deck +' device."""
33+
KeypadControllerType = Literal["Keypad"]
34+
"""The 'Keypad' controller type refers to a standard action on a Stream Deck device, e.g. buttons or a pedal."""
35+
ControllerType = Literal[EncoderControllerType, KeypadControllerType]
36+
"""Defines the controller type the action is applicable to."""
37+
38+
CT = TypeVar("CT", bound=ControllerType, default=ControllerType)
39+
40+
2641
PluginDefinedData = dict[str, Any]
2742
"""Data of arbitrary structure that is defined in and relevant to the plugin."""
2843

@@ -66,6 +81,28 @@ def coordinates(self) -> Coordinates:
6681
return Coordinates(**self.coordinates_obj)
6782

6883

84+
class BasePayload(ConfiguredBaseModel, Generic[CT], ABC):
85+
"""Base class for all complex payload models."""
86+
controller: CT
87+
"""Defines the controller type the action is applicable to.
88+
89+
'Keypad' refers to a standard action on a Stream Deck device, e.g. buttons or a pedal.
90+
'Encoder' refers to a dial / touchscreen on a 'Stream Deck +' device.
91+
"""
92+
settings: PluginDefinedData
93+
"""Settings associated with the action instance."""
94+
95+
96+
class BaseActionPayload(BasePayload[CT], ABC):
97+
"""Base class for payloads of action events."""
98+
99+
state: Optional[int] = None # noqa: UP007
100+
"""Current state of the action.
101+
102+
Only applicable to actions that have multiple states defined within the manifest.json file.
103+
"""
104+
105+
69106
class SingleActionPayloadMixin:
70107
"""Mixin class for event models that have a single action payload."""
71108

@@ -88,36 +125,3 @@ class MultiActionPayloadMixin:
88125
Field(discriminator="is_in_multi_action"),
89126
]
90127
"""Generic type for a payload that either subclasses SingleActionPayloadMixin or MultiActionPayloadMixin—meaning it can be either a single action or a multi-action."""
91-
92-
93-
class SingleActionPayload(ConfiguredBaseModel, CoordinatesPayloadMixin, SingleActionPayloadMixin):
94-
"""Contextualized information for a willAppear, willDisappear, and didReceiveSettings events that are not part of a multi-action."""
95-
controller: Literal["Encoder", "Keypad"]
96-
"""Defines the controller type the action is applicable to.
97-
98-
'Keypad' refers to a standard action on a Stream Deck device, e.g. buttons or a pedal.
99-
'Encoder' refers to a dial / touchscreen.
100-
"""
101-
state: Optional[int] = None # noqa: UP007
102-
"""Current state of the action; only applicable to actions that have multiple states defined within the manifest.json file."""
103-
settings: PluginDefinedData
104-
"""Settings associated with the action instance."""
105-
106-
107-
class MultiActionPayload(ConfiguredBaseModel, MultiActionPayloadMixin):
108-
"""Contextualized information for a willAppear, willDisappear, and didReceiveSettings events that are part of a multi-action.
109-
110-
NOTE: Action instances that are part of a multi-action are only applicable to the 'Keypad' controller type.
111-
"""
112-
controller: Literal["Keypad"]
113-
"""The 'Keypad' controller type refers to a standard action on a Stream Deck device, e.g. buttons or a pedal.
114-
115-
Action instances that are part of a multi-action are only applicable to the 'Keypad' controller type.
116-
"""
117-
state: Optional[int] = None # noqa: UP007
118-
"""Current state of the action; only applicable to actions that have multiple states defined within the manifest.json file."""
119-
settings: PluginDefinedData
120-
"""Settings associated with the action instance."""
121-
122-
123-

streamdeck/models/events/dials.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,28 @@
22

33
from typing import Literal
44

5-
from streamdeck.models.events.base import ConfiguredBaseModel, EventBase
5+
from streamdeck.models.events.base import EventBase
66
from streamdeck.models.events.common import (
7+
BasePayload,
78
ContextualEventMixin,
89
CoordinatesPayloadMixin,
910
DeviceSpecificEventMixin,
10-
PluginDefinedData,
11+
EncoderControllerType,
1112
)
1213

1314

1415
## Payload models used in the below DialDown, DialRotate, and DialUp events
1516

16-
class EncoderPayload(ConfiguredBaseModel, CoordinatesPayloadMixin):
17+
class EncoderPayload(BasePayload[EncoderControllerType], CoordinatesPayloadMixin):
1718
"""Contextualized information for a DialDown or DialUp event."""
18-
controller: Literal["Encoder"]
19-
"""The 'Encoder' controller type refers to a dial or touchscreen on a 'Stream Deck +' device."""
20-
settings: PluginDefinedData
2119

2220

23-
class DialRotatePayload(ConfiguredBaseModel, CoordinatesPayloadMixin):
21+
class DialRotatePayload(EncoderPayload):
2422
"""Contextualized information for a DialRotate event."""
25-
controller: Literal["Encoder"]
26-
"""The 'Encoder' controller type refers to a dial or touchscreen on a 'Stream Deck +' device."""
2723
pressed: bool
2824
"""Determines whether the dial was pressed whilst the rotation occurred."""
2925
ticks: int
3026
"""Number of ticks the dial was rotated; this can be a positive (clockwise) or negative (counter-clockwise) number."""
31-
settings: PluginDefinedData
32-
"""Settings associated with the action instance."""
3327

3428

3529
## Event models for DialDown, DialRotate, and DialUp events

streamdeck/models/events/keys.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,39 @@
11
from __future__ import annotations
22

3-
from typing import Annotated, Literal, Optional, Union
3+
from typing import Annotated, Literal
44

55
from pydantic import Field
66

7-
from streamdeck.models.events.base import ConfiguredBaseModel, EventBase
7+
from streamdeck.models.events.base import EventBase
88
from streamdeck.models.events.common import (
9+
BaseActionPayload,
910
CardinalityDiscriminated,
1011
ContextualEventMixin,
1112
CoordinatesPayloadMixin,
1213
DeviceSpecificEventMixin,
14+
KeypadControllerType,
1315
MultiActionPayloadMixin,
1416
SingleActionPayloadMixin,
1517
)
1618

1719

1820
## Payload models used in the below KeyDown and KeyUp events
1921

20-
class SingleActionKeyGesturePayload(ConfiguredBaseModel, SingleActionPayloadMixin, CoordinatesPayloadMixin):
22+
# It seems like for keyDown and keyUp events, the "controller" field is probably always missing, despite being defined in the official documentation.
23+
OptionalKeyControllerTypeField = Annotated[KeypadControllerType, Field(default=None)]
24+
25+
26+
class SingleActionKeyGesturePayload(BaseActionPayload[OptionalKeyControllerTypeField], SingleActionPayloadMixin, CoordinatesPayloadMixin):
2127
"""Contextualized information for a KeyDown or KeyUp event that is not part of a multi-action."""
22-
controller: Optional[Literal["Keypad"]] = None # noqa: UP007
23-
"""The 'Keypad' controller type refers to a standard action on a Stream Deck device, e.g. buttons or a pedal."""
24-
state: Optional[int] = None # noqa: UP007
25-
"""Current state of the action; only applicable to actions that have multiple states defined within the manifest.json file."""
26-
settings: PluginDefinedData
27-
"""Settings associated with the action instance."""
2828

2929

30-
class MultiActionKeyGesturePayload(ConfiguredBaseModel, MultiActionPayloadMixin):
30+
class MultiActionKeyGesturePayload(BaseActionPayload[OptionalKeyControllerTypeField], MultiActionPayloadMixin):
3131
"""Contextualized information for a KeyDown or KeyUp event that is part of a multi-action."""
32-
controller: Optional[Literal["Keypad"]] = None # noqa: UP007
33-
"""The 'Keypad' controller type refers to a standard action on a Stream Deck device, e.g. buttons or a pedal."""
34-
state: Optional[int] = None # noqa: UP007
35-
"""Current state of the action; only applicable to actions that have multiple states defined within the manifest.json file."""
3632
user_desired_state: Annotated[int, Field(alias="userDesiredState")]
3733
"""Desired state as specified by the user.
3834
3935
Only applicable to actions that have multiple states defined within the manifest.json file, and when this action instance is part of a multi-action.
4036
"""
41-
settings: PluginDefinedData
4237

4338

4439
## Event models for KeyDown and KeyUp events

streamdeck/models/events/settings.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
11
from __future__ import annotations
22

3-
from typing import Annotated, Literal, Union
4-
5-
from pydantic import Field
3+
from typing import Literal
64

75
from streamdeck.models.events.base import ConfiguredBaseModel, EventBase
86
from streamdeck.models.events.common import (
7+
BaseActionPayload,
98
CardinalityDiscriminated,
109
ContextualEventMixin,
10+
CoordinatesPayloadMixin,
1111
DeviceSpecificEventMixin,
12-
MultiActionPayload,
12+
KeypadControllerType,
13+
MultiActionPayloadMixin,
1314
PluginDefinedData,
14-
SingleActionPayload,
15+
SingleActionPayloadMixin,
1516
)
1617

1718

19+
## Models for didReceiveSettings event and its specific payloads.
20+
21+
class SingleActionSettingsPayload(BaseActionPayload, SingleActionPayloadMixin, CoordinatesPayloadMixin):
22+
"""Contextualized information for a didReceiveSettings events that are not part of a multi-action."""
23+
24+
25+
class MultiActionSettingsPayload(BaseActionPayload[KeypadControllerType], MultiActionPayloadMixin):
26+
"""Contextualized information for a didReceiveSettings events that are part of a multi-action.
27+
28+
NOTE: Action instances that are part of a multi-action are only applicable to the 'Keypad' controller type.
29+
"""
30+
31+
1832
class DidReceiveSettings(EventBase, ContextualEventMixin, DeviceSpecificEventMixin):
1933
"""Occurs when the settings associated with an action instance are requested, or when the the settings were updated by the property inspector."""
2034
event: Literal["didReceiveSettings"] # type: ignore[override]
21-
payload: CardinalityDiscriminated[SingleActionPayload, MultiActionPayload]
35+
payload: CardinalityDiscriminated[SingleActionSettingsPayload, MultiActionSettingsPayload]
2236
"""Contextualized information for this event."""
2337

2438

streamdeck/models/events/title_parameters.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
from __future__ import annotations
22

3-
from typing import Annotated, Literal, Optional
3+
from typing import Annotated, Literal
44

55
from pydantic import Field
66

77
from streamdeck.models.events.base import ConfiguredBaseModel, EventBase
88
from streamdeck.models.events.common import (
9+
BaseActionPayload,
910
CoordinatesPayloadMixin,
1011
DeviceSpecificEventMixin,
11-
PluginDefinedData,
1212
)
1313

1414

@@ -34,17 +34,13 @@ class TitleParameters(ConfiguredBaseModel):
3434
"""Color of the title, represented as a hexadecimal value."""
3535

3636

37-
class TitleParametersDidChangePayload(ConfiguredBaseModel, CoordinatesPayloadMixin):
37+
class TitleParametersDidChangePayload(BaseActionPayload, CoordinatesPayloadMixin):
3838
"""Contextualized information for this event."""
39-
controller: Literal["Keypad", "Encoder"]
40-
"""Defines the controller type the action is applicable to."""
4139
title: str
4240
"""Title of the action, as specified by the user or dynamically by the plugin."""
4341
title_parameters: Annotated[TitleParameters, Field(alias="titleParameters")]
4442
"""Defines aesthetic properties that determine how the title should be rendered."""
45-
state: Optional[int] # noqa: UP007
46-
"""Current state of the action; only applicable to actions that have multiple states defined within the manifest.json file."""
47-
settings: PluginDefinedData
43+
4844

4945
class TitleParametersDidChange(EventBase, DeviceSpecificEventMixin):
5046
"""Occurs when the user updates an action's title settings in the Stream Deck application."""

streamdeck/models/events/touch_tap.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,21 @@
66

77
from streamdeck.models.events.base import EventBase
88
from streamdeck.models.events.common import (
9+
BasePayload,
910
ContextualEventMixin,
1011
CoordinatesPayloadMixin,
1112
DeviceSpecificEventMixin,
12-
PluginDefinedData,
13+
EncoderControllerType,
1314
)
1415

1516

16-
class TouchTapPayload(ConfiguredBaseModel, CoordinatesPayloadMixin):
17+
class TouchTapPayload(BasePayload[EncoderControllerType], CoordinatesPayloadMixin):
1718
"""Contextualized information for a TouchTap event."""
18-
controller: Literal["Encoder"]
19-
"""The 'Encoder' controller type refers to a dial or touchscreen on a 'Stream Deck +' device."""
2019
hold: bool
2120
"""Determines whether the tap was considered 'held'."""
2221
tap_position: Annotated[tuple[int, int], Field(alias="tapPos")]
2322
"""Coordinates of where the touchscreen tap occurred, relative to the canvas of the action."""
24-
settings: PluginDefinedData
23+
2524

2625
class TouchTap(EventBase, ContextualEventMixin, DeviceSpecificEventMixin):
2726
"""Occurs when the user taps the touchscreen (Stream Deck +)."""
Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,28 @@
11
from __future__ import annotations
22

3-
from typing import Annotated, Literal, Union
4-
5-
from pydantic import Field
3+
from typing import Literal
64

75
from streamdeck.models.events.base import EventBase
86
from streamdeck.models.events.common import (
7+
BaseActionPayload,
98
CardinalityDiscriminated,
109
ContextualEventMixin,
10+
CoordinatesPayloadMixin,
1111
DeviceSpecificEventMixin,
12-
MultiActionPayload,
13-
SingleActionPayload,
12+
KeypadControllerType,
13+
MultiActionPayloadMixin,
14+
SingleActionPayloadMixin,
1415
)
1516

1617

18+
class SingleActionVisibilityPayload(BaseActionPayload, SingleActionPayloadMixin, CoordinatesPayloadMixin):
19+
"""Contextualized information for willAppear and willDisappear events that is not part of a multi-action."""
20+
21+
22+
class MultiActionVisibilityPayload(BaseActionPayload[KeypadControllerType], MultiActionPayloadMixin):
23+
"""Contextualized information for willAppear and willDisappear events that is part of a multi-action."""
24+
25+
1726
## Event models for WillAppear and WillDisappear events
1827

1928
class WillAppear(EventBase, ContextualEventMixin, DeviceSpecificEventMixin):
@@ -23,7 +32,7 @@ class WillAppear(EventBase, ContextualEventMixin, DeviceSpecificEventMixin):
2332
An action refers to all types of actions, e.g. keys, dials, touchscreens, pedals, etc.
2433
"""
2534
event: Literal["willAppear"] # type: ignore[override]
26-
payload: CardinalityDiscriminated[SingleActionPayload, MultiActionPayload]
35+
payload: CardinalityDiscriminated[SingleActionVisibilityPayload, MultiActionVisibilityPayload]
2736

2837

2938
class WillDisappear(EventBase, ContextualEventMixin, DeviceSpecificEventMixin):
@@ -32,4 +41,4 @@ class WillDisappear(EventBase, ContextualEventMixin, DeviceSpecificEventMixin):
3241
An action refers to all types of actions, e.g. keys, dials, touchscreens, pedals, etc.
3342
"""
3443
event: Literal["willDisappear"] # type: ignore[override]
35-
payload: CardinalityDiscriminated[SingleActionPayload, MultiActionPayload]
44+
payload: CardinalityDiscriminated[SingleActionVisibilityPayload, MultiActionVisibilityPayload]

0 commit comments

Comments
 (0)