-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmanager.py
More file actions
113 lines (85 loc) · 4.99 KB
/
manager.py
File metadata and controls
113 lines (85 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
from __future__ import annotations
import functools
import inspect
import logging
from logging import getLogger
from typing import TYPE_CHECKING
from streamdeck.actions import ActionRegistry
from streamdeck.command_sender import StreamDeckCommandSender
from streamdeck.models.events import ContextualEventMixin, event_adapter
from streamdeck.utils.logging import configure_streamdeck_logger
from streamdeck.websocket import WebSocketClient
if TYPE_CHECKING:
from collections.abc import Callable
from typing import Any, Literal
from streamdeck.actions import Action
from streamdeck.models.events import EventBase
# TODO: Fix this up to push to a log in the apropos directory and filename.
logger = getLogger("streamdeck.manager")
class PluginManager:
"""Manages plugin actions and communicates with a WebSocket server to handle events."""
def __init__(
self,
port: int,
plugin_uuid: str,
plugin_registration_uuid: str, # Passed in by Streamdeck to the entry-point script. should we compare what's in the manifest.json file?
register_event: Literal["registerPlugin"], # Passed in by Streamdeck to the entry-point script. Will this always be "registerPlugin"?
info: dict[str, Any]
):
"""Initialize a PluginManager instance.
Args:
port (int): The port number for WebSocket connection.
plugin_uuid (str): The unique identifier for the plugin, as configured in the manifest.json file.
This can be retrieved either from the manifest.json, or from the -info json object string option passed in by
the Stream Deck software under `{"plugin": {"uuid": "MY-PLUGIN-UUID"}}`
plugin_registration_uuid (str): Randomly-generated unique ID passed in by Stream Deck as -pluginUUID option,
used to send back in the registerPlugin event.
register_event (str): The registration event type, passed in by the Stream Deck software as -registerEvent option.
It's value will almost definitely will be "registerPlugin".
info (dict[str, Any]): The information related to the plugin.
"""
self._port = port
self.uuid = plugin_uuid
self._registration_uuid = plugin_registration_uuid
self._register_event = register_event
self._info = info
self._registry = ActionRegistry()
def register_action(self, action: Action) -> None:
"""Register an action with the PluginManager, and configure its logger.
Args:
action (Action): The action to register.
"""
# First, configure a logger for the action, giving it the last part of its uuid as name (if it has one).
action_component_name = action.uuid.split(".")[-1] if hasattr(action, "uuid") else "global"
configure_streamdeck_logger(name=action_component_name, plugin_uuid=self.uuid)
self._registry.register(action)
def _inject_command_sender(self, handler: Callable[..., None], command_sender: StreamDeckCommandSender) -> Callable[..., None]:
"""Inject command_sender into handler if it accepts it as a parameter.
Args:
handler: The event handler function
command_sender: The StreamDeckCommandSender instance
Returns:
The handler with command_sender injected if needed
"""
args: dict[str, inspect.Parameter] = inspect.signature(handler).parameters
# Check dynamically if the `command_sender`'s name is in the handler's arguments.
if "command_sender" in args:
return functools.partial(handler, command_sender=command_sender)
return handler
def run(self) -> None:
"""Run the PluginManager by connecting to the WebSocket server and processing incoming events.
This method establishes a WebSocket connection, registers the plugin, listens for incoming messages,
and triggers the appropriate action handlers based on the received events.
"""
with WebSocketClient(port=self._port) as client:
command_sender = StreamDeckCommandSender(client)
command_sender.send_action_registration(register_event=self._register_event, plugin_registration_uuid=self._registration_uuid)
for message in client.listen_forever():
data: EventBase = event_adapter.validate_json(message)
logger.debug("Event received: %s", data.event)
# 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 = data.action if isinstance(data, ContextualEventMixin) else None
for handler in self._registry.get_action_handlers(event_name=data.event, event_action_uuid=event_action_uuid):
handler = self._inject_command_sender(handler, command_sender)
# TODO: from contextual event occurences, save metadata to the action's properties.
handler(data)