pax_global_header00006660000000000000000000000064146362655070014530gustar00rootroot0000000000000052 comment=4031079216754eaf236ebbef2ef0965d6bb615c6 bluetooth-adapters-0.19.3/000077500000000000000000000000001463626550700154305ustar00rootroot00000000000000bluetooth-adapters-0.19.3/.all-contributorsrc000066400000000000000000000004711463626550700212630ustar00rootroot00000000000000{ "projectName": "bluetooth-adapters", "projectOwner": "bluetooth-devices", "repoType": "github", "repoHost": "https://github.com", "files": [ "README.md" ], "imageSize": 80, "commit": true, "commitConvention": "angular", "contributors": [], "contributorsPerLine": 7, "skipCi": true } bluetooth-adapters-0.19.3/.editorconfig000066400000000000000000000004441463626550700201070ustar00rootroot00000000000000# http://editorconfig.org root = true [*] indent_style = space indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true charset = utf-8 end_of_line = lf [*.bat] indent_style = tab end_of_line = crlf [LICENSE] insert_final_newline = false [Makefile] indent_style = tab bluetooth-adapters-0.19.3/.flake8000066400000000000000000000000561463626550700166040ustar00rootroot00000000000000[flake8] exclude = docs max-line-length = 180 bluetooth-adapters-0.19.3/.github/000077500000000000000000000000001463626550700167705ustar00rootroot00000000000000bluetooth-adapters-0.19.3/.github/FUNDING.yml000066400000000000000000000000361463626550700206040ustar00rootroot00000000000000github: ["bluetooth-devices"] bluetooth-adapters-0.19.3/.github/ISSUE_TEMPLATE/000077500000000000000000000000001463626550700211535ustar00rootroot00000000000000bluetooth-adapters-0.19.3/.github/ISSUE_TEMPLATE/1-bug_report.md000066400000000000000000000004221463626550700240010ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve labels: bug --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: **Additional context** Add any other context about the problem here. bluetooth-adapters-0.19.3/.github/ISSUE_TEMPLATE/2-feature-request.md000066400000000000000000000006721463626550700247620ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project labels: enhancement --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Additional context** Add any other context or screenshots about the feature request here. bluetooth-adapters-0.19.3/.github/dependabot.yml000066400000000000000000000007661463626550700216310ustar00rootroot00000000000000# To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "pip" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "weekly" bluetooth-adapters-0.19.3/.github/labels.toml000066400000000000000000000035151463626550700211330ustar00rootroot00000000000000[breaking] color = "ffcc00" name = "breaking" description = "Breaking change." [bug] color = "d73a4a" name = "bug" description = "Something isn't working" [dependencies] color = "0366d6" name = "dependencies" description = "Pull requests that update a dependency file" [github_actions] color = "000000" name = "github_actions" description = "Update of github actions" [documentation] color = "1bc4a5" name = "documentation" description = "Improvements or additions to documentation" [duplicate] color = "cfd3d7" name = "duplicate" description = "This issue or pull request already exists" [enhancement] color = "a2eeef" name = "enhancement" description = "New feature or request" ["good first issue"] color = "7057ff" name = "good first issue" description = "Good for newcomers" ["help wanted"] color = "008672" name = "help wanted" description = "Extra attention is needed" [invalid] color = "e4e669" name = "invalid" description = "This doesn't seem right" [nochangelog] color = "555555" name = "nochangelog" description = "Exclude pull requests from changelog" [question] color = "d876e3" name = "question" description = "Further information is requested" [removed] color = "e99695" name = "removed" description = "Removed piece of functionalities." [tests] color = "bfd4f2" name = "tests" description = "CI, CD and testing related changes" [wontfix] color = "ffffff" name = "wontfix" description = "This will not be worked on" [discussion] color = "c2e0c6" name = "discussion" description = "Some discussion around the project" [hacktoberfest] color = "ffa663" name = "hacktoberfest" description = "Good issues for Hacktoberfest" [answered] color = "0ee2b6" name = "answered" description = "Automatically closes as answered after a delay" [waiting] color = "5f7972" name = "waiting" description = "Automatically closes if no answer after a delay" bluetooth-adapters-0.19.3/.github/workflows/000077500000000000000000000000001463626550700210255ustar00rootroot00000000000000bluetooth-adapters-0.19.3/.github/workflows/ci.yml000066400000000000000000000041441463626550700221460ustar00rootroot00000000000000name: CI on: push: branches: - main pull_request: concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 with: python-version: "3.9" - uses: pre-commit/action@v2.0.3 # Make sure commit messages follow the conventional commits convention: # https://www.conventionalcommits.org commitlint: name: Lint Commit Messages runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - uses: wagoid/commitlint-github-action@v5 test: strategy: fail-fast: false matrix: python-version: - "3.9" - "3.10" - "3.11" - "3.12" os: - ubuntu-latest - macos-latest - windows-latest runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - uses: snok/install-poetry@v1 - name: Install Dependencies shell: bash run: poetry install - name: Test with Pytest shell: bash run: poetry run pytest --cov-report=xml - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} release: runs-on: ubuntu-latest environment: release if: github.ref == 'refs/heads/main' needs: - test - lint - commitlint steps: - uses: actions/checkout@v3 with: fetch-depth: 0 # Run semantic release: # - Update CHANGELOG.md # - Update version in code # - Create git tag # - Create GitHub release # - Publish to PyPI - name: Python Semantic Release uses: python-semantic-release/python-semantic-release@v7.34.6 with: github_token: ${{ secrets.GITHUB_TOKEN }} pypi_token: ${{ secrets.PYPI_TOKEN }} bluetooth-adapters-0.19.3/.github/workflows/hacktoberfest.yml000066400000000000000000000005341463626550700243760ustar00rootroot00000000000000name: Hacktoberfest on: schedule: # Run every day in October - cron: "0 0 * 10 *" # Run on the 1st of November to revert - cron: "0 13 1 11 *" jobs: hacktoberfest: runs-on: ubuntu-latest steps: - uses: browniebroke/hacktoberfest-labeler-action@v2.2.0 with: github_token: ${{ secrets.GH_PAT }} bluetooth-adapters-0.19.3/.github/workflows/issue-manager.yml000066400000000000000000000013401463626550700243060ustar00rootroot00000000000000name: Issue Manager on: schedule: - cron: "0 0 * * *" issue_comment: types: - created issues: types: - labeled pull_request_target: types: - labeled workflow_dispatch: jobs: issue-manager: runs-on: ubuntu-latest steps: - uses: tiangolo/issue-manager@0.4.0 with: token: ${{ secrets.GITHUB_TOKEN }} config: > { "answered": { "message": "Assuming the original issue was solved, it will be automatically closed now." }, "waiting": { "message": "Automatically closing. To re-open, please provide the additional information requested." } } bluetooth-adapters-0.19.3/.github/workflows/labels.yml000066400000000000000000000007741463626550700230220ustar00rootroot00000000000000name: Sync Github labels on: push: branches: - main paths: - ".github/**" jobs: labels: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v3 with: python-version: 3.8 - name: Install labels run: pip install labels - name: Sync config with Github run: labels -u ${{ github.repository_owner }} -t ${{ secrets.GITHUB_TOKEN }} sync -f .github/labels.toml bluetooth-adapters-0.19.3/.gitignore000066400000000000000000000040661463626550700174260ustar00rootroot00000000000000# Created by .ignore support plugin (hsz.mobi) ### Python template # 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 # 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/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ bluetooth-adapters-0.19.3/.gitpod.yml000066400000000000000000000003061463626550700175160ustar00rootroot00000000000000tasks: - command: | pip install poetry PIP_USER=false poetry install - command: | pip install pre-commit pre-commit install PIP_USER=false pre-commit install-hooks bluetooth-adapters-0.19.3/.idea/000077500000000000000000000000001463626550700164105ustar00rootroot00000000000000bluetooth-adapters-0.19.3/.idea/bluetooth-adapters.iml000066400000000000000000000005151463626550700227220ustar00rootroot00000000000000 bluetooth-adapters-0.19.3/.idea/watcherTasks.xml000066400000000000000000000052531463626550700216020ustar00rootroot00000000000000 bluetooth-adapters-0.19.3/.idea/workspace.xml000066400000000000000000000027361463626550700211400ustar00rootroot00000000000000 bluetooth-adapters-0.19.3/.pre-commit-config.yaml000066400000000000000000000033061463626550700217130ustar00rootroot00000000000000# See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks exclude: "CHANGELOG.md" default_stages: [commit] ci: autofix_commit_msg: "chore(pre-commit.ci): auto fixes" autoupdate_commit_msg: "chore(pre-commit.ci): pre-commit autoupdate" repos: - repo: https://github.com/commitizen-tools/commitizen rev: v2.28.0 hooks: - id: commitizen stages: [commit-msg] - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.3.0 hooks: - id: debug-statements - id: check-builtin-literals - id: check-case-conflict - id: check-docstring-first - id: check-json - id: check-toml - id: check-xml - id: check-yaml - id: detect-private-key - id: end-of-file-fixer - id: trailing-whitespace - id: debug-statements - repo: https://github.com/pre-commit/mirrors-prettier rev: v2.7.1 hooks: - id: prettier args: ["--tab-width", "2"] - repo: https://github.com/asottile/pyupgrade rev: v2.37.2 hooks: - id: pyupgrade args: [--py37-plus] - repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks: - id: isort - repo: https://github.com/psf/black rev: 22.6.0 hooks: - id: black - repo: https://github.com/codespell-project/codespell rev: v2.1.0 hooks: - id: codespell - repo: https://github.com/PyCQA/flake8 rev: 4.0.1 hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy rev: v0.931 hooks: - id: mypy additional_dependencies: [] - repo: https://github.com/PyCQA/bandit rev: 1.7.4 hooks: - id: bandit args: [-x, tests] bluetooth-adapters-0.19.3/.readthedocs.yml000066400000000000000000000010041463626550700205110ustar00rootroot00000000000000# Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/source/conf.py # Set the version of Python and other tools you might need build: os: ubuntu-20.04 tools: python: "3.9" # Optionally declare the Python requirements required to build your docs python: install: - method: pip path: . extra_requirements: - docs bluetooth-adapters-0.19.3/CHANGELOG.md000066400000000000000000000301731463626550700172450ustar00rootroot00000000000000# Changelog ## v0.19.3 (2024-06-24) ### Fix * Fix license classifier ([#141](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/141)) ([`b3d7010`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/b3d70101ca83ed844ec12c5c0e980bfe29a30fef)) ## v0.19.2 (2024-05-04) ### Fix * Handle AuthError while probing for adapters ([#130](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/130)) ([`aec92f3`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/aec92f32493637a237c3c8490b689b13da20b93f)) ## v0.19.1 (2024-04-30) ### Fix * Handle missing hci devices when enumerating adapters ([#129](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/129)) ([`95759e7`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/95759e78176cb83dfae2ea2b8bd98dfc80b652ae)) ## v0.19.0 (2024-04-19) ### Feature * Improve adapter data with UART devices ([#124](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/124)) ([`5db938f`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/5db938f16b3d1c6f4b5dbd9a988bafd8cbf06b1e)) ## v0.18.0 (2024-02-24) ### Feature * Switch to using aiooui for mac lookups ([#115](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/115)) ([`6934ad8`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/6934ad806d06efa073437dc4dd44807e6623829c)) ## v0.17.0 (2024-01-04) ### Feature * Ignore adapters with a 00:00:00:00:00:00 mac address ([#105](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/105)) ([`cdca7b2`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/cdca7b2b8e115cdc44011c8959f76c4870213695)) * Py312 support ([#107](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/107)) ([`4b14786`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/4b14786585d42593a96fde057fb22c8064cf6914)) ## v0.16.2 (2023-12-16) ### Fix * Workaround fcntl and dbus_fast not available on windows ([#102](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/102)) ([`0c48649`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/0c4864943966cdaab44259bfc6b8236f7695e9b6)) ## v0.16.1 (2023-09-07) ### Fix * Ensure timeouts work with py3.11 ([#89](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/89)) ([`fb9e2b5`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/fb9e2b571f85e2f5fbef88ad8b651a119d58384a)) ## v0.16.0 (2023-07-12) ### Feature * Add get_adapters_from_hci ([#74](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/74)) ([`9801501`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/9801501fa294dc46bc40fe215a79fca7021ec571)) ## v0.15.5 (2023-07-12) ### Fix * Make sure down adapters are still listed ([#71](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/71)) ([`0411edd`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/0411edde528c133a57093a6b07332ae630a15632)) ## v0.15.4 (2023-05-09) ### Fix * Don't import from dbus module on Windows ([#57](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/57)) ([`f936758`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/f936758748fc7ec4829f91dcf600e4fbf9f354b5)) ## v0.15.3 (2023-03-18) ### Fix * Update for bleak 0.20 ([#39](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/39)) ([`1e18be2`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/1e18be264b1fcac131ca6fcc4b7418d04c92c6ee)) * Fix CI ([#43](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/43)) ([`f43d748`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/f43d748cb200a3151b840b280d167fdfdbfc5772)) ## v0.15.2 (2022-12-22) ### Fix * Missing ADAPTER_CONNECTION_SLOTS from __all__ ([#38](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/38)) ([`d2bed5e`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/d2bed5ef0c4fb9e31e31011544815599c5b81b4a)) ## v0.15.1 (2022-12-22) ### Fix * Align naming with Home Assistant for new connection slots code ([#36](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/36)) ([`0605de5`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/0605de55906b5025cb49eab18bd3bfe8e344ffd1)) ## v0.15.0 (2022-12-21) ### Feature * Add connection_slots to AdapterDetails ([#35](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/35)) ([`c0a7b52`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/c0a7b52835cf8c7a68c54e483fa86436ea0c3342)) ## v0.14.1 (2022-12-10) ### Fix * Handle data corruption ([#34](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/34)) ([`77f2e9b`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/77f2e9b3f7a5012ddb3c78e3529a7f01146f74b6)) ## v0.14.0 (2022-12-10) ### Feature * Cleanup new storage apis and add coverage ([#33](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/33)) ([`07cfbdf`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/07cfbdfdc03f1065978c57e9097b1c07326ab781)) ## v0.13.0 (2022-12-10) ### Feature * Add storage to save and restore discovered devices ([#32](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/32)) ([`dc88a36`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/dc88a3620dac8ec8404e80c65631b366baebf85a)) ## v0.12.0 (2022-12-05) ### Feature * Expose load_history_from_managed_objects ([#31](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/31)) ([`823daa8`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/823daa805b2c5d170485c2129403cf3612cd2378)) ## v0.11.0 (2022-11-27) ### Feature * Detect vendor of uart bluetooth adapters by mac oui ([#30](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/30)) ([`12b1383`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/12b1383d249395526add62e6e6d6d7770fb4a401)) ## v0.10.1 (2022-11-27) ### Fix * Bump usb-devices ([#28](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/28)) ([`9c1a8f8`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/9c1a8f8e852854ba0f6039aafd35632e361060a7)) ## v0.10.0 (2022-11-27) ### Feature * Export ADAPTER_ constants for the underlying usb device ([#27](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/27)) ([`f3f7619`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/f3f76198d329794c00d921661215376bb20a3a1d)) ## v0.9.0 (2022-11-27) ### Feature * Add hardware info to adapter details ([#26](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/26)) ([`dc2682d`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/dc2682dff34c92654bab42c452125d8ae6c6a8be)) ## v0.8.0 (2022-11-16) ### Feature * Add new multi platform adapter manager ([#23](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/23)) ([`58119d1`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/58119d1c6f08c9804809b8cc94220231f5bc636d)) ## v0.7.0 (2022-11-04) ### Feature * Speed up connecting to Dbus ([#22](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/22)) ([`6b45f84`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/6b45f84479e58d8a55277289ec6f4c1597d6b98a)) ## v0.6.0 (2022-10-02) ### Feature * Use unpack_variants from dbus-fast ([#21](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/21)) ([`f5e0cf4`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/f5e0cf4837bac063aa2aae239a30908b13f55b93)) ## v0.5.3 (2022-10-01) ### Fix * Compat with upcoming bleak ([#20](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/20)) ([`61fcdbf`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/61fcdbfd6865df17dda8369bf7b26ae51ed49d20)) ## v0.5.2 (2022-09-26) ### Fix * Handle ConnectionRefusedError ([#19](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/19)) ([`7e85613`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/7e85613cb2f972773748b4cde4ba826f75d807d2)) ## v0.5.1 (2022-09-17) ### Fix * Restoring manufacturer_data ([#18](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/18)) ([`264d63a`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/264d63a93af8aae9cfb4c4f75e2f1632366edb20)) ## v0.5.0 (2022-09-17) ### Feature * Add support for restoring bluetooth history from the bus ([#17](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/17)) ([`3aaf104`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/3aaf104fe5798285f995e840310ea06f3d6a9a4d)) ## v0.4.1 (2022-09-10) ### Fix * Bump dbus-fast ([#16](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/16)) ([`6e665ae`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/6e665ae38834e42d849ac33db228a9aa6578ddeb)) ## v0.4.0 (2022-09-09) ### Feature * Switch from dbus-next to dbus-fast ([#15](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/15)) ([`90d9ca5`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/90d9ca50ac687ecef129c0a080242ce8daa0edda)) ## v0.3.6 (2022-09-09) ### Fix * Handle Dbus closing the connection on us ([#14](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/14)) ([`847698f`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/847698f14001b41790412e8cec38369ccb117402)) ## v0.3.5 (2022-09-08) ### Fix * Downgrade more loggers to debug in case they do not have bluez installed and do not want bluetooth ([#13](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/13)) ([`cae2700`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/cae2700f26b06f461647875f48e0f8a1baae298c)) ## v0.3.4 (2022-09-02) ### Fix * Downgrade more loggers ([#12](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/12)) ([`3deb74f`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/3deb74f2ddd10738029b64d9865a6cbeada83b7b)) ## v0.3.3 (2022-09-01) ### Fix * Downgrade dbus timeouts to debug logging as it likely means they have no bluez ([#11](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/11)) ([`4f6ae64`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/4f6ae64c22711022be449c19adc9bd97b2769846)) ## v0.3.2 (2022-08-27) ### Fix * Seperate FileNotFoundError and BrokenPipeError errors ([#10](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/10)) ([`f0b3d81`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/f0b3d81d65586e536b78055426bd7118d1803587)) ## v0.3.1 (2022-08-27) ### Fix * Manage BrokenPipeError thrown by MessageBus.connect() ([#9](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/9)) ([`5d0fbaa`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/5d0fbaa1533924c2e256a1a682c6ea7982cf8ed7)) ## v0.3.0 (2022-08-27) ### Feature * Add get_dbus_managed_objects ([#8](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/8)) ([`ce613ea`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/ce613ea7fcdda5fdacfc8848ed2a1ef290e64b92)) ## v0.2.0 (2022-08-18) ### Feature * Add get_bluetooth_adapter_details ([#7](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/7)) ([`619f1ac`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/619f1acc9efd953ff57c9f126f64c21579b65e7e)) ## v0.1.3 (2022-08-01) ### Fix * Add a timeout in case dbus fails to respond ([#6](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/6)) ([`eff1022`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/eff10222a6f1e0be4a599e6e47f20bace4ffd711)) ## v0.1.2 (2022-07-24) ### Fix * Adapters now returns a list instead of a set since order matters ([#5](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/5)) ([`b4f153b`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/b4f153bf4198ad34e9e12113272c798fd6bddad6)) ## v0.1.1 (2022-07-22) ### Fix * Disconnect is not a coro ([#4](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/4)) ([`05022b2`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/05022b20cc55bff8fe927dadd746d7879085f702)) ## v0.1.0 (2022-07-22) ### Feature * Bump to resync verison ([#3](https://github.com/Bluetooth-Devices/bluetooth-adapters/issues/3)) ([`d0ec824`](https://github.com/Bluetooth-Devices/bluetooth-adapters/commit/d0ec82419d96052c8315a0518622b794bbb502d2)) bluetooth-adapters-0.19.3/CONTRIBUTING.md000066400000000000000000000074661463626550700176760ustar00rootroot00000000000000# Contributing Contributions are welcome, and they are greatly appreciated! Every little helps, and credit will always be given. You can contribute in many ways: ## Types of Contributions ### Report Bugs Report bugs to [our issue page][gh-issues]. If you are reporting a bug, please include: - Your operating system name and version. - Any details about your local setup that might be helpful in troubleshooting. - Detailed steps to reproduce the bug. ### Fix Bugs Look through the GitHub issues for bugs. Anything tagged with "bug" and "help wanted" is open to whoever wants to implement it. ### Implement Features Look through the GitHub issues for features. Anything tagged with "enhancement" and "help wanted" is open to whoever wants to implement it. ### Write Documentation Bluetooth Adapters could always use more documentation, whether as part of the official Bluetooth Adapters docs, in docstrings, or even on the web in blog posts, articles, and such. ### Submit Feedback The best way to send feedback [our issue page][gh-issues] on GitHub. If you are proposing a feature: - Explain in detail how it would work. - Keep the scope as narrow as possible, to make it easier to implement. - Remember that this is a volunteer-driven project, and that contributions are welcome 😊 ## Get Started! Ready to contribute? Here's how to set yourself up for local development. 1. Fork the repo on GitHub. 2. Clone your fork locally: ```shell $ git clone git@github.com:your_name_here/bluetooth-adapters.git ``` 3. Install the project dependencies with [Poetry](https://python-poetry.org): ```shell $ poetry install ``` 4. Create a branch for local development: ```shell $ git checkout -b name-of-your-bugfix-or-feature ``` Now you can make your changes locally. 5. When you're done making changes, check that your changes pass our tests: ```shell $ poetry run pytest ``` 6. Linting is done through [pre-commit](https://pre-commit.com). Provided you have the tool installed globally, you can run them all as one-off: ```shell $ pre-commit run -a ``` Or better, install the hooks once and have them run automatically each time you commit: ```shell $ pre-commit install ``` 7. Commit your changes and push your branch to GitHub: ```shell $ git add . $ git commit -m "feat(something): your detailed description of your changes" $ git push origin name-of-your-bugfix-or-feature ``` Note: the commit message should follow [the conventional commits](https://www.conventionalcommits.org). We run [`commitlint` on CI](https://github.com/marketplace/actions/commit-linter) to validate it, and if you've installed pre-commit hooks at the previous step, the message will be checked at commit time. 8. Submit a pull request through the GitHub website or using the GitHub CLI (if you have it installed): ```shell $ gh pr create --fill ``` ## Pull Request Guidelines We like to have the pull request open as soon as possible, that's a great place to discuss any piece of work, even unfinished. You can use draft pull request if it's still a work in progress. Here are a few guidelines to follow: 1. Include tests for feature or bug fixes. 2. Update the documentation for significant features. 3. Ensure tests are passing on CI. ## Tips To run a subset of tests: ```shell $ pytest tests ``` ## Making a new release The deployment should be automated and can be triggered from the Semantic Release workflow in GitHub. The next version will be based on [the commit logs](https://python-semantic-release.readthedocs.io/en/latest/commit-log-parsing.html#commit-log-parsing). This is done by [python-semantic-release](https://python-semantic-release.readthedocs.io/en/latest/index.html) via a GitHub action. [gh-issues]: https://github.com/bluetooth-devices/bluetooth-adapters/issues bluetooth-adapters-0.19.3/LICENSE000066400000000000000000000261211463626550700164370ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2022 J. Nick Koston Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. bluetooth-adapters-0.19.3/README.md000066400000000000000000000070411463626550700167110ustar00rootroot00000000000000# Bluetooth Adapters

CI Status Documentation Status Test coverage percentage

Poetry black pre-commit

PyPI Version Supported Python versions License

Tools to enumerate and find Bluetooth Adapters ## Installation Install this via pip (or your favourite package manager): `pip install bluetooth-adapters` ## Contributors ✨ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! ## Credits This package was created with [Cookiecutter](https://github.com/audreyr/cookiecutter) and the [browniebroke/cookiecutter-pypackage](https://github.com/browniebroke/cookiecutter-pypackage) project template. bluetooth-adapters-0.19.3/commitlint.config.js000066400000000000000000000003641463626550700214140ustar00rootroot00000000000000module.exports = { extends: ["@commitlint/config-conventional"], rules: { "header-max-length": [0, "always", Infinity], "body-max-line-length": [0, "always", Infinity], "footer-max-line-length": [0, "always", Infinity], }, }; bluetooth-adapters-0.19.3/docs/000077500000000000000000000000001463626550700163605ustar00rootroot00000000000000bluetooth-adapters-0.19.3/docs/Makefile000066400000000000000000000011751463626550700200240ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = source BUILDDIR = build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) bluetooth-adapters-0.19.3/docs/make.bat000066400000000000000000000013741463626550700177720ustar00rootroot00000000000000@ECHO OFF pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set SOURCEDIR=source set BUILDDIR=build if "%1" == "" goto help %SPHINXBUILD% >NUL 2>NUL if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% :end popd bluetooth-adapters-0.19.3/docs/source/000077500000000000000000000000001463626550700176605ustar00rootroot00000000000000bluetooth-adapters-0.19.3/docs/source/_static/000077500000000000000000000000001463626550700213065ustar00rootroot00000000000000bluetooth-adapters-0.19.3/docs/source/_static/.gitkeep000066400000000000000000000000001463626550700227250ustar00rootroot00000000000000bluetooth-adapters-0.19.3/docs/source/changelog.md000066400000000000000000000000451463626550700221300ustar00rootroot00000000000000```{include} ../../CHANGELOG.md ``` bluetooth-adapters-0.19.3/docs/source/conf.py000066400000000000000000000036621463626550700211660ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) from typing import Any, List # -- Project information ----------------------------------------------------- project = "Bluetooth Adapters" copyright = "2020, J. Nick Koston" author = "J. Nick Koston" # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "myst_parser", ] # The suffix of source filenames. source_suffix = [".rst", ".md"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns: List[Any] = [] # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = "sphinx_rtd_theme" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] bluetooth-adapters-0.19.3/docs/source/contributing.md000066400000000000000000000000501463626550700227040ustar00rootroot00000000000000```{include} ../../CONTRIBUTING.md ``` bluetooth-adapters-0.19.3/docs/source/index.md000066400000000000000000000003621463626550700213120ustar00rootroot00000000000000# Welcome to Bluetooth Adapters documentation! ```{toctree} :caption: Installation & Usage :maxdepth: 2 installation usage ``` ```{toctree} :caption: Project Info :maxdepth: 2 changelog contributing ``` ```{include} ../../README.md ``` bluetooth-adapters-0.19.3/docs/source/installation.md000066400000000000000000000002751463626550700227070ustar00rootroot00000000000000# Installation The package is published on [PyPI](https://pypi.org/project/deezer-python/) and can be installed with `pip` (or any equivalent): ```bash pip install bluetooth-adapters ``` bluetooth-adapters-0.19.3/docs/source/usage.md000066400000000000000000000001501463626550700213020ustar00rootroot00000000000000# Usage To use this package, import it: ```python import bluetooth_adapters ``` TODO: Document usage bluetooth-adapters-0.19.3/example.py000066400000000000000000000003401463626550700174320ustar00rootroot00000000000000import asyncio from bluetooth_adapters import get_adapters async def go() -> None: bluetooth_adapters = get_adapters() await bluetooth_adapters.refresh() print(bluetooth_adapters.adapters) asyncio.run(go()) bluetooth-adapters-0.19.3/poetry.lock000066400000000000000000003051071463626550700176320ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiooui" version = "0.1.5" description = "Async OUI lookups" optional = false python-versions = ">=3.9,<4.0" files = [ {file = "aiooui-0.1.5-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:e03f77f1b0c1ed775404792dec321f2e11740aa4b3c142e6890b3902502d6444"}, {file = "aiooui-0.1.5-py3-none-any.whl", hash = "sha256:f92e485a9d2fb7aea5b626c107485517ff93dc2507d957dcc24bd60c2024b19e"}, {file = "aiooui-0.1.5.tar.gz", hash = "sha256:68015a428c46521cdf82896d920376ebac885711a937179cf1deca20b8e1f3ea"}, ] [[package]] name = "alabaster" version = "0.7.16" description = "A light, configurable Sphinx theme" optional = true python-versions = ">=3.9" files = [ {file = "alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92"}, {file = "alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65"}, ] [[package]] name = "async-timeout" version = "4.0.3" description = "Timeout context manager for asyncio programs" optional = false python-versions = ">=3.7" files = [ {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, ] [[package]] name = "babel" version = "2.14.0" description = "Internationalization utilities" optional = true python-versions = ">=3.7" files = [ {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] [package.extras] dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "bleak" version = "0.21.1" description = "Bluetooth Low Energy platform Agnostic Klient" optional = false python-versions = ">=3.8,<3.13" files = [ {file = "bleak-0.21.1-py3-none-any.whl", hash = "sha256:ccec260a0f5ec02dd133d68b0351c0151b2ecf3ddd0bcabc4c04a1cdd7f33256"}, {file = "bleak-0.21.1.tar.gz", hash = "sha256:ec4a1a2772fb315b992cbaa1153070c7e26968a52b0e2727035f443a1af5c18f"}, ] [package.dependencies] async-timeout = {version = ">=3.0.0,<5", markers = "python_version < \"3.11\""} bleak-winrt = {version = ">=1.2.0,<2.0.0", markers = "platform_system == \"Windows\" and python_version < \"3.12\""} dbus-fast = {version = ">=1.83.0,<3", markers = "platform_system == \"Linux\""} pyobjc-core = {version = ">=9.2,<10.0", markers = "platform_system == \"Darwin\""} pyobjc-framework-CoreBluetooth = {version = ">=9.2,<10.0", markers = "platform_system == \"Darwin\""} pyobjc-framework-libdispatch = {version = ">=9.2,<10.0", markers = "platform_system == \"Darwin\""} typing-extensions = {version = ">=4.7.0", markers = "python_version < \"3.12\""} "winrt-Windows.Devices.Bluetooth" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} "winrt-Windows.Devices.Bluetooth.Advertisement" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} "winrt-Windows.Devices.Bluetooth.GenericAttributeProfile" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} "winrt-Windows.Devices.Enumeration" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} "winrt-Windows.Foundation" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} "winrt-Windows.Foundation.Collections" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} "winrt-Windows.Storage.Streams" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} [[package]] name = "bleak-winrt" version = "1.2.0" description = "Python WinRT bindings for Bleak" optional = false python-versions = "*" files = [ {file = "bleak-winrt-1.2.0.tar.gz", hash = "sha256:0577d070251b9354fc6c45ffac57e39341ebb08ead014b1bdbd43e211d2ce1d6"}, {file = "bleak_winrt-1.2.0-cp310-cp310-win32.whl", hash = "sha256:a2ae3054d6843ae0cfd3b94c83293a1dfd5804393977dd69bde91cb5099fc47c"}, {file = "bleak_winrt-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:677df51dc825c6657b3ae94f00bd09b8ab88422b40d6a7bdbf7972a63bc44e9a"}, {file = "bleak_winrt-1.2.0-cp311-cp311-win32.whl", hash = "sha256:9449cdb942f22c9892bc1ada99e2ccce9bea8a8af1493e81fefb6de2cb3a7b80"}, {file = "bleak_winrt-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:98c1b5a6a6c431ac7f76aa4285b752fe14a1c626bd8a1dfa56f66173ff120bee"}, {file = "bleak_winrt-1.2.0-cp37-cp37m-win32.whl", hash = "sha256:623ac511696e1f58d83cb9c431e32f613395f2199b3db7f125a3d872cab968a4"}, {file = "bleak_winrt-1.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:13ab06dec55469cf51a2c187be7b630a7a2922e1ea9ac1998135974a7239b1e3"}, {file = "bleak_winrt-1.2.0-cp38-cp38-win32.whl", hash = "sha256:5a36ff8cd53068c01a795a75d2c13054ddc5f99ce6de62c1a97cd343fc4d0727"}, {file = "bleak_winrt-1.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:810c00726653a962256b7acd8edf81ab9e4a3c66e936a342ce4aec7dbd3a7263"}, {file = "bleak_winrt-1.2.0-cp39-cp39-win32.whl", hash = "sha256:dd740047a08925bde54bec357391fcee595d7b8ca0c74c87170a5cbc3f97aa0a"}, {file = "bleak_winrt-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:63130c11acfe75c504a79c01f9919e87f009f5e742bfc7b7a5c2a9c72bf591a7"}, ] [[package]] name = "certifi" version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = true python-versions = ">=3.6" files = [ {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, ] [[package]] name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = true python-versions = ">=3.7.0" files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" version = "7.4.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, ] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] toml = ["tomli"] [[package]] name = "dbus-fast" version = "2.21.2" description = "A faster version of dbus-next" optional = false python-versions = "<4.0,>=3.7" files = [ {file = "dbus_fast-2.21.2-cp310-cp310-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:b5f79edcb0dd48e98b1a1e3e4a655fd0ecc2ba72275f9e8379e8655b4411edcc"}, {file = "dbus_fast-2.21.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40aa9068759bbf7e062f074c965b391b95f18f897cc9be6eb906ee48a6f77724"}, {file = "dbus_fast-2.21.2-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:d2406b838ccbda9bd49dda4a7620ce228da306cd8f9a3f8c9f42b2d792a491fb"}, {file = "dbus_fast-2.21.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ed431895630135da9cec736326304f0833ac31919043efdbecf8f6c7bed40d05"}, {file = "dbus_fast-2.21.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:90f09498ac91f0e6ddc7fa569e851a2b258a70917cd07ae8412ad5725ef1d411"}, {file = "dbus_fast-2.21.2-cp311-cp311-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:b17f1eafeaa825e8933a5394157db9e0a24e65eac188a244dbbbc01dc23fde7a"}, {file = "dbus_fast-2.21.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9f4191f7108f9433e5c017915e60ec57231aaf58c82fde6e20bd497998ebc97"}, {file = "dbus_fast-2.21.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3b96a645cbd035f47f3b934130cd0ae977c043480ad7fe9838f78fdcb480c189"}, {file = "dbus_fast-2.21.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bc696304ce0f5da374ddfb3e83273e9d89602a8f20e7fab57b079378f2cb5789"}, {file = "dbus_fast-2.21.2-cp312-cp312-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:b5e2015a385f0b364eff1827b151313429d3148d2718d679bec8a9c67b78721a"}, {file = "dbus_fast-2.21.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32efcbe276a4fdf6946450c512355e7ae22836cf3595d48c59330687cda52117"}, {file = "dbus_fast-2.21.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:601c3c8796e7edd23bce0432e44ca8f0b85c48a17ab5258f57cd8fe815f9c07a"}, {file = "dbus_fast-2.21.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:194899057b8382c1902c32e1a565a2d47bcc99e06aafe9d660348394532a4bf6"}, {file = "dbus_fast-2.21.2-cp37-cp37m-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:81ac390d4e26711b3ac46b3dd81a29bcbc1eddd4a408b336c67f0c94eb6d7ff0"}, {file = "dbus_fast-2.21.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f056f2bfee24e87a4184202d3b108a56176344303bb1278988f13f5e90777da"}, {file = "dbus_fast-2.21.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9ba884d102e069e105f22986fccf1d21776e6ced11f4b75aeddcc37e728a80fd"}, {file = "dbus_fast-2.21.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:60d989030403cc1611105bec6a90df22967e523ae28486dee5f9bd644e37f797"}, {file = "dbus_fast-2.21.2-cp38-cp38-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:aabe539f0e9961a1beb6e8c0078112a1a60de18958335678edb3f26021951ff9"}, {file = "dbus_fast-2.21.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6edec4f92d32b9a288b38457a114086a0d5f5fdec9c3e9b7ff6052fd45963c1d"}, {file = "dbus_fast-2.21.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:37e6f717dedc299fc15ab8f5ec5b180725d2b896ba1aaef07c1921df0b7113a0"}, {file = "dbus_fast-2.21.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eade5ed18327bf306b75e525ded98c08921e1b21d42e715b7f0a1371a7669168"}, {file = "dbus_fast-2.21.2-cp39-cp39-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:2fd1be6967a92957f517dbd3755ee7cddc128ec840af2ef4ad6fb023a0dac74d"}, {file = "dbus_fast-2.21.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5db0737471e60228c1a6aabecbf883c972f0b9e50bf7fc0878a8b35ebdf1d1e"}, {file = "dbus_fast-2.21.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6c6f1fda6f318061a023d6da96ee50ad2d30c04557012a60a0f1abd39c2a8704"}, {file = "dbus_fast-2.21.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0b78f2116fb745a7623c8e18d9c435bfe4732e4f9284a923c4b9a44ef68ae2d4"}, {file = "dbus_fast-2.21.2-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:886ce5750d4e64636bd933f22513e9ba06b7ee9650f28699c553c162b52db666"}, {file = "dbus_fast-2.21.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3159f1cecd4b86f565c01da787ad6eaa57e8ba210d355836fa849e4c0b1ee57"}, {file = "dbus_fast-2.21.2-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:51279b69ac6b872208f3aa1b00b910dd9ef9c3d625b79eb378405dbd72a29cab"}, {file = "dbus_fast-2.21.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29f07ef89e35b93afa87dea86abec2aff68802572944485250f50def15dc5ef8"}, {file = "dbus_fast-2.21.2-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:38138fc5a24797cc443c6894d25497271ccf3399c8aa8cdba228a7bdda2d2921"}, {file = "dbus_fast-2.21.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afde99d085a330e8aed59535d808636f1f563cb08d12900d0e415508e6270a1d"}, {file = "dbus_fast-2.21.2-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:c2bb0fd813bf3cafc6796d86d42cc8a9d37c2633d973dd963c3ad4c080d7061d"}, {file = "dbus_fast-2.21.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:044eec5d0668d3229480094f5b2aefafb336afa6976d686bd0cd8770eee1bb2c"}, {file = "dbus_fast-2.21.2.tar.gz", hash = "sha256:8645187b2e86c5141217adcb462d6dbecd37fb2ab8705f66b3773a66206ef83d"}, ] [[package]] name = "docutils" version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = true python-versions = ">=3.7" files = [ {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] [[package]] name = "exceptiongroup" version = "1.2.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] [package.extras] test = ["pytest (>=6)"] [[package]] name = "idna" version = "3.7" description = "Internationalized Domain Names in Applications (IDNA)" optional = true python-versions = ">=3.5" files = [ {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] [[package]] name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] [[package]] name = "importlib-metadata" version = "7.0.1" description = "Read metadata from Python packages" optional = true python-versions = ">=3.8" files = [ {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, ] [package.dependencies] zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "jinja2" version = "3.1.3" description = "A very fast and expressive template engine." optional = true python-versions = ">=3.7" files = [ {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] [package.dependencies] MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] [[package]] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = true python-versions = ">=3.8" files = [ {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, ] [package.dependencies] mdurl = ">=0.1,<1.0" [package.extras] benchmarking = ["psutil", "pytest", "pytest-benchmark"] code-style = ["pre-commit (>=3.0,<4.0)"] compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] linkify = ["linkify-it-py (>=1,<3)"] plugins = ["mdit-py-plugins"] profiling = ["gprof2dot"] rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "markupsafe" version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." optional = true python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, ] [[package]] name = "mdit-py-plugins" version = "0.4.0" description = "Collection of plugins for markdown-it-py" optional = true python-versions = ">=3.8" files = [ {file = "mdit_py_plugins-0.4.0-py3-none-any.whl", hash = "sha256:b51b3bb70691f57f974e257e367107857a93b36f322a9e6d44ca5bf28ec2def9"}, {file = "mdit_py_plugins-0.4.0.tar.gz", hash = "sha256:d8ab27e9aed6c38aa716819fedfde15ca275715955f8a185a8e1cf90fb1d2c1b"}, ] [package.dependencies] markdown-it-py = ">=1.0.0,<4.0.0" [package.extras] code-style = ["pre-commit"] rtd = ["myst-parser", "sphinx-book-theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" optional = true python-versions = ">=3.7" files = [ {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] [[package]] name = "myst-parser" version = "2.0.0" description = "An extended [CommonMark](https://spec.commonmark.org/) compliant parser," optional = true python-versions = ">=3.8" files = [ {file = "myst_parser-2.0.0-py3-none-any.whl", hash = "sha256:7c36344ae39c8e740dad7fdabf5aa6fc4897a813083c6cc9990044eb93656b14"}, {file = "myst_parser-2.0.0.tar.gz", hash = "sha256:ea929a67a6a0b1683cdbe19b8d2e724cd7643f8aa3e7bb18dd65beac3483bead"}, ] [package.dependencies] docutils = ">=0.16,<0.21" jinja2 = "*" markdown-it-py = ">=3.0,<4.0" mdit-py-plugins = ">=0.4,<1.0" pyyaml = "*" sphinx = ">=6,<8" [package.extras] code-style = ["pre-commit (>=3.0,<4.0)"] linkify = ["linkify-it-py (>=2.0,<3.0)"] rtd = ["ipython", "pydata-sphinx-theme (==v0.13.0rc4)", "sphinx-autodoc2 (>=0.4.2,<0.5.0)", "sphinx-book-theme (==1.0.0rc2)", "sphinx-copybutton", "sphinx-design2", "sphinx-pyscript", "sphinx-tippy (>=0.3.1)", "sphinx-togglebutton", "sphinxext-opengraph (>=0.8.2,<0.9.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"] testing = ["beautifulsoup4", "coverage[toml]", "pytest (>=7,<8)", "pytest-cov", "pytest-param-files (>=0.3.4,<0.4.0)", "pytest-regressions", "sphinx-pytest"] testing-docutils = ["pygments", "pytest (>=7,<8)", "pytest-param-files (>=0.3.4,<0.4.0)"] [[package]] name = "packaging" version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] name = "pluggy" version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "pygments" version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = true python-versions = ">=3.7" files = [ {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [package.extras] plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyobjc-core" version = "9.2" description = "Python<->ObjC Interoperability Module" optional = false python-versions = ">=3.7" files = [ {file = "pyobjc-core-9.2.tar.gz", hash = "sha256:d734b9291fec91ff4e3ae38b9c6839debf02b79c07314476e87da8e90b2c68c3"}, {file = "pyobjc_core-9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fa674a39949f5cde8e5c7bbcd24496446bfc67592b028aedbec7f81dc5fc4daa"}, {file = "pyobjc_core-9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc8de304ee322a1ee530b4d2daca135a49b4a49aa3cedc6b2c26c43885f4842"}, {file = "pyobjc_core-9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0fa950f092673883b8bd28bc18397415cabb457bf410920762109b411789ade9"}, {file = "pyobjc_core-9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:586e4cae966282eaa61b21cae66ccdcee9d69c036979def26eebdc08ddebe20f"}, {file = "pyobjc_core-9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:41189c2c680931c0395a55691763c481fc681f454f21bb4f1644f98c24a45954"}, {file = "pyobjc_core-9.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:2d23ee539f2ba5e9f5653d75a13f575c7e36586fc0086792739e69e4c2617eda"}, {file = "pyobjc_core-9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b9809cf96678797acb72a758f34932fe8e2602d5ab7abec15c5ac68ddb481720"}, ] [[package]] name = "pyobjc-framework-cocoa" version = "9.2" description = "Wrappers for the Cocoa frameworks on macOS" optional = false python-versions = ">=3.7" files = [ {file = "pyobjc-framework-Cocoa-9.2.tar.gz", hash = "sha256:efd78080872d8c8de6c2b97e0e4eac99d6203a5d1637aa135d071d464eb2db53"}, {file = "pyobjc_framework_Cocoa-9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9e02d8a7cc4eb7685377c50ba4f17345701acf4c05b1e7480d421bff9e2f62a4"}, {file = "pyobjc_framework_Cocoa-9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3b1e6287b3149e4c6679cdbccd8e9ef6557a4e492a892e80a77df143f40026d2"}, {file = "pyobjc_framework_Cocoa-9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:312977ce2e3989073c6b324c69ba24283de206fe7acd6dbbbaf3e29238a22537"}, {file = "pyobjc_framework_Cocoa-9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:aae7841cf40c26dd915f4dd828f91c6616e6b7998630b72e704750c09e00f334"}, {file = "pyobjc_framework_Cocoa-9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:739a421e14382a46cbeb9a883f192dceff368ad28ec34d895c48c0ad34cf2c1d"}, {file = "pyobjc_framework_Cocoa-9.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:32d9ac1033fac1b821ddee8c68f972a7074ad8c50bec0bea9a719034c1c2fb94"}, {file = "pyobjc_framework_Cocoa-9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b236bb965e41aeb2e215d4e98a5a230d4b63252c6d26e00924ea2e69540a59d6"}, ] [package.dependencies] pyobjc-core = ">=9.2" [[package]] name = "pyobjc-framework-corebluetooth" version = "9.2" description = "Wrappers for the framework CoreBluetooth on macOS" optional = false python-versions = ">=3.7" files = [ {file = "pyobjc-framework-CoreBluetooth-9.2.tar.gz", hash = "sha256:cb2481b1dfe211ae9ce55f36537dc8155dbf0dc8ff26e0bc2e13f7afb0a291d1"}, {file = "pyobjc_framework_CoreBluetooth-9.2-cp36-abi3-macosx_10_9_universal2.whl", hash = "sha256:53d888742119d0f0c725d0b0c2389f68e8f21f0cba6d6aec288c53260a0196b6"}, {file = "pyobjc_framework_CoreBluetooth-9.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:179532882126526e38fe716a50fb0ee8f440e0b838d290252c515e622b5d0e49"}, {file = "pyobjc_framework_CoreBluetooth-9.2-cp36-abi3-macosx_11_0_universal2.whl", hash = "sha256:256a5031ea9d8a7406541fa1b0dfac549b1de93deae8284605f9355b13fb58be"}, ] [package.dependencies] pyobjc-core = ">=9.2" pyobjc-framework-Cocoa = ">=9.2" [[package]] name = "pyobjc-framework-libdispatch" version = "9.2" description = "Wrappers for libdispatch on macOS" optional = false python-versions = ">=3.7" files = [ {file = "pyobjc-framework-libdispatch-9.2.tar.gz", hash = "sha256:542e7f7c2b041939db5ed6f3119c1d67d73ec14a996278b92485f8513039c168"}, {file = "pyobjc_framework_libdispatch-9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88d4091d4bcb5702783d6e86b4107db973425a17d1de491543f56bd348909b60"}, {file = "pyobjc_framework_libdispatch-9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1a67b007113328538b57893cc7829a722270764cdbeae6d5e1460a1d911314df"}, {file = "pyobjc_framework_libdispatch-9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6fccea1a57436cf1ac50d9ebc6e3e725bcf77f829ba6b118e62e6ed7866d359d"}, {file = "pyobjc_framework_libdispatch-9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6eba747b7ad91b0463265a7aee59235bb051fb97687f35ca2233690369b5e4e4"}, {file = "pyobjc_framework_libdispatch-9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2e835495860d04f63c2d2f73ae3dd79da4222864c107096dc0f99e8382700026"}, {file = "pyobjc_framework_libdispatch-9.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1b107e5c3580b09553030961ea6b17abad4a5132101eab1af3ad2cb36d0f08bb"}, {file = "pyobjc_framework_libdispatch-9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:83cdb672acf722717b5ecf004768f215f02ac02d7f7f2a9703da6e921ab02222"}, ] [package.dependencies] pyobjc-core = ">=9.2" [[package]] name = "pytest" version = "8.2.0" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ {file = "pytest-8.2.0-py3-none-any.whl", hash = "sha256:1733f0620f6cda4095bbf0d9ff8022486e91892245bb9e7d5542c018f612f233"}, {file = "pytest-8.2.0.tar.gz", hash = "sha256:d507d4482197eac0ba2bae2e9babf0672eb333017bcedaa5fb1a3d42c1174b3f"}, ] [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=1.5,<2.0" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" version = "0.23.6" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"}, {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"}, ] [package.dependencies] pytest = ">=7.0.0,<9" [package.extras] docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] [[package]] name = "pytest-cov" version = "5.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.8" files = [ {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, ] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" optional = true python-versions = ">=3.6" files = [ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] [[package]] name = "requests" version = "2.31.0" description = "Python HTTP for Humans." optional = true python-versions = ">=3.7" files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." optional = true python-versions = "*" files = [ {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] [[package]] name = "sphinx" version = "7.3.7" description = "Python documentation generator" optional = true python-versions = ">=3.9" files = [ {file = "sphinx-7.3.7-py3-none-any.whl", hash = "sha256:413f75440be4cacf328f580b4274ada4565fb2187d696a84970c23f77b64d8c3"}, {file = "sphinx-7.3.7.tar.gz", hash = "sha256:a4a7db75ed37531c05002d56ed6948d4c42f473a36f46e1382b0bd76ca9627bc"}, ] [package.dependencies] alabaster = ">=0.7.14,<0.8.0" babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.22" imagesize = ">=1.3" importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.14" requests = ">=2.25.0" snowballstemmer = ">=2.0" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" sphinxcontrib-htmlhelp = ">=2.0.0" sphinxcontrib-jsmath = "*" sphinxcontrib-qthelp = "*" sphinxcontrib-serializinghtml = ">=1.1.9" tomli = {version = ">=2", markers = "python_version < \"3.11\""} [package.extras] docs = ["sphinxcontrib-websupport"] lint = ["flake8 (>=3.5.0)", "importlib_metadata", "mypy (==1.9.0)", "pytest (>=6.0)", "ruff (==0.3.7)", "sphinx-lint", "tomli", "types-docutils", "types-requests"] test = ["cython (>=3.0)", "defusedxml (>=0.7.1)", "pytest (>=6.0)", "setuptools (>=67.0)"] [[package]] name = "sphinx-rtd-theme" version = "2.0.0" description = "Read the Docs theme for Sphinx" optional = true python-versions = ">=3.6" files = [ {file = "sphinx_rtd_theme-2.0.0-py2.py3-none-any.whl", hash = "sha256:ec93d0856dc280cf3aee9a4c9807c60e027c7f7b461b77aeffed682e68f0e586"}, {file = "sphinx_rtd_theme-2.0.0.tar.gz", hash = "sha256:bd5d7b80622406762073a04ef8fadc5f9151261563d47027de09910ce03afe6b"}, ] [package.dependencies] docutils = "<0.21" sphinx = ">=5,<8" sphinxcontrib-jquery = ">=4,<5" [package.extras] dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client", "wheel"] [[package]] name = "sphinxcontrib-applehelp" version = "1.0.8" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = true python-versions = ">=3.9" files = [ {file = "sphinxcontrib_applehelp-1.0.8-py3-none-any.whl", hash = "sha256:cb61eb0ec1b61f349e5cc36b2028e9e7ca765be05e49641c97241274753067b4"}, {file = "sphinxcontrib_applehelp-1.0.8.tar.gz", hash = "sha256:c40a4f96f3776c4393d933412053962fac2b84f4c99a7982ba42e09576a70619"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-devhelp" version = "1.0.6" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp documents" optional = true python-versions = ">=3.9" files = [ {file = "sphinxcontrib_devhelp-1.0.6-py3-none-any.whl", hash = "sha256:6485d09629944511c893fa11355bda18b742b83a2b181f9a009f7e500595c90f"}, {file = "sphinxcontrib_devhelp-1.0.6.tar.gz", hash = "sha256:9893fd3f90506bc4b97bdb977ceb8fbd823989f4316b28c3841ec128544372d3"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" version = "2.0.5" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = true python-versions = ">=3.9" files = [ {file = "sphinxcontrib_htmlhelp-2.0.5-py3-none-any.whl", hash = "sha256:393f04f112b4d2f53d93448d4bce35842f62b307ccdc549ec1585e950bc35e04"}, {file = "sphinxcontrib_htmlhelp-2.0.5.tar.gz", hash = "sha256:0dc87637d5de53dd5eec3a6a01753b1ccf99494bd756aafecd74b4fa9e729015"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["html5lib", "pytest"] [[package]] name = "sphinxcontrib-jquery" version = "4.1" description = "Extension to include jQuery on newer Sphinx releases" optional = true python-versions = ">=2.7" files = [ {file = "sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a"}, {file = "sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae"}, ] [package.dependencies] Sphinx = ">=1.8" [[package]] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" optional = true python-versions = ">=3.5" files = [ {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, ] [package.extras] test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" version = "1.0.7" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp documents" optional = true python-versions = ">=3.9" files = [ {file = "sphinxcontrib_qthelp-1.0.7-py3-none-any.whl", hash = "sha256:e2ae3b5c492d58fcbd73281fbd27e34b8393ec34a073c792642cd8e529288182"}, {file = "sphinxcontrib_qthelp-1.0.7.tar.gz", hash = "sha256:053dedc38823a80a7209a80860b16b722e9e0209e32fea98c90e4e6624588ed6"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "sphinxcontrib-serializinghtml" version = "1.1.10" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)" optional = true python-versions = ">=3.9" files = [ {file = "sphinxcontrib_serializinghtml-1.1.10-py3-none-any.whl", hash = "sha256:326369b8df80a7d2d8d7f99aa5ac577f51ea51556ed974e7716cfd4fca3f6cb7"}, {file = "sphinxcontrib_serializinghtml-1.1.10.tar.gz", hash = "sha256:93f3f5dc458b91b192fe10c397e324f262cf163d79f3282c158e8436a2c4511f"}, ] [package.extras] lint = ["docutils-stubs", "flake8", "mypy"] standalone = ["Sphinx (>=5)"] test = ["pytest"] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "typing-extensions" version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] [[package]] name = "uart-devices" version = "0.1.0" description = "UART Devices for Linux" optional = false python-versions = "<4.0,>=3.8" files = [ {file = "uart_devices-0.1.0-py3-none-any.whl", hash = "sha256:f019357945a4f2d619e43a7cef7cee4f52aeff06aa5c674f9da448dce3c9cd64"}, {file = "uart_devices-0.1.0.tar.gz", hash = "sha256:7f0342c0ba0bc2a4c13c9ead5462dc9feeaca507e5c7017ebd074a69567ad9b1"}, ] [[package]] name = "urllib3" version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = true python-versions = ">=3.8" files = [ {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [[package]] name = "usb-devices" version = "0.4.5" description = "Tools for mapping, describing, and resetting USB devices" optional = false python-versions = ">=3.9,<4.0" files = [ {file = "usb_devices-0.4.5-py3-none-any.whl", hash = "sha256:8a415219ef1395e25aa0bddcad484c88edf9673acdeae8a07223ca7222a01dcf"}, {file = "usb_devices-0.4.5.tar.gz", hash = "sha256:9b5c7606df2bc791c6c45b7f76244a0cbed83cb6fa4c68791a143c03345e195d"}, ] [[package]] name = "winrt-runtime" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-runtime-2.0.0b1.tar.gz", hash = "sha256:28db2ebe7bfb347d110224e9f23fe8079cea45af0fcbd643d039524ced07d22c"}, {file = "winrt_runtime-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:8f812b01e2c8dd3ca68aa51a7aa02e815cc2ac3c8520a883b4ec7a4fc63afb04"}, {file = "winrt_runtime-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:f36f6102f9b7a08d917a6809117c085639b66be2c579f4089d3fd47b83e8f87b"}, {file = "winrt_runtime-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:4a99f267da96edc977623355b816b46c1344c66dc34732857084417d8cf9a96b"}, {file = "winrt_runtime-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:ba998e3fc452338c5e2d7bf5174a6206580245066d60079ee4130082d0eb61c2"}, {file = "winrt_runtime-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:e7838f0fdf5653ce245888590214177a1f54884cece2c8dfbfe3d01b2780171e"}, {file = "winrt_runtime-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:2afa45b7385e99a63d55ccda29096e6a84fcd4c654479005c147b0e65e274abf"}, {file = "winrt_runtime-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:edda124ff965cec3a6bfdb26fbe88e004f96975dd84115176e30c1efbcb16f4c"}, {file = "winrt_runtime-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:d8935951efeec6b3d546dce8f48bb203aface57a1ba991c066f0e12e84c8f91e"}, {file = "winrt_runtime-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:509fb9a03af5e1125433f58522725716ceef040050d33625460b5a5eb98a46ac"}, {file = "winrt_runtime-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:41138fe4642345d7143e817ce0905d82e60b3832558143e0a17bfea8654c6512"}, {file = "winrt_runtime-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:081a429fe85c33cb6610c4a799184b7650b30f15ab1d89866f2bda246d3a5c0a"}, {file = "winrt_runtime-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:e6984604c6ae1f3258973ba2503d1ea5aa15e536ca41d6a131ad305ebbb6519d"}, ] [[package]] name = "winrt-windows-devices-bluetooth" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Devices.Bluetooth-2.0.0b1.tar.gz", hash = "sha256:786bd43786b873a083b89debece538974f720584662a2573d6a8a8501a532860"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:79631bf3f96954da260859df9228a028835ffade0d885ba3942c5a86a853d150"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:cd85337a95065d0d2045c06db1a5edd4a447aad47cf7027818f6fb69f831c56c"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:6a963869ed003d260e90e9bedc334129303f263f068ea1c0d994df53317db2bc"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:7c5951943a3911d94a8da190f4355dc70128d7d7f696209316372c834b34d462"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:b0bb154ae92235649ed234982f609c490a467d5049c27d63397be9abbb00730e"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:6688dfb0fc3b7dc517bf8cf40ae00544a50b4dec91470d37be38fc33c4523632"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:613c6ff4125df46189b3bef6d3110d94ec725d357ab734f00eedb11c4116c367"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:59c403b64e9f4e417599c6f6aea6ee6fac960597c21eac6b3fd8a84f64aa387c"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:b7f6e1b9bb6e33be80045adebd252cf25cd648759fad6e86c61a393ddd709f7f"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:eae7a89106eab047e96843e28c3c6ce0886dd7dee60180a1010498925e9503f9"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:8dfd1915c894ac19dd0b24aba38ef676c92c3473c0d9826762ba9616ad7df68b"}, {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:49058587e6d82ba33da0767b97a378ddfea8e3a5991bdeff680faa287bfae57e"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.Devices.Bluetooth.GenericAttributeProfile[all] (==2.0.0-beta.1)", "winrt-Windows.Devices.Bluetooth.Rfcomm[all] (==2.0.0-beta.1)", "winrt-Windows.Devices.Enumeration[all] (==2.0.0-beta.1)", "winrt-Windows.Devices.Radios[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Networking[all] (==2.0.0-beta.1)", "winrt-Windows.Storage.Streams[all] (==2.0.0-beta.1)"] [[package]] name = "winrt-windows-devices-bluetooth-advertisement" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Devices.Bluetooth.Advertisement-2.0.0b1.tar.gz", hash = "sha256:d9050faa4377d410d4f0e9cabb5ec555a267531c9747370555ac9ec93ec9f399"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:ac9b703d16adc87c3541585525b8fcf6d84391e2fa010c2f001e714c405cc3b7"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:593cade7853a8b0770e8ef30462b5d5f477b82e17e0aa590094b1c26efd3e05a"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:574698c08895e2cfee7379bdf34a5f319fe440d7dfcc7bc9858f457c08e9712c"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:652a096f8210036bbb539d7f971eaf1f472a3aeb60b7e31278e3d0d30a355292"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:e5cfb866c44dad644fb44b441f4fdbddafc9564075f1f68f756e20f438105c67"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:6c2503eaaf5cd988b5510b86347dba45ad6ee52656f9656a1a97abae6d35386e"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:780c766725a55f4211f921c773c92c2331803e70f65d6ad6676a60f903d39a54"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:39c8633d01039eb2c2f6f20cfc43c045a333b9f3a45229e2ce443f71bb2a562c"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:eaa0d44b4158b16937eac8102249e792f0299dbb0aefc56cc9adc9552e8f9afe"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:d171487e23f7671ad2923544bfa6545d0a29a1a9ae1f5c1d5e5e5f473a5d62b2"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:442eecac87653a03617e65bdb2ef79ddc0582dfdacc2be8af841fba541577f8b"}, {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:b30ab9b8c1ecf818be08bac86bee425ef40f75060c4011d4e6c2e624a7b9916e"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.Devices.Bluetooth[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Storage.Streams[all] (==2.0.0-beta.1)"] [[package]] name = "winrt-windows-devices-bluetooth-genericattributeprofile" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1.tar.gz", hash = "sha256:93b745d51ecfb3e9d3a21623165cc065735c9e0146cb7a26744182c164e63e14"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:db740aaedd80cca5b1a390663b26c7733eb08f4c57ade6a04b055d548e9d042b"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:7c81aa6c066cdab58bcc539731f208960e094a6d48b59118898e1e804dbbdf7f"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:92277a6bbcbe2225ad1be92968af597dc77bc37a63cd729690d2d9fb5094ae25"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:6b48209669c1e214165530793cf9916ae44a0ae2618a9be7a489e8c94f7e745f"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:2f17216e6ce748eaef02fb0658213515d3ff31e2dbb18f070a614876f818c90d"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:db798a0f0762e390da5a9f02f822daff00692bd951a492224bf46782713b2938"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:b8d9dba04b9cfa53971c35117fc3c68c94bfa5e2ed18ce680f731743598bf246"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:e5260b3f33dee8a896604297e05efc04d04298329c205a74ded8e2d6333e84b7"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:822ef539389ecb546004345c4dce8b9b7788e2e99a1d6f0947a4b123dceb7fed"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:11e6863e7a94d2b6dd76ddcd19c01e311895810a4ce6ad08c7b5534294753243"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:20de8d04c301c406362c93e78d41912aea0af23c4b430704aba329420d7c2cdf"}, {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:918059796f2f123216163b928ecde8ecec17994fb7a94042af07fda82c132a6d"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.Devices.Bluetooth[all] (==2.0.0-beta.1)", "winrt-Windows.Devices.Enumeration[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Storage.Streams[all] (==2.0.0-beta.1)"] [[package]] name = "winrt-windows-devices-enumeration" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Devices.Enumeration-2.0.0b1.tar.gz", hash = "sha256:8f214040e4edbe57c4943488887db89f4a00d028c34169aafd2205e228026100"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:dcb9e7d230aefec8531a46d393ecb1063b9d4b97c9f3ff2fc537ce22bdfa2444"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:22a3e1fef40786cc8d51320b6f11ff25de6c674475f3ba608a46915e1dadf0f5"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:2edcfeb70a71d40622873cad96982a28e92a7ee71f33968212dd3598b2d8d469"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:ce4eb88add7f5946d2666761a97a3bb04cac2a061d264f03229c1e15dbd7ce91"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:a9001f17991572abdddab7ab074e08046e74e05eeeaf3b2b01b8b47d2879b64c"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:0440b91ce144111e207f084cec6b1277162ef2df452d321951e989ce87dc9ced"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:e4fae13126f13a8d9420b74fb5a5ff6a6b2f91f7718c4be2d4a8dc1337c58f59"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:e352eebc23dc94fb79e67a056c057fb0e16c20c8cb881dc826094c20ed4791e3"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:b43f5c1f053a170e6e4b44ba69838ac223f9051adca1a56506d4c46e98d1485f"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:ed245fad8de6a134d5c3a630204e7f8238aa944a40388005bce0ce3718c410fa"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:22a9eefdbfe520778512266d0b48ff239eaa8d272fce6f5cb1ff352bed0619f4"}, {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:397d43f8fd2621a7719b9eab6a4a8e72a1d6fa2d9c36525a30812f8e7bad3bdf"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.ApplicationModel.Background[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Security.Credentials[all] (==2.0.0-beta.1)", "winrt-Windows.Storage.Streams[all] (==2.0.0-beta.1)", "winrt-Windows.UI.Popups[all] (==2.0.0-beta.1)", "winrt-Windows.UI[all] (==2.0.0-beta.1)"] [[package]] name = "winrt-windows-foundation" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Foundation-2.0.0b1.tar.gz", hash = "sha256:976b6da942747a7ca5a179a35729d8dc163f833e03b085cf940332a5e9070d54"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:5337ac1ec260132fbff868603e73a3738d4001911226e72669b3d69c8a256d5e"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:af969e5bb9e2e41e4e86a361802528eafb5eb8fe87ec1dba6048c0702d63caa8"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:bbbfa6b3c444a1074a630fd4a1b71171be7a5c9bb07c827ad9259fadaed56cf2"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:b91bd92b1854c073acd81aa87cf8df571d2151b1dd050b6181aa36f7acc43df4"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:2f5359f25703347e827dbac982150354069030f1deecd616f7ce37ad90cbcb00"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:0f1f1978173ddf0ee6262c2edb458f62d628b9fa0df10cd1e8c78c833af3197e"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:c1d23b737f733104b91c89c507b58d0b3ef5f3234a1b608ef6dfb6dbbb8777ea"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:95de6c29e9083fe63f127b965b54dfa52a6424a93a94ce87cfad4c1900a6e887"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:4707063a5a6980e3f71aebeea5ac93101c753ec13a0b47be9ea4dbc0d5ff361e"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:d0259f1f4a1b8e20d0cbd935a889c0f7234f720645590260f9cf3850fdc1e1fa"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:15c7b324d0f59839fb4492d84bb1c870881c5c67cb94ac24c664a7c4dce1c475"}, {file = "winrt_Windows.Foundation-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:16ad741f4d38e99f8409ba5760299d0052003255f970f49f4b8ba2e0b609c8b7"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)"] [[package]] name = "winrt-windows-foundation-collections" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Foundation.Collections-2.0.0b1.tar.gz", hash = "sha256:185d30f8103934124544a40aac005fa5918a9a7cb3179f45e9863bb86e22ad43"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:042142e916a170778b7154498aae61254a1a94c552954266b73479479d24f01d"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:9f68e66055121fc1e04c4fda627834aceee6fbe922e77d6ccaecf9582e714c57"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:a4609411263cc7f5e93a9a5677b21e2ef130e26f9030bfa960b3e82595324298"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:5296858aa44c53936460a119794b80eedd6bd094016c1bf96822f92cb95ea419"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:3db1e1c80c97474e7c88b6052bd8982ca61723fd58ace11dc91a5522662e0b2a"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:c3a594e660c59f9fab04ae2f40bda7c809e8ec4748bada4424dfb02b43d4bfe1"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:0f355ee943ec5b835e694d97e9e93545a42d6fb984a61f442467789550d62c3f"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:c4a0cd2eb9f47c7ca3b66d12341cc822250bf26854a93fd58ab77f7a48dfab3a"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:744dbef50e8b8f34904083cae9ad43ac6e28facb9e166c4f123ce8e758141067"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:b7c767184aec3a3d7cba2cd84fadcd68106854efabef1a61092052294d6d6f4f"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:7c1ffe99c12f14fc4ab7027757780e6d850fa2fb23ec404a54311fbd9f1970d3"}, {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:870fa040ed36066e4c240c35973d8b2e0d7c38cc6050a42d993715ec9e3b748c"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.Foundation[all] (==2.0.0-beta.1)"] [[package]] name = "winrt-windows-storage-streams" version = "2.0.0b1" description = "Python projection of Windows Runtime (WinRT) APIs" optional = false python-versions = "<3.13,>=3.9" files = [ {file = "winrt-Windows.Storage.Streams-2.0.0b1.tar.gz", hash = "sha256:029d67cdc9b092d56c682740fe3c42f267dc5d3346b5c0b12ebc03f38e7d2f1f"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:49c90d4bfd539f6676226dfcb4b3574ddd6be528ffc44aa214c55af88c2de89e"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:22cc82779cada84aa2633841e25b33f3357737d912a1d9ecc1ee5a8b799b5171"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:b1750a111be32466f4f0781cbb5df195ac940690571dff4564492b921b162563"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:e79b1183ab26d9b95cf3e6dbe3f488a40605174a5a112694dbb7dbfb50899daf"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:3e90a1207eb3076f051a7785132f7b056b37343a68e9481a50c6defb3f660099"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:4da06522b4fa9cfcc046b604cc4aa1c6a887cc4bb5b8a637ed9bff8028a860bb"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:6f74f8ab8ac0d8de61c709043315361d8ac63f8144f3098d428472baadf8246a"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:5cf7c8d67836c60392d167bfe4f98ac7abcb691bfba2d19e322d0f9181f58347"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:f7f679f2c0f71791eca835856f57942ee5245094c1840a6c34bc7c2176b1bcd6"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:5beb53429fa9a11ede56b4a7cefe28c774b352dd355f7951f2a4dd7e9ec9b39a"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:f84233c4b500279d8f5840cb8c47776bc040fcecba05c6c9ab9767053698fc8b"}, {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:cfb163ddbb435906f75ef92a768573b0190e194e1438cea5a4c1d4d32a6b9386"}, ] [package.dependencies] winrt-runtime = "2.0.0-beta.1" [package.extras] all = ["winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Storage[all] (==2.0.0-beta.1)", "winrt-Windows.System[all] (==2.0.0-beta.1)"] [[package]] name = "zipp" version = "3.17.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = true python-versions = ">=3.8" files = [ {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [extras] docs = ["Sphinx", "myst-parser", "sphinx-rtd-theme"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" content-hash = "fcbe711a8ecae2d769e04f7b00acf353dc13e1f891e30cb7e04f65dc6242c55c" bluetooth-adapters-0.19.3/pyproject.toml000066400000000000000000000053751463626550700203560ustar00rootroot00000000000000[tool.poetry] name = "bluetooth-adapters" version = "0.19.3" description = "Tools to enumerate and find Bluetooth Adapters" authors = ["J. Nick Koston "] readme = "README.md" repository = "https://github.com/bluetooth-devices/bluetooth-adapters" documentation = "https://bluetooth-adapters.readthedocs.io" classifiers = [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Developers", "Natural Language :: English", "Operating System :: OS Independent", "Topic :: Software Development :: Libraries", "License :: OSI Approved :: Apache Software License", ] packages = [ { include = "bluetooth_adapters", from = "src" }, ] [tool.poetry.urls] "Bug Tracker" = "https://github.com/bluetooth-devices/bluetooth-adapters/issues" "Changelog" = "https://github.com/bluetooth-devices/bluetooth-adapters/blob/main/CHANGELOG.md" [tool.poetry.dependencies] python = ">=3.9,<3.13" # Documentation Dependencies Sphinx = {version = ">=5,<8", optional = true} sphinx-rtd-theme = {version = ">=1,<3", optional = true} myst-parser = {version = ">=0.18,<2.1", optional = true} async-timeout = {version = ">=3.0.0", python = "<3.11"} dbus-fast = ">=1.21.0" bleak = ">=0.21.1" usb-devices = ">=0.4.5" aiooui = ">=0.1.1" uart-devices = ">=0.1.0" [tool.poetry.extras] docs = [ "myst-parser", "sphinx", "sphinx-rtd-theme", ] [tool.poetry.dev-dependencies] pytest = "^8.2" pytest-cov = "^5.0" pytest-asyncio = "^0.23.6" [tool.semantic_release] branch = "main" version_toml = "pyproject.toml:tool.poetry.version" version_variable = "src/bluetooth_adapters/__init__.py:__version__" build_command = "pip install poetry && poetry build" [tool.pytest.ini_options] addopts = "-v -Wdefault --cov=bluetooth_adapters --cov-report=term-missing:skip-covered" pythonpath = ["src"] log_format = "%(asctime)s.%(msecs)03d %(levelname)-8s %(threadName)s %(name)s:%(filename)s:%(lineno)s %(message)s" log_date_format = "%Y-%m-%d %H:%M:%S" asyncio_mode = "auto" log_cli = "true" log_level = "NOTSET" [tool.coverage.run] branch = true [tool.coverage.report] exclude_lines = [ "pragma: no cover", "@overload", "if TYPE_CHECKING", "raise NotImplementedError", ] [tool.isort] profile = "black" known_first_party = ["bluetooth_adapters", "tests"] [tool.mypy] check_untyped_defs = true disallow_any_generics = true disallow_incomplete_defs = true disallow_untyped_defs = true mypy_path = "src/" no_implicit_optional = true show_error_codes = true warn_unreachable = true warn_unused_ignores = true exclude = [ 'docs/.*', 'setup.py', ] [[tool.mypy.overrides]] module = "tests.*" allow_untyped_defs = true [[tool.mypy.overrides]] module = "docs.*" ignore_errors = true [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" bluetooth-adapters-0.19.3/renovate.json000066400000000000000000000001011463626550700201360ustar00rootroot00000000000000{ "extends": ["github>browniebroke/renovate-configs:python"] } bluetooth-adapters-0.19.3/setup.py000066400000000000000000000003701463626550700171420ustar00rootroot00000000000000#!/usr/bin/env python # This is a shim to allow GitHub to detect the package, build is done with poetry # Taken from https://github.com/Textualize/rich import setuptools if __name__ == "__main__": setuptools.setup(name="bluetooth-adapters") bluetooth-adapters-0.19.3/src/000077500000000000000000000000001463626550700162175ustar00rootroot00000000000000bluetooth-adapters-0.19.3/src/bluetooth_adapters/000077500000000000000000000000001463626550700221075ustar00rootroot00000000000000bluetooth-adapters-0.19.3/src/bluetooth_adapters/__init__.py000066400000000000000000000044431463626550700242250ustar00rootroot00000000000000__version__ = "0.19.3" from platform import system from .adapters import BluetoothAdapters from .const import ( DEFAULT_ADDRESS, DEFAULT_CONNECTION_SLOTS, MACOS_DEFAULT_BLUETOOTH_ADAPTER, UNIX_DEFAULT_BLUETOOTH_ADAPTER, WINDOWS_DEFAULT_BLUETOOTH_ADAPTER, ) if system() != "Windows": from .dbus import ( BlueZDBusObjects, get_bluetooth_adapter_details, get_bluetooth_adapters, get_dbus_managed_objects, ) from .history import AdvertisementHistory, load_history_from_managed_objects from .models import ( ADAPTER_ADDRESS, ADAPTER_CONNECTION_SLOTS, ADAPTER_HW_VERSION, ADAPTER_MANUFACTURER, ADAPTER_PASSIVE_SCAN, ADAPTER_PRODUCT, ADAPTER_PRODUCT_ID, ADAPTER_SW_VERSION, ADAPTER_VENDOR_ID, AdapterDetails, ) from .storage import ( DiscoveredDeviceAdvertisementData, DiscoveredDeviceAdvertisementDataDict, DiscoveryStorageType, discovered_device_advertisement_data_from_dict, discovered_device_advertisement_data_to_dict, expire_stale_scanner_discovered_device_advertisement_data, ) from .systems import get_adapters from .systems.linux_hci import get_adapters_from_hci from .util import adapter_human_name, adapter_model, adapter_unique_name __all__ = [ "AdvertisementHistory", "BluetoothAdapters", "BlueZDBusObjects", "DiscoveredDeviceAdvertisementData", "DiscoveredDeviceAdvertisementDataDict", "DiscoveryStorageType", "adapter_human_name", "adapter_unique_name", "adapter_model", "discovered_device_advertisement_data_to_dict", "discovered_device_advertisement_data_from_dict", "expire_stale_scanner_discovered_device_advertisement_data", "get_adapters_from_hci", "get_bluetooth_adapters", "get_bluetooth_adapter_details", "get_dbus_managed_objects", "get_adapters", "load_history_from_managed_objects", "AdapterDetails", "ADAPTER_ADDRESS", "ADAPTER_CONNECTION_SLOTS", "ADAPTER_SW_VERSION", "ADAPTER_HW_VERSION", "ADAPTER_PASSIVE_SCAN", "ADAPTER_MANUFACTURER", "ADAPTER_PRODUCT", "ADAPTER_VENDOR_ID", "ADAPTER_PRODUCT_ID", "WINDOWS_DEFAULT_BLUETOOTH_ADAPTER", "MACOS_DEFAULT_BLUETOOTH_ADAPTER", "UNIX_DEFAULT_BLUETOOTH_ADAPTER", "DEFAULT_ADDRESS", "DEFAULT_CONNECTION_SLOTS", ] bluetooth-adapters-0.19.3/src/bluetooth_adapters/adapters.py000066400000000000000000000013161463626550700242650ustar00rootroot00000000000000"""Base class for Bluetooth adapters.""" from __future__ import annotations from abc import abstractproperty from .history import AdvertisementHistory from .models import AdapterDetails class BluetoothAdapters: """Class for getting the bluetooth adapters on a system.""" async def refresh(self) -> None: """Refresh the adapters.""" @property def history(self) -> dict[str, AdvertisementHistory]: """Get the history.""" return {} @abstractproperty @property def adapters(self) -> dict[str, AdapterDetails]: """Get the adapter details.""" @abstractproperty @property def default_adapter(self) -> str: """Get the default adapter.""" bluetooth-adapters-0.19.3/src/bluetooth_adapters/const.py000066400000000000000000000006671463626550700236200ustar00rootroot00000000000000"""Constants for bluetooth adapters.""" from typing import Final WINDOWS_DEFAULT_BLUETOOTH_ADAPTER: Final = "bluetooth" MACOS_DEFAULT_BLUETOOTH_ADAPTER: Final = "Core Bluetooth" UNIX_DEFAULT_BLUETOOTH_ADAPTER: Final = "hci0" # Some operating systems hide the adapter address for privacy reasons (ex MacOS) DEFAULT_ADDRESS: Final = "00:00:00:00:00:00" EMPTY_MAC_ADDRESS: Final = "00:00:00:00:00:00" DEFAULT_CONNECTION_SLOTS: Final = 5 bluetooth-adapters-0.19.3/src/bluetooth_adapters/dbus.py000066400000000000000000000133611463626550700234220ustar00rootroot00000000000000from __future__ import annotations import asyncio import logging from functools import cache from pathlib import Path from typing import Any try: from dbus_fast import AuthError, BusType, Message, MessageType, unpack_variants from dbus_fast.aio import MessageBus except (AttributeError, ImportError): # dbus_fast is not available on Windows AuthError = None # pragma: no cover BusType = None # pragma: no cover Message = None # pragma: no cover MessageType = None # pragma: no cover unpack_variants = None # pragma: no cover MessageBus = None # pragma: no cover from .history import AdvertisementHistory, load_history_from_managed_objects from .util import asyncio_timeout _LOGGER = logging.getLogger(__name__) REPLY_TIMEOUT = 8 class BlueZDBusObjects: """Fetch and parse BlueZObjects.""" def __init__(self) -> None: """Init the manager.""" self._packed_managed_objects: dict[str, Any] = {} self._unpacked_managed_objects: dict[str, Any] = {} async def load(self) -> None: """Load from the bus.""" self._packed_managed_objects = await _get_dbus_managed_objects() self._unpacked_managed_objects = {} @property def adapters(self) -> list[str]: """Get adapters.""" return list(self.adapter_details) @property def unpacked_managed_objects(self) -> dict[str, Any]: """Get unpacked managed objects.""" if not self._unpacked_managed_objects: self._unpacked_managed_objects = unpack_variants( self._packed_managed_objects ) return self._unpacked_managed_objects @property def adapter_details(self) -> dict[str, dict[str, Any]]: """Get adapters.""" return _adapters_from_managed_objects(self.unpacked_managed_objects) @property def history(self) -> dict[str, AdvertisementHistory]: """Get history from managed objects.""" return load_history_from_managed_objects(self.unpacked_managed_objects) def _adapters_from_managed_objects( managed_objects: dict[str, Any] ) -> dict[str, dict[str, Any]]: adapters: dict[str, dict[str, Any]] = {} for path, unpacked_data in managed_objects.items(): path_str = str(path) if path_str.startswith("/org/bluez/hci"): split_path = path_str.split("/") adapter = split_path[3] if adapter not in adapters: adapters[adapter] = unpacked_data return adapters async def get_bluetooth_adapters() -> list[str]: """Return a list of bluetooth adapters.""" return list(await get_bluetooth_adapter_details()) async def get_bluetooth_adapter_details() -> dict[str, dict[str, Any]]: """Return a list of bluetooth adapter with details.""" results = await _get_dbus_managed_objects() return { adapter: unpack_variants(packed_data) for adapter, packed_data in _adapters_from_managed_objects(results).items() } async def get_dbus_managed_objects() -> dict[str, Any]: """Return a list of bluetooth adapter with details.""" results = await _get_dbus_managed_objects() return {path: unpack_variants(packed_data) for path, packed_data in results.items()} async def _get_dbus_managed_objects() -> dict[str, Any]: try: bus = await MessageBus(bus_type=BusType.SYSTEM).connect() except AuthError as ex: _LOGGER.warning( "DBus authentication error; make sure the DBus socket " "is available and the user has the correct permissions: %s", ex, ) return {} except FileNotFoundError as ex: if is_docker_env(): _LOGGER.debug( "DBus service not found; docker config may " "be missing `-v /run/dbus:/run/dbus:ro`: %s", ex, ) _LOGGER.debug( "DBus service not found; make sure the DBus socket " "is available: %s", ex, ) return {} except BrokenPipeError as ex: if is_docker_env(): _LOGGER.debug( "DBus connection broken: %s; try restarting " "`bluetooth`, `dbus`, and finally the docker container", ex, ) _LOGGER.debug( "DBus connection broken: %s; try restarting " "`bluetooth` and `dbus`", ex ) return {} except ConnectionRefusedError as ex: if is_docker_env(): _LOGGER.debug( "DBus connection refused: %s; try restarting " "`bluetooth`, `dbus`, and finally the docker container", ex, ) _LOGGER.debug( "DBus connection refused: %s; try restarting " "`bluetooth` and `dbus`", ex ) return {} msg = Message( destination="org.bluez", path="/", interface="org.freedesktop.DBus.ObjectManager", member="GetManagedObjects", ) try: async with asyncio_timeout(REPLY_TIMEOUT): reply = await bus.call(msg) except EOFError as ex: _LOGGER.debug("DBus connection closed: %s", ex) return {} except asyncio.TimeoutError: _LOGGER.debug( "Dbus timeout waiting for reply to GetManagedObjects; try restarting " "`bluetooth` and `dbus`" ) return {} bus.disconnect() if not reply or reply.message_type != MessageType.METHOD_RETURN: _LOGGER.debug( "Received an unexpected reply from Dbus while " "calling GetManagedObjects on org.bluez: %s", reply, ) return {} results: dict[str, Any] = reply.body[0] return results @cache def is_docker_env() -> bool: """Return True if we run in a docker env.""" return Path("/.dockerenv").exists() bluetooth-adapters-0.19.3/src/bluetooth_adapters/history.py000066400000000000000000000040331463626550700241620ustar00rootroot00000000000000from __future__ import annotations from dataclasses import dataclass from typing import Any from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData MIN_RSSI = -127 @dataclass class AdvertisementHistory: device: BLEDevice advertisement_data: AdvertisementData source: str def load_history_from_managed_objects( managed_objects: dict[str, Any], source_adapter: str | None = None ) -> dict[str, AdvertisementHistory]: """Load the history from the bus.""" history: dict[str, AdvertisementHistory] = {} for path, packed_data in managed_objects.items(): path_str = str(path) if not path_str.startswith("/org/bluez/hci"): continue if not (props := packed_data.get("org.bluez.Device1")): continue address = props["Address"] rssi = props.get("RSSI", MIN_RSSI) if ( prev_history := history.get(address) ) and prev_history.advertisement_data.rssi >= rssi: continue split_path = path_str.split("/") adapter = split_path[3] if source_adapter and adapter != source_adapter: continue uuids = props.get("UUIDs", []) manufacturer_data = { k: bytes(v) for k, v in props.get("ManufacturerData", {}).items() } device = BLEDevice( address, props["Alias"], {"path": path, "props": props}, rssi, uuids=uuids, manufacturer_data=manufacturer_data, ) advertisement_data = AdvertisementData( local_name=props.get("Name"), manufacturer_data=manufacturer_data, service_data={k: bytes(v) for k, v in props.get("ServiceData", {}).items()}, service_uuids=uuids, platform_data=props, tx_power=props.get("TxPower"), rssi=rssi, ) history[device.address] = AdvertisementHistory( device, advertisement_data, adapter ) return history bluetooth-adapters-0.19.3/src/bluetooth_adapters/models.py000066400000000000000000000014251463626550700237460ustar00rootroot00000000000000"""Models for bluetooth adapters.""" from __future__ import annotations from typing import Final, TypedDict class AdapterDetails(TypedDict, total=False): """Adapter details.""" address: str sw_version: str hw_version: str | None manufacturer: str | None product: str | None vendor_id: str | None product_id: str | None passive_scan: bool connection_slots: int | None ADAPTER_ADDRESS: Final = "address" ADAPTER_SW_VERSION: Final = "sw_version" ADAPTER_HW_VERSION: Final = "hw_version" ADAPTER_PASSIVE_SCAN: Final = "passive_scan" ADAPTER_MANUFACTURER: Final = "manufacturer" ADAPTER_PRODUCT: Final = "product" ADAPTER_VENDOR_ID: Final = "vendor_id" ADAPTER_PRODUCT_ID: Final = "product_id" ADAPTER_CONNECTION_SLOTS: Final = "connection_slots" bluetooth-adapters-0.19.3/src/bluetooth_adapters/py.typed000066400000000000000000000000001463626550700235740ustar00rootroot00000000000000bluetooth-adapters-0.19.3/src/bluetooth_adapters/storage.py000066400000000000000000000207271463626550700241350ustar00rootroot00000000000000"""Serialize/Deserialize bluetooth adapter discoveries.""" from __future__ import annotations import logging import time from dataclasses import dataclass from typing import Any, Final, TypedDict from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData _LOGGER = logging.getLogger(__name__) @dataclass class DiscoveredDeviceAdvertisementData: """Discovered device advertisement data deserialized from storage.""" connectable: bool expire_seconds: float discovered_device_advertisement_datas: dict[ str, tuple[BLEDevice, AdvertisementData] ] discovered_device_timestamps: dict[str, float] CONNECTABLE: Final = "connectable" EXPIRE_SECONDS: Final = "expire_seconds" DISCOVERED_DEVICE_ADVERTISEMENT_DATAS: Final = "discovered_device_advertisement_datas" DISCOVERED_DEVICE_TIMESTAMPS: Final = "discovered_device_timestamps" class DiscoveredDeviceAdvertisementDataDict(TypedDict): """Discovered device advertisement data dict in storage.""" connectable: bool expire_seconds: float discovered_device_advertisement_datas: dict[str, DiscoveredDeviceDict] discovered_device_timestamps: dict[str, float] ADDRESS: Final = "address" NAME: Final = "name" RSSI: Final = "rssi" DETAILS: Final = "details" class BLEDeviceDict(TypedDict): """BLEDevice dict.""" address: str name: str | None rssi: int | None details: dict[str, Any] LOCAL_NAME: Final = "local_name" MANUFACTURER_DATA: Final = "manufacturer_data" SERVICE_DATA: Final = "service_data" SERVICE_UUIDS: Final = "service_uuids" TX_POWER: Final = "tx_power" PLATFORM_DATA: Final = "platform_data" class AdvertisementDataDict(TypedDict): """AdvertisementData dict.""" local_name: str | None manufacturer_data: dict[str, str] service_data: dict[str, str] service_uuids: list[str] rssi: int tx_power: int | None platform_data: list[Any] class DiscoveredDeviceDict(TypedDict): """Discovered device dict.""" device: BLEDeviceDict advertisement_data: AdvertisementDataDict def expire_stale_scanner_discovered_device_advertisement_data( data_by_scanner: dict[str, DiscoveredDeviceAdvertisementDataDict] ) -> None: """Expire stale discovered device advertisement data.""" now = time.time() expired_scanners: list[str] = [] for scanner, data in data_by_scanner.items(): expire: list[str] = [] expire_seconds = data[EXPIRE_SECONDS] timestamps = data[DISCOVERED_DEVICE_TIMESTAMPS] discovered_device_advertisement_datas = data[ DISCOVERED_DEVICE_ADVERTISEMENT_DATAS ] for address, timestamp in timestamps.items(): if now - timestamp > expire_seconds: expire.append(address) for address in expire: del timestamps[address] del discovered_device_advertisement_datas[address] if not timestamps: expired_scanners.append(scanner) for scanner in expired_scanners: del data_by_scanner[scanner] def discovered_device_advertisement_data_from_dict( data: DiscoveredDeviceAdvertisementDataDict, ) -> DiscoveredDeviceAdvertisementData | None: """Build discovered_device_advertisement_data dict.""" try: return DiscoveredDeviceAdvertisementData( data[CONNECTABLE], data[EXPIRE_SECONDS], _deserialize_discovered_device_advertisement_datas( data[DISCOVERED_DEVICE_ADVERTISEMENT_DATAS] ), _deserialize_discovered_device_timestamps( data[DISCOVERED_DEVICE_TIMESTAMPS] ), ) except Exception as err: # pylint: disable=broad-except _LOGGER.exception( "Error deserializing discovered_device_advertisement_data, adapter startup will be slow: %s", err, ) return None def discovered_device_advertisement_data_to_dict( data: DiscoveredDeviceAdvertisementData, ) -> DiscoveredDeviceAdvertisementDataDict: """Build discovered_device_advertisement_data dict.""" return DiscoveredDeviceAdvertisementDataDict( connectable=data.connectable, expire_seconds=data.expire_seconds, discovered_device_advertisement_datas=_serialize_discovered_device_advertisement_datas( data.discovered_device_advertisement_datas ), discovered_device_timestamps=_serialize_discovered_device_timestamps( data.discovered_device_timestamps ), ) def _serialize_discovered_device_advertisement_datas( discovered_device_advertisement_datas: dict[ str, tuple[BLEDevice, AdvertisementData] ] ) -> dict[str, DiscoveredDeviceDict]: """Serialize discovered_device_advertisement_datas.""" return { address: DiscoveredDeviceDict( device=_ble_device_to_dict(device, advertisement_data), advertisement_data=_advertisement_data_to_dict(advertisement_data), ) for ( address, (device, advertisement_data), ) in discovered_device_advertisement_datas.items() } def _deserialize_discovered_device_advertisement_datas( discovered_device_advertisement_datas: dict[str, DiscoveredDeviceDict] ) -> dict[str, tuple[BLEDevice, AdvertisementData]]: """Deserialize discovered_device_advertisement_datas.""" return { address: ( BLEDevice(**device_advertisement_data["device"]), _advertisement_data_from_dict( device_advertisement_data["advertisement_data"] ), ) for ( address, device_advertisement_data, ) in discovered_device_advertisement_datas.items() } def _ble_device_to_dict( ble_device: BLEDevice, advertisement_data: AdvertisementData ) -> BLEDeviceDict: """Serialize ble_device.""" return BLEDeviceDict( address=ble_device.address, name=ble_device.name, rssi=advertisement_data.rssi, # For backwards compatibility details=ble_device.details, ) def _advertisement_data_from_dict( advertisement_data: AdvertisementDataDict, ) -> AdvertisementData: """Deserialize advertisement_data.""" return AdvertisementData( local_name=advertisement_data[LOCAL_NAME], manufacturer_data={ int(manufacturer_id): bytes.fromhex(manufacturer_data) for manufacturer_id, manufacturer_data in advertisement_data[ MANUFACTURER_DATA ].items() }, service_data={ service_uuid: bytes.fromhex(service_data) for service_uuid, service_data in advertisement_data[SERVICE_DATA].items() }, service_uuids=advertisement_data[SERVICE_UUIDS], rssi=advertisement_data[RSSI], tx_power=advertisement_data[TX_POWER], platform_data=tuple(advertisement_data[PLATFORM_DATA]), ) def _advertisement_data_to_dict( advertisement_data: AdvertisementData, ) -> AdvertisementDataDict: """Serialize advertisement_data.""" return AdvertisementDataDict( local_name=advertisement_data.local_name, manufacturer_data={ str(manufacturer_id): manufacturer_data.hex() for manufacturer_id, manufacturer_data in advertisement_data.manufacturer_data.items() }, service_data={ service_uuid: service_data.hex() for service_uuid, service_data in advertisement_data.service_data.items() }, service_uuids=advertisement_data.service_uuids, rssi=advertisement_data.rssi, tx_power=advertisement_data.tx_power, platform_data=list(advertisement_data.platform_data), ) def _get_monotonic_time_diff() -> float: """Get monotonic time diff.""" return time.time() - time.monotonic() def _deserialize_discovered_device_timestamps( discovered_device_timestamps: dict[str, float] ) -> dict[str, float]: """Deserialize discovered_device_timestamps.""" time_diff = _get_monotonic_time_diff() return { address: unix_time - time_diff for address, unix_time in discovered_device_timestamps.items() } def _serialize_discovered_device_timestamps( discovered_device_timestamps: dict[str, float] ) -> dict[str, float]: """Serialize discovered_device_timestamps.""" time_diff = _get_monotonic_time_diff() return { address: monotonic_time + time_diff for address, monotonic_time in discovered_device_timestamps.items() } DiscoveryStorageType = dict[str, DiscoveredDeviceAdvertisementDataDict] bluetooth-adapters-0.19.3/src/bluetooth_adapters/systems/000077500000000000000000000000001463626550700236165ustar00rootroot00000000000000bluetooth-adapters-0.19.3/src/bluetooth_adapters/systems/__init__.py000066400000000000000000000007131463626550700257300ustar00rootroot00000000000000from __future__ import annotations import platform from ..adapters import BluetoothAdapters def get_adapters() -> BluetoothAdapters: """Get the adapters.""" if platform.system() == "Windows": from .windows import WindowsAdapters return WindowsAdapters() if platform.system() == "Darwin": from .macos import MacOSAdapters return MacOSAdapters() from .linux import LinuxAdapters return LinuxAdapters() bluetooth-adapters-0.19.3/src/bluetooth_adapters/systems/linux.py000066400000000000000000000142671463626550700253410ustar00rootroot00000000000000from __future__ import annotations import asyncio import logging from typing import Any import aiooui from uart_devices import BluetoothDevice as UARTBluetoothDevice from uart_devices import NotAUARTDeviceError from usb_devices import BluetoothDevice as USBBluetoothDevice from usb_devices import NotAUSBDeviceError from ..adapters import BluetoothAdapters from ..const import EMPTY_MAC_ADDRESS, UNIX_DEFAULT_BLUETOOTH_ADAPTER from ..dbus import BlueZDBusObjects from ..history import AdvertisementHistory from ..models import AdapterDetails from .linux_hci import get_adapters_from_hci _LOGGER = logging.getLogger(__name__) class LinuxAdapters(BluetoothAdapters): """Class for getting the bluetooth adapters on a Linux system.""" def __init__(self) -> None: """Initialize the adapter.""" self._bluez = BlueZDBusObjects() self._adapters: dict[str, AdapterDetails] | None = None self._devices: dict[str, UARTBluetoothDevice | USBBluetoothDevice] = {} self._hci_output: dict[int, dict[str, Any]] | None = None async def refresh(self) -> None: """Refresh the adapters.""" loop = asyncio.get_running_loop() load_task = asyncio.create_task(self._bluez.load()) adapters_from_hci_future = loop.run_in_executor(None, get_adapters_from_hci) futures: list[asyncio.Future[Any]] = [load_task, adapters_from_hci_future] if not aiooui.is_loaded(): futures.append(aiooui.async_load()) await asyncio.gather(*futures) self._hci_output = await adapters_from_hci_future self._adapters = None # clear cache self._devices = {} for adapter in self._bluez.adapter_details: i = int(adapter[3:]) for cls in (USBBluetoothDevice, UARTBluetoothDevice): dev = cls(i) self._devices[adapter] = dev try: dev.setup() except (NotAUARTDeviceError, NotAUSBDeviceError): continue except FileNotFoundError: break except Exception: # pylint: disable=broad-except _LOGGER.exception("Unexpected error setting up device hci%s", dev) break else: break @property def history(self) -> dict[str, AdvertisementHistory]: """Get the bluez history.""" return self._bluez.history @property def adapters(self) -> dict[str, AdapterDetails]: """Get the adapter details.""" manufacturer: str | None if self._adapters is None: adapters: dict[str, AdapterDetails] = {} if self._hci_output: for hci_details in self._hci_output.values(): name = hci_details["name"] mac_address = hci_details["bdaddr"].upper() if mac_address == EMPTY_MAC_ADDRESS: manufacturer = None else: manufacturer = aiooui.get_vendor(mac_address) adapters[name] = AdapterDetails( address=mac_address, sw_version="Unknown", hw_version=None, passive_scan=False, # assume false if we don't know manufacturer=manufacturer, product=None, vendor_id=None, product_id=None, ) adapter_details = self._bluez.adapter_details for adapter, details in adapter_details.items(): if not (adapter1 := details.get("org.bluez.Adapter1")): continue mac_address = adapter1["Address"] device = self._devices[adapter] product: str | None = None manufacturer = None vendor_id: str | None = None product_id: str | None = None if isinstance(device, USBBluetoothDevice): usb_device = device.usb_device if mac_address != EMPTY_MAC_ADDRESS and ( usb_device is None or usb_device.vendor_id == usb_device.manufacturer or usb_device.manufacturer is None or usb_device.manufacturer == "Unknown" ): manufacturer = aiooui.get_vendor(mac_address) elif usb_device is not None: manufacturer = usb_device.manufacturer if usb_device is not None: product = usb_device.product vendor_id = usb_device.vendor_id product_id = usb_device.product_id elif isinstance(device, UARTBluetoothDevice): uart_device = device.uart_device if uart_device is None: if mac_address != EMPTY_MAC_ADDRESS: manufacturer = aiooui.get_vendor(mac_address) else: product = uart_device.product if mac_address == EMPTY_MAC_ADDRESS: manufacturer = uart_device.manufacturer else: manufacturer = ( aiooui.get_vendor(mac_address) or uart_device.manufacturer ) adapters[adapter] = AdapterDetails( address=mac_address, sw_version=adapter1["Name"], # This is actually the BlueZ version hw_version=adapter1.get("Modalias"), passive_scan="org.bluez.AdvertisementMonitorManager1" in details, manufacturer=manufacturer, product=product, vendor_id=vendor_id, product_id=product_id, ) self._adapters = adapters return self._adapters @property def default_adapter(self) -> str: """Get the default adapter.""" return UNIX_DEFAULT_BLUETOOTH_ADAPTER bluetooth-adapters-0.19.3/src/bluetooth_adapters/systems/linux_hci.py000066400000000000000000000062461463626550700261620ustar00rootroot00000000000000from __future__ import annotations import ctypes try: import fcntl except ImportError: # fcntl is not available on Windows fcntl = None # type: ignore import logging import socket from typing import Any _LOGGER = logging.getLogger(__name__) AF_BLUETOOTH = 31 PF_BLUETOOTH = AF_BLUETOOTH BTPROTO_HCI = 1 HCI_MAX_DEV = 16 HCIGETDEVLIST = 0x800448D2 # _IOR('H', 210, int) HCIGETDEVINFO = 0x800448D3 # _IOR('H', 211, int) class hci_dev_req(ctypes.Structure): _fields_ = [("dev_id", ctypes.c_uint16), ("dev_opt", ctypes.c_uint32)] class hci_dev_list_req(ctypes.Structure): _fields_ = [("dev_num", ctypes.c_uint16), ("dev_req", hci_dev_req * HCI_MAX_DEV)] class bdaddr_t(ctypes.Structure): _fields_ = [("b", ctypes.c_uint8 * 6)] def __str__(self) -> str: return ":".join(["%02X" % x for x in reversed(self.b)]) class hci_dev_stats(ctypes.Structure): _fields_ = [ ("err_rx", ctypes.c_uint32), ("err_tx", ctypes.c_uint32), ("cmd_tx", ctypes.c_uint32), ("evt_rx", ctypes.c_uint32), ("acl_tx", ctypes.c_uint32), ("acl_rx", ctypes.c_uint32), ("sco_tx", ctypes.c_uint32), ("sco_rx", ctypes.c_uint32), ("byte_rx", ctypes.c_uint32), ("byte_tx", ctypes.c_uint32), ] class hci_dev_info(ctypes.Structure): _fields_ = [ ("dev_id", ctypes.c_uint16), ("name", ctypes.c_char * 8), ("bdaddr", bdaddr_t), ("flags", ctypes.c_uint32), ("type", ctypes.c_uint8), ("features", ctypes.c_uint8 * 8), ("pkt_type", ctypes.c_uint32), ("link_policy", ctypes.c_uint32), ("link_mode", ctypes.c_uint32), ("acl_mtu", ctypes.c_uint16), ("acl_pkts", ctypes.c_uint16), ("sco_mtu", ctypes.c_uint16), ("sco_pkts", ctypes.c_uint16), ("stat", hci_dev_stats), ] hci_dev_info_p = ctypes.POINTER(hci_dev_info) def get_adapters_from_hci() -> dict[int, dict[str, Any]]: """Get bluetooth adapters from HCI.""" if not fcntl: raise RuntimeError("fcntl is not available") out: dict[int, dict[str, Any]] = {} sock: socket.socket | None = None try: sock = socket.socket(AF_BLUETOOTH, socket.SOCK_RAW, BTPROTO_HCI) buf = hci_dev_list_req() buf.dev_num = HCI_MAX_DEV ret = fcntl.ioctl(sock.fileno(), HCIGETDEVLIST, buf) if ret < 0: raise OSError(f"HCIGETDEVLIST failed: {ret}") for i in range(buf.dev_num): dev_req = buf.dev_req[i] dev = hci_dev_info() dev.dev_id = dev_req.dev_id ret = fcntl.ioctl(sock.fileno(), HCIGETDEVINFO, dev) info = {str(k): getattr(dev, k) for k, v_ in dev._fields_} info["bdaddr"] = str(info["bdaddr"]) info["name"] = info["name"].decode() out[int(dev.dev_id)] = info except OSError as error: _LOGGER.debug("Error while getting HCI devices: %s", error) return out except Exception as error: # pylint: disable=broad-except _LOGGER.exception("Unexpected error while getting HCI devices: %s", error) return out finally: if sock: sock.close() return out bluetooth-adapters-0.19.3/src/bluetooth_adapters/systems/macos.py000066400000000000000000000016131463626550700252730ustar00rootroot00000000000000import platform from ..adapters import BluetoothAdapters from ..const import DEFAULT_ADDRESS, MACOS_DEFAULT_BLUETOOTH_ADAPTER from ..models import AdapterDetails class MacOSAdapters(BluetoothAdapters): """Class for getting the bluetooth adapters on a MacOS system.""" @property def adapters(self) -> dict[str, AdapterDetails]: """Get the adapter details.""" return { MACOS_DEFAULT_BLUETOOTH_ADAPTER: AdapterDetails( address=DEFAULT_ADDRESS, sw_version=platform.release(), passive_scan=False, manufacturer="Apple", product="Unknown MacOS Model", vendor_id="Unknown", product_id="Unknown", ) } @property def default_adapter(self) -> str: """Get the default adapter.""" return MACOS_DEFAULT_BLUETOOTH_ADAPTER bluetooth-adapters-0.19.3/src/bluetooth_adapters/systems/windows.py000066400000000000000000000016771463626550700256750ustar00rootroot00000000000000from __future__ import annotations import platform from ..adapters import BluetoothAdapters from ..const import DEFAULT_ADDRESS, WINDOWS_DEFAULT_BLUETOOTH_ADAPTER from ..models import AdapterDetails class WindowsAdapters(BluetoothAdapters): """Class for getting the bluetooth adapters on a Windows system.""" @property def adapters(self) -> dict[str, AdapterDetails]: """Get the adapter details.""" return { WINDOWS_DEFAULT_BLUETOOTH_ADAPTER: AdapterDetails( address=DEFAULT_ADDRESS, sw_version=platform.release(), passive_scan=False, manufacturer="Microsoft", product="Unknown Windows Model", vendor_id="Unknown", product_id="Unknown", ) } @property def default_adapter(self) -> str: """Get the default adapter.""" return WINDOWS_DEFAULT_BLUETOOTH_ADAPTER bluetooth-adapters-0.19.3/src/bluetooth_adapters/util.py000066400000000000000000000020601463626550700234340ustar00rootroot00000000000000"""Utils.""" from __future__ import annotations import sys from .const import DEFAULT_ADDRESS from .models import ( ADAPTER_PRODUCT, ADAPTER_PRODUCT_ID, ADAPTER_VENDOR_ID, AdapterDetails, ) if sys.version_info[:2] < (3, 11): from async_timeout import timeout as asyncio_timeout # noqa: F401 else: from asyncio import timeout as asyncio_timeout # noqa: F401 def adapter_human_name(adapter: str, address: str) -> str: """Return a human readable name for the adapter.""" return adapter if address == DEFAULT_ADDRESS else f"{adapter} ({address})" def adapter_unique_name(adapter: str, address: str) -> str: """Return a unique name for the adapter.""" return adapter if address == DEFAULT_ADDRESS else address def adapter_model(details: AdapterDetails) -> str: """Return a model for the adapter.""" if (vendor_id := details.get(ADAPTER_VENDOR_ID)) and vendor_id != "Unknown": return f"{details[ADAPTER_PRODUCT]} ({vendor_id}:{details[ADAPTER_PRODUCT_ID]})" return details.get(ADAPTER_PRODUCT) or "Unknown" bluetooth-adapters-0.19.3/tests/000077500000000000000000000000001463626550700165725ustar00rootroot00000000000000bluetooth-adapters-0.19.3/tests/__init__.py000066400000000000000000000000001463626550700206710ustar00rootroot00000000000000bluetooth-adapters-0.19.3/tests/test_init.py000066400000000000000000002067441463626550700211630ustar00rootroot00000000000000import asyncio import time from platform import system from typing import Any from unittest.mock import ANY, AsyncMock, MagicMock, patch import pytest from bleak.backends.device import BLEDevice from bleak.backends.scanner import AdvertisementData try: from dbus_fast import AuthError, MessageType except (AttributeError, ImportError): MessageType = None AuthError = None # dbus_fast is not available on Windows from uart_devices import BluetoothDevice as UARTBluetoothDevice from uart_devices import UARTDevice from usb_devices import BluetoothDevice as USBBluetoothDevice from usb_devices import NotAUSBDeviceError, USBDevice import bluetooth_adapters.dbus as bluetooth_adapters_dbus if system() != "Windows": from bluetooth_adapters import ( BlueZDBusObjects, get_bluetooth_adapters, get_dbus_managed_objects, ) else: BlueZDBusObjects = None # type: ignore get_bluetooth_adapters = None # type: ignore get_dbus_managed_objects = None # type: ignore from bluetooth_adapters import ( DEFAULT_ADDRESS, AdapterDetails, AdvertisementHistory, DiscoveredDeviceAdvertisementData, DiscoveredDeviceAdvertisementDataDict, adapter_human_name, adapter_model, adapter_unique_name, discovered_device_advertisement_data_from_dict, discovered_device_advertisement_data_to_dict, expire_stale_scanner_discovered_device_advertisement_data, get_adapters, load_history_from_managed_objects, ) @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_file_not_found(): """Test get_bluetooth_adapters().""" class MockMessageBus: def __init__(self, *args, **kwargs): raise FileNotFoundError with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus): assert await get_bluetooth_adapters() == [] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_connection_refused(): """Test get_bluetooth_adapters with connection refused.""" class MockMessageBus: def __init__(self, *args, **kwargs): raise ConnectionRefusedError with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus): assert await get_bluetooth_adapters() == [] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_auth_eror(): """Test get_bluetooth_adapters with auth error.""" class MockMessageBus: def __init__(self, *args, **kwargs): raise AuthError with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus): assert await get_bluetooth_adapters() == [] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_connect_refused_docker(): class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): raise ConnectionRefusedError async def call(self): return None with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus), patch( "bluetooth_adapters.dbus.is_docker_env", return_value=True ): assert await get_bluetooth_adapters() == [] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_connect_fails(): class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): raise FileNotFoundError async def call(self): return None with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus): assert await get_bluetooth_adapters() == [] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_connect_fails_docker(): class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): raise FileNotFoundError async def call(self): return None with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus), patch( "bluetooth_adapters.dbus.is_docker_env", return_value=True ): assert await get_bluetooth_adapters() == [] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_connect_broken_pipe(): class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): raise BrokenPipeError async def call(self): return None with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus): assert await get_bluetooth_adapters() == [] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_connect_broken_pipe_docker(): class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): raise BrokenPipeError async def call(self): return None with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus), patch( "bluetooth_adapters.dbus.is_docker_env", return_value=True ): assert await get_bluetooth_adapters() == [] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_connect_eof_error(): class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): return AsyncMock( disconnect=MagicMock(), call=AsyncMock(side_effect=EOFError) ) async def call(self): raise EOFError with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus): assert await get_bluetooth_adapters() == [] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_no_call_return(): class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): return AsyncMock(disconnect=MagicMock(), call=AsyncMock()) async def call(self): return None with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus): assert await get_bluetooth_adapters() == [] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_times_out(): async def _stall(*args: Any) -> None: await asyncio.sleep(10) class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): return AsyncMock( disconnect=MagicMock(), call=AsyncMock(side_effect=_stall), ) with patch.object(bluetooth_adapters_dbus, "REPLY_TIMEOUT", 0), patch( "bluetooth_adapters.dbus.MessageBus", MockMessageBus ): assert await get_bluetooth_adapters() == [] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_no_wrong_return(): class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): return AsyncMock( disconnect=MagicMock(), call=AsyncMock( return_value=MagicMock( body=[ { "/org/bluez/hci0": "", "/org/bluez/hci1": "", "/org/bluez/hci1/any": "", } ], message_type="wrong", ) ), ) with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus): assert await get_bluetooth_adapters() == [] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_bluetooth_adapters_correct_return_valid_message(): class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): return AsyncMock( disconnect=MagicMock(), call=AsyncMock( return_value=MagicMock( body=[ { "/other": {}, "/org/bluez/hci0": {}, "/org/bluez/hci1": {}, "/org/bluez/hci1/any": {}, } ], message_type=MessageType.METHOD_RETURN, ) ), ) with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus): assert await get_bluetooth_adapters() == ["hci0", "hci1"] @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_dbus_managed_objects(): class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): return AsyncMock( disconnect=MagicMock(), call=AsyncMock( return_value=MagicMock( body=[ { "/other": {}, "/org/bluez/hci0": {}, "/org/bluez/hci1": {}, "/org/bluez/hci1/any": {}, } ], message_type=MessageType.METHOD_RETURN, ) ), ) with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus): assert await get_dbus_managed_objects() == { "/other": {}, "/org/bluez/hci0": {}, "/org/bluez/hci1": {}, "/org/bluez/hci1/any": {}, } @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_BlueZDBusObjects(): class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): return AsyncMock( disconnect=MagicMock(), call=AsyncMock( return_value=MagicMock( body=[ { "/other": {}, "/org/bluez/hci0": {}, "/org/bluez/hci1": {}, "/org/bluez/hci1/any": {}, "/org/bluez/hci0/dev_54_D2_72_AB_35_95": { "org.freedesktop.DBus.Introspectable": {}, "org.bluez.Device1": { "Address": "54:D2:72:AB:35:95", "AddressType": "public", "Name": "Nuki_1EAB3595", "Alias": "Nuki_1EAB3595", "Paired": False, "Trusted": False, "Blocked": False, "LegacyPairing": False, "RSSI": -78, "Connected": False, "UUIDs": [], "Adapter": "/org/bluez/hci0", "ManufacturerData": { "76": b"\\x02\\x15\\xa9.\\xe2\\x00U\\x01\\x11\\xe4\\x91l\\x08\\x00 \\x0c\\x9af\\x1e\\xab5\\x95\\xc4" }, "ServicesResolved": False, "AdvertisingFlags": { "__type": "", "repr": "bytearray(b'\\x06')", }, }, "org.freedesktop.DBus.Properties": {}, }, "/org/bluez/hci1/dev_54_D2_72_AB_35_95": { "org.freedesktop.DBus.Introspectable": {}, "org.bluez.Device1": { "Address": "54:D2:72:AB:35:95", "AddressType": "public", "Name": "Nuki_1EAB3595", "Alias": "Nuki_1EAB3595", "Paired": False, "Trusted": False, "Blocked": False, "LegacyPairing": False, "RSSI": -100, "Connected": False, "UUIDs": [], "Adapter": "/org/bluez/hci0", "ManufacturerData": { "76": b"\\x02\\x15\\xa9.\\xe2\\x00U\\x01\\x11\\xe4\\x91l\\x08\\x00 \\x0c\\x9af\\x1e\\xab5\\x95\\xc4" }, "ServicesResolved": False, "AdvertisingFlags": { "__type": "", "repr": "bytearray(b'\\x06')", }, }, "org.freedesktop.DBus.Properties": {}, }, } ], message_type=MessageType.METHOD_RETURN, ) ), ) with patch("bluetooth_adapters.dbus.MessageBus", MockMessageBus): bluez = BlueZDBusObjects() await bluez.load() assert bluez.adapters == ["hci0", "hci1"] assert bluez.adapter_details == {"hci0": {}, "hci1": {}} assert bluez.history == { "54:D2:72:AB:35:95": AdvertisementHistory(ANY, ANY, "hci0") } assert bluez.history["54:D2:72:AB:35:95"].device.rssi == -78 assert ( len( load_history_from_managed_objects( bluez.unpacked_managed_objects, "hci1" ) ) == 1 ) @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_adapters_linux(): """Test get_adapters.""" class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): return AsyncMock( disconnect=MagicMock(), call=AsyncMock( return_value=MagicMock( body=[ { "/other": {}, "/org/bluez/hci0": { "org.bluez.Adapter1": { "Address": "00:1A:7D:DA:71:04", "AddressType": "public", "Alias": "homeassistant", "Class": 2883584, "Discoverable": False, "DiscoverableTimeout": 180, "Discovering": True, "Modalias": "usb:v1D6Bp0246d053F", "Name": "homeassistant", "Pairable": False, "PairableTimeout": 0, "Powered": True, "Roles": ["central", "peripheral"], "UUIDs": [ "0000110e-0000-1000-8000-00805f9b34fb", "0000110a-0000-1000-8000-00805f9b34fb", "00001200-0000-1000-8000-00805f9b34fb", "0000110b-0000-1000-8000-00805f9b34fb", "00001108-0000-1000-8000-00805f9b34fb", "0000110c-0000-1000-8000-00805f9b34fb", "00001800-0000-1000-8000-00805f9b34fb", "00001801-0000-1000-8000-00805f9b34fb", "0000180a-0000-1000-8000-00805f9b34fb", "00001112-0000-1000-8000-00805f9b34fb", ], }, "org.bluez.GattManager1": {}, "org.bluez.LEAdvertisingManager1": { "ActiveInstances": 0, "SupportedIncludes": [ "tx-power", "appearance", "local-name", ], "SupportedInstances": 5, }, "org.bluez.Media1": {}, "org.bluez.NetworkServer1": {}, "org.freedesktop.DBus.Introspectable": {}, "org.freedesktop.DBus.Properties": {}, }, "/org/bluez/hci1": {}, "/org/bluez/hci2": { "org.bluez.Adapter1": { "Address": "00:00:00:00:00:00", "AddressType": "public", "Alias": "homeassistant", "Class": 2883584, "Discoverable": False, "DiscoverableTimeout": 180, "Discovering": True, "Modalias": "usb:v1D6Bp0246d053F", "Name": "homeassistant", "Pairable": False, "PairableTimeout": 0, "Powered": True, "Roles": ["central", "peripheral"], "UUIDs": [ "0000110e-0000-1000-8000-00805f9b34fb", "0000110a-0000-1000-8000-00805f9b34fb", "00001200-0000-1000-8000-00805f9b34fb", "0000110b-0000-1000-8000-00805f9b34fb", "00001108-0000-1000-8000-00805f9b34fb", "0000110c-0000-1000-8000-00805f9b34fb", "00001800-0000-1000-8000-00805f9b34fb", "00001801-0000-1000-8000-00805f9b34fb", "0000180a-0000-1000-8000-00805f9b34fb", "00001112-0000-1000-8000-00805f9b34fb", ], }, "org.bluez.GattManager1": {}, "org.bluez.LEAdvertisingManager1": { "ActiveInstances": 0, "SupportedIncludes": [ "tx-power", "appearance", "local-name", ], "SupportedInstances": 5, }, "org.bluez.Media1": {}, "org.bluez.NetworkServer1": {}, "org.freedesktop.DBus.Introspectable": {}, "org.freedesktop.DBus.Properties": {}, }, "/org/bluez/hci3": { "org.bluez.Adapter1": { "Address": "00:1A:7D:DA:71:05", "AddressType": "public", "Alias": "homeassistant", "Class": 2883584, "Discoverable": False, "DiscoverableTimeout": 180, "Discovering": True, "Modalias": "usb:v1D6Bp0246d053F", "Name": "homeassistant", "Pairable": False, "PairableTimeout": 0, "Powered": True, "Roles": ["central", "peripheral"], "UUIDs": [ "0000110e-0000-1000-8000-00805f9b34fb", "0000110a-0000-1000-8000-00805f9b34fb", "00001200-0000-1000-8000-00805f9b34fb", "0000110b-0000-1000-8000-00805f9b34fb", "00001108-0000-1000-8000-00805f9b34fb", "0000110c-0000-1000-8000-00805f9b34fb", "00001800-0000-1000-8000-00805f9b34fb", "00001801-0000-1000-8000-00805f9b34fb", "0000180a-0000-1000-8000-00805f9b34fb", "00001112-0000-1000-8000-00805f9b34fb", ], }, "org.bluez.GattManager1": {}, "org.bluez.LEAdvertisingManager1": { "ActiveInstances": 0, "SupportedIncludes": [ "tx-power", "appearance", "local-name", ], "SupportedInstances": 5, }, "org.bluez.Media1": {}, "org.bluez.NetworkServer1": {}, "org.freedesktop.DBus.Introspectable": {}, "org.freedesktop.DBus.Properties": {}, }, "/org/bluez/hci1/any": {}, "/org/bluez/hci0/dev_54_D2_72_AB_35_95": { "org.freedesktop.DBus.Introspectable": {}, "org.bluez.Device1": { "Address": "54:D2:72:AB:35:95", "AddressType": "public", "Name": "Nuki_1EAB3595", "Alias": "Nuki_1EAB3595", "Paired": False, "Trusted": False, "Blocked": False, "LegacyPairing": False, "RSSI": -78, "Connected": False, "UUIDs": [], "Adapter": "/org/bluez/hci0", "ManufacturerData": { "76": b"\\x02\\x15\\xa9.\\xe2\\x00U\\x01\\x11\\xe4\\x91l\\x08\\x00 \\x0c\\x9af\\x1e\\xab5\\x95\\xc4" }, "ServicesResolved": False, "AdvertisingFlags": { "__type": "", "repr": "bytearray(b'\\x06')", }, }, "org.freedesktop.DBus.Properties": {}, }, "/org/bluez/hci1/dev_54_D2_72_AB_35_95": { "org.freedesktop.DBus.Introspectable": {}, "org.bluez.Device1": { "Address": "54:D2:72:AB:35:95", "AddressType": "public", "Name": "Nuki_1EAB3595", "Alias": "Nuki_1EAB3595", "Paired": False, "Trusted": False, "Blocked": False, "LegacyPairing": False, "RSSI": -100, "Connected": False, "UUIDs": [], "Adapter": "/org/bluez/hci0", "ManufacturerData": { "76": b"\\x02\\x15\\xa9.\\xe2\\x00U\\x01\\x11\\xe4\\x91l\\x08\\x00 \\x0c\\x9af\\x1e\\xab5\\x95\\xc4" }, "ServicesResolved": False, "AdvertisingFlags": { "__type": "", "repr": "bytearray(b'\\x06')", }, }, "org.freedesktop.DBus.Properties": {}, }, } ], message_type=MessageType.METHOD_RETURN, ) ), ) class MockUSBDevice(USBDevice): def __init__(self, *args, **kwargs): self.manufacturer = "XTech" self.product = "Bluetooth 4.0 USB Adapter" self.vendor_id = "0a12" self.product_id = "0001" pass class MockBluetoothDevice(USBBluetoothDevice): def __init__(self, *args, **kwargs): self.usb_device = MockUSBDevice() pass def setup(self, *args, **kwargs): pass with patch("platform.system", return_value="Linux"), patch( "bluetooth_adapters.dbus.MessageBus", MockMessageBus ), patch( "bluetooth_adapters.systems.linux.USBBluetoothDevice", MockBluetoothDevice ): bluetooth_adapters = get_adapters() await bluetooth_adapters.refresh() assert bluetooth_adapters.default_adapter == "hci0" assert bluetooth_adapters.history == { "54:D2:72:AB:35:95": AdvertisementHistory( device=ANY, advertisement_data=ANY, source="hci0" ) } # hci0 should show # hci1 is empty so it should not be in the list # hci2 should not show as 00:00:00:00:00:00 are filtered downstream now # hci3 should show assert bluetooth_adapters.adapters == { "hci0": { "address": "00:1A:7D:DA:71:04", "hw_version": "usb:v1D6Bp0246d053F", "manufacturer": "XTech", "passive_scan": False, "product": "Bluetooth 4.0 USB Adapter", "product_id": "0001", "sw_version": "homeassistant", "vendor_id": "0a12", }, "hci2": { "address": "00:00:00:00:00:00", "hw_version": "usb:v1D6Bp0246d053F", "manufacturer": "XTech", "passive_scan": False, "product": "Bluetooth 4.0 USB Adapter", "product_id": "0001", "sw_version": "homeassistant", "vendor_id": "0a12", }, "hci3": { "address": "00:1A:7D:DA:71:05", "hw_version": "usb:v1D6Bp0246d053F", "manufacturer": "XTech", "passive_scan": False, "product": "Bluetooth 4.0 USB Adapter", "product_id": "0001", "sw_version": "homeassistant", "vendor_id": "0a12", }, } @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_adapters_linux_uart(): """Test get_adapters with uart devices.""" class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): return AsyncMock( disconnect=MagicMock(), call=AsyncMock( return_value=MagicMock( body=[ { "/other": {}, "/org/bluez/hci0": { "org.bluez.Adapter1": { "Address": "00:1A:7D:DA:71:04", "AddressType": "public", "Alias": "homeassistant", "Class": 2883584, "Discoverable": False, "DiscoverableTimeout": 180, "Discovering": True, "Modalias": "usb:v1D6Bp0246d053F", "Name": "homeassistant", "Pairable": False, "PairableTimeout": 0, "Powered": True, "Roles": ["central", "peripheral"], "UUIDs": [ "0000110e-0000-1000-8000-00805f9b34fb", "0000110a-0000-1000-8000-00805f9b34fb", "00001200-0000-1000-8000-00805f9b34fb", "0000110b-0000-1000-8000-00805f9b34fb", "00001108-0000-1000-8000-00805f9b34fb", "0000110c-0000-1000-8000-00805f9b34fb", "00001800-0000-1000-8000-00805f9b34fb", "00001801-0000-1000-8000-00805f9b34fb", "0000180a-0000-1000-8000-00805f9b34fb", "00001112-0000-1000-8000-00805f9b34fb", ], }, "org.bluez.GattManager1": {}, "org.bluez.LEAdvertisingManager1": { "ActiveInstances": 0, "SupportedIncludes": [ "tx-power", "appearance", "local-name", ], "SupportedInstances": 5, }, "org.bluez.Media1": {}, "org.bluez.NetworkServer1": {}, "org.freedesktop.DBus.Introspectable": {}, "org.freedesktop.DBus.Properties": {}, }, "/org/bluez/hci1": {}, "/org/bluez/hci2": { "org.bluez.Adapter1": { "Address": "00:00:00:00:00:00", "AddressType": "public", "Alias": "homeassistant", "Class": 2883584, "Discoverable": False, "DiscoverableTimeout": 180, "Discovering": True, "Modalias": "usb:v1D6Bp0246d053F", "Name": "homeassistant", "Pairable": False, "PairableTimeout": 0, "Powered": True, "Roles": ["central", "peripheral"], "UUIDs": [ "0000110e-0000-1000-8000-00805f9b34fb", "0000110a-0000-1000-8000-00805f9b34fb", "00001200-0000-1000-8000-00805f9b34fb", "0000110b-0000-1000-8000-00805f9b34fb", "00001108-0000-1000-8000-00805f9b34fb", "0000110c-0000-1000-8000-00805f9b34fb", "00001800-0000-1000-8000-00805f9b34fb", "00001801-0000-1000-8000-00805f9b34fb", "0000180a-0000-1000-8000-00805f9b34fb", "00001112-0000-1000-8000-00805f9b34fb", ], }, "org.bluez.GattManager1": {}, "org.bluez.LEAdvertisingManager1": { "ActiveInstances": 0, "SupportedIncludes": [ "tx-power", "appearance", "local-name", ], "SupportedInstances": 5, }, "org.bluez.Media1": {}, "org.bluez.NetworkServer1": {}, "org.freedesktop.DBus.Introspectable": {}, "org.freedesktop.DBus.Properties": {}, }, "/org/bluez/hci3": { "org.bluez.Adapter1": { "Address": "00:1A:7D:DA:71:05", "AddressType": "public", "Alias": "homeassistant", "Class": 2883584, "Discoverable": False, "DiscoverableTimeout": 180, "Discovering": True, "Modalias": "usb:v1D6Bp0246d053F", "Name": "homeassistant", "Pairable": False, "PairableTimeout": 0, "Powered": True, "Roles": ["central", "peripheral"], "UUIDs": [ "0000110e-0000-1000-8000-00805f9b34fb", "0000110a-0000-1000-8000-00805f9b34fb", "00001200-0000-1000-8000-00805f9b34fb", "0000110b-0000-1000-8000-00805f9b34fb", "00001108-0000-1000-8000-00805f9b34fb", "0000110c-0000-1000-8000-00805f9b34fb", "00001800-0000-1000-8000-00805f9b34fb", "00001801-0000-1000-8000-00805f9b34fb", "0000180a-0000-1000-8000-00805f9b34fb", "00001112-0000-1000-8000-00805f9b34fb", ], }, "org.bluez.GattManager1": {}, "org.bluez.LEAdvertisingManager1": { "ActiveInstances": 0, "SupportedIncludes": [ "tx-power", "appearance", "local-name", ], "SupportedInstances": 5, }, "org.bluez.Media1": {}, "org.bluez.NetworkServer1": {}, "org.freedesktop.DBus.Introspectable": {}, "org.freedesktop.DBus.Properties": {}, }, "/org/bluez/hci1/any": {}, "/org/bluez/hci0/dev_54_D2_72_AB_35_95": { "org.freedesktop.DBus.Introspectable": {}, "org.bluez.Device1": { "Address": "54:D2:72:AB:35:95", "AddressType": "public", "Name": "Nuki_1EAB3595", "Alias": "Nuki_1EAB3595", "Paired": False, "Trusted": False, "Blocked": False, "LegacyPairing": False, "RSSI": -78, "Connected": False, "UUIDs": [], "Adapter": "/org/bluez/hci0", "ManufacturerData": { "76": b"\\x02\\x15\\xa9.\\xe2\\x00U\\x01\\x11\\xe4\\x91l\\x08\\x00 \\x0c\\x9af\\x1e\\xab5\\x95\\xc4" }, "ServicesResolved": False, "AdvertisingFlags": { "__type": "", "repr": "bytearray(b'\\x06')", }, }, "org.freedesktop.DBus.Properties": {}, }, "/org/bluez/hci1/dev_54_D2_72_AB_35_95": { "org.freedesktop.DBus.Introspectable": {}, "org.bluez.Device1": { "Address": "54:D2:72:AB:35:95", "AddressType": "public", "Name": "Nuki_1EAB3595", "Alias": "Nuki_1EAB3595", "Paired": False, "Trusted": False, "Blocked": False, "LegacyPairing": False, "RSSI": -100, "Connected": False, "UUIDs": [], "Adapter": "/org/bluez/hci0", "ManufacturerData": { "76": b"\\x02\\x15\\xa9.\\xe2\\x00U\\x01\\x11\\xe4\\x91l\\x08\\x00 \\x0c\\x9af\\x1e\\xab5\\x95\\xc4" }, "ServicesResolved": False, "AdvertisingFlags": { "__type": "", "repr": "bytearray(b'\\x06')", }, }, "org.freedesktop.DBus.Properties": {}, }, } ], message_type=MessageType.METHOD_RETURN, ) ), ) class MockUARTDevice(UARTDevice): def __init__(self, *args, **kwargs): self.manufacturer = "XTech" self.product = "Bluetooth 4.0 USB Adapter" pass class MockUARTBluetoothDevice(UARTBluetoothDevice): def __init__(self, *args, **kwargs): self.uart_device = MockUARTDevice() pass def setup(self, *args, **kwargs): pass class MockUSBBluetoothDevice(UARTBluetoothDevice): def __init__(self, *args, **kwargs): self.uart_device = MockUARTDevice() pass def setup(self, *args, **kwargs): raise NotAUSBDeviceError with patch("platform.system", return_value="Linux"), patch( "bluetooth_adapters.dbus.MessageBus", MockMessageBus ), patch( "bluetooth_adapters.systems.linux.USBBluetoothDevice", MockUSBBluetoothDevice ), patch( "bluetooth_adapters.systems.linux.UARTBluetoothDevice", MockUARTBluetoothDevice ): bluetooth_adapters = get_adapters() await bluetooth_adapters.refresh() assert bluetooth_adapters.default_adapter == "hci0" assert bluetooth_adapters.history == { "54:D2:72:AB:35:95": AdvertisementHistory( device=ANY, advertisement_data=ANY, source="hci0" ) } # hci0 should show # hci1 is empty so it should not be in the list # hci2 should not show as 00:00:00:00:00:00 are filtered downstream now # hci3 should show assert bluetooth_adapters.adapters == { "hci0": { "address": "00:1A:7D:DA:71:04", "hw_version": "usb:v1D6Bp0246d053F", "manufacturer": "cyber-blue(HK)Ltd", "passive_scan": False, "product": "Bluetooth 4.0 USB Adapter", "product_id": None, "sw_version": "homeassistant", "vendor_id": None, }, "hci2": { "address": "00:00:00:00:00:00", "hw_version": "usb:v1D6Bp0246d053F", "manufacturer": "XTech", "passive_scan": False, "product": "Bluetooth 4.0 USB Adapter", "product_id": None, "sw_version": "homeassistant", "vendor_id": None, }, "hci3": { "address": "00:1A:7D:DA:71:05", "hw_version": "usb:v1D6Bp0246d053F", "manufacturer": "cyber-blue(HK)Ltd", "passive_scan": False, "product": "Bluetooth 4.0 USB Adapter", "product_id": None, "sw_version": "homeassistant", "vendor_id": None, }, } @pytest.mark.asyncio @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) async def test_get_adapters_linux_no_usb_device(): """Test get_adapters.""" class MockMessageBus: def __init__(self, *args, **kwargs): pass async def connect(self): return AsyncMock( disconnect=MagicMock(), call=AsyncMock( return_value=MagicMock( body=[ { "/other": {}, "/org/bluez/hci3": { "org.bluez.Adapter1": { "Address": "00:1A:7D:DA:71:04", "AddressType": "public", "Alias": "homeassistant", "Class": 2883584, "Discoverable": False, "DiscoverableTimeout": 180, "Discovering": True, "Modalias": "usb:v1D6Bp0246d053F", "Name": "homeassistant", "Pairable": False, "PairableTimeout": 0, "Powered": True, "Roles": ["central", "peripheral"], "UUIDs": [ "0000110e-0000-1000-8000-00805f9b34fb", "0000110a-0000-1000-8000-00805f9b34fb", "00001200-0000-1000-8000-00805f9b34fb", "0000110b-0000-1000-8000-00805f9b34fb", "00001108-0000-1000-8000-00805f9b34fb", "0000110c-0000-1000-8000-00805f9b34fb", "00001800-0000-1000-8000-00805f9b34fb", "00001801-0000-1000-8000-00805f9b34fb", "0000180a-0000-1000-8000-00805f9b34fb", "00001112-0000-1000-8000-00805f9b34fb", ], }, "org.bluez.GattManager1": {}, "org.bluez.LEAdvertisingManager1": { "ActiveInstances": 0, "SupportedIncludes": [ "tx-power", "appearance", "local-name", ], "SupportedInstances": 5, }, "org.bluez.Media1": {}, "org.bluez.NetworkServer1": {}, "org.freedesktop.DBus.Introspectable": {}, "org.freedesktop.DBus.Properties": {}, }, "/org/bluez/hci4": {}, "/org/bluez/hci5/any": {}, "/org/bluez/hci3/dev_54_D2_72_AB_35_95": { "org.freedesktop.DBus.Introspectable": {}, "org.bluez.Device1": { "Address": "54:D2:72:AB:35:95", "AddressType": "public", "Name": "Nuki_1EAB3595", "Alias": "Nuki_1EAB3595", "Paired": False, "Trusted": False, "Blocked": False, "LegacyPairing": False, "RSSI": -78, "Connected": False, "UUIDs": [], "Adapter": "/org/bluez/hci3", "ManufacturerData": { "76": b"\\x02\\x15\\xa9.\\xe2\\x00U\\x01\\x11\\xe4\\x91l\\x08\\x00 \\x0c\\x9af\\x1e\\xab5\\x95\\xc4" }, "ServicesResolved": False, "AdvertisingFlags": { "__type": "", "repr": "bytearray(b'\\x06')", }, }, "org.freedesktop.DBus.Properties": {}, }, "/org/bluez/hci1/dev_54_D2_72_AB_35_95": { "org.freedesktop.DBus.Introspectable": {}, "org.bluez.Device1": { "Address": "54:D2:72:AB:35:95", "AddressType": "public", "Name": "Nuki_1EAB3595", "Alias": "Nuki_1EAB3595", "Paired": False, "Trusted": False, "Blocked": False, "LegacyPairing": False, "RSSI": -100, "Connected": False, "UUIDs": [], "Adapter": "/org/bluez/hci0", "ManufacturerData": { "76": b"\\x02\\x15\\xa9.\\xe2\\x00U\\x01\\x11\\xe4\\x91l\\x08\\x00 \\x0c\\x9af\\x1e\\xab5\\x95\\xc4" }, "ServicesResolved": False, "AdvertisingFlags": { "__type": "", "repr": "bytearray(b'\\x06')", }, }, "org.freedesktop.DBus.Properties": {}, }, } ], message_type=MessageType.METHOD_RETURN, ) ), ) class NoMfrMockUSBDevice(USBDevice): def __init__(self, *args, **kwargs): self.manufacturer = None self.product = "Bluetooth 4.0 USB Adapter" self.vendor_id = "0a12" self.product_id = "0001" pass class NoMfrMockBluetoothDevice(USBBluetoothDevice): def __init__(self, *args, **kwargs): self.usb_device = NoMfrMockUSBDevice() pass def setup(self, *args, **kwargs): pass with patch("platform.system", return_value="Linux"), patch( "bluetooth_adapters.dbus.MessageBus", MockMessageBus ), patch( "bluetooth_adapters.systems.linux.USBBluetoothDevice", NoMfrMockBluetoothDevice ): bluetooth_adapters = get_adapters() await bluetooth_adapters.refresh() assert bluetooth_adapters.default_adapter == "hci0" assert bluetooth_adapters.history == { "54:D2:72:AB:35:95": AdvertisementHistory( device=ANY, advertisement_data=ANY, source="hci3" ) } assert bluetooth_adapters.adapters == { "hci3": { "address": "00:1A:7D:DA:71:04", "manufacturer": "cyber-blue(HK)Ltd", "product": "Bluetooth 4.0 USB Adapter", "vendor_id": "0a12", "product_id": "0001", "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": False, "sw_version": "homeassistant", }, } @pytest.mark.asyncio async def test_get_adapters_macos(): """Test get_adapters macos.""" with patch("platform.system", return_value="Darwin"), patch( "platform.release", return_value="18.7.0" ): bluetooth_adapters = get_adapters() await bluetooth_adapters.refresh() assert bluetooth_adapters.default_adapter == "Core Bluetooth" assert bluetooth_adapters.history == {} assert bluetooth_adapters.adapters == { "Core Bluetooth": { "address": "00:00:00:00:00:00", "passive_scan": False, "sw_version": "18.7.0", "manufacturer": "Apple", "product": "Unknown MacOS Model", "vendor_id": "Unknown", "product_id": "Unknown", } } @pytest.mark.asyncio async def test_get_adapters_windows(): """Test get_adapters windows.""" with patch("platform.system", return_value="Windows"), patch( "platform.release", return_value="18.7.0" ): bluetooth_adapters = get_adapters() await bluetooth_adapters.refresh() assert bluetooth_adapters.default_adapter == "bluetooth" assert bluetooth_adapters.history == {} assert bluetooth_adapters.adapters == { "bluetooth": { "address": "00:00:00:00:00:00", "passive_scan": False, "sw_version": "18.7.0", "manufacturer": "Microsoft", "product": "Unknown Windows Model", "vendor_id": "Unknown", "product_id": "Unknown", } } def test_adapter_human_name(): """Test adapter human name.""" assert adapter_human_name("hci0", DEFAULT_ADDRESS) == "hci0" assert adapter_human_name("hci0", "aa:bb:cc:dd:ee:ff") == "hci0 (aa:bb:cc:dd:ee:ff)" def test_adapter_unique_name(): """Test adapter unique name.""" assert adapter_unique_name("hci0", DEFAULT_ADDRESS) == "hci0" assert adapter_unique_name("hci0", "aa:bb:cc:dd:ee:ff") == "aa:bb:cc:dd:ee:ff" def test_adapter_model(): """Test adapter model.""" windows_details = AdapterDetails( { "address": "00:00:00:00:00:00", "passive_scan": False, "sw_version": "18.7.0", "manufacturer": "Microsoft", "product": "Unknown Windows Model", "vendor_id": "Unknown", "product_id": "Unknown", } ) assert adapter_model(windows_details) == "Unknown Windows Model" linux_details = AdapterDetails( { "address": "00:1A:7D:DA:71:04", "manufacturer": "XTech", "product": "Bluetooth 4.0 USB Adapter", "vendor_id": "0a12", "product_id": "0001", "hw_version": "usb:v1D6Bp0246d053F", "passive_scan": False, "sw_version": "homeassistant", } ) assert adapter_model(linux_details) == "Bluetooth 4.0 USB Adapter (0a12:0001)" def test_discovered_device_advertisement_data_to_dict(): """Test discovered device advertisement data to dict.""" result = discovered_device_advertisement_data_to_dict( DiscoveredDeviceAdvertisementData( True, 100, { "AA:BB:CC:DD:EE:FF": ( BLEDevice( address="AA:BB:CC:DD:EE:FF", name="Test Device", details={"details": "test"}, rssi=-50, ), AdvertisementData( local_name="Test Device", manufacturer_data={0x004C: b"\x02\x15\xaa\xbb\xcc\xdd\xee\xff"}, tx_power=50, service_data={ "0000180d-0000-1000-8000-00805f9b34fb": b"\x00\x00\x00\x00" }, service_uuids=["0000180d-0000-1000-8000-00805f9b34fb"], platform_data=("Test Device", ""), rssi=-50, ), ) }, {"AA:BB:CC:DD:EE:FF": 100000}, ) ) assert result == { "connectable": True, "discovered_device_advertisement_datas": { "AA:BB:CC:DD:EE:FF": { "advertisement_data": { "local_name": "Test " "Device", "manufacturer_data": {"76": "0215aabbccddeeff"}, "rssi": -50, "service_data": { "0000180d-0000-1000-8000-00805f9b34fb": "00000000" }, "service_uuids": ["0000180d-0000-1000-8000-00805f9b34fb"], "tx_power": 50, "platform_data": ["Test Device", ""], }, "device": { "address": "AA:BB:CC:DD:EE:FF", "details": {"details": "test"}, "name": "Test " "Device", "rssi": -50, }, } }, "discovered_device_timestamps": {"AA:BB:CC:DD:EE:FF": ANY}, "expire_seconds": 100, } def test_discovered_device_advertisement_data_from_dict(): now = time.time() result = discovered_device_advertisement_data_from_dict( { "connectable": True, "discovered_device_advertisement_datas": { "AA:BB:CC:DD:EE:FF": { "advertisement_data": { "local_name": "Test " "Device", "manufacturer_data": {"76": "0215aabbccddeeff"}, "rssi": -50, "service_data": { "0000180d-0000-1000-8000-00805f9b34fb": "00000000" }, "service_uuids": ["0000180d-0000-1000-8000-00805f9b34fb"], "tx_power": 50, "platform_data": ["Test Device", ""], }, "device": { "address": "AA:BB:CC:DD:EE:FF", "details": {"details": "test"}, "name": "Test " "Device", "rssi": -50, }, } }, "discovered_device_timestamps": {"AA:BB:CC:DD:EE:FF": now}, "expire_seconds": 100, } ) expected_ble_device = BLEDevice( address="AA:BB:CC:DD:EE:FF", name="Test Device", details={"details": "test"}, rssi=-50, ) expected_advertisement_data = AdvertisementData( local_name="Test Device", manufacturer_data={0x004C: b"\x02\x15\xAA\xBB\xCC\xDD\xEE\xFF"}, tx_power=50, service_data={"0000180d-0000-1000-8000-00805f9b34fb": b"\x00\x00\x00\x00"}, service_uuids=["0000180d-0000-1000-8000-00805f9b34fb"], platform_data=("Test Device", ""), rssi=-50, ) assert result is not None out_ble_device = result.discovered_device_advertisement_datas["AA:BB:CC:DD:EE:FF"][ 0 ] out_advertisement_data = result.discovered_device_advertisement_datas[ "AA:BB:CC:DD:EE:FF" ][1] assert out_ble_device.address == expected_ble_device.address assert out_ble_device.name == expected_ble_device.name assert out_ble_device.details == expected_ble_device.details assert out_ble_device.rssi == expected_ble_device.rssi assert out_ble_device.metadata == expected_ble_device.metadata assert out_advertisement_data == expected_advertisement_data assert result == DiscoveredDeviceAdvertisementData( connectable=True, expire_seconds=100, discovered_device_advertisement_datas={ "AA:BB:CC:DD:EE:FF": ( ANY, expected_advertisement_data, ) }, discovered_device_timestamps={"AA:BB:CC:DD:EE:FF": ANY}, ) @pytest.mark.skipif( MessageType is None or get_dbus_managed_objects is None, reason="dbus_fast is not available", ) def test_expire_stale_scanner_discovered_device_advertisement_data(): """Test expire_stale_scanner_discovered_device_advertisement_data.""" now = time.time() data = { "myscanner": DiscoveredDeviceAdvertisementDataDict( { "connectable": True, "discovered_device_advertisement_datas": { "AA:BB:CC:DD:EE:FF": { "advertisement_data": { "local_name": "Test " "Device", "manufacturer_data": {"76": "0215aabbccddeeff"}, "rssi": -50, "service_data": { "0000180d-0000-1000-8000-00805f9b34fb": "00000000" }, "service_uuids": ["0000180d-0000-1000-8000-00805f9b34fb"], "tx_power": 50, "platform_data": ["Test Device", ""], }, "device": { "address": "AA:BB:CC:DD:EE:FF", "details": {"details": "test"}, "name": "Test " "Device", "rssi": -50, }, }, "CC:DD:EE:FF:AA:BB": { "advertisement_data": { "local_name": "Test " "Device Expired", "manufacturer_data": {"76": "0215aabbccddeeff"}, "rssi": -50, "service_data": { "0000180d-0000-1000-8000-00805f9b34fb": "00000000" }, "service_uuids": ["0000180d-0000-1000-8000-00805f9b34fb"], "tx_power": 50, "platform_data": ["Test Device", ""], }, "device": { "address": "CC:DD:EE:FF:AA:BB", "details": {"details": "test"}, "name": "Test " "Device Expired", "rssi": -50, }, }, }, "discovered_device_timestamps": { "AA:BB:CC:DD:EE:FF": now, "CC:DD:EE:FF:AA:BB": now - 100, }, "expire_seconds": 100, } ), "all_expired": DiscoveredDeviceAdvertisementDataDict( { "connectable": True, "discovered_device_advertisement_datas": { "CC:DD:EE:FF:AA:BB": { "advertisement_data": { "local_name": "Test " "Device Expired", "manufacturer_data": {"76": "0215aabbccddeeff"}, "rssi": -50, "service_data": { "0000180d-0000-1000-8000-00805f9b34fb": "00000000" }, "service_uuids": ["0000180d-0000-1000-8000-00805f9b34fb"], "tx_power": 50, "platform_data": ["Test Device", ""], }, "device": { "address": "CC:DD:EE:FF:AA:BB", "details": {"details": "test"}, "name": "Test " "Device Expired", "rssi": -50, }, } }, "discovered_device_timestamps": {"CC:DD:EE:FF:AA:BB": now - 100}, "expire_seconds": 100, } ), } expire_stale_scanner_discovered_device_advertisement_data(data) assert len(data["myscanner"]["discovered_device_advertisement_datas"]) == 1 assert ( "CC:DD:EE:FF:AA:BB" not in data["myscanner"]["discovered_device_advertisement_datas"] ) assert "all_expired" not in data def test_discovered_device_advertisement_data_from_dict_corrupt(caplog): """Test discovered_device_advertisement_data_from_dict with corrupt data.""" now = time.time() result = discovered_device_advertisement_data_from_dict( { "connectable": True, "discovered_device_advertisement_datas": { "AA:BB:CC:DD:EE:FF": { "advertisement_data": { # type: ignore[typeddict-item] "local_name": "Test " "Device", "manufacturer_data": {"76": "0215aabbccddeeff"}, "rssi": -50, "service_data": { "0000180d-0000-1000-8000-00805f9b34fb": "00000000" }, "service_uuids": ["0000180d-0000-1000-8000-00805f9b34fb"], }, "device": { # type: ignore[typeddict-item] "address": "AA:BB:CC:DD:EE:FF", "details": {"details": "test"}, "rssi": -50, }, } }, "discovered_device_timestamps": {"AA:BB:CC:DD:EE:FF": now}, "expire_seconds": 100, } ) assert result is None assert "Error deserializing discovered_device_advertisement_data" in caplog.text