reorganization - created package

This commit is contained in:
Robert Martin
2020-02-01 21:13:06 +09:00
parent 6c94f31dec
commit 88d94b78a0
16 changed files with 100 additions and 211 deletions
-134
View File
@@ -1,134 +0,0 @@
.idea
# PYTHON
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
+1 -1
View File
@@ -9,7 +9,7 @@ Tested with Raspberry Pi 4B Raspbian GNU/Linux 10 (buster)
- Start the program
```bash
sudo python3 run_and_pair_switch.py
sudo python3 run_test_controller_buttons.py
```
- Open the "Change Grip/Order" menu of the Switch
- The emulated controller pairs with the Switch and automatically navigates to the "Test Controller Buttons" menu
View File
@@ -1,4 +1,4 @@
import utils
from joycontrol import utils
class ButtonState:
@@ -1,7 +1,7 @@
import asyncio
from button_state import ButtonState
from protocol import ControllerProtocol
from joycontrol.button_state import ButtonState
from joycontrol.protocol import ControllerProtocol
class ControllerState:
+1 -1
View File
@@ -3,7 +3,7 @@ import uuid
import dbus
import utils
from joycontrol import utils
logger = logging.getLogger(__name__)
+2 -2
View File
@@ -3,8 +3,8 @@ import logging
from asyncio import BaseTransport, BaseProtocol
from typing import Optional, Union, Tuple, Text
from controller import Controller
from report import OutputReport, SubCommand, InputReport
from joycontrol.controller import Controller
from joycontrol.report import OutputReport, SubCommand, InputReport
logger = logging.getLogger(__name__)
+2 -2
View File
@@ -1,8 +1,8 @@
import asyncio
from enum import Enum
from button_state import ButtonState
from controller import Controller
from joycontrol.button_state import ButtonState
from joycontrol.controller import Controller
class InputReport:
+72
View File
@@ -0,0 +1,72 @@
import asyncio
import logging
import os
import socket
import joycontrol
from joycontrol import utils
from joycontrol.device import HidDevice
from joycontrol.report import InputReport
from joycontrol.transport import L2CAP_Transport
PROFILE_PATH = os.path.join(os.path.dirname(joycontrol.__file__), 'profile/sdp_record_hid.xml')
logger = logging.getLogger(__name__)
async def _send_empty_input_reports(transport):
report = InputReport()
while True:
await transport.write(report)
await asyncio.sleep(1)
async def create_hid_server(protocol_factory, ctl_psm, itr_psm):
ctl_sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_L2CAP)
itr_sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_L2CAP)
# for some reason we need to restart bluetooth here, the Switch does not connect to the sockets if we don't...
logger.info('Restarting bluetooth service...')
await utils.run_system_command('systemctl restart bluetooth.service')
await asyncio.sleep(1)
ctl_sock.setblocking(False)
itr_sock.setblocking(False)
ctl_sock.bind((socket.BDADDR_ANY, ctl_psm))
itr_sock.bind((socket.BDADDR_ANY, itr_psm))
ctl_sock.listen(1)
itr_sock.listen(1)
protocol = protocol_factory()
hid = HidDevice()
# setting bluetooth adapter name and class to the device we wish to emulate
await hid.set_name(protocol.controller.device_name())
await hid.set_class()
logger.info('Advertising the Bluetooth SDP record...')
hid.register_sdp_record(PROFILE_PATH)
hid.discoverable()
loop = asyncio.get_event_loop()
client_ctl, ctl_address = await loop.sock_accept(ctl_sock)
logger.info(f'Accepted connection at psm {ctl_psm} from {ctl_address}')
client_itr, itr_address = await loop.sock_accept(itr_sock)
logger.info(f'Accepted connection at psm {itr_psm} from {itr_address}')
assert ctl_address[0] == itr_address[0]
transport = L2CAP_Transport(asyncio.get_event_loop(), protocol, client_itr, 50)
protocol.connection_made(transport)
# send some empty input reports until the switch decides to reply
future = asyncio.ensure_future(_send_empty_input_reports(transport))
await protocol.wait_for_output_report()
future.cancel()
try:
await future
except asyncio.CancelledError:
pass
return transport, protocol
+1 -1
View File
@@ -2,7 +2,7 @@ import asyncio
import logging
from typing import Any
from report import InputReport
from joycontrol.report import InputReport
logger = logging.getLogger(__name__)
View File
@@ -1,69 +1,15 @@
import asyncio
import logging
import os
import socket
import logging_default as log
import utils
from controller_state import ButtonState, ControllerState
from device import HidDevice
from protocol import controller_protocol_factory, Controller
from report import InputReport
from transport import L2CAP_Transport
from joycontrol import logging_default as log
from joycontrol.controller_state import ButtonState, ControllerState
from joycontrol.protocol import controller_protocol_factory, Controller
from joycontrol.server import create_hid_server
logger = logging.getLogger(__name__)
async def create_hid_server(protocol_factory, ctl_psm, itr_psm):
ctl_sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_L2CAP)
itr_sock = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_SEQPACKET, socket.BTPROTO_L2CAP)
# for some reason we need to restart bluetooth here, the Switch does not connect to the sockets if we don't...
logger.info('Restarting bluetooth service...')
await utils.run_system_command('systemctl restart bluetooth.service')
await asyncio.sleep(1)
ctl_sock.setblocking(False)
itr_sock.setblocking(False)
ctl_sock.bind((socket.BDADDR_ANY, ctl_psm))
itr_sock.bind((socket.BDADDR_ANY, itr_psm))
ctl_sock.listen(1)
itr_sock.listen(1)
protocol = protocol_factory()
hid = HidDevice()
# setting bluetooth adapter name and class to the device we wish to emulate
await hid.set_name(protocol.controller.device_name())
await hid.set_class()
logger.info('Advertising the Bluetooth SDP record...')
hid.register_sdp_record('profile/sdp_record_hid_pro.xml')
hid.discoverable()
loop = asyncio.get_event_loop()
client_ctl, ctl_address = await loop.sock_accept(ctl_sock)
logger.info(f'Accepted connection at psm {ctl_psm} from {ctl_address}')
client_itr, itr_address = await loop.sock_accept(itr_sock)
logger.info(f'Accepted connection at psm {itr_psm} from {itr_address}')
assert ctl_address[0] == itr_address[0]
transport = L2CAP_Transport(asyncio.get_event_loop(), protocol, client_itr, 50)
protocol.connection_made(transport)
return transport, protocol
async def send_empty_input_reports(transport):
report = InputReport()
while True:
await transport.write(report)
await asyncio.sleep(1)
async def button_push(controller_state, button, sec=0.1):
button_state = ButtonState()
@@ -142,15 +88,6 @@ async def test_controller_buttons(controller_state: ControllerState):
async def main():
transport, protocol = await create_hid_server(controller_protocol_factory(Controller.PRO_CONTROLLER), 17, 19)
# send some empty input reports until the switch decides to reply
future = asyncio.ensure_future(send_empty_input_reports(transport))
await protocol.wait_for_output_report()
future.cancel()
try:
await future
except asyncio.CancelledError:
pass
await test_controller_buttons(ControllerState(transport, protocol))
logger.info('Stopping communication...')
+14
View File
@@ -0,0 +1,14 @@
from setuptools import setup, find_packages
setup(name='joycontrol',
version='0.1',
author='Robert Martin',
author_email='martinro@informatik.hu-berlin.de',
description='Emulate Nintendo Switch Controllers over Bluetooth',
packages=find_packages(),
zip_safe=False,
install_requires=[
# TODO
]
)