-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSwitchController.py
180 lines (147 loc) · 6.84 KB
/
SwitchController.py
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import asyncio
import glob
import logging
import os
import sys
sys.path.insert(0, "./joycontrol")
from joycontrol.controller import Controller
from joycontrol.controller_state import button_press, button_release
from joycontrol.memory import FlashMemory
from joycontrol.protocol import controller_protocol_factory
from joycontrol.server import create_hid_server
from joycontrol import logging_default as log
class SwitchController:
def __init__(self):
self.handlers = []
Handlers(self);
async def create(self, configuration={}):
"""Initialize connection to the switch"""
log.configure(logging.INFO)
print("Connecting...")
self.spi_flash = FlashMemory()
self.controller = Controller.PRO_CONTROLLER
self.factory = controller_protocol_factory(
self.controller, spi_flash=self.spi_flash
)
self.transport, self.protocol = await create_hid_server(
self.factory, reconnect_bt_addr=configuration.get("reconnect_bt_addr")
)
self.controller_state = self.protocol.get_controller_state()
print("Connected!")
def is_valid_button(self, *buttons):
"""Check if the button press is available on the controller"""
for button in buttons:
if button not in self.controller_state.button_state.get_available_buttons():
return False
return True
async def hold(self, *buttons):
"""Hold given buttons down"""
if self.is_valid_button(*buttons) is False:
return
# Make sure controller is connected
await self.controller_state.connect()
await button_press(self.controller_state, *buttons)
async def release(self, *buttons):
"""Release given buttons"""
if self.is_valid_button(*buttons) is False:
return
# Make sure controller is connected
await self.controller_state.connect()
await button_release(self.controller_state, *buttons)
async def stick(self, side, direction=None, release=False):
"""Handle stick direction, remembering previous pressed. Allowing left+up etc."""
if direction in ("left", "right"):
if release:
await self.stick_h_pct(side)
else:
await self.stick_h_pct(side, direction == "right")
if direction in ("up", "down"):
if release:
await self.stick_v_pct(side)
else:
await self.stick_v_pct(side, direction == "up")
async def map_pct_to_stick(self, pct, center, left, right):
"""Convert percentage to stick position"""
return int((center - left) + (pct * (left + right)))
async def stick_h_pct(self, side, pct=0.5):
"""Convert percentage value to horizontal switch input"""
stick = (
self.controller_state.r_stick_state
if side == "right"
else self.controller_state.l_stick_state
)
calibration = stick.get_calibration()
pos = await self.map_pct_to_stick(pct, calibration.h_center, calibration.h_max_below_center, calibration.h_max_above_center)
stick.set_h(pos)
async def stick_v_pct(self, side, pct=0.5):
"""Convert percentage value to vertical switch input"""
stick = (
self.controller_state.r_stick_state
if side == "right"
else self.controller_state.l_stick_state
)
calibration = stick.get_calibration()
pos = await self.map_pct_to_stick(pct, calibration.v_center, calibration.v_max_below_center, calibration.v_max_above_center)
stick.set_v(pos)
async def nfc(self, file_path):
"""Load nfc file from file path"""
with open(file_path, "rb") as nfc_file:
loop = asyncio.get_event_loop()
content = await loop.run_in_executor(None, nfc_file.read)
self.controller_state.set_nfc(content)
async def load_nfc(self):
"""Load first nfc file in nfc folder"""
files=glob.glob("nfc/*.bin")
if len(files) == 0:
self.controller_state.set_nfc(None)
print("Removed NFC from controller")
for file in files:
filepath = os.path.join(os.getcwd(), file)
await self.nfc(filepath)
print("Loaded in NFC from %s" % filepath)
break
def register_handler(self, handle, should_handle):
if callable(handle) and callable(should_handle):
self.handlers.append({'handle': handle, 'should_handle': should_handle})
async def handle(self, **args):
for handler in self.handlers:
should_handle = handler.get('should_handle')
handle = handler.get('handle')
if should_handle is not None and handle is not None and await should_handle(**args):
await handle(**args)
def ensure_list(item):
if type(item) is not list:
return [item]
return item
class Handlers:
def __init__(self, switch_controller):
self.switch_controller = switch_controller
switch_controller.register_handler(self.stick, self.should_stick)
switch_controller.register_handler(self.nfc, self.should_nfc)
switch_controller.register_handler(self.hold, self.should_hold)
switch_controller.register_handler(self.release, self.should_release)
async def is_contoller_button(self, **args):
return args.get('buttons') and self.switch_controller.is_valid_button(*ensure_list(args.get('buttons')))
async def hold(self, **args):
await self.switch_controller.hold(*ensure_list(args.get('buttons')))
async def should_hold(self, **args):
return args.get('side') is None and await self.is_contoller_button(**args) and args.get('release') is False
async def release(self, **args):
await self.switch_controller.release(*ensure_list(args.get('buttons')))
async def should_release(self, **args):
return args.get('side') is None and await self.is_contoller_button(**args) and args.get('release') is True
async def stick(self, **args):
if args.get('direction'):
await self.switch_controller.stick(args.get('side'), args.get('direction'), args.get('release'))
if args.get('invert'):
args['pct'] = 1-args.get('pct')
if args.get('angle') in ('h', 'horizontal'):
await self.switch_controller.stick_h_pct(args.get('side'), args.get('pct'))
elif args.get('angle') in ('v', 'vertical'):
await self.switch_controller.stick_v_pct(args.get('side'), args.get('pct'))
async def should_stick(self, **args):
return args.get('side') and (args.get('direction') or args.get('angle'))
async def nfc(self, **args):
await self.switch_controller.load_nfc()
async def should_nfc(self, **args):
return not args.get('release') and args.get('buttons') in ('nfc', 'amiibo')