1
- from __future__ import annotations
2
-
3
1
import json
4
2
import logging
5
3
import sys
6
- from argparse import ArgumentParser
7
4
from pathlib import Path
8
- from typing import Protocol , cast
5
+ from typing import Annotated , Union
6
+
7
+ import typer
9
8
10
9
from streamdeck .manager import PluginManager
11
10
from streamdeck .models .configs import PyProjectConfigs
16
15
17
16
18
17
19
- class DirectoryNotFoundError (FileNotFoundError ):
20
- """Custom exception to indicate that a specified directory was not found."""
21
- def __init__ (self , * args : object , directory : Path ):
22
- super ().__init__ (* args )
23
- self .directory = directory
24
-
25
-
26
- class CliArgsNamespace (Protocol ):
27
- """Represents the command-line arguments namespace."""
28
- plugin_dir : Path | None
29
- action_scripts : list [str ] | None
30
-
31
- # Args always passed in by StreamDeck software
32
- port : int
33
- pluginUUID : str # noqa: N815
34
- registerEvent : str # noqa: N815
35
- info : str # Actually a string representation of json object
18
+ plugin = typer .Typer ()
36
19
37
20
38
- def setup_cli () -> ArgumentParser :
39
- """Set up the command-line interface for the script.
21
+ @plugin .command ()
22
+ def main (
23
+ port : Annotated [int , typer .Option ("-p" , "-port" )],
24
+ plugin_registration_uuid : Annotated [str , typer .Option ("-pluginUUID" )],
25
+ register_event : Annotated [str , typer .Option ("-registerEvent" )],
26
+ info : Annotated [str , typer .Option ("-info" )],
27
+ plugin_dir : Annotated [Path , typer .Option (file_okay = False , exists = True , readable = True )] = Path .cwd (), # noqa: B008
28
+ action_scripts : Union [list [str ], None ] = None , # noqa: UP007
29
+ ) -> None :
30
+ """Start the Stream Deck plugin with the given configuration.
40
31
41
- Returns:
42
- argparse.ArgumentParser: The argument parser for the CLI .
32
+ NOTE: Single flag long-name options are extected & passed in by the Stream Deck software.
33
+ Double flag long-name options are used during development and testing .
43
34
"""
44
- parser = ArgumentParser (description = "CLI to load Actions from action scripts." )
45
- group = parser .add_mutually_exclusive_group (required = False )
46
- group .add_argument (
47
- "plugin_dir" ,
48
- type = Path ,
49
- nargs = "?" ,
50
- help = "The directory containing plugin files to load Actions from." ,
51
- )
52
- group .add_argument (
53
- "--action-scripts" ,
54
- type = str ,
55
- nargs = "+" ,
56
- help = "A list of action script file paths to load Actions from or a single value to be processed." ,
57
- )
58
-
59
- # Options that will always be passed in by the StreamDeck software when running this plugin.
60
- parser .add_argument ("-port" , dest = "port" , type = int , help = "Port" , required = True )
61
- parser .add_argument (
62
- "-pluginUUID" , dest = "pluginUUID" , type = str , help = "pluginUUID" , required = True
63
- )
64
- parser .add_argument (
65
- "-registerEvent" , dest = "registerEvent" , type = str , help = "registerEvent" , required = True
66
- )
67
- parser .add_argument ("-info" , dest = "info" , type = str , help = "info" , required = True )
68
-
69
- return parser
70
-
71
-
72
- def main () -> None :
73
- """Main function to parse arguments, load actions, and execute them."""
74
- parser = setup_cli ()
75
- args = cast (CliArgsNamespace , parser .parse_args ())
76
-
77
- # If `plugin_dir` was not passed in as a cli option, then fall back to using the CWD.
78
- if args .plugin_dir is None :
79
- plugin_dir = Path .cwd ()
80
- # Also validate the plugin_dir argument.
81
- elif not args .plugin_dir .is_dir ():
82
- msg = f"The provided plugin directory '{ args .plugin_dir } ' is not a directory."
83
- raise NotADirectoryError (msg )
84
- elif not args .plugin_dir .exists ():
85
- msg = f"The provided plugin directory '{ args .plugin_dir } ' does not exist."
86
- raise DirectoryNotFoundError (msg , directory = args .plugin_dir )
87
- else :
88
- plugin_dir = args .plugin_dir
89
-
90
35
# Ensure plugin_dir is in `sys.path`, so that import statements in the plugin module will work as expected.
91
36
if str (plugin_dir ) not in sys .path :
92
37
sys .path .insert (0 , str (plugin_dir ))
93
38
94
- info = json .loads (args . info )
95
- plugin_uuid = info ["plugin" ]["uuid" ]
39
+ info_data = json .loads (info )
40
+ plugin_uuid = info_data ["plugin" ]["uuid" ]
96
41
97
42
# After configuring once here, we can grab the logger in any other module with `logging.getLogger("streamdeck")`, or
98
43
# a child logger with `logging.getLogger("streamdeck.mycomponent")`, all with the same handler/formatter configuration.
99
44
configure_streamdeck_logger (name = "streamdeck" , plugin_uuid = plugin_uuid )
100
45
101
- pyproject = PyProjectConfigs .validate_from_toml_file (plugin_dir / "pyproject.toml" )
102
- actions = list ( pyproject .streamdeck_plugin_actions )
46
+ pyproject = PyProjectConfigs .validate_from_toml_file (plugin_dir / "pyproject.toml" , action_scripts = action_scripts )
47
+ actions = pyproject .streamdeck_plugin_actions
103
48
104
49
manager = PluginManager (
105
- port = args . port ,
50
+ port = port ,
106
51
plugin_uuid = plugin_uuid ,
107
52
# NOT the configured plugin UUID in the manifest.json,
108
53
# which can be pulled out of `info["plugin"]["uuid"]`
109
- plugin_registration_uuid = args . pluginUUID ,
110
- register_event = args . registerEvent ,
111
- info = info ,
54
+ plugin_registration_uuid = plugin_registration_uuid ,
55
+ register_event = register_event ,
56
+ info = info_data ,
112
57
)
113
58
114
59
for action in actions :
@@ -117,5 +62,9 @@ def main() -> None:
117
62
manager .run ()
118
63
119
64
120
- if __name__ == "__main__" :
121
- main ()
65
+ # Also run the plugin if this script is ran as a console script.
66
+ if __name__ in ("__main__" , "streamdeck.__main__" ):
67
+ plugin ()
68
+
69
+
70
+
0 commit comments