Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions streamdeck/contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from streamdeck.event_handlers.actions import GlobalAction
from streamdeck.models.events import DEFAULT_EVENT_MODELS
from streamdeck.models.events.common import (
ActionInstanceEventMixin,
ActionInstancePayloadEventMixin,
BasePayload,
ContextualEventMixin,
ContextualPayloadEventMixin,
ControllerType,
Coordinates,
CoordinatesPayloadMixin,
Expand Down Expand Up @@ -165,23 +165,23 @@ def _update_device_disconnected(self, event_data: DeviceDidDisconnect) -> None:
self.get_device_context(event_data.device).connected = False

@update_from_event.register
def _update_contextual_event(self, event_data: ContextualEventMixin) -> None:
"""Update action-instance identity for a contextual event.
def _update_action_instance_event(self, event_data: ActionInstanceEventMixin) -> None:
"""Update action-instance identity from an event.

Args:
event_data: Event identifying an action and one of its instances.
"""
self._get_or_create_action_instance_context(event_data)

@update_from_event.register
def _update_contextual_payload_event(
def _update_action_instance_payload_event(
self,
event_data: ContextualPayloadEventMixin,
event_data: ActionInstancePayloadEventMixin,
) -> None:
"""Update action-instance context values from a contextual event payload.
"""Update cached action-instance values from an event payload.

Args:
event_data: Contextual event with payload data that may update the context.
event_data: Event with payload data that may update the cached action instance.
"""
instance = self._get_or_create_action_instance_context(event_data)
payload = event_data.payload
Expand Down Expand Up @@ -234,7 +234,7 @@ def get_action_context(self, action_uuid: str) -> ActionContext:

def _get_or_create_action_instance_context(
self,
event_data: ContextualEventMixin,
event_data: ActionInstanceEventMixin,
) -> ActionInstanceContext:
"""Get or create an action-instance context using event identity fields.

Expand Down Expand Up @@ -282,7 +282,7 @@ def resolve_device_context(self, event_data: EventBase) -> DeviceContext:
return self.get_device_context(event_data.device)

def resolve_action_context(self, event_data: EventBase) -> ActionContext:
"""Resolve the action context identified by a contextual event.
"""Resolve the action context identified by an action-instance event.

Args:
event_data: Event expected to identify an action.
Expand All @@ -293,13 +293,13 @@ def resolve_action_context(self, event_data: EventBase) -> ActionContext:
Raises:
TypeError: If the event does not identify an action.
"""
if not isinstance(event_data, ContextualEventMixin):
if not isinstance(event_data, ActionInstanceEventMixin):
msg = f"Event {event_data.event!r} does not identify an action"
raise TypeError(msg)
return self.get_action_context(event_data.action)

def resolve_action_instance_context(self, event_data: EventBase) -> ActionInstanceContext:
"""Resolve the action-instance context identified by a contextual event.
"""Resolve the action-instance context identified by an action-instance event.

Args:
event_data: Event expected to identify an action instance.
Expand All @@ -310,7 +310,7 @@ def resolve_action_instance_context(self, event_data: EventBase) -> ActionInstan
Raises:
TypeError: If the event does not identify an action instance.
"""
if not isinstance(event_data, ContextualEventMixin):
if not isinstance(event_data, ActionInstanceEventMixin):
msg = f"Event {event_data.event!r} does not identify an action instance"
raise TypeError(msg)

Expand All @@ -333,15 +333,15 @@ def update_context(event_data: EventBase) -> None:
context_store.update_from_event(event_data)

for event_model in DEFAULT_EVENT_MODELS:
# Skip sendToPlugin: its arbitrary payload is the only contextual event payload not used
# Skip sendToPlugin: its arbitrary payload is the only action-instance event payload not used
# to update cached context values.
if event_model is DidReceivePropertyInspectorMessage:
continue

# Only register events that identify an action instance, a device, or global settings.
if not issubclass(
event_model,
(ContextualEventMixin, DeviceSpecificEventMixin, DidReceiveGlobalSettings),
(ActionInstanceEventMixin, DeviceSpecificEventMixin, DidReceiveGlobalSettings),
):
continue

Expand Down
4 changes: 2 additions & 2 deletions streamdeck/event_handlers/scopes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from enum import Flag, auto
from typing import TYPE_CHECKING, Literal

from streamdeck.models.events.common import ContextualEventMixin, DeviceSpecificEventMixin
from streamdeck.models.events.common import ActionInstanceEventMixin, DeviceSpecificEventMixin


if TYPE_CHECKING:
Expand Down Expand Up @@ -75,7 +75,7 @@ def scope_for_event(event_or_model: EventBase | type[EventBase]) -> Scope:
issubclass if isinstance(event_or_model, type) else isinstance
)

if scope_check(event_or_model, ContextualEventMixin):
if scope_check(event_or_model, ActionInstanceEventMixin):
scope |= Scope.ACTION_INSTANCE
if scope_check(event_or_model, DeviceSpecificEventMixin):
scope |= Scope.DEVICE
Expand Down
6 changes: 3 additions & 3 deletions streamdeck/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from streamdeck.event_handlers.scopes import ScopeName, scope_for_event
from streamdeck.event_listener import EventListener, EventListenerManager
from streamdeck.models.events.adapter import EventAdapter
from streamdeck.models.events.common import ContextualEventMixin
from streamdeck.models.events.common import ActionInstanceEventMixin
from streamdeck.utils.logging import configure_streamdeck_logger
from streamdeck.websocket import WebSocketClient

Expand Down Expand Up @@ -164,10 +164,10 @@ def _dispatch_event(self, event: EventBase) -> None:
event (EventBase): The event data model for the event to dispatch.
"""
# If the event is action-specific, we'll pass the action's uuid to the handler to ensure only the correct action is triggered.
event_action_uuid = event.action if isinstance(event, ContextualEventMixin) else None
event_action_uuid = event.action if isinstance(event, ActionInstanceEventMixin) else None
for event_handler in self._handlers_registry.get_event_handlers(event_name=event.event, event_action_uuid=event_action_uuid):
injected_params = self._injectable_handler_param_store.resolve_for_handler(event_handler, event)
# TODO: from contextual event occurrences, save metadata to the action's properties.
# TODO: from action-instance event occurrences, save metadata to the action's properties.

event_handler(event, **injected_params)

Expand Down
24 changes: 13 additions & 11 deletions streamdeck/models/events/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

## Mixin classes for common event model fields.

class ContextualEventMixin:
"""Mixin class for event models that have action and context fields."""
class ActionInstanceEventMixin:
"""Mixin for events that identify an action and one of its instances."""
action: EventNameStr
"""Unique identifier of the action"""
context: ActionInstanceUUIDStr
Expand Down Expand Up @@ -113,24 +113,26 @@ class StatefulActionPayloadMixin:
"""


ContextualPayload: TypeAlias = (
ActionInstancePayload: TypeAlias = (
SettingsPayloadMixin | StatefulActionPayloadMixin | CoordinatesPayloadMixin
)
"""Payload mixins that provide values copied into action-instance contexts."""


ContextualPayloadT = TypeVar(
"ContextualPayloadT",
bound=ContextualPayload,
default=ContextualPayload,
ActionInstancePayloadT = TypeVar(
"ActionInstancePayloadT",
bound=ActionInstancePayload,
default=ActionInstancePayload,
)


class ContextualPayloadEventMixin(ContextualEventMixin, Generic[ContextualPayloadT]):
"""Mixin for contextual event models with payloads that update action-instance context."""
class ActionInstancePayloadEventMixin(
ActionInstanceEventMixin, Generic[ActionInstancePayloadT]
):
"""Mixin for action-instance event models with payloads that update action-instance context."""

payload: ContextualPayloadT
"""Payload data associated with the contextual event."""
payload: ActionInstancePayloadT
"""Payload data associated with the action-instance event."""


class SingleActionPayloadMixin:
Expand Down
18 changes: 9 additions & 9 deletions streamdeck/models/events/dials.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

from streamdeck.models.events.base import EventBase
from streamdeck.models.events.common import (
ActionInstancePayloadEventMixin,
BasePayload,
ContextualPayloadEventMixin,
CoordinatesPayloadMixin,
DeviceSpecificEventMixin,
EncoderControllerType,
Expand All @@ -16,11 +16,11 @@


class EncoderPayload(BasePayload[EncoderControllerType], CoordinatesPayloadMixin):
"""Contextualized information for a DialDown or DialUp event."""
"""Action-instance event data for a DialDown or DialUp event."""


class DialRotatePayload(EncoderPayload):
"""Contextualized information for a DialRotate event."""
"""Action-instance event data for a DialRotate event."""

pressed: bool
"""Determines whether the dial was pressed whilst the rotation occurred."""
Expand All @@ -31,22 +31,22 @@ class DialRotatePayload(EncoderPayload):
## Event models for DialDown, DialRotate, and DialUp events


class DialDown(EventBase[Literal["dialDown"]], ContextualPayloadEventMixin[EncoderPayload], DeviceSpecificEventMixin): # fmt: skip
class DialDown(EventBase[Literal["dialDown"]], ActionInstancePayloadEventMixin[EncoderPayload], DeviceSpecificEventMixin): # fmt: skip
"""Occurs when the user presses a dial (Stream Deck +)."""

payload: EncoderPayload
"""Contextualized information for this event."""
"""Action-instance event data for this event."""


class DialRotate(EventBase[Literal["dialRotate"]], ContextualPayloadEventMixin[DialRotatePayload], DeviceSpecificEventMixin): # fmt: skip
class DialRotate(EventBase[Literal["dialRotate"]], ActionInstancePayloadEventMixin[DialRotatePayload], DeviceSpecificEventMixin): # fmt: skip
"""Occurs when the user rotates a dial (Stream Deck +)."""

payload: DialRotatePayload
"""Contextualized information for this event."""
"""Action-instance event data for this event."""


class DialUp(EventBase[Literal["dialUp"]], ContextualPayloadEventMixin[EncoderPayload], DeviceSpecificEventMixin): # fmt: skip
class DialUp(EventBase[Literal["dialUp"]], ActionInstancePayloadEventMixin[EncoderPayload], DeviceSpecificEventMixin): # fmt: skip
"""Occurs when the user releases a pressed dial (Stream Deck +)."""

payload: EncoderPayload
"""Contextualized information for this event."""
"""Action-instance event data for this event."""
14 changes: 7 additions & 7 deletions streamdeck/models/events/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

from streamdeck.models.events.base import EventBase
from streamdeck.models.events.common import (
ActionInstancePayloadEventMixin,
BasePayload,
CardinalityDiscriminated,
ContextualPayloadEventMixin,
CoordinatesPayloadMixin,
DeviceSpecificEventMixin,
KeypadControllerType,
Expand All @@ -30,15 +30,15 @@ class SingleActionKeyGesturePayload(
StatefulActionPayloadMixin,
CoordinatesPayloadMixin,
):
"""Contextualized information for a KeyDown or KeyUp event that is not part of a multi-action."""
"""Action-instance event data for a KeyDown or KeyUp event that is not part of a multi-action."""


class MultiActionKeyGesturePayload(
BasePayload[OptionalKeyControllerTypeField],
MultiActionPayloadMixin,
StatefulActionPayloadMixin,
):
"""Contextualized information for a KeyDown or KeyUp event that is part of a multi-action."""
"""Action-instance event data for a KeyDown or KeyUp event that is part of a multi-action."""

user_desired_state: Annotated[int, Field(alias="userDesiredState")]
"""Desired state as specified by the user.
Expand All @@ -50,15 +50,15 @@ class MultiActionKeyGesturePayload(
## Event models for KeyDown and KeyUp events


class KeyDown(EventBase[Literal["keyDown"]], ContextualPayloadEventMixin[CardinalityDiscriminated[SingleActionKeyGesturePayload, MultiActionKeyGesturePayload]], DeviceSpecificEventMixin): # fmt: skip
class KeyDown(EventBase[Literal["keyDown"]], ActionInstancePayloadEventMixin[CardinalityDiscriminated[SingleActionKeyGesturePayload, MultiActionKeyGesturePayload]], DeviceSpecificEventMixin): # fmt: skip
"""Occurs when the user presses a action down."""

payload: CardinalityDiscriminated[SingleActionKeyGesturePayload, MultiActionKeyGesturePayload]
"""Contextualized information for this event."""
"""Action-instance event data for this event."""


class KeyUp(EventBase[Literal["keyUp"]], ContextualPayloadEventMixin[CardinalityDiscriminated[SingleActionKeyGesturePayload, MultiActionKeyGesturePayload]], DeviceSpecificEventMixin): # fmt: skip
class KeyUp(EventBase[Literal["keyUp"]], ActionInstancePayloadEventMixin[CardinalityDiscriminated[SingleActionKeyGesturePayload, MultiActionKeyGesturePayload]], DeviceSpecificEventMixin): # fmt: skip
"""Occurs when the user releases a pressed action."""

payload: CardinalityDiscriminated[SingleActionKeyGesturePayload, MultiActionKeyGesturePayload]
"""Contextualized information for this event."""
"""Action-instance event data for this event."""
8 changes: 4 additions & 4 deletions streamdeck/models/events/property_inspector.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

from streamdeck.models.events.base import EventBase
from streamdeck.models.events.common import (
ContextualEventMixin,
ActionInstanceEventMixin,
DeviceSpecificEventMixin,
)
from streamdeck.types import PluginDefinedData # noqa: TC001


class DidReceivePropertyInspectorMessage(EventBase[Literal["sendToPlugin"]], ContextualEventMixin):
class DidReceivePropertyInspectorMessage(EventBase[Literal["sendToPlugin"]], ActionInstanceEventMixin):
"""Occurs when a message was received from the UI."""

payload: PluginDefinedData
Expand All @@ -19,7 +19,7 @@ class DidReceivePropertyInspectorMessage(EventBase[Literal["sendToPlugin"]], Con

class PropertyInspectorDidAppear(
EventBase[Literal["propertyInspectorDidAppear"]],
ContextualEventMixin,
ActionInstanceEventMixin,
DeviceSpecificEventMixin,
):
"""Occurs when the property inspector associated with the action becomes visible.
Expand All @@ -28,7 +28,7 @@ class PropertyInspectorDidAppear(
"""


class PropertyInspectorDidDisappear(EventBase[Literal["propertyInspectorDidDisappear"]], ContextualEventMixin, DeviceSpecificEventMixin): # fmt: skip
class PropertyInspectorDidDisappear(EventBase[Literal["propertyInspectorDidDisappear"]], ActionInstanceEventMixin, DeviceSpecificEventMixin): # fmt: skip
"""Occurs when the property inspector associated with the action becomes invisible.

I.e. the user unselected the action in the Stream Deck application.
Expand Down
10 changes: 5 additions & 5 deletions streamdeck/models/events/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

from streamdeck.models.events.base import ConfiguredBaseModel, EventBase
from streamdeck.models.events.common import (
ActionInstancePayloadEventMixin,
BasePayload,
CardinalityDiscriminated,
ContextualPayloadEventMixin,
CoordinatesPayloadMixin,
DeviceSpecificEventMixin,
KeypadControllerType,
Expand All @@ -26,25 +26,25 @@ class SingleActionSettingsPayload(
StatefulActionPayloadMixin,
CoordinatesPayloadMixin,
):
"""Contextualized information for a didReceiveSettings events that are not part of a multi-action."""
"""Action-instance event data for a didReceiveSettings events that are not part of a multi-action."""


class MultiActionSettingsPayload(
BasePayload[KeypadControllerType],
MultiActionPayloadMixin,
StatefulActionPayloadMixin,
):
"""Contextualized information for a didReceiveSettings events that are part of a multi-action.
"""Action-instance event data for a didReceiveSettings events that are part of a multi-action.

NOTE: Action instances that are part of a multi-action are only applicable to the 'Keypad' controller type.
"""


class DidReceiveSettings(EventBase[Literal["didReceiveSettings"]], ContextualPayloadEventMixin[CardinalityDiscriminated[SingleActionSettingsPayload, MultiActionSettingsPayload]], DeviceSpecificEventMixin): # fmt: skip
class DidReceiveSettings(EventBase[Literal["didReceiveSettings"]], ActionInstancePayloadEventMixin[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]
"""Contextualized information for this event."""
"""Action-instance event data for this event."""


## Models for didReceiveGlobalSettings event and its specific payload.
Expand Down
6 changes: 3 additions & 3 deletions streamdeck/models/events/title_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

from streamdeck.models.events.base import ConfiguredBaseModel, EventBase
from streamdeck.models.events.common import (
ActionInstancePayloadEventMixin,
BasePayload,
ContextualPayloadEventMixin,
CoordinatesPayloadMixin,
DeviceSpecificEventMixin,
StatefulActionPayloadMixin,
Expand Down Expand Up @@ -42,15 +42,15 @@ class TitleParametersDidChangePayload(
CoordinatesPayloadMixin,
StatefulActionPayloadMixin,
):
"""Contextualized information for this event."""
"""Action-instance event data for this event."""

title: str
"""Title of the action, as specified by the user or dynamically by the plugin."""
title_parameters: Annotated[TitleParameters, Field(alias="titleParameters")]
"""Defines aesthetic properties that determine how the title should be rendered."""


class TitleParametersDidChange(EventBase[Literal["titleParametersDidChange"]], ContextualPayloadEventMixin[TitleParametersDidChangePayload], DeviceSpecificEventMixin): # fmt: skip
class TitleParametersDidChange(EventBase[Literal["titleParametersDidChange"]], ActionInstancePayloadEventMixin[TitleParametersDidChangePayload], DeviceSpecificEventMixin): # fmt: skip
"""Occurs when the user updates an action's title settings in the Stream Deck application."""

payload: TitleParametersDidChangePayload
Loading
Loading