diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 68e6b91..0000000 --- a/.gitignore +++ /dev/null @@ -1,361 +0,0 @@ -# Created by https://www.toptal.com/developers/gitignore/api/linux,windows,macos,python,pycharm+all,direnv,vim -# Edit at https://www.toptal.com/developers/gitignore?templates=linux,windows,macos,python,pycharm+all,direnv,vim - -### direnv ### -.direnv -.envrc - -### Linux ### -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### macOS ### -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### macOS Patch ### -# iCloud generated files -*.icloud - -### PyCharm+all ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# AWS User-specific -.idea/**/aws.xml - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# SonarLint plugin -.idea/sonarlint/ - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -### PyCharm+all Patch ### -# Ignore everything but code style settings and run configurations -# that are supposed to be shared within teams. - -.idea/* - -!.idea/codeStyles -!.idea/runConfigurations - -### 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/ -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/ -cover/ - -# 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 -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .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 - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__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/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -### Python Patch ### -# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration -poetry.toml - -# ruff -.ruff_cache/ - -# LSP config files -pyrightconfig.json - -### Vim ### -# Swap -[._]*.s[a-v][a-z] -!*.svg # comment out if you don't need vector files -[._]*.sw[a-p] -[._]s[a-rt-v][a-z] -[._]ss[a-gi-z] -[._]sw[a-p] - -# Session -Session.vim -Sessionx.vim - -# Temporary -.netrwhist -# Auto-generated tag files -tags -# Persistent undo -[._]*.un~ - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# End of https://www.toptal.com/developers/gitignore/api/linux,windows,macos,python,pycharm+all,direnv,vim diff --git a/.python-version b/.python-version deleted file mode 100644 index 24ee5b1..0000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.13 diff --git a/mixer.py b/mixer.py index be22af4..ec2d568 100755 --- a/mixer.py +++ b/mixer.py @@ -8,7 +8,6 @@ from enum import Enum MIXER_PORT = 51325 - class Mixer: def __init__(self, ip, port): self.sock = socket.create_connection((ip, port)) @@ -28,7 +27,7 @@ class Mixer: GET_METER_DATA_REQUEST = 0x12 GET_METER_DATA_RESPONSE = 0x13 - def recv_sys_ex(self, response_msg_filter: SysExMessageId = None): + def recv(self, response_msg_filter: SysExMessageId = None): p = mido.Parser() while True: @@ -48,7 +47,7 @@ class Mixer: print("Hex: ", " ".join(f"{b:02X}" for b in msg.data)) if response_msg_filter is not None: - if msg.type != "sysex": + if msg.type != 'sysex': continue msg_id = int(msg.data[8]) @@ -64,13 +63,13 @@ class Mixer: data = self.SYSEX_ALL_CALL + [msg_id, i_pad_flag] - msg = mido.Message("sysex", data=data) + msg = mido.Message('sysex', data=data) msg_bytes = bytes(msg.bytes()) self.sock.sendall(msg_bytes) print("Sent:", " ".join(f"{b:02X}" for b in msg_bytes)) - response = self.recv_sys_ex(self.SysExMessageId.GET_SYSTEM_STATE_RESPONSE) + response = self.recv() class QuModel(Enum): QU16 = 1 @@ -110,15 +109,15 @@ class Mixer: data = self.SYSEX_HEADER + [int(0), msg_id, int(channel_no)] if name_to_set: - data += list(name_to_set.encode("utf-8")) + data += list(name_to_set.encode('utf-8')) - msg = mido.Message("sysex", data=data) + msg = mido.Message('sysex', data=data) msg_bytes = bytes(msg.bytes()) print("Sent:", " ".join(f"{b:02X}" for b in msg_bytes)) self.sock.sendall(msg_bytes) - response = self.recv_sys_ex(self.SysExMessageId.GET_NAME_FROM_QU_RESPONSE) + response = self.recv(self.SysExMessageId.GET_NAME_FROM_QU_RESPONSE) sysex_header = response.data[:8] @@ -132,77 +131,61 @@ class Mixer: print(f"Channel Name: {channel_name}") def nrpn_parameter_control(self, midi_ch, mixer_ch, id, va, vx): - a = mido.Message( - "control_change", channel=midi_ch, control=0x63, value=mixer_ch - ) - b = mido.Message("control_change", channel=midi_ch, control=0x62, value=id) - c = mido.Message("control_change", channel=midi_ch, control=0x06, value=va) - d = mido.Message("control_change", channel=midi_ch, control=0x26, value=vx) + a = mido.Message('control_change', channel=midi_ch, control=0x63, value=mixer_ch) + b = mido.Message('control_change', channel=midi_ch, control=0x62, value=id) + c = mido.Message('control_change', channel=midi_ch, control=0x06, value=va) + d = mido.Message('control_change', channel=midi_ch, control=0x26, value=vx) msg_bytes = bytes(a.bytes() + b.bytes() + c.bytes() + d.bytes()) - print(" ".join(f"{b:02X}" for b in msg_bytes)) + print(' '.join(f"{b:02X}" for b in msg_bytes)) self.sock.sendall(msg_bytes) def shutdown(self): self.nrpn_parameter_control(midi_ch=0, mixer_ch=0, id=0x5F, va=0x00, vx=0x00) def set_bank_1(self): - msb = mido.Message("control_change", channel=0, control=0x00, value=0x00) - lsb = mido.Message("control_change", channel=0, control=0x20, value=0x00) + msb = mido.Message('control_change', channel=0, control=0x00, value=0x00) + lsb = mido.Message('control_change', channel=0, control=0x20, value=0x00) msg_bytes = bytes(msb.bytes() + lsb.bytes()) - print(" ".join(f"{b:02X}" for b in msg_bytes)) + print(' '.join(f"{b:02X}" for b in msg_bytes)) self.sock.sendall(msg_bytes) def scene_recall(self, scene_id): print(f"scene_recall: scene_id={scene_id}", scene_id) self.set_bank_1() - msb = mido.Message("program_change", channel=0, program=scene_id) + msb = mido.Message('program_change', channel=0, program=scene_id) msg_bytes = bytes(msb.bytes()) - print(" ".join(f"{b:02X}" for b in msg_bytes)) + print(' '.join(f"{b:02X}" for b in msg_bytes)) self.sock.sendall(msg_bytes) def watch(self): while True: - self.recv_sys_ex() - + self.recv() def main(): parser = argparse.ArgumentParser(description="Allen & Heath Qu Remote Control") parser.add_argument("ip", help="IP of the mixer") - subparsers = parser.add_subparsers( - dest="command", required=True, help="Available commands" - ) + subparsers = parser.add_subparsers(dest="command", required=True, help="Available commands") - channel_naming_parser = subparsers.add_parser( - "get_name_from_qu", help="Channel naming" - ) - channel_naming_parser.add_argument( - "channel_id", - type=int, - choices=range(0, 16), - help="Number of channel to get or set its name", - ) + channel_naming_parser = subparsers.add_parser("get_name_from_qu", help="Channel naming") + channel_naming_parser.add_argument("channel_id", type=int, choices=range(0, 16), help="Number of channel to get or set its name") channel_naming_parser.add_argument("-n", "--name", help="Channel name to set") subparsers.add_parser("get_system_state", help="Get system information") subparsers.add_parser("shutdown", help="Shut down the mixer") scene_parser = subparsers.add_parser("scene_recall", help="Recall a specific scene") - scene_parser.add_argument( - "scene_number", type=int, choices=range(0, 100), help="Scene number to recall" - ) + scene_parser.add_argument("scene_number", type=int, choices=range(0, 100), help="Scene number to recall") subparsers.add_parser("scene_recall_default", help="Set the default scene 0") - subparsers.add_parser( - "watch", help="Just receive data from mixer and print it to console" - ) + subparsers.add_parser("watch", help="Just receive data from mixer and print it to console") args = parser.parse_args() @@ -214,19 +197,12 @@ def main(): print(f"Args: {vars(args)}") match args.command: - case "get_name_from_qu": - mixer.get_name_from_qu(args.channel_id, args.name) - case "get_system_state": - mixer.get_system_state() - case "shutdown": - mixer.shutdown() - case "scene_recall": - mixer.scene_recall(args.scene_number) - case "scene_recall_default": - mixer.scene_recall(0) - case "watch": - mixer.watch() + case 'get_name_from_qu': mixer.get_name_from_qu(args.channel_id, args.name) + case 'get_system_state': mixer.get_system_state() + case 'shutdown': mixer.shutdown() + case 'scene_recall': mixer.scene_recall(args.scene_number) + case 'scene_recall_default': mixer.scene_recall(0) + case 'watch': mixer.watch() - -if __name__ == "__main__": +if __name__ == '__main__': main() diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index afece10..0000000 --- a/pyproject.toml +++ /dev/null @@ -1,18 +0,0 @@ -[project] -name = "mixer" -version = "0.1.0" -description = "talks to atten & heath mixer via mqtt" -readme = "README.md" -authors = [ - { name = "coon", email = "coon@mailbox.org" } -] -requires-python = ">=3.13" -dependencies = [ - "mido>=1.3.3", - "paho-mqtt>=2.1.0", -] - -[dependency-groups] -dev = [ - "ruff>=0.13.1", -] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b85a359 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,15 @@ +blessed==1.21.0 +bpython==0.25 +certifi==2025.8.3 +charset-normalizer==3.4.3 +curtsies==0.4.3 +cwcwidth==0.1.10 +greenlet==3.2.4 +idna==3.10 +mido==1.3.3 +packaging==25.0 +Pygments==2.19.2 +pyxdg==0.28 +requests==2.32.5 +urllib3==2.5.0 +wcwidth==0.2.13 diff --git a/uv.lock b/uv.lock deleted file mode 100644 index b5aa861..0000000 --- a/uv.lock +++ /dev/null @@ -1,82 +0,0 @@ -version = 1 -revision = 3 -requires-python = ">=3.13" - -[[package]] -name = "mido" -version = "1.3.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/23/14/cfda3fe61ce4c0f50a9f707ae02b46cb53211732b2cd4522bf06272848f4/mido-1.3.3.tar.gz", hash = "sha256:1aecb30b7f282404f17e43768cbf74a6a31bf22b3b783bdd117a1ce9d22cb74c", size = 124288, upload-time = "2024-10-25T15:05:21.847Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fd/28/45deb15c11859d2f10702b32e71de9328a9fa494f989626916db39a9617f/mido-1.3.3-py3-none-any.whl", hash = "sha256:01033c9b10b049e4436fca2762194ca839b09a4334091dd3c34e7f4ae674fd8a", size = 54614, upload-time = "2024-10-25T15:05:20.349Z" }, -] - -[[package]] -name = "mixer" -version = "0.1.0" -source = { virtual = "." } -dependencies = [ - { name = "mido" }, - { name = "paho-mqtt" }, -] - -[package.dev-dependencies] -dev = [ - { name = "ruff" }, -] - -[package.metadata] -requires-dist = [ - { name = "mido", specifier = ">=1.3.3" }, - { name = "paho-mqtt", specifier = ">=2.1.0" }, -] - -[package.metadata.requires-dev] -dev = [{ name = "ruff", specifier = ">=0.13.1" }] - -[[package]] -name = "packaging" -version = "25.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, -] - -[[package]] -name = "paho-mqtt" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/39/15/0a6214e76d4d32e7f663b109cf71fb22561c2be0f701d67f93950cd40542/paho_mqtt-2.1.0.tar.gz", hash = "sha256:12d6e7511d4137555a3f6ea167ae846af2c7357b10bc6fa4f7c3968fc1723834", size = 148848, upload-time = "2024-04-29T19:52:55.591Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/cb/00451c3cf31790287768bb12c6bec834f5d292eaf3022afc88e14b8afc94/paho_mqtt-2.1.0-py3-none-any.whl", hash = "sha256:6db9ba9b34ed5bc6b6e3812718c7e06e2fd7444540df2455d2c51bd58808feee", size = 67219, upload-time = "2024-04-29T19:52:48.345Z" }, -] - -[[package]] -name = "ruff" -version = "0.13.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ab/33/c8e89216845615d14d2d42ba2bee404e7206a8db782f33400754f3799f05/ruff-0.13.1.tar.gz", hash = "sha256:88074c3849087f153d4bb22e92243ad4c1b366d7055f98726bc19aa08dc12d51", size = 5397987, upload-time = "2025-09-18T19:52:44.33Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/41/ca37e340938f45cfb8557a97a5c347e718ef34702546b174e5300dbb1f28/ruff-0.13.1-py3-none-linux_armv6l.whl", hash = "sha256:b2abff595cc3cbfa55e509d89439b5a09a6ee3c252d92020bd2de240836cf45b", size = 12304308, upload-time = "2025-09-18T19:51:56.253Z" }, - { url = "https://files.pythonhosted.org/packages/ff/84/ba378ef4129415066c3e1c80d84e539a0d52feb250685091f874804f28af/ruff-0.13.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4ee9f4249bf7f8bb3984c41bfaf6a658162cdb1b22e3103eabc7dd1dc5579334", size = 12937258, upload-time = "2025-09-18T19:52:00.184Z" }, - { url = "https://files.pythonhosted.org/packages/8d/b6/ec5e4559ae0ad955515c176910d6d7c93edcbc0ed1a3195a41179c58431d/ruff-0.13.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c5da4af5f6418c07d75e6f3224e08147441f5d1eac2e6ce10dcce5e616a3bae", size = 12214554, upload-time = "2025-09-18T19:52:02.753Z" }, - { url = "https://files.pythonhosted.org/packages/70/d6/cb3e3b4f03b9b0c4d4d8f06126d34b3394f6b4d764912fe80a1300696ef6/ruff-0.13.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80524f84a01355a59a93cef98d804e2137639823bcee2931f5028e71134a954e", size = 12448181, upload-time = "2025-09-18T19:52:05.279Z" }, - { url = "https://files.pythonhosted.org/packages/d2/ea/bf60cb46d7ade706a246cd3fb99e4cfe854efa3dfbe530d049c684da24ff/ruff-0.13.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff7f5ce8d7988767dd46a148192a14d0f48d1baea733f055d9064875c7d50389", size = 12104599, upload-time = "2025-09-18T19:52:07.497Z" }, - { url = "https://files.pythonhosted.org/packages/2d/3e/05f72f4c3d3a69e65d55a13e1dd1ade76c106d8546e7e54501d31f1dc54a/ruff-0.13.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c55d84715061f8b05469cdc9a446aa6c7294cd4bd55e86a89e572dba14374f8c", size = 13791178, upload-time = "2025-09-18T19:52:10.189Z" }, - { url = "https://files.pythonhosted.org/packages/81/e7/01b1fc403dd45d6cfe600725270ecc6a8f8a48a55bc6521ad820ed3ceaf8/ruff-0.13.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:ac57fed932d90fa1624c946dc67a0a3388d65a7edc7d2d8e4ca7bddaa789b3b0", size = 14814474, upload-time = "2025-09-18T19:52:12.866Z" }, - { url = "https://files.pythonhosted.org/packages/fa/92/d9e183d4ed6185a8df2ce9faa3f22e80e95b5f88d9cc3d86a6d94331da3f/ruff-0.13.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c366a71d5b4f41f86a008694f7a0d75fe409ec298685ff72dc882f882d532e36", size = 14217531, upload-time = "2025-09-18T19:52:15.245Z" }, - { url = "https://files.pythonhosted.org/packages/3b/4a/6ddb1b11d60888be224d721e01bdd2d81faaf1720592858ab8bac3600466/ruff-0.13.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4ea9d1b5ad3e7a83ee8ebb1229c33e5fe771e833d6d3dcfca7b77d95b060d38", size = 13265267, upload-time = "2025-09-18T19:52:17.649Z" }, - { url = "https://files.pythonhosted.org/packages/81/98/3f1d18a8d9ea33ef2ad508f0417fcb182c99b23258ec5e53d15db8289809/ruff-0.13.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0f70202996055b555d3d74b626406476cc692f37b13bac8828acff058c9966a", size = 13243120, upload-time = "2025-09-18T19:52:20.332Z" }, - { url = "https://files.pythonhosted.org/packages/8d/86/b6ce62ce9c12765fa6c65078d1938d2490b2b1d9273d0de384952b43c490/ruff-0.13.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:f8cff7a105dad631085d9505b491db33848007d6b487c3c1979dd8d9b2963783", size = 13443084, upload-time = "2025-09-18T19:52:23.032Z" }, - { url = "https://files.pythonhosted.org/packages/a1/6e/af7943466a41338d04503fb5a81b2fd07251bd272f546622e5b1599a7976/ruff-0.13.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:9761e84255443316a258dd7dfbd9bfb59c756e52237ed42494917b2577697c6a", size = 12295105, upload-time = "2025-09-18T19:52:25.263Z" }, - { url = "https://files.pythonhosted.org/packages/3f/97/0249b9a24f0f3ebd12f007e81c87cec6d311de566885e9309fcbac5b24cc/ruff-0.13.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:3d376a88c3102ef228b102211ef4a6d13df330cb0f5ca56fdac04ccec2a99700", size = 12072284, upload-time = "2025-09-18T19:52:27.478Z" }, - { url = "https://files.pythonhosted.org/packages/f6/85/0b64693b2c99d62ae65236ef74508ba39c3febd01466ef7f354885e5050c/ruff-0.13.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cbefd60082b517a82c6ec8836989775ac05f8991715d228b3c1d86ccc7df7dae", size = 12970314, upload-time = "2025-09-18T19:52:30.212Z" }, - { url = "https://files.pythonhosted.org/packages/96/fc/342e9f28179915d28b3747b7654f932ca472afbf7090fc0c4011e802f494/ruff-0.13.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dd16b9a5a499fe73f3c2ef09a7885cb1d97058614d601809d37c422ed1525317", size = 13422360, upload-time = "2025-09-18T19:52:32.676Z" }, - { url = "https://files.pythonhosted.org/packages/37/54/6177a0dc10bce6f43e392a2192e6018755473283d0cf43cc7e6afc182aea/ruff-0.13.1-py3-none-win32.whl", hash = "sha256:55e9efa692d7cb18580279f1fbb525146adc401f40735edf0aaeabd93099f9a0", size = 12178448, upload-time = "2025-09-18T19:52:35.545Z" }, - { url = "https://files.pythonhosted.org/packages/64/51/c6a3a33d9938007b8bdc8ca852ecc8d810a407fb513ab08e34af12dc7c24/ruff-0.13.1-py3-none-win_amd64.whl", hash = "sha256:3a3fb595287ee556de947183489f636b9f76a72f0fa9c028bdcabf5bab2cc5e5", size = 13286458, upload-time = "2025-09-18T19:52:38.198Z" }, - { url = "https://files.pythonhosted.org/packages/fd/04/afc078a12cf68592345b1e2d6ecdff837d286bac023d7a22c54c7a698c5b/ruff-0.13.1-py3-none-win_arm64.whl", hash = "sha256:c0bae9ffd92d54e03c2bf266f466da0a65e145f298ee5b5846ed435f6a00518a", size = 12437893, upload-time = "2025-09-18T19:52:41.283Z" }, -]