├── tests ├── __init__.py ├── spin_wheel_test.py └── run_controller_test.py ├── .gitignore ├── logidrivepy ├── dll │ └── LogitechSteeringWheelEnginesWrapper.dll ├── __init__.py ├── controller.py ├── structs.py ├── constants.py └── functions.py ├── setup.py ├── LICENSE.txt └── README.md /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | build 3 | dist 4 | logidrivepy.egg-info 5 | -------------------------------------------------------------------------------- /logidrivepy/dll/LogitechSteeringWheelEnginesWrapper.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cengizozel/LogiDrivePy/HEAD/logidrivepy/dll/LogitechSteeringWheelEnginesWrapper.dll -------------------------------------------------------------------------------- /logidrivepy/__init__.py: -------------------------------------------------------------------------------- 1 | from .constants import LogitechControllerConstants 2 | from .structs import LogitechControllerStructs 3 | from .functions import LogitechControllerFunctions 4 | from .controller import LogitechController 5 | -------------------------------------------------------------------------------- /tests/spin_wheel_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('../logidrivepy') 3 | from logidrivepy import LogitechController 4 | import time 5 | 6 | def spin_controller(controller): 7 | for i in range(-100, 102, 2): 8 | controller.LogiPlaySpringForce(0, i, 100, 40) 9 | controller.logi_update() 10 | time.sleep(0.1) 11 | 12 | 13 | def spin_test(): 14 | controller = LogitechController() 15 | 16 | controller.steering_initialize() 17 | print("\n---Logitech Spin Test---") 18 | spin_controller(controller) 19 | print("Spin test passed.\n") 20 | 21 | controller.steering_shutdown() 22 | 23 | 24 | if __name__ == "__main__": 25 | spin_test() 26 | -------------------------------------------------------------------------------- /logidrivepy/controller.py: -------------------------------------------------------------------------------- 1 | from .constants import LogitechControllerConstants 2 | from .structs import LogitechControllerStructs 3 | from .functions import LogitechControllerFunctions 4 | import pathlib 5 | 6 | class LogitechController(LogitechControllerConstants, LogitechControllerStructs, LogitechControllerFunctions): 7 | def __init__(self, dll_path=None): 8 | if dll_path is None: 9 | # Get the location of the current file (controller.py) 10 | current_file_path = pathlib.Path(__file__) 11 | # Get the absolute path to the DLL 12 | dll_path = current_file_path.parent / 'dll' / 'LogitechSteeringWheelEnginesWrapper.dll' 13 | 14 | self.structs = LogitechControllerStructs() 15 | LogitechControllerFunctions.__init__(self, str(dll_path), self.structs) 16 | -------------------------------------------------------------------------------- /tests/run_controller_test.py: -------------------------------------------------------------------------------- 1 | import sys 2 | sys.path.append('../logidrivepy') 3 | from logidrivepy import LogitechController 4 | 5 | 6 | def run_test(): 7 | controller = LogitechController() 8 | 9 | steering_initialize = controller.steering_initialize() 10 | logi_update = controller.logi_update() 11 | is_connected = controller.is_connected(0) 12 | 13 | print(f"\n---Logitech Controller Test---") 14 | print(f"steering_initialize: {steering_initialize}") 15 | print(f"logi_update: {logi_update}") 16 | print(f"is_connected: {is_connected}") 17 | 18 | if steering_initialize and logi_update and is_connected: 19 | print(f"All tests passed.\n") 20 | else: 21 | print(f"Did not pass all tests.\n") 22 | 23 | controller.steering_shutdown() 24 | 25 | 26 | if __name__ == "__main__": 27 | run_test() 28 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='logidrivepy', 5 | version='0.2.0', 6 | description='A Python module for interfacing with Logitech steering wheels.', 7 | long_description=open('README.md').read(), 8 | long_description_content_type='text/markdown', 9 | url='https://github.com/cengizozel/logidrivepy', 10 | author='Cengiz Ozel', 11 | author_email='cozel@cs.rochester.edu', 12 | license='MIT', 13 | packages=find_packages(), 14 | include_package_data=True, 15 | package_data={'logidrivepy': ['dll/*.dll']}, 16 | classifiers=[ 17 | 'Development Status :: 4 - Beta', 18 | 'Intended Audience :: Developers', 19 | 'License :: OSI Approved :: MIT License', 20 | 'Programming Language :: Python :: 3.8', 21 | 'Programming Language :: Python :: 3.9', 22 | 'Programming Language :: Python :: 3.10', 23 | ], 24 | keywords='logitech, g920, driving, steering-wheel, controller', 25 | install_requires=[ 26 | ], 27 | ) 28 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Cengiz Ozel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /logidrivepy/structs.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | 3 | class LogitechControllerStructs: 4 | # Structs 5 | class LogiControllerPropertiesData(ctypes.Structure): 6 | _fields_ = [ 7 | ("forceEnable", ctypes.c_bool), 8 | ("overallGain", ctypes.c_int), 9 | ("springGain", ctypes.c_int), 10 | ("damperGain", ctypes.c_int), 11 | ("defaultSpringEnabled", ctypes.c_bool), 12 | ("defaultSpringGain", ctypes.c_int), 13 | ("combinePedals", ctypes.c_bool), 14 | ("wheelRange", ctypes.c_int), 15 | ("gameSettingsEnabled", ctypes.c_bool), 16 | ("allowGameSettings", ctypes.c_bool) 17 | ] 18 | 19 | class DIJOYSTATE2ENGINES(ctypes.Structure): 20 | _fields_ = [ 21 | ("lX", ctypes.c_int), 22 | ("lY", ctypes.c_int), 23 | ("lZ", ctypes.c_int), 24 | ("lRx", ctypes.c_int), 25 | ("lRy", ctypes.c_int), 26 | ("lRz", ctypes.c_int), 27 | ("rglSlider", ctypes.c_int * 2), 28 | ("rgdwPOV", ctypes.c_uint * 4), 29 | ("rgbButtons", ctypes.c_byte * 128), 30 | ("lVX", ctypes.c_int), 31 | ("lVY", ctypes.c_int), 32 | ("lVZ", ctypes.c_int), 33 | ("lVRx", ctypes.c_int), 34 | ("lVRy", ctypes.c_int), 35 | ("lVRz", ctypes.c_int), 36 | ("rglVSlider", ctypes.c_int * 2), 37 | ("lAX", ctypes.c_int), 38 | ("lAY", ctypes.c_int), 39 | ("lAZ", ctypes.c_int), 40 | ("lARx", ctypes.c_int), 41 | ("lARy", ctypes.c_int), 42 | ("lARz", ctypes.c_int), 43 | ("rglASlider", ctypes.c_int * 2), 44 | ("lFX", ctypes.c_int), 45 | ("lFY", ctypes.c_int), 46 | ("lFZ", ctypes.c_int), 47 | ("lFRx", ctypes.c_int), 48 | ("lFRy", ctypes.c_int), 49 | ("lFRz", ctypes.c_int), 50 | ("rglFSlider", ctypes.c_int * 2) 51 | ] 52 | -------------------------------------------------------------------------------- /logidrivepy/constants.py: -------------------------------------------------------------------------------- 1 | class LogitechControllerConstants: 2 | # CONSTANTS 3 | LOGI_MAX_CONTROLLERS = 4 4 | 5 | # Force types 6 | LOGI_FORCE_NONE = -1 7 | LOGI_FORCE_SPRING = 0 8 | LOGI_FORCE_CONSTANT = 1 9 | LOGI_FORCE_DAMPER = 2 10 | LOGI_FORCE_SIDE_COLLISION = 3 11 | LOGI_FORCE_FRONTAL_COLLISION = 4 12 | LOGI_FORCE_DIRT_ROAD = 5 13 | LOGI_FORCE_BUMPY_ROAD = 6 14 | LOGI_FORCE_SLIPPERY_ROAD = 7 15 | LOGI_FORCE_SURFACE_EFFECT = 8 16 | LOGI_NUMBER_FORCE_EFFECTS = 9 17 | LOGI_FORCE_SOFTSTOP = 10 18 | LOGI_FORCE_CAR_AIRBORNE = 11 19 | 20 | # Periodic types for surface effect 21 | LOGI_PERIODICTYPE_NONE = -1 22 | LOGI_PERIODICTYPE_SINE = 0 23 | LOGI_PERIODICTYPE_SQUARE = 1 24 | LOGI_PERIODICTYPE_TRIANGLE = 2 25 | 26 | # Device types 27 | LOGI_DEVICE_TYPE_NONE = -1 28 | LOGI_DEVICE_TYPE_WHEEL = 0 29 | LOGI_DEVICE_TYPE_JOYSTICK = 1 30 | LOGI_DEVICE_TYPE_GAMEPAD = 2 31 | LOGI_DEVICE_TYPE_OTHER = 3 32 | LOGI_NUMBER_DEVICE_TYPES = 4 33 | 34 | # Manufacturer types 35 | LOGI_MANUFACTURER_NONE = -1 36 | LOGI_MANUFACTURER_LOGITECH = 0 37 | LOGI_MANUFACTURER_MICROSOFT = 1 38 | LOGI_MANUFACTURER_OTHER = 2 39 | 40 | # Model types 41 | LOGI_MODEL_G27 = 0 42 | LOGI_MODEL_DRIVING_FORCE_GT = 1 43 | LOGI_MODEL_G25 = 2 44 | LOGI_MODEL_MOMO_RACING = 3 45 | LOGI_MODEL_MOMO_FORCE = 4 46 | LOGI_MODEL_DRIVING_FORCE_PRO = 5 47 | LOGI_MODEL_DRIVING_FORCE = 6 48 | LOGI_MODEL_NASCAR_RACING_WHEEL = 7 49 | LOGI_MODEL_FORMULA_FORCE = 8 50 | LOGI_MODEL_FORMULA_FORCE_GP = 9 51 | LOGI_MODEL_FORCE_3D_PRO = 10 52 | LOGI_MODEL_EXTREME_3D_PRO = 11 53 | LOGI_MODEL_FREEDOM_24 = 12 54 | LOGI_MODEL_ATTACK_3 = 13 55 | LOGI_MODEL_FORCE_3D = 14 56 | LOGI_MODEL_STRIKE_FORCE_3D = 15 57 | LOGI_MODEL_G940_JOYSTICK = 16 58 | LOGI_MODEL_G940_THROTTLE = 17 59 | LOGI_MODEL_G940_PEDALS = 18 60 | LOGI_MODEL_RUMBLEPAD = 19 61 | LOGI_MODEL_RUMBLEPAD_2 = 20 62 | LOGI_MODEL_CORDLESS_RUMBLEPAD_2 = 21 63 | LOGI_MODEL_CORDLESS_GAMEPAD = 22 64 | LOGI_MODEL_DUAL_ACTION_GAMEPAD = 23 65 | LOGI_MODEL_PRECISION_GAMEPAD_2 = 24 66 | LOGI_MODEL_CHILLSTREAM = 25 67 | LOGI_MODEL_G29 = 26 68 | LOGI_MODEL_G920 = 27 69 | LOGI_NUMBER_MODELS = 28 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LogiDrivePy - Logitech Controller Python Module 2 | 3 | A Python module for interfacing with Logitech steering wheels. This module was tested with the Logitech G920 Driving Force Racing Wheel but should work with other Logitech devices as specified in the [Supported Devices](#supported-devices) section. 4 | 5 | ## Introduction 6 | 7 | This Python module facilitates interaction with Logitech steering wheels, serving as a bridge between Python and the Logitech Steering Wheel's software components. 8 | 9 | The original functionality was provided in the form of a C# implementation as part of the [Logitech Steering Wheel SDK](https://www.logitechg.com/en-us/innovation/developer-lab.html) for the [Unity Game Engine](https://assetstore.unity.com/packages/tools/integration/logitech-gaming-sdk-6630). The SDK allows developers to easily add Logitech steering wheel support to their games, using a set of predefined force feedback effects or creating custom effects by defining specific forces. 10 | 11 | With the aim to extend these capabilities to the Python community, this module was developed as an accessible tool for Python developers. Utilizing the `ctypes` Python library, the module enables Python scripts to load and interact with Logitech steering wheels through Logitech's DLL (LogitechSteeringWheelEnginesWrapper.dll), providing a seamless integration experience for developers working with these devices. 12 | 13 | ## Supported Devices 14 | 15 | This library was tested with the Logitech G920 Driving Force Racing Wheel, but according to Logitech documentation, it should also work with the following devices: 16 | 17 | ### Logitech 18 | - G29 19 | - G920 20 | - Driving Force GT 21 | - G27 22 | - G25 23 | - Driving Force Pro 24 | - MOMO Force 25 | - MOMO Racing 26 | - Formula Force GP 27 | - Driving Force 28 | - Formula Force 29 | - Force 3D 30 | - Strike Force 3D 31 | - Freedom 2.4 Cordless Joystick 32 | - Cordless Rumblepad 33 | - Cordless Rumblepad 2 34 | - Rumblepad 35 | 36 | ### Microsoft 37 | - Sidewinder Force Feedback 2 (Stick) 38 | - Sidewinder Force (Wheel) 39 | 40 | ### Other (with Immersion drivers) 41 | - Saitek Cyborg 3D Force 42 | - Act-Labs Force RS Wheel 43 | 44 | ## Installation 45 | 46 | To install the package, simply use pip: 47 | ``` 48 | pip install logidrivepy 49 | ``` 50 | 51 | The package directory layout is organized as follows: 52 | ``` 53 | LogiDrivePy 54 | | .gitignore 55 | | LICENSE.txt 56 | | README.md 57 | | setup.py 58 | | 59 | +---logidrivepy 60 | | | constants.py 61 | | | controller.py 62 | | | functions.py 63 | | | structs.py 64 | | | __init__.py 65 | | | 66 | | +---dll 67 | | | LogitechSteeringWheelEnginesWrapper.dll 68 | | 69 | +---tests 70 | | run_controller_test.py 71 | | spin_wheel_test.py 72 | | __init__.py 73 | ``` 74 | 75 | ## Usage 76 | 77 | Here's a simple example on how to use the Logitech Controller module in your Python script. 78 | 79 | ```python 80 | from logidrivepy import LogitechController 81 | 82 | controller = LogitechController() 83 | 84 | print(f"steering_initialize: {controller.steering_initialize()}") 85 | print(f"logi_update: {controller.logi_update()}") 86 | print(f"is_connected: {controller.is_connected(0)}") 87 | 88 | controller.steering_shutdown() 89 | ``` 90 | 91 | ## Dependencies 92 | 93 | This library uses the `ctypes` Python library to load and call functions from the Logitech's DLL (LogitechSteeringWheelEnginesWrapper.dll). The ctypes library is part of the standard Python library and should be installed by default with a standard Python installation. 94 | 95 | This library also requires `Tkinter`, a Python binding to the Tk GUI toolkit. Tkinter is part of the standard Python library for Python 3 and should be installed by default with a standard Python installation. 96 | 97 | ## License 98 | 99 | This project is licensed under the terms of the MIT license. For more details, see the `LICENSE.txt` file. 100 | 101 | Please note that this project is **not** a reverse engineering of the Logitech Gaming Steering Wheel SDK. Instead, this project aims to provide an interface to Logitech steering wheels in Python by utilizing the SDK's provided DLL file (LogitechSteeringWheelEnginesWrapper.dll) without decompiling, disassembling, or otherwise altering the SDK's components. This project is designed to extend the functionality of the SDK for Python developers while respecting and complying with the original End-User License Agreement of the SDK. 102 | -------------------------------------------------------------------------------- /logidrivepy/functions.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import tkinter as tk 3 | 4 | class LogitechControllerFunctions: 5 | def __init__(self, dll_path, structs, use_gui=True): 6 | self.structs = structs 7 | self.logi_dll = ctypes.cdll.LoadLibrary(dll_path) 8 | 9 | # If use_gui flag is True, then create a hidden Tkinter window 10 | if use_gui: 11 | self.root = tk.Tk() 12 | self.root.withdraw() 13 | self.root.update() 14 | 15 | # Function Definitions 16 | # LogiSteeringInitialize 17 | self.LogiSteeringInitialize = self.logi_dll.LogiSteeringInitialize 18 | self.LogiSteeringInitialize.argtypes = [ctypes.c_bool] 19 | self.LogiSteeringInitialize.restype = ctypes.c_bool 20 | 21 | # LogiUpdate 22 | self.LogiUpdate = self.logi_dll.LogiUpdate 23 | self.LogiUpdate.argtypes = [] 24 | self.LogiUpdate.restype = ctypes.c_bool 25 | 26 | # LogiGetStateENGINES 27 | self.LogiGetStateENGINES = self.logi_dll.LogiGetStateENGINES 28 | self.LogiGetStateENGINES.argtypes = [ctypes.c_int] 29 | self.LogiGetStateENGINES.restype = ctypes.POINTER(self.structs.DIJOYSTATE2ENGINES) 30 | 31 | def LogiGetStateCSharp(index): 32 | ret = self.structs.DIJOYSTATE2ENGINES() 33 | ret.rglSlider = (ctypes.c_int * 2)() 34 | ret.rgdwPOV = (ctypes.c_uint * 4)() 35 | ret.rgbButtons = (ctypes.c_byte * 128)() 36 | ret.rglVSlider = (ctypes.c_int * 2)() 37 | ret.rglASlider = (ctypes.c_int * 2)() 38 | ret.rglFSlider = (ctypes.c_int * 2)() 39 | try: 40 | ret = self.LogiGetStateENGINES(index).contents 41 | except ctypes.ArgumentError: 42 | pass 43 | return ret 44 | 45 | # LogiGetDevicePath 46 | self.LogiGetDevicePath = self.logi_dll.LogiGetDevicePath 47 | self.LogiGetDevicePath.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int] 48 | self.LogiGetDevicePath.restype = ctypes.c_bool 49 | 50 | # LogiGetFriendlyProductName 51 | self.LogiGetFriendlyProductName = self.logi_dll.LogiGetFriendlyProductName 52 | self.LogiGetFriendlyProductName.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_int] 53 | self.LogiGetFriendlyProductName.restype = ctypes.c_bool 54 | 55 | # LogiIsConnected 56 | self.LogiIsConnected = self.logi_dll.LogiIsConnected 57 | self.LogiIsConnected.argtypes = [ctypes.c_int] 58 | self.LogiIsConnected.restype = ctypes.c_bool 59 | 60 | # LogiIsDeviceConnected 61 | self.LogiIsDeviceConnected = self.logi_dll.LogiIsDeviceConnected 62 | self.LogiIsDeviceConnected.argtypes = [ctypes.c_int, ctypes.c_int] 63 | self.LogiIsDeviceConnected.restype = ctypes.c_bool 64 | 65 | # LogiIsManufacturerConnected 66 | self.LogiIsManufacturerConnected = self.logi_dll.LogiIsManufacturerConnected 67 | self.LogiIsManufacturerConnected.argtypes = [ctypes.c_int, ctypes.c_int] 68 | self.LogiIsManufacturerConnected.restype = ctypes.c_bool 69 | 70 | # LogiIsModelConnected 71 | self.LogiIsModelConnected = self.logi_dll.LogiIsModelConnected 72 | self.LogiIsModelConnected.argtypes = [ctypes.c_int, ctypes.c_int] 73 | self.LogiIsModelConnected.restype = ctypes.c_bool 74 | 75 | # LogiButtonTriggered 76 | self.LogiButtonTriggered = self.logi_dll.LogiButtonTriggered 77 | self.LogiButtonTriggered.argtypes = [ctypes.c_int, ctypes.c_int] 78 | self.LogiButtonTriggered.restype = ctypes.c_bool 79 | 80 | # LogiButtonReleased 81 | self.LogiButtonReleased = self.logi_dll.LogiButtonReleased 82 | self.LogiButtonReleased.argtypes = [ctypes.c_int, ctypes.c_int] 83 | self.LogiButtonReleased.restype = ctypes.c_bool 84 | 85 | # LogiButtonIsPressed 86 | self.LogiButtonIsPressed = self.logi_dll.LogiButtonIsPressed 87 | self.LogiButtonIsPressed.argtypes = [ctypes.c_int, ctypes.c_int] 88 | self.LogiButtonIsPressed.restype = ctypes.c_bool 89 | 90 | # LogiGenerateNonLinearValues 91 | self.LogiGenerateNonLinearValues = self.logi_dll.LogiGenerateNonLinearValues 92 | self.LogiGenerateNonLinearValues.argtypes = [ctypes.c_int, ctypes.c_int] 93 | self.LogiGenerateNonLinearValues.restype = ctypes.c_bool 94 | 95 | # LogiGetNonLinearValue 96 | self.LogiGetNonLinearValue = self.logi_dll.LogiGetNonLinearValue 97 | self.LogiGetNonLinearValue.argtypes = [ctypes.c_int, ctypes.c_int] 98 | self.LogiGetNonLinearValue.restype = ctypes.c_int 99 | 100 | # LogiHasForceFeedback 101 | self.LogiHasForceFeedback = self.logi_dll.LogiHasForceFeedback 102 | self.LogiHasForceFeedback.argtypes = [ctypes.c_int] 103 | self.LogiHasForceFeedback.restype = ctypes.c_bool 104 | 105 | # LogiIsPlaying 106 | self.LogiIsPlaying = self.logi_dll.LogiIsPlaying 107 | self.LogiIsPlaying.argtypes = [ctypes.c_int, ctypes.c_int] 108 | self.LogiIsPlaying.restype = ctypes.c_bool 109 | 110 | # LogiPlaySpringForce 111 | self.LogiPlaySpringForce = self.logi_dll.LogiPlaySpringForce 112 | self.LogiPlaySpringForce.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int] 113 | self.LogiPlaySpringForce.restype = ctypes.c_bool 114 | 115 | # LogiStopSpringForce 116 | self.LogiStopSpringForce = self.logi_dll.LogiStopSpringForce 117 | self.LogiStopSpringForce.argtypes = [ctypes.c_int] 118 | self.LogiStopSpringForce.restype = ctypes.c_bool 119 | 120 | # LogiPlayConstantForce 121 | self.LogiPlayConstantForce = self.logi_dll.LogiPlayConstantForce 122 | self.LogiPlayConstantForce.argtypes = [ctypes.c_int, ctypes.c_int] 123 | self.LogiPlayConstantForce.restype = ctypes.c_bool 124 | 125 | # LogiStopConstantForce 126 | self.LogiStopConstantForce = self.logi_dll.LogiStopConstantForce 127 | self.LogiStopConstantForce.argtypes = [ctypes.c_int] 128 | self.LogiStopConstantForce.restype = ctypes.c_bool 129 | 130 | # LogiPlayDamperForce 131 | self.LogiPlayDamperForce = self.logi_dll.LogiPlayDamperForce 132 | self.LogiPlayDamperForce.argtypes = [ctypes.c_int, ctypes.c_int] 133 | self.LogiPlayDamperForce.restype = ctypes.c_bool 134 | 135 | # LogiStopDamperForce 136 | self.LogiStopDamperForce = self.logi_dll.LogiStopDamperForce 137 | self.LogiStopDamperForce.argtypes = [ctypes.c_int] 138 | self.LogiStopDamperForce.restype = ctypes.c_bool 139 | 140 | # LogiPlaySideCollisionForce 141 | self.LogiPlaySideCollisionForce = self.logi_dll.LogiPlaySideCollisionForce 142 | self.LogiPlaySideCollisionForce.argtypes = [ctypes.c_int, ctypes.c_int] 143 | self.LogiPlaySideCollisionForce.restype = ctypes.c_bool 144 | 145 | # LogiPlayFrontalCollisionForce 146 | self.LogiPlayFrontalCollisionForce = self.logi_dll.LogiPlayFrontalCollisionForce 147 | self.LogiPlayFrontalCollisionForce.argtypes = [ctypes.c_int, ctypes.c_int] 148 | self.LogiPlayFrontalCollisionForce.restype = ctypes.c_bool 149 | 150 | # LogiPlayDirtRoadEffect 151 | self.LogiPlayDirtRoadEffect = self.logi_dll.LogiPlayDirtRoadEffect 152 | self.LogiPlayDirtRoadEffect.argtypes = [ctypes.c_int, ctypes.c_int] 153 | self.LogiPlayDirtRoadEffect.restype = ctypes.c_bool 154 | 155 | # LogiStopDirtRoadEffect 156 | self.LogiStopDirtRoadEffect = self.logi_dll.LogiStopDirtRoadEffect 157 | self.LogiStopDirtRoadEffect.argtypes = [ctypes.c_int] 158 | self.LogiStopDirtRoadEffect.restype = ctypes.c_bool 159 | 160 | # LogiPlayBumpyRoadEffect 161 | self.LogiPlayBumpyRoadEffect = self.logi_dll.LogiPlayBumpyRoadEffect 162 | self.LogiPlayBumpyRoadEffect.argtypes = [ctypes.c_int, ctypes.c_int] 163 | self.LogiPlayBumpyRoadEffect.restype = ctypes.c_bool 164 | 165 | # LogiStopBumpyRoadEffect 166 | self.LogiStopBumpyRoadEffect = self.logi_dll.LogiStopBumpyRoadEffect 167 | self.LogiStopBumpyRoadEffect.argtypes = [ctypes.c_int] 168 | self.LogiStopBumpyRoadEffect.restype = ctypes.c_bool 169 | 170 | # LogiPlaySlipperyRoadEffect 171 | self.LogiPlaySlipperyRoadEffect = self.logi_dll.LogiPlaySlipperyRoadEffect 172 | self.LogiPlaySlipperyRoadEffect.argtypes = [ctypes.c_int, ctypes.c_int] 173 | self.LogiPlaySlipperyRoadEffect.restype = ctypes.c_bool 174 | 175 | # LogiStopSlipperyRoadEffect 176 | self.LogiStopSlipperyRoadEffect = self.logi_dll.LogiStopSlipperyRoadEffect 177 | self.LogiStopSlipperyRoadEffect.argtypes = [ctypes.c_int] 178 | self.LogiStopSlipperyRoadEffect.restype = ctypes.c_bool 179 | 180 | # LogiPlaySurfaceEffect 181 | self.LogiPlaySurfaceEffect = self.logi_dll.LogiPlaySurfaceEffect 182 | self.LogiPlaySurfaceEffect.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int] 183 | self.LogiPlaySurfaceEffect.restype = ctypes.c_bool 184 | 185 | # LogiStopSurfaceEffect 186 | self.LogiStopSurfaceEffect = self.logi_dll.LogiStopSurfaceEffect 187 | self.LogiStopSurfaceEffect.argtypes = [ctypes.c_int] 188 | self.LogiStopSurfaceEffect.restype = ctypes.c_bool 189 | 190 | # LogiPlayCarAirborne 191 | self.LogiPlayCarAirborne = self.logi_dll.LogiPlayCarAirborne 192 | self.LogiPlayCarAirborne.argtypes = [ctypes.c_int] 193 | self.LogiPlayCarAirborne.restype = ctypes.c_bool 194 | 195 | # LogiStopCarAirborne 196 | self.LogiStopCarAirborne = self.logi_dll.LogiStopCarAirborne 197 | self.LogiStopCarAirborne.argtypes = [ctypes.c_int] 198 | self.LogiStopCarAirborne.restype = ctypes.c_bool 199 | 200 | # LogiPlaySoftstopForce 201 | self.LogiPlaySoftstopForce = self.logi_dll.LogiPlaySoftstopForce 202 | self.LogiPlaySoftstopForce.argtypes = [ctypes.c_int, ctypes.c_int] 203 | self.LogiPlaySoftstopForce.restype = ctypes.c_bool 204 | 205 | # LogiStopSoftstopForce 206 | self.LogiStopSoftstopForce = self.logi_dll.LogiStopSoftstopForce 207 | self.LogiStopSoftstopForce.argtypes = [ctypes.c_int] 208 | self.LogiStopSoftstopForce.restype = ctypes.c_bool 209 | 210 | # LogiSetPreferredControllerProperties 211 | self.LogiSetPreferredControllerProperties = self.logi_dll.LogiSetPreferredControllerProperties 212 | self.LogiSetPreferredControllerProperties.argtypes = [self.structs.LogiControllerPropertiesData] 213 | self.LogiSetPreferredControllerProperties.restype = ctypes.c_bool 214 | 215 | # LogiGetCurrentControllerProperties 216 | self.LogiGetCurrentControllerProperties = self.logi_dll.LogiGetCurrentControllerProperties 217 | self.LogiGetCurrentControllerProperties.argtypes = [ctypes.c_int, ctypes.POINTER(self.structs.LogiControllerPropertiesData)] 218 | self.LogiGetCurrentControllerProperties.restype = ctypes.c_bool 219 | 220 | # LogiGetShifterMode 221 | self.LogiGetShifterMode = self.logi_dll.LogiGetShifterMode 222 | self.LogiGetShifterMode.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_int)] 223 | self.LogiGetShifterMode.restype = ctypes.c_bool 224 | 225 | # LogiGetOperatingRange 226 | self.LogiGetOperatingRange = self.logi_dll.LogiGetOperatingRange 227 | self.LogiGetOperatingRange.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_int)] 228 | self.LogiGetOperatingRange.restype = ctypes.c_bool 229 | 230 | # LogiSetOperatingRange 231 | self.LogiSetOperatingRange = self.logi_dll.LogiSetOperatingRange 232 | self.LogiSetOperatingRange.argtypes = [ctypes.c_int, ctypes.c_int] 233 | self.LogiSetOperatingRange.restype = ctypes.c_bool 234 | 235 | # LogiPlayLeds 236 | self.LogiPlayLeds = self.logi_dll.LogiPlayLeds 237 | self.LogiPlayLeds.argtypes = [ctypes.c_int, ctypes.c_float, ctypes.c_float, ctypes.c_float] 238 | self.LogiPlayLeds.restype = ctypes.c_bool 239 | 240 | # LogiSteeringShutdown 241 | self.LogiSteeringShutdown = self.logi_dll.LogiSteeringShutdown 242 | self.LogiSteeringShutdown.argtypes = [] 243 | self.LogiSteeringShutdown.restype = None 244 | 245 | def steering_initialize(self, ignore_xinput_controllers=True): 246 | """ 247 | Initializes the steering system if certain conditions are met. 248 | 249 | Args: 250 | - ignore_xinput_controllers (bool, optional): If true, X-input controllers are ignored. Default is True. 251 | 252 | Returns: 253 | - bool: Returns True if: 254 | * No other instance is running, and 255 | * The main window is in the foreground. 256 | Otherwise, returns False. 257 | 258 | Note: 259 | - This is a part of the Logitech G Hub APIs. Make sure to install the necessary drivers and libraries. 260 | 261 | Example: 262 | >>> steering_initialize(ignore_xinput_controllers=False) 263 | """ 264 | return self.LogiSteeringInitialize(ignore_xinput_controllers) 265 | 266 | def logi_update(self): 267 | """ 268 | Updates forces and controller connections, if the main window handler is found. This function needs to be 269 | called every frame of your application. 270 | 271 | Args: 272 | None 273 | 274 | Returns: 275 | - bool: Returns True if successful, False if either: 276 | * LogiSteeringInitialize() hasn't been called, or 277 | * The main window handler could not be found. 278 | 279 | Example: 280 | >>> logi_update() 281 | """ 282 | return self.LogiUpdate() 283 | 284 | def get_state_engines(self, index): 285 | """ 286 | A simplified version of LogiGetState for use without DirectInput. Returns a DIJOYSTATE2ENGINES struct. 287 | 288 | Args: 289 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 290 | Index 1 to the second game controller. 291 | 292 | Returns: 293 | - DIJOYSTATE2ENGINES: A structure containing the device's positional information for axes, POVs and buttons. 294 | 295 | Example: 296 | >>> get_state_engines(0) 297 | """ 298 | return self.LogiGetStateENGINES(index) 299 | 300 | def get_device_path(self, index, buffer, buffer_size): 301 | """ 302 | Retrieves the device's USB path for determining unique devices. 303 | 304 | Args: 305 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 306 | Index 1 to the second game controller. 307 | - buffer (wchar_t*): A pre-allocated buffer that will hold the device path. 308 | - buffer_size (int): The size in bytes of the buffer. 309 | 310 | Returns: 311 | - bool: True if successful, False if there was an error copying the path into the buffer. 312 | 313 | Example: 314 | >>> get_device_path(0, buffer, buffer_size) 315 | """ 316 | return self.LogiGetDevicePath(index, buffer, buffer_size) 317 | 318 | def get_friendly_product_name(self, index, buffer, buffer_size): 319 | """ 320 | Retrieves the device's friendly product name. 321 | 322 | Args: 323 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 324 | Index 1 to the second game controller. 325 | - buffer (wchar_t*): A pre-allocated buffer that will contain the device's friendly product name. 326 | - buffer_size (int): The size in bytes of the buffer. 327 | 328 | Returns: 329 | - bool: True if successful, False if there was an error copying the name into the buffer. 330 | 331 | Example: 332 | >>> get_friendly_product_name(0, buffer, buffer_size) 333 | """ 334 | return self.LogiGetFriendlyProductName(index, buffer, buffer_size) 335 | 336 | def is_connected(self, index): 337 | """ 338 | Checks if a game controller is connected at the specified index. 339 | 340 | Args: 341 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 342 | Index 1 to the second game controller. 343 | 344 | Returns: 345 | - bool: True if a device is connected at the specified index, False otherwise. 346 | 347 | Example: 348 | >>> is_connected(0) 349 | """ 350 | return self.LogiIsConnected(index) 351 | 352 | 353 | def is_device_connected(self, index, device_type): 354 | """ 355 | Checks if the specified device is connected at the given index. 356 | 357 | Args: 358 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 359 | Index 1 to the second game controller. 360 | - device_type (int): Type of the device to check for. Possible types are: 361 | * LOGI_DEVICE_TYPE_WHEEL 362 | * LOGI_DEVICE_TYPE_JOYSTICK 363 | * LOGI_DEVICE_TYPE_GAMEPAD 364 | * LOGI_DEVICE_TYPE_OTHER 365 | 366 | Returns: 367 | - bool: True if the specified device is connected at that index, False otherwise. 368 | 369 | Example: 370 | >>> is_device_connected(0, LOGI_DEVICE_TYPE_WHEEL) 371 | """ 372 | return self.LogiIsDeviceConnected(index, device_type) 373 | 374 | def is_manufacturer_connected(self, index, manufacturer_name): 375 | """ 376 | Checks if the device connected at the given index is made by the specified manufacturer. 377 | 378 | Args: 379 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 380 | Index 1 to the second game controller. 381 | - manufacturer_name (int): Name of the manufacturer the device has been made by. Possible types are: 382 | * LOGI_MANUFACTURER_LOGITECH 383 | * LOGI_MANUFACTURER_MICROSOFT 384 | * LOGI_MANUFACTURER_OTHER 385 | 386 | Returns: 387 | - bool: True if a PC controller of the specified manufacturer is connected, False otherwise. 388 | 389 | Example: 390 | >>> is_manufacturer_connected(0, LOGI_MANUFACTURER_LOGITECH) 391 | """ 392 | return self.LogiIsManufacturerConnected(index, manufacturer_name) 393 | 394 | def is_model_connected(self, index, model_name): 395 | """ 396 | Checks if a controller of the specified model is connected at the given index. 397 | 398 | Args: 399 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 400 | Index 1 to the second game controller. 401 | - model_name (int): Name of the model of the device. Possible types include various models such as: 402 | * LOGI_MODEL_G27 403 | * LOGI_MODEL_G25 404 | * LOGI_MODEL_MOMO_RACING 405 | * LOGI_MODEL_MOMO_FORCE 406 | * LOGI_MODEL_DRIVING_FORCE_PRO 407 | * And many more... 408 | 409 | Returns: 410 | - bool: True if a controller of the specified model is connected, False otherwise. 411 | 412 | Example: 413 | >>> is_model_connected(0, LOGI_MODEL_G27) 414 | """ 415 | return self.LogiIsModelConnected(index, model_name) 416 | 417 | def button_triggered(self, index, button_number): 418 | """ 419 | Checks if the device connected at the given index is currently triggering the specified button. 420 | 421 | Args: 422 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 423 | Index 1 to the second game controller. 424 | - button_number (int): The number of the button that we want to check. Possible numbers are: 0 to 127. 425 | 426 | Returns: 427 | - bool: True if the button was triggered, False otherwise. 428 | 429 | Example: 430 | >>> button_triggered(0, 1) 431 | """ 432 | return self.LogiButtonTriggered(index, button_number) 433 | 434 | def button_released(self, index, button_number): 435 | """ 436 | Checks if the specified button on the device connected at the given index has been released. 437 | 438 | Args: 439 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 440 | Index 1 to the second game controller. 441 | - button_number (int): The number of the button that we want to check. Possible numbers are: 0 to 127. 442 | 443 | Returns: 444 | - bool: True if the button was released, False otherwise. 445 | 446 | Example: 447 | >>> button_released(0, 1) 448 | """ 449 | return self.LogiButtonReleased(index, button_number) 450 | 451 | def button_is_pressed(self, index, button_number): 452 | """ 453 | Checks if the specified button on the device connected at the given index is currently being pressed. 454 | 455 | Args: 456 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 457 | Index 1 to the second game controller. 458 | - button_number (int): The number of the button that we want to check. Possible numbers are: 0 to 127. 459 | 460 | Returns: 461 | - bool: True if the button is being pressed, False otherwise. 462 | 463 | Example: 464 | >>> button_is_pressed(0, 1) 465 | """ 466 | return self.LogiButtonIsPressed(index, button_number) 467 | 468 | def generate_non_linear_values(self, index, non_lin_coeff): 469 | """ 470 | Generates non-linear values for the game controller's axis to improve gameplay by reducing 471 | sensitivity issues. This method defines a non-linearity coefficient which determines how strongly 472 | non-linear the curve will be, creating a mapping table in the form of an array. 473 | 474 | Args: 475 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 476 | Index 1 to the second game controller. 477 | - non_lin_coeff (int): Value representing how much non-linearity should be applied. Range is -100 to 100. 478 | 0 = linear curve, 100 = maximum non-linear curve with less sensitivity around center, 479 | -100 = maximum non-linearity with more sensitivity around center position. 480 | 481 | Returns: 482 | - bool: Returns True if successful, False otherwise. 483 | 484 | Example: 485 | >>> generate_non_linear_values(0, 50) 486 | """ 487 | return self.LogiGenerateNonLinearValues(index, non_lin_coeff) 488 | 489 | def get_non_linear_value(self, index, input_value): 490 | """ 491 | Returns a non-linear value from a table previously generated using LogiGenerateNonLinearValues(). 492 | This can be used for the response of a steering wheel. 493 | 494 | Args: 495 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 496 | Index 1 to the second game controller. 497 | - input_value (int): Value between -32768 and 32767 corresponding to the original value of an axis. 498 | 499 | Returns: 500 | - int: Value between -32768 and 32767, corresponding to the level of non-linearity previously set with 501 | LogiGenerateNonLinearValues(). 502 | 503 | Example: 504 | >>> get_non_linear_value(0, 16384) 505 | """ 506 | return self.LogiGetNonLinearValue(index, input_value) 507 | 508 | def has_force_feedback(self, index): 509 | """ 510 | Checks if the controller at the given index has force feedback capability. 511 | 512 | Args: 513 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 514 | Index 1 to the second game controller. 515 | 516 | Returns: 517 | - bool: Returns True if the device can do force feedback, False otherwise. 518 | 519 | Example: 520 | >>> has_force_feedback(0) 521 | """ 522 | return self.LogiHasForceFeedback(index) 523 | 524 | def is_playing(self, index, force_type): 525 | """ 526 | Checks if a certain force effect is currently playing on the given game controller. 527 | 528 | Args: 529 | - index (int): Index of the game controller. Index 0 corresponds to the first game controller connected. 530 | Index 1 to the second game controller. 531 | - force_type (int): The type of force that we want to check if it is playing. Possible types are: 532 | - LOGI_FORCE_SPRING 533 | - LOGI_FORCE_CONSTANT 534 | - LOGI_FORCE_DAMPER 535 | - LOGI_FORCE_SIDE_COLLISION 536 | - LOGI_FORCE_FRONTAL_COLLISION 537 | - LOGI_FORCE_DIRT_ROAD 538 | - LOGI_FORCE_BUMPY_ROAD 539 | - LOGI_FORCE_SLIPPERY_ROAD 540 | - LOGI_FORCE_SURFACE_EFFECT 541 | - LOGI_FORCE_CAR_AIRBORNE 542 | 543 | Returns: 544 | - bool: Returns True if the force is playing, False otherwise. 545 | 546 | Example: 547 | >>> is_playing(0, LOGI_FORCE_SPRING) 548 | """ 549 | return self.LogiIsPlaying(index, force_type) 550 | 551 | def play_spring_force(self, index, offset_percentage, saturation_percentage, coefficient_percentage): 552 | """ 553 | Plays the spring force. 554 | 555 | Args: 556 | - index (int): Index of the game controller. 557 | - offset_percentage (int): Center of the spring force effect (-100 to 100). 558 | - saturation_percentage (int): Saturation level of the spring force effect (0 to 100). 559 | - coefficient_percentage (int): Slope of effect strength increase (-100 to 100). 560 | 561 | Returns: 562 | - bool: Returns True if successful, False otherwise. 563 | 564 | Example: 565 | >>> play_spring_force(0, 0, 50, 30) 566 | """ 567 | return self.LogiPlaySpringForce(index, offset_percentage, saturation_percentage, coefficient_percentage) 568 | 569 | def stop_spring_force(self, index): 570 | """ 571 | Stops the spring force. 572 | 573 | Args: 574 | - index (int): Index of the game controller. 575 | 576 | Returns: 577 | - bool: Returns True if successful, False otherwise. 578 | 579 | Example: 580 | >>> stop_spring_force(0) 581 | """ 582 | return self.LogiStopSpringForce(index) 583 | 584 | def play_constant_force(self, index, magnitude_percentage): 585 | """ 586 | Plays the constant force. 587 | 588 | Args: 589 | - index (int): Index of the game controller. 590 | - magnitude_percentage (int): Magnitude of the constant force effect (-100 to 100). 591 | 592 | Returns: 593 | - bool: Returns True if successful, False otherwise. 594 | 595 | Example: 596 | >>> play_constant_force(0, 50) 597 | """ 598 | return self.LogiPlayConstantForce(index, magnitude_percentage) 599 | 600 | def stop_constant_force(self, index): 601 | """ 602 | Stops the constant force. 603 | 604 | Args: 605 | - index (int): Index of the game controller. 606 | 607 | Returns: 608 | - bool: Returns True if successful, False otherwise. 609 | 610 | Example: 611 | >>> stop_constant_force(0) 612 | """ 613 | return self.LogiStopConstantForce(index) 614 | 615 | def play_damper_force(self, index, coefficient_percentage): 616 | """ 617 | Plays the damper force. 618 | 619 | Args: 620 | - index (int): Index of the game controller. 621 | - coefficient_percentage (int): Slope of the effect strength increase relative to the amount of 622 | deflection from the center of the condition (-100 to 100). 623 | 624 | Returns: 625 | - bool: Returns True if successful, False otherwise. 626 | 627 | Example: 628 | >>> play_damper_force(0, 50) 629 | """ 630 | return self.LogiPlayDamperForce(index, coefficient_percentage) 631 | 632 | def stop_damper_force(self, index): 633 | """ 634 | Stops the damper force. 635 | 636 | Args: 637 | - index (int): Index of the game controller. 638 | 639 | Returns: 640 | - bool: Returns True if successful, False otherwise. 641 | 642 | Example: 643 | >>> stop_damper_force(0) 644 | """ 645 | return self.LogiStopDamperForce(index) 646 | 647 | def play_side_collision_force(self, index, magnitude_percentage): 648 | """ 649 | Plays the side collision force. 650 | 651 | Args: 652 | - index (int): Index of the game controller. 653 | - magnitude_percentage (int): Specifies the magnitude of the side collision force effect (-100 to 100). 654 | 655 | Returns: 656 | - bool: Returns True if successful, False otherwise. 657 | 658 | Note: 659 | If you are already using a constant force tied to a vector force from the physics engine, side collisions 660 | may be automatically taken care of by the constant force. 661 | 662 | Example: 663 | >>> play_side_collision_force(0, 50) 664 | """ 665 | return self.LogiPlaySideCollisionForce(index, magnitude_percentage) 666 | 667 | def play_frontal_collision_force(self, index, magnitude_percentage): 668 | """ 669 | Plays the frontal collision force. 670 | 671 | Args: 672 | - index (int): Index of the game controller. 673 | - magnitude_percentage (int): Specifies the magnitude of the frontal collision force effect (0 to 100). 674 | 675 | Returns: 676 | - bool: Returns True if successful, False otherwise. 677 | 678 | Example: 679 | >>> play_frontal_collision_force(0, 50) 680 | """ 681 | return self.LogiPlayFrontalCollisionForce(index, magnitude_percentage) 682 | 683 | def play_dirt_road_effect(self, index, magnitude_percentage): 684 | """ 685 | Plays the dirt road effect. 686 | 687 | Args: 688 | - index (int): Index of the game controller. 689 | - magnitude_percentage (int): Specifies the magnitude of the dirt road effect (0 to 100). 690 | 691 | Returns: 692 | - bool: Returns True if successful, False otherwise. 693 | 694 | Example: 695 | >>> play_dirt_road_effect(0, 50) 696 | """ 697 | return self.LogiPlayDirtRoadEffect(index, magnitude_percentage) 698 | 699 | def stop_dirt_road_effect(self, index): 700 | """ 701 | Stops the dirt road effect. 702 | 703 | Args: 704 | - index (int): Index of the game controller. 705 | 706 | Returns: 707 | - bool: Returns True if successful, False otherwise. 708 | 709 | Example: 710 | >>> stop_dirt_road_effect(0) 711 | """ 712 | return self.LogiStopDirtRoadEffect(index) 713 | 714 | def play_bumpy_road_effect(self, index, magnitude_percentage): 715 | """ 716 | Plays the bumpy road effect. 717 | 718 | Args: 719 | - index (int): Index of the game controller. 720 | - magnitude_percentage (int): Specifies the magnitude of the bumpy road effect (0 to 100). 721 | 722 | Returns: 723 | - bool: Returns True if successful, False otherwise. 724 | 725 | Example: 726 | >>> play_bumpy_road_effect(0, 50) 727 | """ 728 | return self.LogiPlayBumpyRoadEffect(index, magnitude_percentage) 729 | 730 | def stop_bumpy_road_effect(self, index): 731 | """ 732 | Stops the bumpy road effect. 733 | 734 | Args: 735 | - index (int): Index of the game controller. 736 | 737 | Returns: 738 | - bool: Returns True if successful, False otherwise. 739 | 740 | Example: 741 | >>> stop_bumpy_road_effect(0) 742 | """ 743 | return self.LogiStopBumpyRoadEffect(index) 744 | 745 | def play_slippery_road_effect(self, index, magnitude_percentage): 746 | """ 747 | Plays the slippery road effect. 748 | 749 | Args: 750 | - index (int): Index of the game controller. 751 | - magnitude_percentage (int): Specifies the magnitude of the slippery road effect (0 to 100). 752 | 100 corresponds to the most slippery effect. 753 | 754 | Returns: 755 | - bool: Returns True if successful, False otherwise. 756 | 757 | Example: 758 | >>> play_slippery_road_effect(0, 50) 759 | """ 760 | return self.LogiPlaySlipperyRoadEffect(index, magnitude_percentage) 761 | 762 | def stop_slippery_road_effect(self, index): 763 | """ 764 | Stops the slippery road effect. 765 | 766 | Args: 767 | - index (int): Index of the game controller. 768 | 769 | Returns: 770 | - bool: Returns True if successful, False otherwise. 771 | 772 | Example: 773 | >>> stop_slippery_road_effect(0) 774 | """ 775 | return self.LogiStopSlipperyRoadEffect(index) 776 | 777 | def play_surface_effect(self, index, type, magnitude_percentage, period): 778 | """ 779 | Plays the surface effect. 780 | 781 | Args: 782 | - index (int): Index of the game controller. 783 | - type (int): Specifies the type of force effect. Can be one of the following values: 784 | - LOGI_PERIODICTYPE_SINE 785 | - LOGI_PERIODICTYPE_SQUARE 786 | - LOGI_PERIODICTYPE_TRIANGLE 787 | - magnitude_percentage (int): Specifies the magnitude of the surface effect (0 to 100). 788 | - period (int): Specifies the period of the periodic force effect in milliseconds (20-150ms). 789 | 790 | Returns: 791 | - bool: Returns True if successful, False otherwise. 792 | 793 | Example: 794 | >>> play_surface_effect(0, LOGI_PERIODICTYPE_SINE, 50, 120) 795 | """ 796 | return self.LogiPlaySurfaceEffect(index, type, magnitude_percentage, period) 797 | 798 | def stop_surface_effect(self, index): 799 | """ 800 | Stops the surface effect. 801 | 802 | Args: 803 | - index (int): Index of the game controller. 804 | 805 | Returns: 806 | - bool: Returns True if successful, False otherwise. 807 | 808 | Example: 809 | >>> stop_surface_effect(0) 810 | """ 811 | return self.LogiStopSurfaceEffect(index) 812 | 813 | def play_car_airborne(self, index): 814 | """ 815 | Plays the car airborne effect. 816 | 817 | Args: 818 | - index (int): Index of the game controller. 819 | 820 | Returns: 821 | - bool: Returns True if successful, False otherwise. 822 | 823 | Example: 824 | >>> play_car_airborne(0) 825 | """ 826 | return self.LogiPlayCarAirborne(index) 827 | 828 | def stop_car_airborne(self, index): 829 | """ 830 | Stops the car airborne road effect. 831 | 832 | Args: 833 | - index (int): Index of the game controller. 834 | 835 | Returns: 836 | - bool: Returns True if successful, False otherwise. 837 | 838 | Example: 839 | >>> stop_car_airborne(0) 840 | """ 841 | return self.LogiStopCarAirborne(index) 842 | 843 | def play_softstop_force(self, index, usable_range_percentage): 844 | """ 845 | Plays the soft stop force. 846 | 847 | Args: 848 | - index (int): Index of the game controller. 849 | - usable_range_percentage (int): Specifies the deadband in percentage of the softstop force effect. 850 | 851 | Returns: 852 | - bool: Returns True if successful, False otherwise. 853 | 854 | Example: 855 | >>> play_softstop_force(0, 50) 856 | """ 857 | return self.LogiPlaySoftstopForce(index, usable_range_percentage) 858 | 859 | def stop_softstop_force(self, index): 860 | """ 861 | Stops the soft stop force. 862 | 863 | Args: 864 | - index (int): Index of the game controller. 865 | 866 | Returns: 867 | - bool: Returns True if successful, False otherwise. 868 | 869 | Example: 870 | >>> stop_softstop_force(0) 871 | """ 872 | return self.LogiStopSoftstopForce(index) 873 | 874 | def set_preferred_controller_properties(self, properties): 875 | """ 876 | Sets preferred wheel properties. 877 | 878 | Args: 879 | - properties (LogiControllerPropertiesData): Structure containing all the fields to be set. 880 | 881 | Returns: 882 | - bool: Returns True if successful, False otherwise. 883 | 884 | Example: 885 | >>> set_preferred_controller_properties(properties) 886 | """ 887 | return self.LogiSetPreferredControllerProperties(properties) 888 | 889 | def get_current_controller_properties(self, index, properties): 890 | """ 891 | Fills the properties parameter with the current controller properties. 892 | 893 | Args: 894 | - index (int): Index of the game controller. 895 | - properties (LogiControllerPropertiesData): Structure to receive current properties. 896 | 897 | Returns: 898 | - bool: Returns True if successful, False otherwise. 899 | 900 | Example: 901 | >>> get_current_controller_properties(0, properties) 902 | """ 903 | return self.LogiGetCurrentControllerProperties(index, properties) 904 | 905 | def get_shifter_mode(self, index): 906 | """ 907 | Gets the current shifter mode (gated or sequential) for the game controller. 908 | 909 | Args: 910 | - index (int): Index of the game controller. 911 | 912 | Returns: 913 | - int: Returns 1 if the shifter is gated, 0 if the shifter is sequential, or -1 if unknown. 914 | 915 | Example: 916 | >>> get_shifter_mode(0) 917 | """ 918 | return self.LogiGetShifterMode(index) 919 | 920 | def get_operating_range(self, index, range): 921 | """ 922 | Retrieves the current operating range of the game controller. 923 | 924 | Args: 925 | - index (int): Index of the game controller. 926 | - range (int): Integer to receive the current operating range. 927 | 928 | Returns: 929 | - bool: Returns True if successful, False otherwise. 930 | 931 | Example: 932 | >>> get_operating_range(0, range) 933 | """ 934 | return self.LogiGetOperatingRange(index, range) 935 | 936 | def set_operating_range(self, index, range): 937 | """ 938 | Sets the operating range of the controller with the range parameter. 939 | 940 | Args: 941 | - index (int): Index of the game controller. 942 | - range (int): The operating range to be set. 943 | 944 | Returns: 945 | - bool: Returns True if successful, False otherwise. 946 | 947 | Example: 948 | >>> set_operating_range(0, 50) 949 | """ 950 | return self.LogiSetOperatingRange(index, range) 951 | 952 | def play_leds(self, index, current_rpm, rpm_first_led_turns_on, rpm_red_line): 953 | """ 954 | Plays the LEDs on the game controller. 955 | 956 | Args: 957 | - index (int): Index of the game controller. 958 | - current_rpm (float): Current RPM. 959 | - rpm_first_led_turns_on (float): RPM when first LEDs are to turn on. 960 | - rpm_red_line (float): Just below this RPM, all LEDs will be on. Just above, all LEDs will start flashing. 961 | 962 | Returns: 963 | - bool: Returns True if successful, False otherwise. 964 | 965 | Example: 966 | >>> play_leds(0, 5000, 6000, 7000) 967 | """ 968 | return self.LogiPlayLeds(index, current_rpm, rpm_first_led_turns_on, rpm_red_line) 969 | 970 | def steering_shutdown(self): 971 | """ 972 | Shuts down the SDK and destroys the controller objects. Also destroys the GUI window if it exists. 973 | 974 | Args: 975 | None 976 | 977 | Returns: 978 | None 979 | 980 | Example: 981 | >>> steering_shutdown() 982 | """ 983 | # Destroy the GUI window if it exists 984 | if hasattr(self, 'root'): 985 | self.root.destroy() 986 | return self.LogiSteeringShutdown() 987 | --------------------------------------------------------------------------------