pax_global_header 0000666 0000000 0000000 00000000064 14636265507 0014530 g ustar 00root root 0000000 0000000 52 comment=4031079216754eaf236ebbef2ef0965d6bb615c6
bluetooth-adapters-0.19.3/ 0000775 0000000 0000000 00000000000 14636265507 0015430 5 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/.all-contributorsrc 0000664 0000000 0000000 00000000471 14636265507 0021263 0 ustar 00root root 0000000 0000000 {
"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/.editorconfig 0000664 0000000 0000000 00000000444 14636265507 0020107 0 ustar 00root root 0000000 0000000 # 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/.flake8 0000664 0000000 0000000 00000000056 14636265507 0016604 0 ustar 00root root 0000000 0000000 [flake8]
exclude = docs
max-line-length = 180
bluetooth-adapters-0.19.3/.github/ 0000775 0000000 0000000 00000000000 14636265507 0016770 5 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/.github/FUNDING.yml 0000664 0000000 0000000 00000000036 14636265507 0020604 0 ustar 00root root 0000000 0000000 github: ["bluetooth-devices"]
bluetooth-adapters-0.19.3/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 14636265507 0021153 5 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/.github/ISSUE_TEMPLATE/1-bug_report.md 0000664 0000000 0000000 00000000422 14636265507 0024001 0 ustar 00root root 0000000 0000000 ---
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.md 0000664 0000000 0000000 00000000672 14636265507 0024762 0 ustar 00root root 0000000 0000000 ---
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.yml 0000664 0000000 0000000 00000000766 14636265507 0021631 0 ustar 00root root 0000000 0000000 # 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.toml 0000664 0000000 0000000 00000003515 14636265507 0021133 0 ustar 00root root 0000000 0000000 [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/ 0000775 0000000 0000000 00000000000 14636265507 0021025 5 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/.github/workflows/ci.yml 0000664 0000000 0000000 00000004144 14636265507 0022146 0 ustar 00root root 0000000 0000000 name: 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.yml 0000664 0000000 0000000 00000000534 14636265507 0024376 0 ustar 00root root 0000000 0000000 name: 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.yml 0000664 0000000 0000000 00000001340 14636265507 0024306 0 ustar 00root root 0000000 0000000 name: 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.yml 0000664 0000000 0000000 00000000774 14636265507 0023022 0 ustar 00root root 0000000 0000000 name: 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/.gitignore 0000664 0000000 0000000 00000004066 14636265507 0017426 0 ustar 00root root 0000000 0000000 # 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.yml 0000664 0000000 0000000 00000000306 14636265507 0017516 0 ustar 00root root 0000000 0000000 tasks:
- 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/ 0000775 0000000 0000000 00000000000 14636265507 0016410 5 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/.idea/bluetooth-adapters.iml 0000664 0000000 0000000 00000000515 14636265507 0022722 0 ustar 00root root 0000000 0000000
bluetooth-adapters-0.19.3/.idea/watcherTasks.xml 0000664 0000000 0000000 00000005253 14636265507 0021602 0 ustar 00root root 0000000 0000000
bluetooth-adapters-0.19.3/.idea/workspace.xml 0000664 0000000 0000000 00000002736 14636265507 0021140 0 ustar 00root root 0000000 0000000
bluetooth-adapters-0.19.3/.pre-commit-config.yaml 0000664 0000000 0000000 00000003306 14636265507 0021713 0 ustar 00root root 0000000 0000000 # 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.yml 0000664 0000000 0000000 00000001004 14636265507 0020511 0 ustar 00root root 0000000 0000000 # 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.md 0000664 0000000 0000000 00000030173 14636265507 0017245 0 ustar 00root root 0000000 0000000 # 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.md 0000664 0000000 0000000 00000007466 14636265507 0017676 0 ustar 00root root 0000000 0000000 # 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/LICENSE 0000664 0000000 0000000 00000026121 14636265507 0016437 0 ustar 00root root 0000000 0000000
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.md 0000664 0000000 0000000 00000007041 14636265507 0016711 0 ustar 00root root 0000000 0000000 # Bluetooth Adapters
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.js 0000664 0000000 0000000 00000000364 14636265507 0021414 0 ustar 00root root 0000000 0000000 module.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/ 0000775 0000000 0000000 00000000000 14636265507 0016360 5 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/docs/Makefile 0000664 0000000 0000000 00000001175 14636265507 0020024 0 ustar 00root root 0000000 0000000 # 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.bat 0000664 0000000 0000000 00000001374 14636265507 0017772 0 ustar 00root root 0000000 0000000 @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/ 0000775 0000000 0000000 00000000000 14636265507 0017660 5 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/docs/source/_static/ 0000775 0000000 0000000 00000000000 14636265507 0021306 5 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/docs/source/_static/.gitkeep 0000664 0000000 0000000 00000000000 14636265507 0022725 0 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/docs/source/changelog.md 0000664 0000000 0000000 00000000045 14636265507 0022130 0 ustar 00root root 0000000 0000000 ```{include} ../../CHANGELOG.md
```
bluetooth-adapters-0.19.3/docs/source/conf.py 0000664 0000000 0000000 00000003662 14636265507 0021166 0 ustar 00root root 0000000 0000000 # 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.md 0000664 0000000 0000000 00000000050 14636265507 0022704 0 ustar 00root root 0000000 0000000 ```{include} ../../CONTRIBUTING.md
```
bluetooth-adapters-0.19.3/docs/source/index.md 0000664 0000000 0000000 00000000362 14636265507 0021312 0 ustar 00root root 0000000 0000000 # 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.md 0000664 0000000 0000000 00000000275 14636265507 0022707 0 ustar 00root root 0000000 0000000 # 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.md 0000664 0000000 0000000 00000000150 14636265507 0021302 0 ustar 00root root 0000000 0000000 # Usage
To use this package, import it:
```python
import bluetooth_adapters
```
TODO: Document usage
bluetooth-adapters-0.19.3/example.py 0000664 0000000 0000000 00000000340 14636265507 0017432 0 ustar 00root root 0000000 0000000 import 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.lock 0000664 0000000 0000000 00000305107 14636265507 0017632 0 ustar 00root root 0000000 0000000 # 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.toml 0000664 0000000 0000000 00000005375 14636265507 0020356 0 ustar 00root root 0000000 0000000 [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.json 0000664 0000000 0000000 00000000101 14636265507 0020136 0 ustar 00root root 0000000 0000000 {
"extends": ["github>browniebroke/renovate-configs:python"]
}
bluetooth-adapters-0.19.3/setup.py 0000664 0000000 0000000 00000000370 14636265507 0017142 0 ustar 00root root 0000000 0000000 #!/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/ 0000775 0000000 0000000 00000000000 14636265507 0016217 5 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/src/bluetooth_adapters/ 0000775 0000000 0000000 00000000000 14636265507 0022107 5 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/src/bluetooth_adapters/__init__.py 0000664 0000000 0000000 00000004443 14636265507 0024225 0 ustar 00root root 0000000 0000000 __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.py 0000664 0000000 0000000 00000001316 14636265507 0024265 0 ustar 00root root 0000000 0000000 """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.py 0000664 0000000 0000000 00000000667 14636265507 0023620 0 ustar 00root root 0000000 0000000 """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.py 0000664 0000000 0000000 00000013361 14636265507 0023422 0 ustar 00root root 0000000 0000000 from __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.py 0000664 0000000 0000000 00000004033 14636265507 0024162 0 ustar 00root root 0000000 0000000 from __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.py 0000664 0000000 0000000 00000001425 14636265507 0023746 0 ustar 00root root 0000000 0000000 """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.typed 0000664 0000000 0000000 00000000000 14636265507 0023574 0 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/src/bluetooth_adapters/storage.py 0000664 0000000 0000000 00000020727 14636265507 0024135 0 ustar 00root root 0000000 0000000 """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/ 0000775 0000000 0000000 00000000000 14636265507 0023616 5 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/src/bluetooth_adapters/systems/__init__.py 0000664 0000000 0000000 00000000713 14636265507 0025730 0 ustar 00root root 0000000 0000000 from __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.py 0000664 0000000 0000000 00000014267 14636265507 0025341 0 ustar 00root root 0000000 0000000 from __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.py 0000664 0000000 0000000 00000006246 14636265507 0026162 0 ustar 00root root 0000000 0000000 from __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.py 0000664 0000000 0000000 00000001613 14636265507 0025273 0 ustar 00root root 0000000 0000000 import 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.py 0000664 0000000 0000000 00000001677 14636265507 0025675 0 ustar 00root root 0000000 0000000 from __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.py 0000664 0000000 0000000 00000002060 14636265507 0023434 0 ustar 00root root 0000000 0000000 """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/ 0000775 0000000 0000000 00000000000 14636265507 0016572 5 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/tests/__init__.py 0000664 0000000 0000000 00000000000 14636265507 0020671 0 ustar 00root root 0000000 0000000 bluetooth-adapters-0.19.3/tests/test_init.py 0000664 0000000 0000000 00000206744 14636265507 0021163 0 ustar 00root root 0000000 0000000 import 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