Skip to content

Commit ce48932

Browse files
committed
Merge branch 'develop'
2 parents 2490550 + 5cd21b3 commit ce48932

12 files changed

Lines changed: 211 additions & 121 deletions

JoycontrolPlugin/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from JoycontrolPlugin.commands import JoycontrolCommands
2-
from JoycontrolPlugin.plugin import JoycontrolPlugin
3-
from JoycontrolPlugin.loader import load_plugin
2+
from JoycontrolPlugin.plugin import JoycontrolPlugin, JoycontrolPluginError
3+
from JoycontrolPlugin.loader import PluginLoader

JoycontrolPlugin/loader.py

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,92 @@
1-
import os
1+
import argparse
2+
import asyncio
23
import logging
4+
import os
35

46
from importlib.machinery import SourceFileLoader
57

8+
from joycontrol import logging_default as log, utils
9+
from joycontrol.controller import Controller
10+
from joycontrol.memory import FlashMemory
11+
from joycontrol.protocol import controller_protocol_factory
12+
from joycontrol.server import create_hid_server
13+
614
logger = logging.getLogger(__name__)
715

8-
def load_plugin(plugin, controller_state, *plugin_options):
9-
logger.info(f'Loading: {plugin}')
10-
module = SourceFileLoader(plugin, plugin).load_module()
11-
plugin_name = os.path.splitext(os.path.basename(plugin))[0]
12-
joycontrol_plugin = module.__dict__[plugin_name](controller_state, *plugin_options)
13-
return joycontrol_plugin
16+
class PluginLoader:
17+
def __init__(self):
18+
self.transport = None
19+
20+
21+
def __load_plugin(self, plugin, controller_state, *plugin_options):
22+
logger.info(f'Loading: {plugin}')
23+
module = SourceFileLoader(plugin, plugin).load_module()
24+
plugin_name = os.path.splitext(os.path.basename(plugin))[0]
25+
joycontrol_plugin = module.__dict__[plugin_name](controller_state, *plugin_options)
26+
return joycontrol_plugin
27+
28+
29+
async def start(self, args):
30+
# Create memory containing default controller stick calibration
31+
spi_flash = FlashMemory()
32+
33+
# Get controller name to emulate from arguments
34+
controller = Controller.from_arg('PRO_CONTROLLER')
35+
36+
with utils.get_output(path=None, default=None) as capture_file:
37+
factory = controller_protocol_factory(controller, spi_flash=spi_flash)
38+
ctl_psm, itr_psm = 17, 19
39+
transport, protocol = await create_hid_server(factory, reconnect_bt_addr=args.reconnect_bt_addr,
40+
ctl_psm=ctl_psm,
41+
itr_psm=itr_psm, capture_file=capture_file,
42+
device_id=args.device_id)
43+
44+
controller_state = protocol.get_controller_state()
45+
self.transport = transport
46+
47+
try:
48+
# waits until controller is fully connected
49+
await controller_state.connect()
50+
joycontrol_plugin = self.__load_plugin(args.plugin, controller_state, args.plugin_options)
51+
await joycontrol_plugin.run()
52+
except Exception as e:
53+
logger.error(e)
54+
finally:
55+
logger.info('Stopping communication...')
56+
await transport.close()
57+
self.transport = None
58+
self.joycontrol_cmd = None
59+
60+
61+
async def stop(self):
62+
if self.transport:
63+
logger.info('Stopping communication...')
64+
self.transport = None
65+
66+
67+
def main():
68+
# check if root
69+
if not os.geteuid() == 0:
70+
raise PermissionError('Script must be run as root!')
71+
72+
parser = argparse.ArgumentParser()
73+
parser.add_argument('plugin', type=str, help='joycontrol plugin path')
74+
parser.add_argument('-p', '--plugin-options', nargs='*', help='joycontrol plugin options')
75+
parser.add_argument('-d', '--device_id')
76+
parser.add_argument('-r', '--reconnect_bt_addr', type=str, default=None,
77+
help='The Switch console Bluetooth address, for reconnecting as an already paired controller')
78+
parser.add_argument('-v', '--verbose', action='store_true')
79+
args = parser.parse_args()
80+
81+
if args.verbose:
82+
log.configure()
83+
else:
84+
log.configure(console_level=logging.INFO)
85+
86+
loop = asyncio.get_event_loop()
87+
loader = PluginLoader()
88+
89+
try:
90+
loop.run_until_complete(loader.start(args))
91+
except KeyboardInterrupt:
92+
loop.run_until_complete(loader.stop())

JoycontrolPlugin/plugin.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from JoycontrolPlugin.commands import JoycontrolCommands
22
from abc import abstractmethod
33

4+
class JoycontrolPluginError(Exception):
5+
pass
6+
47
class JoycontrolPlugin(JoycontrolCommands):
58
def __init__(self, controller_state, options):
69
super().__init__(controller_state)

README.md

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ This is a plugin loader for joycontrol that can emulate Nintendo Switch controll
1919
- Install joycontrol-pluginloader
2020

2121
```sh
22-
$ git clone https://github.com/Almtr/joycontrol-pluginloader
22+
$ git clone https://github.com/Almtr/joycontrol-pluginloader.git
2323
$ sudo pip3 install joycontrol-pluginloader/
2424
```
2525

@@ -32,30 +32,62 @@ This is a plugin loader for joycontrol that can emulate Nintendo Switch controll
3232
1. Run the script
3333

3434
```sh
35-
$ sudo python3 joycontrol/run_controller_cli.py PRO_CONTROLLER
35+
$ sudo joycontrol-pluginloader plugins/tests/PairingController.py
3636
```
3737

38+
Run results:
39+
40+
```sh
41+
$ sudo joycontrol-pluginloader plugins/tests/PairingController.py
42+
[17:03:13] joycontrol.server create_hid_server::58 WARNING - [Errno 98] Address already in use
43+
[17:03:13] joycontrol.server create_hid_server::60 WARNING - Fallback: Restarting bluetooth due to incompatibilities with the bluez "input" plugin. Disable the plugin to avoid issues. See https://github.com/mart1nro/joycontrol/issues/8.
44+
[17:03:13] joycontrol.server create_hid_server::65 INFO - Restarting bluetooth service...
45+
[17:03:14] joycontrol.device set_name::69 INFO - setting device name to Pro Controller...
46+
[17:03:14] joycontrol.device set_class::61 INFO - setting device class to 0x002508...
47+
[17:03:14] joycontrol.server create_hid_server::84 INFO - Advertising the Bluetooth SDP record...
48+
[17:03:14] joycontrol.server create_hid_server::94 INFO - Waiting for Switch to connect... Please open the "Change Grip/Order" menu.
49+
[17:03:16] joycontrol.server create_hid_server::98 INFO - Accepted connection at psm 17 from ('01:23:45:67:89:AB', 17)
50+
[17:03:16] joycontrol.server create_hid_server::100 INFO - Accepted connection at psm 19 from ('01:23:45:67:89:AB', 19)
51+
[17:03:18] root _reply_to_sub_command::295 INFO - received output report - Sub command SubCommand.REQUEST_DEVICE_INFO
52+
[17:03:18] root _reply_to_sub_command::295 INFO - received output report - Sub command SubCommand.SET_SHIPMENT_STATE
53+
[17:03:18] root _reply_to_sub_command::295 INFO - received output report - Sub command SubCommand.SPI_FLASH_READ
54+
[17:03:18] root _reply_to_sub_command::295 INFO - received output report - Sub command SubCommand.SPI_FLASH_READ
55+
[17:03:18] root _reply_to_sub_command::295 INFO - received output report - Sub command SubCommand.SET_INPUT_REPORT_MODE
56+
57+
<snip>
58+
59+
[17:04:04] root _reply_to_sub_command::295 INFO - received output report - Sub command SubCommand.SET_NFC_IR_MCU_CONFIG
60+
[17:04:04] root _reply_to_sub_command::295 INFO - received output report - Sub command SubCommand.SET_PLAYER_LIGHTS
61+
[17:04:04] JoycontrolPlugin.loader load_plugin::9 INFO - Loading: plugins/tests/PairingController.py
62+
[17:04:05] plugins/tests/PairingController.py run::11 INFO - Pairing completed.
63+
```
64+
65+
> note:
66+
> '01:23:45:67:89:AB' is your Nintendo Switch Bluetooth Mac address.
67+
> You will use this Mac address later.
68+
3869
## Usage
3970

4071
- Basic Usage
4172

4273
```
43-
$ sudo python3 joycontrol-pluginloader.py -r <Switch Bluetooth Mac address> <Joycontrol Plugin path>
74+
$ sudo joycontrol-pluginloader -r <Switch Bluetooth Mac address> <Joycontrol Plugin path>
4475
```
4576

4677
- Options
4778

4879
```
49-
usage: joycontrol-pluginloader.py [-h] [-d DEVICE_ID] [-r RECONNECT_BT_ADDR]
50-
[-v]
51-
plugin [options [options ...]]
80+
usage: joycontrol-pluginloader [-h] [-p [PLUGIN_OPTIONS [PLUGIN_OPTIONS ...]]]
81+
[-d DEVICE_ID] [-r RECONNECT_BT_ADDR] [-v]
82+
plugin
5283

5384
positional arguments:
5485
plugin joycontrol plugin path
55-
options joycontrol plugin options
5686

5787
optional arguments:
5888
-h, --help show this help message and exit
89+
-p [PLUGIN_OPTIONS [PLUGIN_OPTIONS ...]], --plugin-options [PLUGIN_OPTIONS [PLUGIN_OPTIONS ...]]
90+
joycontrol plugin options
5991
-d DEVICE_ID, --device_id DEVICE_ID
6092
-r RECONNECT_BT_ADDR, --reconnect_bt_addr RECONNECT_BT_ADDR
6193
The Switch console Bluetooth address, for reconnecting
@@ -91,19 +123,19 @@ This is a plugin loader for joycontrol that can emulate Nintendo Switch controll
91123
- Load and run ``SamplePlugin.py``
92124
93125
```sh
94-
$ sudo python3 joycontrol-pluginloader.py -r <Switch Bluetooth Mac address> plugins/samples/SamplePlugin.py arg1 arg2
126+
$ sudo joycontrol-pluginloader -r EC:C4:0D:F0:D1:2E plugins/samples/SamplePlugin.py --plugin-options option1 option2
95127

96128
<snip>
97129

98-
[13:30:00] JoycontrolPlugin.loader load_plugin::9 INFO - Loading: plugins/samples/SamplePlugin.py
99-
[13:30:00] plugins/samples/SamplePlugin.py run::8 INFO - This is sample joycontrol plugin!
100-
[13:30:00] plugins/samples/SamplePlugin.py run::10 INFO - Plugin Options: ['arg1', 'arg2']
101-
[13:30:00] plugins/samples/SamplePlugin.py run::12 INFO - Push the A Button
102-
[13:30:01] plugins/samples/SamplePlugin.py run::16 INFO - Tilt the left stick down
103-
[13:30:01] __main__ _main::45 INFO - Stopping communication...
130+
[20:12:44] JoycontrolPlugin.loader __load_plugin::22 INFO - Loading: plugins/samples/SamplePlugin.py
131+
[20:12:44] plugins/samples/SamplePlugin.py run::8 INFO - This is sample joycontrol plugin!
132+
[20:12:44] plugins/samples/SamplePlugin.py run::10 INFO - Plugin Options: ['option1', 'option2']
133+
[20:12:44] plugins/samples/SamplePlugin.py run::12 INFO - Push the A Button
134+
[20:12:45] plugins/samples/SamplePlugin.py run::16 INFO - Tilt the left stick down
135+
[20:12:45] JoycontrolPlugin.loader start::55 INFO - Stopping communication...
104136
```
105137
106-
## Sample Plugins
138+
## Plugins
107139
108140
### TestControllerButtons
109141
@@ -116,7 +148,7 @@ Check if the controller buttons are working properly.
116148
1. Run TestControllerButtons.py with joycontrol-pluginloader
117149
118150
```
119-
$ sudo python3 joycontrol-pluginloader.py -r <Switch Bluetooth Mac address> plugins/samples/TestControllerButtons.py
151+
$ sudo joycontrol-pluginloader -r <Switch Bluetooth Mac address> plugins/tests/TestControllerButtons.py
120152
```
121153
122154
### TestControllerSticks
@@ -130,7 +162,7 @@ Check if the controller sticks are working properly.
130162
1. Run TestControllerSticks.py with joycontrol-pluginloader
131163
132164
```
133-
$ sudo python3 joycontrol-pluginloader.py -r <Switch Bluetooth Mac address> plugins/samples/TestControllerSticks.py
165+
$ sudo joycontrol-pluginloader -r <Switch Bluetooth Mac address> plugins/tests/TestControllerSticks.py
134166
```
135167
136168
### RepeatA
@@ -140,7 +172,7 @@ Pushing the "A Button" repeatedly.
140172
- Run RepeatA.py with joycontrol-pluginloader
141173
142174
```
143-
$ sudo python3 joycontrol-pluginloader.py -r <Switch Bluetooth Mac address> plugins/samples/RepeatA.py
175+
$ sudo joycontrol-pluginloader -r <Switch Bluetooth Mac address> plugins/utils/RepeatA.py
144176
```
145177
146178
### SimpleMacro
@@ -150,7 +182,7 @@ Press the specified buttons in sequence.
150182
- Run SimpleMacro.py with joycontrol-pluginloader
151183
152184
```
153-
$ sudo python3 joycontrol-pluginloader.py -r <Switch Bluetooth Mac address> plugins/samples/SimpleMacro.py a b x y up down left right
185+
$ sudo joycontrol-pluginloader -r <Switch Bluetooth Mac address> plugins/utils/SimpleMacro.py --plugin-options a b x y up down left right
154186
```
155187
156188
## References

0 commit comments

Comments
 (0)